I am using py2neo v4 with Python 3.6 and it's really hard to just get basic things done because there is basically no real instructions or examples. The official documentation " The Py2neo v4 Handbook" at The Py2neo v4 Handbook — The Py2neo v4 Handbook" is basically worthless because it doesn't include any examples of using most of the code and there is no description of how anything works there.
As a concrete example, I've got a node using thisNode = list(matcher.match('Label1'))[0]
And now I want to get the list of nodes connected to this node via a particular kind of relationship.
I think this Cypher query works:
`list(graph.run("MATCH (n1)<-[:PARTOF]-(n2) WHERE ID(n1) = $thisID RETURN n2", parameters={"thisID":thisPrefNode.identity}).data())
And the question is: How do I do that in py2neo without using Cypher?
Since the parent node is already stored in a variable in python, this should be an easy thing to do, but...
And the broader question is where can I go real/learn about how py2neo works? There is barely any assistance on Stack Exchange, most of which is obsolete anyway. Is py2neo basically just not supported? In that case, is there a better way to interface between Python and Neo4j?
For this specific question, I actually found a partial answer in the handbook:
for rel in graph.match((alice, ), r_type="FRIEND"): print(rel.end_node["name"])
but, for my case the direction is in the wrong direction. The syntax is totally opaque, and the only guidance on what to even try to get the desired results is from the minimalist documentation: Parameters: nodes – Sequence or Set of start and end nodes (None means any node); a Set implies a match in any direction
There are no real instructions to explain the syntax nor any other examples for other use cases (like mine).
By trial and error I was able to figure out that for rel in graph.match((None,alice), r_type="FRIEND"): print(rel.start_node["name"])
actually gets me what I want in this case, but by just putting a few more examples in the documentation would clarify the syntax and help many (and especially new) users.
So I guess the answer is "No. There is no real support or documentation for py2neo. And there is no better way for Python to interact with Neo4j. You're on your own and it's totally hopeless." That's pretty sad if you are looking for customers for your technology.
Thank you for responding, but if those are the two best sources you can point me to, then it seems the answer hasn't changed. There is basically no real explanation or useful examples for the vast majority of the commands listed in the documentation, and no resources for getting help learning to use Neo4j or its Python drivers.
I'm working on a pilot project using graph databases in my company, but after my experiences struggling for hours to perform basic functions, I really can't recommend this technology. Not because of what it can and cannot do, but because there is no way to train our engineers to do what they need to do.
I highly recommend committing serious time/effort in some actual instruction and more complete documentation (basically just add a lot more examples covering a lot more use cases to the documentation). You might also considering having people respond to posts on Stack Exchange and/or your own forum.
Regarding the Python bolt driver, the page I linked to should have additional links at the bottom to our drivers documentation which should be applicable for all drivers (including Python)
I just wanted to make sure you've visited that part of the documentation, as it does contain examples of usage.
When using the bolt drivers, most of your Python code around driver usage won't change much, it will just be the Cypher executed (and perhaps parameters passed) and how you consume the results that will change.
Py2Neo isn't officially company supported. It's a community driver, volunteer-based project, and while there are some engineers who contribute to it, it's not something Neo4j the company has adopted for full support/documentation/love+care. We'll do best-effort, and get those involved who contribute to the project, but it is not a Neo4j product.
The Python bolt drivers, however, are under the company's stewardship, so we have official documentation and examples present, which I've linked to.
While it is cheap to bash documention, I'm afraid I agree with the original question, the docs / examples for learning CYPHER are pretty dire and the support for python is meak.
The examples in the guide are either very basic or partial.
As for what to do: Try around in Cypher in the app, ask stackoverflow and the docs and then work your way through it. Then run the code from python or wherever. Not ideal but so far the only way I can make it work.
I'm very impressed with some of Neo4js capabilities and underwhelmed with others, but I guess that is normal for new tech your learning. I have yet to visit a training session and hope that it will help me along.
I'm not in a position to say how the docs should look, as I haven't learned enough about cypher however an example of a particularly irritating page would be: optional match where the examples given are trivial and return null, hardly something useful to learn from. In general most example pages have only very basic queries and to get to understand how to construct more complex ones is more difficult than necessary.
Just as a guide, the answers on SO rarely link to relevant pages in the docs, while in other languages and packages where the docs are more complete they do so for basic questions like the cases just described.
And no I haven't tried the course and if it addresses the above issues that is great, but really that should go into the manual as a lookup not hidden in a course.
Thanks a lot for your feedback, we will improve the docs overall.
Are you in general looking more for intro to new topics or solutions to really tricky questions?
For the latter the knowledge base is a much better fit (Knowledge Base - Knowledge Base) which is more "how-to" oriented. The docs are more reference docs in that regard.
That's interesting, the examples for OPTIONAL MATCH were chosen specifically because they result in no matches and so return null for the newly introduced variables. This is what differentiates an OPTIONAL MATCH from a MATCH...when the pattern is present then it behaves identically, but when it's not present it behaves differently, since MATCH would wipe out the row entirely, and OPTIONAL MATCH instead keeps the row and sets newly introduced variables to null.
In short, you would use OPTIONAL MATCH when there may be certain connected nodes that you want to use or output, but you don't want to disqualify your match so far (don't want to wipe it out) if such a pattern doesn't exist. The thinking when using this is: I want to get this if it exists, but it's fine if it doesn't.
In light of this, does that help you understand what OPTIONAL MATCH is for, and does that suggest any more specific points that would have made the purpose and usage clearer?
There is a fundamental strangeness about the library that my mind doesn't quite get over:
I want to keep track of a node like this:
node = Node("App", name="JackettIndexers")
But apparently you aren't suppose to just have these "unbounded client side" node objects really.
If i want to use this object i have to go through a few hoops for every single node:
def existOrLoad(db, node):
if not db.exists(node):
#unbound shows up as false, try pulling it from db
toreturn = db.nodes.match(list(node.labels)[0], name=node["name"]).first()
if toreturn == None:
return node
return toreturn
return node
As far as I can tell, I cant use any of the features such as "database.merge()" or "database.nodes.pull()" or anything with the Node object unless i already read that object from the database and I have a "bound" object. I might be wrong, but i think the only thing you can use an "unbound" Node is just before the creation of a brand new entry in the database.
So really I can't work with these objects in a general sense, I have to keep all my node structure as disparate combinations of strings to use the matching functions?
"Fundamental strangeness" is definitely an intentional feature
Can you explain what you mean by "I want to keep track of a node"? Node objects, as you describe, can be bound or unbound. This means that the client-side node object you hold in your application may or may not be linked to a server-side node in the database itself. If you create a local node manually, as in your first example, this holds no link to a remote node; what would you expect it to automatically link to?
If you are asserting that the code node = Node("App", name="JackettIndexers") should create a local node variable that is somehow automagically linked to a remote node in your database, there are several problems with that:
How would the object know where to find the database? A new Node does not automatically have access to a Graph object through which to locate a database. If you are working against two separate databases, which one would the node refer to?
What if multiple nodes existed in the database with those criteria? Neo4j doesn't expect node uniqueness of any form by default, so this node definition could potentially refer to any one of several nodes. Indeed, the usage of .first() in your second example indicates exactly this: you are picking the first match you happen to find from N possible matches.
Internally, the binding information holds two things: a reference to a Graph object and a unique node ID. If these are set to None (as is the case with a new local node by default) then that node is considered unbound. Retrieving a node from a server operation will give you a bound node.
Being new to py2neo, I'm trying to understand its concepts. Regarding bound nodes, I did the following experiment:
make a local node: n = Node('Person', name='Alice')
bind it: graph.create(n) (pushing it does not seem to create a remote node; then what is push() for?)
make a change to the local node: n.add_label('Girl')
check whether the new label is present on the remote node (using web access to the database): it's not; so that's what I should use push() for, I suppose
getting the remote node and binding it to another python variable: a2 = graph.nodes.match("Person", name="Alice").first()
At this point, I would expect the local node a and the node a2 obtained by the match operation to be different. But they are the same: the match operation has erased the new Girl label of a. This is surprising to me: why would match() do this? I would expect that (only) pull() has this behavior.
like many of the responses above I just tried to use py2neo and got stuck on some of the most basic use cases from the handbook. While the API looks very pythonic and simple, py2neo is just not usable.
I think the best solution is to write cypher queries in the neo4j app, then transpose them into python functions wrapped with the official neo4j bolt driver.
That way you learn the cypher query language, which is really powerful.