davebshow / goblin

A Python 3.5 rewrite of the TinkerPop 3 OGM Goblin
Other
93 stars 21 forks source link

Question about using a multi-value/set VertexProperty #106

Closed marciodebarros closed 6 years ago

marciodebarros commented 6 years ago

I have a Vertex class which defines a property (ip_address) as multi-valued set:

class Session(element.Vertex):
    session_id = properties.Property(properties.String)
    ip_address = element.VertexProperty(properties.String, card=Cardinality.set_)
    start = properties.Property(properties.Float)
    end = properties.Property(properties.Float)
    timeout = properties.Property(properties.Float)

   def end_session_at(self, end_datetime):
        self.end = end_datetime
        self.save()

    def timeout_at(self, timeout_datetime):
        self.timeout = timeout_datetime
        self.save()

    @property
    def serialize(self):
        return {
            '_id': self.id,
            'session_id': self.session_id,
            'ip_addresses': json.dumps(self.ip_address).replace("u\'","\'"),
            'start': myconverter(self.start),
            'end': myconverter(self.end),
            'timeout': myconverter(self.timeout_at)
        }

At first when I was trying to insert data into it using the following code:

def set_session_properties(session, ip_address, session_start, session_end, session_timeout):
    if ip_address is not None:
        if session.ip_address is None:
            session.ip_address.add(str(ip_address))
        if (str(ip_address) not in session.ip_address):
            session.ip_address.add(str(ip_address))
        if session_start is not None and session.start is None:
            session.start = session_start
        if session_end is not None and session.end is None:
            session.end = session_end
        if session_timeout is not None and session.time_out is None:
            session.time_out = session_timeout
    return session

I was getting an exception saying that "NoneType object has not attribute add". I fixed that error by adding before the call to the ".add()" method.

session.ip_address = set()
session.ip_address.add(str(ip_address))

In the same code, the purpose of the next if statement is to add a second and different ip address after the first one is entered. However the validate to check if the IP address is in the set fails and it gets added twice, and when I run the code in debug mode I am noticing that the same ip address is getting added twice, and I am getting the following exception:

aiogremlin.exception.GremlinServerError: 500: Key is defined for SINGLE cardinality which conflicts with specified: set

So my questions at this time are:

  1. Do I have to instantiate the set() before I can add any element to the VertexProperty ?
  2. I am assuming that I am getting the error because the same value has already been inserted before ??? So how can I check if a value has already add before I add it again, or how can I avoid the duplication?

Thank you in advance for any help.

--MD

davebshow commented 6 years ago

Hi @marciodebarros. I am super busy this week and I haven't looked closely at this, but I can maybe get you started. Yes, a set card property in goblin expects to be assigned a set. After the set is assigned, then you can add. You should be able to initialize with say, a string literal, and it will wrap it up in a set for you.

As far as the doubled IPs in the set, you shouldn't be able to do that. I will look more closely at this.

The server error looks like you are jusing Janus graph and you have not defined a set card property in your schema...this is a requirement. If you are not using Janus, please let me know.

marciodebarros commented 6 years ago

Hi @davebshow I am indeed using Janus. Regarding the schema, I haven't defined one as I was hoping that would get defined implicitly. Is this something that Goblin currently supports or will I need to do from Gremlin console ? Thank you.

davebshow commented 6 years ago

Made any progress here? You can define the schema either in the gremlin console or by submitting a string using the client provided by aiogremlin. Automatic schema creation does not work (AFAIK) with multi-card properties

marciodebarros commented 6 years ago

Hi Dave. Yes, I defined the schema using the console, but now I am running into a new (expected) issue which I need to resolve before I can validate the .set() issue. Now because I have defined a Unique constraint on some of my Vertices, when I try to add a existing vertex, I get an constraint violation error. In an OGM I was using for Neo4j there was an "add or update" method which would handle that. Does TinkerPop/JanusGraph have a similar option, or would I have to implement it myself? Would this be something that you would eventually consider for implementation on Goblin ?

kevgliss commented 6 years ago

I'm also interested in an create_or_update functionality it looks like this can be accomplished in one go:

e.g.

g.v('userId').fold().coalesce(unfold(), addV('users').property('id', 'userId'))

But I am not sure how to best do this via goblin's session.

davebshow commented 6 years ago

Do you mean that you would like to issue a traversal like that using Goblins session? Or integrate that functionality into Goblin? The traversal should be straightforward:

# all required imports +
from gremlin_python import statics
# loading statics enables __.unfold() to be unfold() etc.
statics.load_statics(globals())

#... in async function

session = await app.session()
await session.g.V('userId').fold().coalesce(unfold(), addV('users').property('id', 'userId')).iterate()

That is untested, but something like that should work.