I am now trying to learn how to connect to Neo4j server and run Cypher queries on it using Bulbflow from Python. And the thing I do not understand is the difference between two possibilities to connect to the neo4j server:
1) Graph
from bulbs.neo4jserver import Graph
g = Graph()
2) Neo4jClient
from bulbs.neo4jserver import Neo4jClient
client = Neo4jClient()
Could anyone please explain the conceptual difference here?
And which way is it better to choose if I then want to execute (quite a lot of) Cypher queries against the server and ultimately in parallel?
PS: I do not have enough reputation to create a tag "bulbflow" for this question :)
Bulbs supports three different graph database servers -- Neo4j Server, Rexster, and now Titan.
Code specific to each backend server is contained within its own Python package (directory). You should see directories for: neo4jserver, rexster, titan:
- Bulbs Source Code: https://github.com/espeed/bulbs/tree/master/bulbs
Neo4jClient
is the low-level adapter for Neo4j Server -- you usually don't need to use this directly unless you are doing custom stuff -- use the high-level Graph class instead.
See the Bulbs docs for...
- Neo4j Server Client: http://bulbflow.com/docs/api/bulbs/neo4jserver/client/
- Neo4j Server Graph: http://bulbflow.com/docs/api/bulbs/neo4jserver/graph/
The Bulbs Quickstart guide provides examples on using the Graph
interface:
- Bulbs Quickstart: http://bulbflow.com/quickstart/
However, your Bulbs objects always have access to the low-level client when you need it via the _client
var.
Lightbulb is an example app I created to show how to use and customize Bulbs models -- it's a Python blog engine that uses Git for source control and a graph database for persistence.
- Lightbulb: https://github.com/espeed/lightbulb
Lightbulb was originally designed for use with the free Neo4j Heroku Add On, but both Bulbs and Lightbulb make heavy use of Gremlin, and the Neo4j Heroko Add On no longer offers Gremlin in the free edition.
The Lightbulb model file contains a heavily customized Entry
model and a custom Graph
class -- the Entry
model makes use of the low-level client:
- Lightbulb Model: https://github.com/espeed/lightbulb/blob/master/lightbulb/model.py
As you can see in the Entry
model, I have access to the low-level client via the _client
var, and I use it to get a Gremlin script from the scripts
library and then again to execute the Gremlin script.
Here's the code for the save_blog_entry
Gremlin script used by the Entry model:
- Lightbulb Gremlin Scripts: https://github.com/espeed/lightbulb/blob/master/lightbulb/gremlin.groovy
NOTE: There is only one Gremlin script in the file, but it is large, contains
several operations, and everything is wrapped in a transaction.
Putting all the operations into a single Gremlin script allows you to
do everything in one transactional request, rather than having the
overhead of sending multiple requests to the server.
Unless you are doing something like customizing a model, you would normally use the scripts
object and the gremlin
object stored on the graph
object:
>>> from bulbs.neo4jserver import Graph
>>> g = Graph()
>>> script = g.scripts.get('some_script')
>>> params = dict(name="James", city="Dallas")
>>> g.gremlin.execute(script, params)
See the Bulbs Neo4j Gremlin docs...
- Neo4j Gremlin: http://bulbflow.com/docs/api/bulbs/neo4jserver/gremlin/
Likewise, when you want to execute a Neo4j Cypher query, use the cypher
object stored on the graph
object.
There are three Cypher methods (unfortunately these aren't documented on the website yet):
g.cypher.query()
: Used when returning a list of nodes/relationships -- it will initialize them to objects.
g.cypher.table()
: Used when returning Cypher table data.
g.cypher.exectue()
: Used when returning arbitrary data -- it returns a generic Response
object.
You can look at the source code to see how they work...
- Cypher Source: https://github.com/espeed/bulbs/blob/master/bulbs/neo4jserver/cypher.py
Here are some examples of using the Cypher query() method (the query simply returns a relationship):
>>> from bulbs.neo4jserver import Graph
>>> g = Graph()
>>> query = "start a = relationship({eid}) return a"
>>> params = dict(eid=123)
>>> edges = g.cypher.query(query, params)
The query method automatically initializes elements to their type. If you created the element as a custom model, Bulbs will try to initialize it to the specific type, otherwise it will default to a generic Vertex
or Edge
.
Note that the Bulbs Cypher query() method returns an iterator.
You can loop over the iterator...
>>> from bulbs.neo4jserver import Graph
>>> g = Graph()
>>> query = "start a = relationship({eid}) return a"
>>> params = dict(eid=123)
>>> edges = g.cypher.query(query, params)
>>> for edge in edges: print edge
...or convert it to a list...
>>> from bulbs.neo4jserver import Graph
>>> g = Graph()
>>> query = "start a = relationship({eid}) return a"
>>> params = dict(eid=123)
>>> edges = g.cypher.query(query, params)
>>> list(edges)
...or get the next item...
>>> from bulbs.neo4jserver import Graph
>>> g = Graph()
>>> query = "start a = relationship({eid}) return a"
>>> params = dict(eid=123)
>>> edges = g.cypher.query(query, params)
>>> edges.next()
Please let me know if you have any more questions.