0

I am using graph-object mapping where I have 2 objects to represent 2 nodes. I want to have properties for the relationships also, how do I define a class for relationship with properties? Here, I saw an example but that is without using objects.

my code looks like this :

class Intent(GraphObject):
   name = Property()
   message = RelatedTo("Message", "HAS")

class Message(GraphObject):
   name = Property()
   owner = RelatedFrom("Intent","HAS")

graph = Graph(password='1234')
intent = Intent()
intent.name = "ABC"
complete_intent = graph.pull(intent)

If I want to have some properties for the relation "HAS", how do I define the class HAS? and what should be the base class from where to inherit (like GraphObject for nodes, is there something for relations?)

Stanislav Kralin
  • 11,070
  • 4
  • 35
  • 58
Sharvari Nagesh
  • 293
  • 3
  • 17

1 Answers1

1

The current version of py2neo doesn't allow for 'relationship objects', which would allow you to put parameters onto a relationship in the standard OOP-y way.

The OGM is very useful for getting started on a project, and for quick insert / retrieval transactions, but for most more complex transactions, you will want to write cypher to achieve your objective.

However, frequently you may wish to organize these queries on a GraphObject, and sometimes you can extend GraphObject to add a method to provide some required functionality.

In your use case, you may wish to add some methods like the below to get or set your relationship properties. I've written a quick demo for retrieving the properties of the relationship, you could add a similar method for setting the relationship, too.

from py2neo.ogm import (GraphObject,
                        INCOMING,
                        OUTGOING,
                        Property,
                        RelatedFrom,
                        RelatedTo,)
# this function makes this kind of thing tonnes easier
from py2neo.types import remote


class GraphObjectWithRelProps(GraphObject):
    """Simple extension to add get method for a relationship
    between self and another node"""

    @staticmethod
    def _get_relationship(source, target, relationship):
        """Returns the relationship connecting source and target as set
        in the class definition.
        Copy this method, adding an argument for the params
        and altering the cypher to add a set function

        Only works if two classes are rleated by only one relationship

        :param source: GraphObject instance - the starting node
        :param target: GraphObject instance - the ending node
        :param relationship: str name of the relationship on source"""

        # get relationship pattern
        rel = getattr(source, relationship)
        # the pattern is calculated for us on this object
        pattern = rel._RelatedObjects__relationship_pattern

        # basic cypher query
        q = ('MATCH {0} '
             'WHERE id(a) = $sId AND id(b) = $tId '
             'RETURN _').format(pattern)
        # for a set operation you would add 
        # DELETE _ CREATE {0} SET _ += $relParams
        # where $relParams is supplied as a dict with your params
        # you could also add a patch method using _ += $relParams
        # the remote function allows us to get a reference to the graph
        graph = remote(source.__ogm__.node).graph
        # as well as the node IDs of the GraphObject
        params = {
            'sId': remote(source.__ogm__.node)._id,
            'tId': remote(target.__ogm__.node)._id,
        }

        return graph.run(q, params).evaluate()


class Intent(GraphObjectWithRelProps):
    name = Property()
    message = RelatedTo("Message", "HAS")

    def get_message_rel(self, n):
        graph = remote(self.__ogm__.node).graph
        return self._get_relationship(graph, self, n, 'message')


class Message(GraphObjectWithRelProps):
    name = Property()
    owner = RelatedFrom("Intent","HAS")

    def get_owner_rel(self, n):
        graph = remote(self.__ogm__.node).graph
        return self._get_relationship(graph, self, n, 'owner')
Dom Weldon
  • 1,728
  • 1
  • 12
  • 24
  • Thanks Dom Weldon. Will try this. One more question, will relationships be supported as objects in future? – Sharvari Nagesh Nov 23 '17 at 02:16
  • So the latest update on the future of py2neo is best taken from this issue https://github.com/technige/py2neo/issues/631 – Dom Weldon Nov 23 '17 at 23:38
  • If I were to add my hot take, py2neo and the OGM is an excellent tool for getting to grips with graphs if you're familiar with python and/or object relational mapping. If you want to get serious with graphs and use them for analysis or production, you're going to need to write cypher. It's a great language, and is open source, get to grips with it and you'll be away. – Dom Weldon Nov 23 '17 at 23:42
  • Using cypher solves one of the main problems of the OGM (which also exists for ORMs), if you're doing something complex or data heavy it puts crazy load on the db because it's not designed for these environments, and you have no control of the queries it sends to the graph to optimize them. – Dom Weldon Nov 23 '17 at 23:46
  • From a python perspective that means (in my experience) mixing lots of cypher and python. My answer to that problem is `pip install oopycql` https://github.com/DomWeldon/oopycql/blob/master/README.md – Dom Weldon Nov 23 '17 at 23:49
  • But that's just one solution, get involved in the community and become part of shaping python and neo4j integration! – Dom Weldon Nov 23 '17 at 23:50
  • Thanks. I tried the non OGM version and so far it is working better for me. Mainly because every node can have a different relation. In OGM, all the relations have to be pre-defined. Where as in non OGM version of py2neo, i can specify the relation name dynamically and fetch the end node. Thanks for all your time and help :) – Sharvari Nagesh Nov 24 '17 at 05:00