Search code examples
memgraphdb

How get all the connected nodes of a starting node using gqlalchemy and Memgraph?


I'm going through the how-to-guide on gqlalchemy and I don't see a way to get all the connected nodes of a starting node. I tried supplying only _start_node_id, but I get GQLAlchemyError: Can't load a relationship without a start_node_id and end_node_id. Here’s part fo my code:

john = UserNode(id="1", username="John").save(db)
lang1 = Language(name="en").save(db)
lang2 = Language(name="fr").save(db)

# want all languages which john speaks
Speaks(_start_node_id=john._id).load(db)

# GQLAlchemyError: Can't load a relationship without a start_node_id and end_node_id

Solution

  • A partial loading is currently not supported when using an OGM. You can try this workaround:

    class Nodes:
    """
    mimic neomodel's SomeNode.nodes.all(), SomeNode.nodes.get_or_none
    """
    def init(self, node_class):
    self.node_class = node_class
    
    def all(self):
        root_label = self.node_class.__name__
    
        q = f'MATCH (n:{root_label}) RETURN n'
        res = db.execute_and_fetch(query=q)
        lst = list(map(lambda ob: ob['n'], res))
        return lst
    
    def get_or_none(self, uid: str):
        q = f'MATCH (n {{uid : "{uid}" }}) RETURN n'
        res = db.execute_and_fetch(query=q)
        lst = list(map(lambda ob: ob['n'], res))
        out = lst[0] if len(lst) else None
        return out
        
    class NodeAPI(Node):
    """
    - neomodel's obj.save(), obj.nodes.all(), obj.get_or_none(),
    - my own materialize function, which overwrites the key with actual data
    """
    def save(self):
    return db.save_node(self)
    
    @classmethod
    def nodes(cls):
        return Nodes(cls)
    
    def materialize(self, base_class=None):
        kw = {}
        for key, _ in self.__fields__.items():
            kw[key] = getattr(self, key)
    
        import inspect
        if base_class is None:
            immediate_parent_class = inspect.getmro(self.__class__)[1]
            return immediate_parent_class(**kw)
        else:
            return base_class(**kw)
    
    def __str__(self):
        kw = {}
        for key, _ in self.__fields__.items():
            kw[key] = getattr(self, key)
    
        return f'<{type(self).__name__}, {str(kw)}>'