Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-edges between two nodes #52

Open
spranger opened this issue May 14, 2024 · 4 comments
Open

Multi-edges between two nodes #52

spranger opened this issue May 14, 2024 · 4 comments

Comments

@spranger
Copy link

spranger commented May 14, 2024

Hello, I use the new version 0.5.1 of grand-graph:

from grand import Graph
from grandcypher import GrandCypher
from grand.backends._sqlbackend import SQLBackend

backend=SQLBackend(db_url="sqlite:///demo2.db")
G = Graph(backend=backend)

G.nx.add_node("spranger", type="Person")
G.nx.add_node("meier", type="Person")
G.nx.add_node("krause", type="Person")
G.nx.add_node("Berlin", type="City")
G.nx.add_node("Paris", type="City")
G.nx.add_node("London", type="City")

G.nx.add_edge("spranger", "Paris", type="LIVES_IN")
G.nx.add_edge("krause", "Berlin", type="LIVES_IN")
G.nx.add_edge("meier", "London", type="LIVES_IN")
G.nx.add_edge("spranger", "Berlin", type="BORN_IN")
G.nx.add_edge("krause", "Berlin", type="BORN_IN")
G.nx.add_edge("meier", "Berlin", type="BORN_IN")


result1 = GrandCypher(G.nx).run("""
MATCH (n)-[r]->(c)
WHERE
    n.type == "Person"
    and
    c.type == "City"
    
RETURN n, r, c    
""")

from lark.lexer import Token
n = result1[Token('CNAME', 'n')]
r = result1[Token('CNAME', 'r')]
c = result1[Token('CNAME', 'c')]

for i in range(len(n)):
    print(f"{n[i]} - {r[i].get('type')} -> {c[i]}")

backend.commit()
backend.close()

results in

  • spranger - BORN_IN -> Berlin
  • spranger - LIVES_IN -> Paris
  • meier - BORN_IN -> Berlin
  • meier - LIVES_IN -> London
  • krause - BORN_IN -> Berlin-
  1. The "krause - LIVES_IN -> Berlin" relation is not stored (as any second relation between two same nodes) .
    This might be due to the cause that our "G.nx" doesn't cope with multigraphs.
  2. In plain grandcypher I can query "Match (p:Person)" . How do I do this in my cypher query above?
  3. Would it be a good idea to have the backend and the graph layer (e.g. netwrokx) completely transparent and just run Cypher queries, also for creating nodes and relations?

Kind Regards.
Steffen, the graphologist

@j6k4m8
Copy link
Member

j6k4m8 commented May 14, 2024

Hi @spranger! @jackboyla just added support for multigraphs in Grand-Cypher here: aplbrain/grand-cypher#42

So that's certainly going to help here. I also think (need to do a deeper dive before I can confirm) that this is indeed a limitation of Grand right now; I don't think we support MultiGraphs very well. (Maybe @acthecoder23 this would be a fun project to chew on?)

Just thinking out loud, probably the correct answer is to split Graph/DiGraph/MultiGraph/MultiDiGraph implementations like we briefly discuss in this issue: #44 ...

A short-term solution might be to create a MultiDiGraph in networkx itself (rather than in Grand) and try out @jackboyla's new implementation (which I'll have on PyPI / pip-installable shortly in grand-cypher==0.8.0). If that works for you, then we'll know the next step is support for multigraphs in the Grand backends!

@j6k4m8
Copy link
Member

j6k4m8 commented May 14, 2024

PS: Just got back from a week in Berlin, and totally thinking about adding a LIVES_IN edge there someday... :)

@acthecoder23
Copy link
Contributor

@j6k4m8 I can give this a shot. I'll have to do a little digging but it'd be worthwhile

@jackboyla
Copy link

jackboyla commented May 15, 2024

@spranger also to answer 2. -- to MATCH (a:friend) you need to assign the node label to the __labels__ attribute, in a set. We should probably include this in the README as it's not immediately obvious :) hopefully the multigraph support helps too:

from grandcypher import GrandCypher
import networkx as nx

host = nx.MultiDiGraph()
host.add_node("a", name="Alice", age=30)
host.add_node("b", name="Bob", age=40)
host.add_node("c", name="Charlie", age=50)
host.add_edge("a", "b", __labels__={"friend"}, years=3)  # <---
host.add_edge("a", "c", __labels__={"colleague"}, years=10)
host.add_edge("a", "c", __labels__={"parent"}, duration='forever')
host.add_edge("b", "c", __labels__={"colleague"}, duration=10)
host.add_edge("b", "c", __labels__={"mentor"}, years=2)

qry = """
MATCH (a)-[r]->(b)
RETURN a.name, b.name, r.__labels__, r.duration
"""
res = GrandCypher(host).run(qry)
print(res)

'''
{'a.name': ['Alice', 'Alice', 'Bob'], 
'b.name': ['Bob', 'Charlie', 'Charlie'], 
'r.__labels__': [{0: {'friend'}}, {0: {'colleague'}, 1: {'parent'}}, {0: {'colleague'}, 1: {'mentor'}}], 
'r.duration': [{0: None}, {0: None, 1: 'forever'}, {0: 10, 1: None}]}
'''

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants