Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.
03-15-2022 08:49 AM
I want to iterate two paths of nodes and make comparison on properties. Then update the nodes in the database accordingly.
I connect to neo4j using the python driver and create a read transaction to get the paths. My function returns the nodes, but I don't understand how I can iterate it.
Just as an example, here is a graph with two node types "Box" and "Replace".
Path 1 is (:Box {number:14})-[:NEXT]->(:Box{number:15})-[:NEXT]->(:Box{number:16})
Path 2 is (:Replace{number:14})-[:NEW]->(:Replace{number:15})
I want a loop on Path 1 starting at the last node (number16), going to the first one (number 14). And on each iteration I want to compare the property "fruit" with the last node of path 2.
If the property is the same I want to do some changes (such as delete node or update node etc) which will update the nodes and the relatioships. But I can't seem to find the right way to do this.
If you could point me to a resource which explains loops, I would really appreciate it. Thanks!!
03-16-2022 04:36 AM
Hello and thanks for reaching out.
The part of the docs you are looking for is here: API Documentation — Neo4j Python Driver 4.4
With path.nodes
you can access all nodes of a path. To iterate them in revers you can do for node in reversed(path.nodes):
.
Here is a code snipped that does what you ask for. If there is something unclear please ask.
import neo4j
def query(tx, query_, **params):
result = tx.run(query_, params)
return list(result)
def main():
auth = ('neo4j', 'pass')
with neo4j.GraphDatabase.driver('bolt://localhost:7687', auth=auth) as d:
with d.session() as s:
# make sure db is clean (delete all nodes and relationships)
s.write_transaction(query, "MATCH (n) DETACH DELETE n")
# create the data
s.write_transaction(
query,
"MERGE (:Box {Fruit: 'Apple', Number: 13})-[:NEXT]->"
"(:Box {Fruit: 'Orange', Number: 14})-[:NEXT]->"
"(:Box {Fruit: 'Apple', Number: 15})-[:NEXT]->"
"(:Box {Fruit: 'Orange', Number: 16})-[:NEXT]->"
"(:Box {Fruit: 'Orange', Number: 17})"
)
s.write_transaction(
query,
"MATCH "
" (s:Box {Fruit: 'Apple', Number: 13}),"
" (e:Box {Fruit: 'Orange', Number: 17}) "
"CREATE (s)-[:NEW]->"
"(:Replace {Fruit: 'Orange', Number: 14})-[:NEW]->"
"(:Replace {Fruit: 'Apple', Number: 15})-[:NEW]->"
"(e)"
)
# fetching the two described paths
with d.session() as s:
path1 = s.read_transaction(
query,
"MATCH path = "
"(:Box {Number: 13})-[:NEXT*]->(:Box {Number: 16}) "
"RETURN path"
)
assert len(path1) == 1 or print(path1)
path1 = path1[0]["path"]
path2 = s.read_transaction(
query,
"MATCH path = "
"(:Replace {Number: 14})-[:NEW]->(:Replace {Number: 15}) "
"RETURN path"
)
assert len(path2) == 1 or print(path2)
path2 = path2[0]["path"]
print(path1, path2)
last_node_path2 = path2.nodes[-1]
# traversing the nodes of path1 in reverse order
for node in reversed(path1.nodes):
if node["Fruit"] == last_node_path2["Fruit"]:
print("Cool, let's do things here!", node, last_node_path2)
if __name__ == "__main__":
main()
Note: since reading and the action (print("Cool, let's do things here!"...)
) are not withing one transaction, the change is not atomic. So you might want to consider moving all queries into a single transaction.
03-18-2022 04:55 AM
Thanks, this is very helpful. It to work well if I have one path. But if my MATCH path
query returns more than one path or if I try to reterive a subset of a path using apoc.path.slice
I get the following error:
Traceback (most recent call last):
File "C:\Python39\lib\site-packages\neo4j\data.py", line 139, in index
return self.__keys.index(key)
ValueError: tuple.index(x): x not in tupleDuring handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "F:\test\example.py", line 60, in
main()
File "F:\test\example.py", line 33, in main
path1 = path1[0]["path"]
File "C:\Python39\lib\site-packages\neo4j\data.py", line 97, in __ getitem__
index = self.index(key)
File "C:\Python39\lib\site-packages\neo4j\data.py", line 141, in index
raise KeyError(key)
KeyError: 'path'
My guess is that in the case of slice, the subset of the path is not returned as a path but something else...and in the case of more than one paths being returned by the query,
path1 = path1[0]["path"]
can't handle more than one path.
What would you suggest for this please?
03-18-2022 05:20 AM
Also, if the path return only one node, I get an index out of range error
Traceback (most recent call last):
File "F:\test\exampe.py", line 72, in
main()
File "F:\test\exampe.py", line 58, in main
path2 = path2[0]["path"]
IndexError: list index out of range
03-18-2022 05:40 AM
I think there is a fundamental misconception and I'm guilty of choosing misleading variable names, sorry.
Let me try to clarify. But before I start, I highly recommend running this script with a debugger and exploring the API that way, or alternatively, running it in an interactive environment, e.g., a Python shell or a Jupyter notebook. This way, you can see what keys and things exist inside path1
and path2
when running different queries.
Anyway here goes:
tx.run(query_, params)
returns a Result
object (see docs).list(result)
which will turn this result into a list
of Record
objects (see docs).path1[0]["path"]
, you could do path1[0][0]
.Regarding your last message Loops nodes on a path using python - #4 by sanna.aizad This error does not stem from the line you quoted me on, but from this line path2 = path2[0]["path"]
, which fails for the reason explained above.
All the sessions of the conference are now available online