Skip to content

Instantly share code, notes, and snippets.

@theladyjaye
Created February 19, 2012 21:08
Show Gist options
  • Save theladyjaye/1865786 to your computer and use it in GitHub Desktop.
Save theladyjaye/1865786 to your computer and use it in GitHub Desktop.
Neo4j REST Traversals Approximating embedded syntax for https://github.com/versae/neo4j-rest-client/
# http://docs.neo4j.org/chunked/snapshot/rest-api-traverse.html#rest-api-traversal-returning-nodes-below-a-certain-depth
try:
import simplejson as json
except ImportError:
import json
from neo4jrestclient import client
from neo4jrestclient.request import Request
from neo4jrestclient.request import NotFoundError
from neo4jrestclient.request import StatusException
class Order(object):
BREADTH_FIRST = "breadth_first"
DEPTH_FIRST = "depth_first"
class Uniqueness(object):
NODE_GLOBAL = "node_global"
NONE = "none"
RELATIONSHIP_GLOBAL = "relationship_global"
NODE_PATH = "node_path"
RELATIONSHIP_PATH = "relationship_path"
class RelationshipDirection(object):
ALL = "all"
INCOMING = "in"
OUTGOING = "out"
class Filters(object):
"""Filters answer the question (return true/false)
Evaluation.INCLUDE_AND_CONTINUE
Evaluation.EXCLUDE_AND_CONTINUE
"""
ALL = {"language":"builtin", "name":"all"}
ALL_BUT_START_NODE = {"language":"builtin", "name":"all_but_start_node"}
class PruneEvaluators(object):
"""PruneEvaluators answer the question (return true/false)
Evaluation.INCLUDE_AND_PRUNE
Evaluation.EXCLUDE_AND_PRUNE
"""
pass
class Traverser(object):
NODE = "node"
RELATIONSHIP = "relationship"
PATH = "path"
FULLPATH = "fullpath"
def __init__(self, start_node, data):
self._data = data
self._endpoint = "http://localhost:7474/db/data/node/" + str(start_node.id) + "/traverse"
self._cache = {}
def request(self, return_type):
try:
return self._cache[return_type]
except KeyError:
response, content = Request().post(self._endpoint + '/' + return_type, data=self._data)
if response.status == 200:
results_list = json.loads(content)
self._cache[return_type] = results_list
return results_list
elif response.status == 404:
raise NotFoundError(response.status, "Node or relationship " \
"not found")
raise StatusException(response.status, "Invalid data sent")
@property
def nodes(self):
results = self.request(Traverser.NODE)
return client.Iterable(client.Node, results, "self")
@property
def relationships(self):
results = self.request(Traverser.RELATIONSHIP)
return client.Iterable(client.Relationship, results, "self")
@property
def fullpaths(self):
raise NotImplementedError()
def __iter__(self):
results = self.request(Traverser.PATH)
return client.Iterable(client.Path, results, "self")
class TraversalDescription(object):
"""https://github.com/neo4j/community/blob/master/kernel/src/main/java/org/neo4j/graphdb/traversal/TraversalDescription.java"""
def __init__(self):
self._data = {}
self.uniqueness(Uniqueness.NODE_GLOBAL)
self.max_depth(1)
def uniqueness(self, value):
self._data["uniqueness"] = value
return self
def filter(self, value):
try:
value["language"]
self._data["return_filter"] = value
except KeyError:
self._data["return_filter"] = {"language":"javascript", "body":value}
return self
def prune(self, value, language="javascript"):
try:
value["language"]
self._data["prune_evaluator"] = value
except KeyError:
self._data["prune_evaluator"] = {"language":language, "body":value}
def order(self, value):
self._data["order"] = value
return self
def depthFirst(self, value):
self.order(Order.DEPTH_FIRST)
return self
def breadthFirst(self, value):
self.order(Order.BREADTH_FIRST)
return self
def relationships(self, name, direction=RelationshipDirection.ALL):
self._data["relationships"] = []
self.relationships_append(name, direction)
self.relationships = self.relationships_append
return self
def relationships_append(self, name, direction=RelationshipDirection.ALL):
self._data["relationships"].append({"direction":direction, "type":name})
return self
def max_depth(self, value):
self._data["max_depth"] = value
def traverse(self, start_node):
try:
self._data['prune_evaluator']
del self._data["max_depth"]
except KeyError:
pass
return Traverser(start_node, self._data)
class Traversal(object):
def __init__(self, start_node):
self._description = Traversal.description()
self._start_node = start_node
@property
def description(self):
return self._description
def __iter__(self):
return self._description.traverse(self._start_node)
@theladyjaye
Copy link
Author

Usage

See:
http://docs.neo4j.org/chunked/milestone/python-embedded-reference-traversal.html#_basic_traversals

    traverser = traversals.TraversalDescription()\
                    .relationships(self.relationship_name, traversals.RelationshipDirection.OUTGOING)\
                    .filter(traversals.Filters.ALL_BUT_START_NODE)\
                    .traverse(start_node)

    for node in traverser.nodes:
        print(node)

Would also allow you to extend Traversal object without first initializing the GraphDatabase object.
Again, right now it's traverser = traversals.TraversalDescription(). That does not align with the embedded driver. I would alter GraphDatabase here with a method called: traversal(), which would return a TraversalDescription object.

@theladyjaye
Copy link
Author

added a small bit of caching, 2 seconds

@theladyjaye
Copy link
Author

removed the caching... My bad. the embedded API does not have a close method. The same traverser instance should maintain it's cache.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment