Written by Nigel Small, Senior Engineer at Neo Technology
Py2neo 2.0, Unleashed!
It’s been over three years since I first began work on
py2neo. If memory serves, the first version was written for Neo4j 1.3 and consisted of barely more than a few lines of hastily-thrown-together Python code.
Neo4j has obviously evolved considerably since then and now boasts
labels, a
schemaand
Cypher transactions. Actually, it also boasts Cypher as a core component – back in the day it was merely a plugin!
And so, inevitably, py2neo has to move on as well. I’d previously tacked on some of the new Neo4j 2.x series features but in doing so had introduced a bit more technical debt than I really wanted. A proper rewrite was needed. And so today (after quite a few months) I’m releasing
py2neo 2.0. This brings with it a heavily refactored core, a cleaner API, better performance and a few new idioms.
https://nigelsmall.com/py2neo/2.0-unleashed
So what’s actually changed? Well, some old py2neo 1.6 code will run in 2.0 but not all of it – most code will require a tweak. But there are many benefits from upgrading to the new library so I’ll highlight some of the big changes below…
Naming
The
py2neo.neo4j namespace has been deprecated completely. That is: while it still exists, most references to it can simply be replaced by just
py2neo. And
GraphDatabaseService is now just known as
Graph. So the code that was previously written as…
from py2neo.neo4j import GraphDatabaseService
graph = GraphDatabaseService()
…can now be written as…
from py2neo import Graph
graph = Graph()
That’s a bit cleaner – I hope you’ll agree 🙂
Another namespace change is the introduction of
py2neo.legacy. This contains legacy nodes (those without labels for pre-2.0 server versions), legacy batch classes (batches are now more specific) and legacy indexes (for clarity on indexes, look
here). You might need these bits and bobs if you have some existing code to port over but they can be ignored for anything new.
Constructors
The
Node and
Relationship constructors have now become a lot more useful but have changed their signatures completely; they are therefore
notbackward compatible. This shouldn’t affect many apps as it would have been rare to construct a node directly by URI, but the equivalent of…
node = Node("https://localhost:7474/db/data/node/1")
…is now…
node = Node()
node.bind("https://localhost:7474/db/data/node/1")
This then leaves the constructor free to be used for passing in labels and properties. So, the
Node class can now more comfortably serve as either a concrete or an abstract entity by having a
bound state.
The
Relationship class has undergone a similar transformation and both
Node and
Relationship now expose a
cast classmethod that replaces the
node and
rel functions in 1.6.
Labels
Labels are now (at last!) a first class feature of py2neo. Due to certain limitations in the REST interface, making this so wasn’t straightforward until certain parts of the library’s core had been rewritten. Now though, a node can be created
with labels instead of having to add labels in a separate step:
alice = Node("Person", name="Alice", email="alice@example.com")
bob = Node("Person", name="Bob", email="bob@mail.net")
graph.create(alice, bob)
Some other handy label-related functions have been introduced too:
Uniqueness constraints…
graph.schema.create_uniqueness_constraint("Person", "email")
Merging nodes by label…
graph.merge_one("Person", "email", "carol@somewhere.org")
Finding nodes by label…
for person in graph.find("Person"):
print(person)
One of the biggest changes in py2neo 2.0 is how data is synchorised between client and server. Previously, changes made to node properties (for instance) were sent instantly to the server. It therefore followed that changing several properties triggered several HTTP requests and this (obviously) didn’t scale well.
So, in py2neo 2.0, data is only synchronised on an explicit pull or push. This gives the application developer far more control over the HTTP requests that occur. This conversation can now even be monitored with the handy
watch function.
Cypher
The story wouldn’t of course be complete without mentioning Cypher. Py2neo 2.0 integrates
Cypher in a much cleaner and more intuitive way and it all hinges on the new
Graph.cypher attribute.
It’s easy to
execute a single statement…
from py2neo import Graph
graph = Graph()
results = graph.cypher.execute("MATCH (n:Person) RETURN n")
…or just as easy to use a
transaction…
from py2neo import Graph
graph = Graph()
statement = "MERGE (n:Person {name:{N}}) RETURN n"
tx = graph.cypher.begin()
def add_names(*names):
for name in names:
tx.append(statement, {"N": name})
tx.process()
add_names("Homer", "Marge", "Bart", "Lisa", "Maggie")
add_names("Peter", "Lois", "Chris", "Meg", "Stewie")
tx.commit()
And the REST
And that’s it. Well… except for the
Rel,
Rev and
Path classes, new
serverand
store modules, a handful of
batch changes and a bunch of extensions and command line tools. The full set of documentation can be found
here, so have a browse,
download the package and
get in touch if you need any pointers.
I guess it’s now time for me to start planning the next three years!
Want to learn more about graph databases? Click below to get your free copy of O’Reilly’s Graph Databases ebook and discover how to use graph technologies for your application today.
Download My Ebook