def __init__(self, config=None, db_name="GratefulDeadConcerts"): # This makes is easy to test different DBs #uri = self._get_uri(db_name) or self.default_uri self.configs = dict() self.request = dict() self.type_system = JSONTypeSystem() self.db_name = db_name for command in ['database', 'command']: uri = "%s/%s/%s" % (self.default_uri.rstrip("/"), command, db_name) print(uri) config = Config(uri, username="******", password="******") self.configs[command] = config self.registry = Registry(config) self.request[command] = self.request_class( config, self.type_system.content_type) self.config = self.configs['database'] # OrientDB supports Gremlin so include the Gremlin-Groovy script library self.scripts = GroovyScripts(self.config) # Also include the OrientDB-specific Gremlin-Groovy scripts scripts_file = get_file_path(__file__, "gremlin.groovy") self.scripts.update(scripts_file) # Add it to the registry. This allows you to have more than one scripts # namespace. self.registry.add_scripts("gremlin", self.scripts)
def __init__(self, config=None): self.config = config or Config(self.default_uri) self.registry = Registry(self.config) self.type_system = JSONTypeSystem() self.request = self.request_class(self.config, self.type_system.content_type) # Neo4j supports Gremlin so include the Gremlin-Groovy script library self.scripts = GroovyScripts(self.config) # Also include the Neo4j Server-specific Gremlin-Groovy scripts scripts_file = get_file_path(__file__, "gremlin.groovy") self.scripts.update(scripts_file) # Add it to the registry. This allows you to have more than one scripts namespace. self.registry.add_scripts("gremlin", self.scripts)
def __init__(self,config): self.config = config self.registry = Registry(config) self.scripts = GroovyScripts() self.scripts.update(self._get_scripts_file("gremlin.groovy")) self.registry.add_scripts("gremlin",self.scripts) self.type_system = JSONTypeSystem() self.request = RexsterRequest(config,self.type_system.content_type)
def __init__(self, config=None, db_name=None): # This makes is easy to test different DBs uri = self._get_uri(db_name) or self.default_uri self.config = config or Config(uri) self.registry = Registry(self.config) self.type_system = JSONTypeSystem() self.request = self.request_class(self.config, self.type_system.content_type) # Rexster supports Gremlin so include the Gremlin-Groovy script library self.scripts = GroovyScripts(self.config) # Also include the Rexster-specific Gremlin-Groovy scripts scripts_file = get_file_path(__file__, "gremlin.groovy") self.scripts.update(scripts_file) # Add it to the registry. This allows you to have more than one scripts namespace. self.registry.add_scripts("gremlin", self.scripts)
def __init__(self, config): """ Initializes a resource object. :param root_uri: the base URL of Rexster. """ self.config = config self.registry = Registry(config) self.scripts = Scripts() dir_name = os.path.dirname(__file__) self.scripts.override(get_file_path(dir_name, "gremlin.groovy")) self.registry.add_scripts("gremlin", self.scripts) self.type_system = self._get_type_system() self.request = Neo4jRequest(config, self.type_system.content_type)
def __init__(self,config): """ Initializes a resource object. :param root_uri: the base URL of Rexster. """ self.config = config self.registry = Registry(config) self.scripts = Scripts() dir_name = os.path.dirname(__file__) self.scripts.override(get_file_path(dir_name,"gremlin.groovy")) self.registry.add_scripts("gremlin",self.scripts) self.type_system = self._get_type_system() self.request = Neo4jRequest(config,self.type_system.content_type)
def __init__(self, config=None): self.config = config or Config(self.default_uri) self.registry = Registry(self.config) self.type_system = JSONTypeSystem() self.request = self.request_class(self.config, self.type_system.content_type) # Neo4j supports Gremlin so include the Gremlin-Groovy script library self.scripts = GroovyScripts() # Also include the Neo4j Server-specific Gremlin-Groovy scripts scripts_file = get_file_path(__file__, "gremlin.groovy") self.scripts.update(scripts_file) # Add it to the registry. This allows you to have more than one scripts namespace. self.registry.add_scripts("gremlin", self.scripts)
def __init__(self, config=None, db_name=None): # This makes is easy to test different DBs uri = self._get_uri(db_name) or self.default_uri self.config = config or Config(uri) self.registry = Registry(self.config) self.type_system = JSONTypeSystem() self.request = self.request_class(self.config, self.type_system.content_type) # Rexster supports Gremlin so include the Gremlin-Groovy script library self.scripts = GroovyScripts() # Also include the Rexster-specific Gremlin-Groovy scripts scripts_file = get_file_path(__file__, "gremlin.groovy") self.scripts.update(scripts_file) # Add it to the registry. This allows you to have more than one scripts namespace. self.registry.add_scripts("gremlin", self.scripts)
class RexsterClient(Client): """ Low-level client that sends a request to Rexster and returns a response. :param config: Optional Config object. Defaults to default Config. :type config: bulbs.config.Config :cvar default_uri: Default URI for the database. :cvar request_class: Request class for the Client. :ivar config: Config object. :ivar registry: Registry object. :ivar scripts: GroovyScripts object. :ivar type_system: JSONTypeSystem object. :ivar request: RexsterRequest object. Example: >>> from bulbs.rexster import RexsterClient >>> client = RexsterClient() >>> script = client.scripts.get("get_vertices") >>> response = client.gremlin(script, params=None) >>> result = response.results.next() """ #: Default URI for the database. default_uri = REXSTER_URI request_class = RexsterRequest def __init__(self, config=None, db_name=None): # This makes is easy to test different DBs uri = self._get_uri(db_name) or self.default_uri self.config = config or Config(uri) self.registry = Registry(self.config) self.type_system = JSONTypeSystem() self.request = self.request_class(self.config, self.type_system.content_type) # Rexster supports Gremlin so include the Gremlin-Groovy script library self.scripts = GroovyScripts() # Also include the Rexster-specific Gremlin-Groovy scripts scripts_file = get_file_path(__file__, "gremlin.groovy") self.scripts.update(scripts_file) # Add it to the registry. This allows you to have more than one scripts namespace. self.registry.add_scripts("gremlin", self.scripts) def _get_uri(self, db_name): if db_name is not None: uri = "http://localhost:8182/graphs/%s" % db_name return uri # Gremlin def gremlin(self, script, params=None): """ Executes a Gremlin script and returns the Response. :param script: Gremlin script to execute. :type script: str :param params: Param bindings for the script. :type params: dict :rtype: RexsterResponse """ params = dict(script=script, params=params) return self.request.post(gremlin_path, params) # Vertex Proxy def create_vertex(self, data): """ Creates a vertex and returns the Response. :param data: Property data. :type data: dict :rtype: RexsterResponse """ data = self._remove_null_values(data) return self.request.post(vertex_path, data) def get_vertex(self, _id): """ Gets the vertex with the _id and returns the Response. :param data: Vertex ID. :type data: int :rtype: RexsterResponse """ path = build_path(vertex_path, _id) return self.request.get(path, params=None) def get_all_vertices(self): """ Returns a Response containing all the vertices in the Graph. :rtype: RexsterResponse """ script = self.scripts.get("get_vertices") params = None return self.gremlin(script, params) def update_vertex(self, _id, data): """ Updates the vertex with the _id and returns the Response. :param _id: Vertex ID. :type _id: dict :param data: Property data. :type data: dict :rtype: RexsterResponse """ data = self._remove_null_values(data) path = build_path(vertex_path, _id) return self.request.put(path, data) def delete_vertex(self, _id): """ Deletes a vertex with the _id and returns the Response. :param _id: Vertex ID. :type _id: dict :rtype: RexsterResponse """ path = build_path(vertex_path, _id) return self.request.delete(path, params=None) # Edge Proxy def create_edge(self, outV, label, inV, data={}): """ Creates a edge and returns the Response. :param outV: Outgoing vertex ID. :type outV: int :param label: Edge label. :type label: str :param inV: Incoming vertex ID. :type inV: int :param data: Property data. :type data: dict or None :rtype: RexsterResponse """ data = self._remove_null_values(data) edge_data = dict(_outV=outV, _label=label, _inV=inV) data.update(edge_data) return self.request.post(edge_path, data) def get_edge(self, _id): """ Gets the edge with the _id and returns the Response. :param data: Edge ID. :type data: int :rtype: RexsterResponse """ path = build_path(edge_path, _id) return self.request.get(path, params=None) def get_all_edges(self): """ Returns a Response containing all the edges in the Graph. :rtype: RexsterResponse """ script = self.scripts.get("get_edges") params = None return self.gremlin(script, params) def update_edge(self, _id, data): """ Updates the edge with the _id and returns the Response. :param _id: Edge ID. :type _id: dict :param data: Property data. :type data: dict :rtype: RexsterResponse """ data = self._remove_null_values(data) path = build_path(edge_path, _id) return self.request.put(path, data) def delete_edge(self, _id): """ Deletes a edge with the _id and returns the Response. :param _id: Edge ID. :type _id: dict :rtype: RexsterResponse """ path = build_path(edge_path, _id) return self.request.delete(path, params=None) # Vertex Container def outE(self, _id, label=None, start=None, limit=None): """ Returns the outgoing edges of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: RexsterResponse """ script = self.scripts.get("outE") params = dict(_id=_id, label=label, start=start, limit=limit) return self.gremlin(script, params) def inE(self, _id, label=None, start=None, limit=None): """ Returns the incoming edges of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: RexsterResponse """ script = self.scripts.get("inE") params = dict(_id=_id, label=label, start=start, limit=limit) return self.gremlin(script, params) def bothE(self, _id, label=None, start=None, limit=None): """ Returns the incoming and outgoing edges of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: RexsterResponse """ script = self.scripts.get("bothE") params = dict(_id=_id, label=label, start=start, limit=limit) return self.gremlin(script, params) def outV(self, _id, label=None, start=None, limit=None): """ Returns the out-adjacent vertices of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: RexsterResponse """ script = self.scripts.get("outV") params = dict(_id=_id, label=label, start=start, limit=limit) return self.gremlin(script, params) def inV(self, _id, label=None, start=None, limit=None): """ Returns the in-adjacent vertices of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: RexsterResponse """ script = self.scripts.get("inV") params = dict(_id=_id, label=label, start=start, limit=limit) return self.gremlin(script, params) def bothV(self, _id, label=None, start=None, limit=None): """ Returns the incoming- and outgoing-adjacent vertices of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: RexsterResponse """ script = self.scripts.get("bothV") params = dict(_id=_id, label=label, start=start, limit=limit) return self.gremlin(script, params) # Index Proxy - General def get_all_indices(self): """Returns a list of all the element indices.""" return self.request.get(index_path, params=None) def get_index(self, name): path = build_path(index_path, name) return self.request.get(path, params=None) def delete_index(self, name): """Deletes the index with the index_name.""" path = build_path(index_path, name) return self.request.delete(path, params=None) # Index Proxy - Vertex def create_vertex_index(self, index_name, *args, **kwds): """ Creates a vertex index with the specified params. :param index_name: Name of the index to create. :type index_name: str :rtype: RexsterResponse """ path = build_path(index_path, index_name) index_type = kwds.get("index_type", "manual") index_keys = kwds.get("index_keys", None) params = {"class": "vertex", "type": index_type} if index_keys: params.update({"keys": index_keys}) return self.request.post(path, params) def get_vertex_index(self, index_name): """ Returns the vertex index with the index_name. :param index_name: Name of the index. :type index_name: str :rtype: RexsterResponse """ return self.get_index(index_name) def get_or_create_vertex_index(self, index_name, index_params=None): script = self.scripts.get("get_or_create_vertex_index") params = dict(index_name=index_name, index_params=index_params) resp = self.gremlin(script, params) # assert "MANUAL" in resp.content['results'][0] result = {"name": index_name, "type": "manual", "class": "vertex"} resp.results = RexsterResult(result, self.config) return resp def delete_vertex_index(self, name): """ Deletes the vertex index with the index_name. :param index_name: Name of the index. :type index_name: str :rtype: RexsterResponse """ return self.delete_index(name) # Index Proxy - Edge def create_edge_index(self, name, *args, **kwds): """ Creates a edge index with the specified params. :param index_name: Name of the index. :type index_name: str :rtype: RexsterResponse """ path = build_path(index_path, name) index_type = kwds.get("index_type", "manual") index_keys = kwds.get("index_keys", None) params = {"class": "edge", "type": index_type} if index_keys: params.update({"keys": index_keys}) return self.request.post(path, params) def get_edge_index(self, name): """ Returns the edge index with the index_name. :param index_name: Name of the index. :type index_name: str :rtype: RexsterResponse """ return self.get_index(name) def get_or_create_edge_index(self, index_name, index_params=None): script = self.scripts.get("get_or_create_edge_index") params = dict(index_name=index_name, index_params=index_params) resp = self.gremlin(script, params) # assert "MANUAL" in resp.content['results'][0] result = {"name": index_name, "type": "manual", "class": "edge"} resp.results = RexsterResult(result, self.config) return resp def delete_edge_index(self, name): """ Deletes the edge index with the index_name. :param index_name: Name of the index. :type index_name: str :rtype: RexsterResponse """ self.delete_index(name) # def create_automatic_vertex_index(self,index_name,element_class,keys=None): # keys = json.dumps(keys) if keys else "null" # params = dict(index_name=index_name,element_class=element_class,keys=keys) # script = self.scripts.get('create_automatic_vertex_index',params) # return self.gremlin(script) # def create_indexed_vertex_automatic(self,data,index_name): # data = json.dumps(data) # params = dict(data=data,index_name=index_name) # script = self.scripts.get('create_automatic_indexed_vertex',params) # return self.gremlin(script) # Index Container - General def index_count(self, index_name, key, value): path = build_path(index_path, index_name, "count") params = dict(key=key, value=value) return self.request.get(path, params) def index_keys(self, index_name): path = build_path(index_path, index_name, "keys") return self.request.get(path, params=None) # Index Container - Vertex def put_vertex(self, index_name, key, value, _id): """ Adds a vertex to the index with the index_name. :param index_name: Name of the index. :type index_name: str :param key: Name of the key. :type key: str :param value: Value of the key. :type value: str :param _id: Vertex ID :type _id: int :rtype: RexsterResponse """ # Rexster's API only supports string lookups so convert value to a string path = build_path(index_path, index_name) params = {"key": key, "value": str(value), "class": "vertex", "id": _id} return self.request.put(path, params) def lookup_vertex(self, index_index_name, key, value): """ Returns the vertices indexed with the key and value. :param index_name: Name of the index. :type index_name: str :param key: Name of the key. :type key: str :param value: Value of the key. :type value: str :rtype: RexsterResponse """ path = build_path(index_path, index_index_name) params = dict(key=key, value=value) return self.request.get(path, params) def query_vertex(self, index_name, params): """Queries for an edge in the index and returns the Response.""" path = build_path(index_path, index_name) return self.request.get(path, params) def remove_vertex(self, index_name, _id, key=None, value=None): """ Removes a vertex from the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param key: Optional. Name of the key. :type key: str :param value: Optional. Value of the key. :type value: str :rtype: RexsterResponse """ # Can Rexster have None for key and value? path = build_path(index_path, index_name) params = {"key": key, "value": value, "class": "vertex", "id": _id} return self.request.delete(path, params) # Index Container - Edge def put_edge(self, index_name, key, value, _id): """ Adds an edge to the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param key: Name of the key. :type key: str :param value: Value of the key. :type value: str :param _id: Edge ID :type _id: int :rtype: RexsterResponse """ # Rexster's API only supports string lookups so convert value to a string path = build_path(index_path, index_name) params = {"key": key, "value": str(value), "class": "edge", "id": _id} return self.request.put(path, params) def lookup_edge(self, index_index_name, key, value): """ Looks up an edge in the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param key: Name of the key. :type key: str :param value: Value of the key. :type value: str :rtype: RexsterResponse """ path = build_path(index_path, index_index_name) params = dict(key=key, value=value) return self.request.get(path, params) def query_edge(self, index_name, params): """Queries for an edge in the index and returns the Response.""" path = build_path(index_path, index_name) return self.request.get(path, params) def remove_edge(self, index_name, _id, key=None, value=None): """ Removes an edge from the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param _id: Edge ID :type _id: int :param key: Optional. Name of the key. :type key: str :param value: Optional. Value of the key. :type value: str :rtype: RexsterResponse """ # Can Rexster have None for key and value? path = build_path(index_path, index_name) params = {"key": key, "value": value, "class": "edge", "id": _id} return self.request.delete(path, params) # Model Proxy - Vertex def create_indexed_vertex(self, data, index_name, keys=None): """ Creates a vertex, indexes it, and returns the Response. :param data: Property data. :type data: dict :param index_name: Name of the index. :type index_name: str :param keys: Property keys to index. :type keys: list :rtype: RexsterResponse """ data = self._remove_null_values(data) params = dict(data=data, index_name=index_name, keys=keys) script = self.scripts.get("create_indexed_vertex") return self.gremlin(script, params) def update_indexed_vertex(self, _id, data, index_name, keys=None): """ Updates an indexed vertex and returns the Response. :param index_name: Name of the index. :type index_name: str :param data: Property data. :type data: dict :param index_name: Name of the index. :type index_name: str :param keys: Property keys to index. :type keys: list :rtype: RexsterResponse """ data = self._remove_null_values(data) params = dict(_id=_id, data=data, index_name=index_name, keys=keys) script = self.scripts.get("update_indexed_vertex") return self.gremlin(script, params) # Model Proxy - Edge def create_indexed_edge(self, outV, label, inV, data, index_name, keys=None): """ Creates a edge, indexes it, and returns the Response. :param outV: Outgoing vertex ID. :type outV: int :param label: Edge label. :type label: str :param inV: Incoming vertex ID. :type inV: int :param data: Property data. :type data: dict :param index_name: Name of the index. :type index_name: str :param keys: Property keys to index. Defaults to None (indexes all properties). :type keys: list :rtype: RexsterResponse """ data = self._remove_null_values(data) edge_params = dict(outV=outV, label=label, inV=inV, label_var=self.config.label_var) params = dict(data=data, index_name=index_name, keys=keys) params.update(edge_params) script = self.scripts.get("create_indexed_edge") return self.gremlin(script, params) def update_indexed_edge(self, _id, data, index_name, keys=None): """ Updates an indexed edge and returns the Response. :param _id: Edge ID. :type _id: int :param data: Property data. :type data: dict :param index_name: Name of the index. :type index_name: str :param keys: Property keys to index. Defaults to None (indexes all properties). :type keys: list :rtype: RexsterResponse """ data = self._remove_null_values(data) params = dict(_id=_id, data=data, index_name=index_name, keys=keys) script = self.scripts.get("update_indexed_edge") return self.gremlin(script, params) # Utils def warm_cache(self): """Warms the server cache by loading elements into memory.""" script = self.scripts.get("warm_cache") return self.gremlin(script, params=None) # Rexster Specific Stuff def rebuild_vertex_index(self, index_name): params = dict(index_name=index_name) script = self.scripts.get("rebuild_vertex_index", params) return self.gremlin(script) def rebuild_edge_index(self, index_name): params = dict(index_name=index_name) script = self.scripts.get("rebuild_edge_index", params) return self.gremlin(script) # TODO: manual/custom index API def multi_get_vertices(self, id_list): path = build_path(multi_get_path, "vertices") idList = self._build_url_list(id_list) params = dict(idList=idList) return self.request.get(path, params) def multi_get_edges(self, id_list): path = build_path(multi_get_path, "edges") idList = self._build_url_list(id_list) params = dict(idList=idList) return self.request.get(path, params) def _build_url_list(self, items): items = [str(item) for item in items] url_list = "[%s]" % ",".join(items) return url_list def execute_transaction(self, transaction): params = dict(tx=transaction.actions) return self.request.post(self.transction_path, params) def _remove_null_values(self, data): """Removes null property values because they aren't valid in Neo4j.""" # using PUTs to overwrite all properties so no need # to worry about deleting props that are being set to null. clean_data = [(k, data[k]) for k in data if data[k] is not None] # Python 3 return dict(clean_data)
class RexsterResource(Resource): """Low-level client that connects to Neo4j Server and returns a response.""" vertex_path = "vertices" edge_path = "edges" index_path = "indices" gremlin_path = "tp/gremlin" transaction_path = "tp/batch/tx" multi_get_path = "tp/batch" def __init__(self,config): self.config = config self.registry = Registry(config) self.scripts = GroovyScripts() self.scripts.update(self._get_scripts_file("gremlin.groovy")) self.registry.add_scripts("gremlin",self.scripts) self.type_system = JSONTypeSystem() self.request = RexsterRequest(config,self.type_system.content_type) # Gremlin def gremlin(self,script,params=None): """Executes a Gremlin script and returns the Response.""" params = dict(script=script,params=params) return self.request.post(self.gremlin_path,params) # Vertex Proxy def create_vertex(self,data): """Creates a vertex and returns the Response.""" data = self._remove_null_values(data) return self.request.post(self.vertex_path,data) def get_vertex(self,_id): """Gets the vertex with the _id and returns the Response.""" path = build_path(self.vertex_path,_id) return self.request.get(path,params=None) def update_vertex(self,_id,data): """Updates the vertex with the _id and returns the Response.""" data = self._remove_null_values(data) path = build_path(self.vertex_path,_id) return self.request.put(path,data) def delete_vertex(self,_id): """Deletes a vertex with the _id and returns the Response.""" path = build_path(self.vertex_path,_id) return self.request.delete(path,params=None) # Edge Proxy def create_edge(self,outV,label,inV,data={}): """Creates a edge and returns the Response.""" data = self._remove_null_values(data) edge_data = dict(_outV=outV,_label=label,_inV=inV) data.update(edge_data) return self.request.post(self.edge_path,data) def get_edge(self,_id): """Gets the edge with the _id and returns the Response.""" path = build_path(self.edge_path,_id) return self.request.get(path,params=None) def update_edge(self,_id,data): """Updates the edge with the _id and returns the Response.""" data = self._remove_null_values(data) path = build_path(self.edge_path,_id) return self.request.put(path,data) def delete_edge(self,_id): """Deletes a edge with the _id and returns the Response.""" path = build_path(self.edge_path,_id) return self.request.delete(path,params=None) # Vertex Container def outE(self,_id,label=None): """Return the outgoing edges of the vertex.""" script = self.scripts.get('outE') params = dict(_id=_id,label=label) return self.gremlin(script,params) def inE(self,_id,label=None): """Return the incoming edges of the vertex.""" script = self.scripts.get('inE') params = dict(_id=_id,label=label) return self.gremlin(script,params) def bothE(self,_id,label=None): """Return all incoming and outgoing edges of the vertex.""" script = self.scripts.get('bothE') params = dict(_id=_id,label=label) return self.gremlin(script,params) def outV(self,_id,label=None): """Return the out-adjacent vertices to the vertex.""" script = self.scripts.get('outV') params = dict(_id=_id,label=label) return self.gremlin(script,params) def inV(self,_id,label=None): """Return the in-adjacent vertices of the vertex.""" script = self.scripts.get('inV') params = dict(_id=_id,label=label) return self.gremlin(script,params) def bothV(self,_id,label=None): """Return all incoming- and outgoing-adjacent vertices of vertex.""" script = self.scripts.get('bothV') params = dict(_id=_id,label=label) return self.gremlin(script,params) # Index Proxy - General def get_all_indices(self): """Returns a list of all the element indices.""" return self.request.get(self.index_path,params=None) def get_index(self,name): path = build_path(self.index_path,name) return self.request.get(path,params=None) def delete_index(self,name): """Deletes the index with the index_name.""" path = build_path(self.index_path,name) return self.request.delete(path,params=None) # Index Proxy - Vertex def create_vertex_index(self,index_name,*args,**kwds): """Creates a vertex index with the specified params.""" path = build_path(self.index_path,index_name) index_type = kwds.get('index_type','manual') index_keys = kwds.get('index_keys',None) params = {'class':'vertex','type':index_type} if index_keys: params.update({'keys':index_keys}) return self.request.post(path,params) def get_vertex_index(self,name): """Returns the vertex index with the index_name.""" return self.get_index(name) def delete_vertex_index(self,name): """Deletes the vertex index with the index_name.""" return self.delete_index(name) # Index Proxy - Edge def create_edge_index(self,name,*args,**kwds): """Creates a edge index with the specified params.""" path = build_path(self.index_path,name) index_type = kwds.get('index_type','manual') index_keys = kwds.get('index_keys',None) params = {'class':'edge','type':index_type} if index_keys: params.update({'keys':index_keys}) return self.request.post(path,params) def get_edge_index(self,name): """Returns the edge index with the index_name.""" return self.get_index(name) def delete_edge_index(self,name): """Deletes the edge index with the index_name.""" self.delete_index(name) #def create_automatic_vertex_index(self,index_name,element_class,keys=None): # keys = json.dumps(keys) if keys else "null" # params = dict(index_name=index_name,element_class=element_class,keys=keys) # script = self.scripts.get('create_automatic_vertex_index',params) # return self.gremlin(script) #def create_indexed_vertex_automatic(self,data,index_name): # data = json.dumps(data) # params = dict(data=data,index_name=index_name) # script = self.scripts.get('create_automatic_indexed_vertex',params) # return self.gremlin(script) # Index Container - General def index_count(self,index_name,key,value): path = build_path(self.index_path,index_name,"count") params = dict(key=key,value=value) return self.request.get(path,params) def index_keys(self,index_name): path = build_path(self.index_path,index_name,"keys") return self.request.get(path,params=None) # Index Container - Vertex def put_vertex(self,index_name,key,value,_id): """Adds a vertex to the index with the index_name.""" # Rexster's API only supports string lookups so convert value to a string path = build_path(self.index_path,index_name) params = {'key':key,'value':str(value),'class':'vertex','id':_id} return self.request.put(path,params) def lookup_vertex(self,index_index_name,key,value): """Returns the vertices indexed with the key and value.""" path = build_path(self.index_path,index_index_name) params = dict(key=key,value=value) return self.request.get(path,params) def query_vertex(self,index_name,params): """Queries for an edge in the index and returns the Response.""" path = build_path(self.index_path,index_name) return self.request.get(path,params) def remove_vertex(self,index_name,_id,key=None,value=None): """Removes a vertex from the index and returns the Response.""" # Can Rexster have None for key and value? path = build_path(self.index_path,index_name) params = {'key':key,'value':value,'class':'vertex','id':_id} return self.request.delete(path,params) # Index Container - Edge def put_edge(self,index_name,key,value,_id): """Adds an edge to the index and returns the Response.""" # Rexster's API only supports string lookups so convert value to a string path = build_path(self.index_path,index_name) params = {'key':key,'value':str(value),'class':'edge','id':_id} return self.request.put(path,params) def lookup_edge(self,index_index_name,key,value): """Looks up an edge in the index and returns the Response.""" path = build_path(self.index_path,index_index_name) params = dict(key=key,value=value) return self.request.get(path,params) def query_edge(self,index_name,params): """Queries for an edge in the index and returns the Response.""" path = build_path(self.index_path,index_name) return self.request.get(path,params) def remove_edge(self,index_name,_id,key=None,value=None): """Removes an edge from the index and returns the Response.""" # Can Rexster have None for key and value? path = build_path(self.index_path,index_name) params = {'key':key,'value':value,'class':'edge','id':_id} return self.request.delete(path,params) # Model Proxy - Vertex def create_indexed_vertex(self,data,index_name,keys=None): """Creates a vertex, indexes it, and returns the Response.""" data = self._remove_null_values(data) params = dict(data=data,index_name=index_name,keys=keys) script = self.scripts.get("create_indexed_vertex") return self.gremlin(script,params) def update_indexed_vertex(self,_id,data,index_name,keys=None): """Updates an indexed vertex and returns the Response.""" data = self._remove_null_values(data) params = dict(_id=_id,data=data,index_name=index_name,keys=keys) script = self.scripts.get("update_indexed_vertex") return self.gremlin(script,params) # Model Proxy - Edge def create_indexed_edge(self,data,index_name,keys=None): """Creates a edge, indexes it, and returns the Response.""" data = self._remove_null_values(data) params = dict(data=data,index_name=index_name,keys=keys) script = self.scripts.get("create_indexed_edge") return self.gremlin(script,params) def update_indexed_edge(self,_id,data,index_name,keys=None): """Updates an indexed edge and returns the Response.""" data = self._remove_null_values(data) params = dict(_id=_id,data=data,index_name=index_name,keys=keys) script = self.scripts.get("update_indexed_edge") return self.gremlin(script,params) # Utils def warm_cache(self): """Warms the server cache by loading elements into memory.""" script = self.scripts.get('warm_cache') return self.gremlin(script,params=None) # Rexster Specific Stuff def rebuild_vertex_index(self,index_name): params = dict(index_name=index_name) script = self.scripts.get('rebuild_vertex_index',params) return self.gremlin(script) def rebuild_edge_index(self,index_name): params = dict(index_name=index_name) script = self.scripts.get('rebuild_edge_index',params) return self.gremlin(script) # TODO: manual/custom index API def multi_get_vertices(self,id_list): path = build_path(self.multi_get_path,"vertices") idList = self._build_url_list(id_list) params = dict(idList=idList) return self.request.get(path,params) def multi_get_edges(self,id_list): path = build_path(self.multi_get_path,"edges") idList = self._build_url_list(id_list) params = dict(idList=idList) return self.request.get(path,params) def _build_url_list(items): items = [str(item) for item in items] url_list = "[%s]" % ",".join(items) return url_list def execute_transaction(self,transaction): params = dict(tx=transaction.actions) return self.request.post(self.transction_path,params) def _get_scripts_file(self,file_name): dir_name = os.path.dirname(__file__) file_path = get_file_path(dir_name,file_name) return file_path def _remove_null_values(self,data): """Removes null property values because they aren't valid in Neo4j.""" # Neo4j Server uses PUTs to overwrite all properties so no need # to worry about deleting props that are being set to null. clean_data = [(k, v) for k, v in data.items() if v is not None] return dict(clean_data)
class RexsterClient(Client): """ Low-level client that sends a request to Rexster and returns a response. :param config: Optional Config object. Defaults to default Config. :type config: bulbs.config.Config :cvar default_uri: Default URI for the database. :cvar request_class: Request class for the Client. :ivar config: Config object. :ivar registry: Registry object. :ivar scripts: GroovyScripts object. :ivar type_system: JSONTypeSystem object. :ivar request: RexsterRequest object. Example: >>> from bulbs.rexster import RexsterClient >>> client = RexsterClient() >>> script = client.scripts.get("get_vertices") >>> response = client.gremlin(script, params=None) >>> result = response.results.next() """ #: Default URI for the database. default_uri = REXSTER_URI request_class = RexsterRequest def __init__(self, config=None, db_name=None): # This makes is easy to test different DBs uri = self._get_uri(db_name) or self.default_uri self.config = config or Config(uri) self.registry = Registry(self.config) self.type_system = JSONTypeSystem() self.request = self.request_class(self.config, self.type_system.content_type) # Rexster supports Gremlin so include the Gremlin-Groovy script library self.scripts = GroovyScripts(self.config) # Also include the Rexster-specific Gremlin-Groovy scripts scripts_file = get_file_path(__file__, "gremlin.groovy") self.scripts.update(scripts_file) # Add it to the registry. This allows you to have more than one scripts namespace. self.registry.add_scripts("gremlin", self.scripts) def _get_uri(self, db_name): if db_name is not None: uri = "http://localhost:8182/graphs/%s" % db_name return uri # Gremlin def gremlin(self, script, params=None, load=None): """ Executes a Gremlin script and returns the Response. :param script: Gremlin script to execute. :type script: str :param params: Param bindings for the script. :type params: dict :rtype: RexsterResponse """ params = dict(script=script, params=params) if self.config.server_scripts is True: params["load"] = load or [self.scripts.default_namespace] return self.request.post(gremlin_path, params) # Vertex Proxy def create_vertex(self, data, keys=None): """ Creates a vertex and returns the Response. :param data: Property data. :type data: dict :rtype: RexsterResponse """ if keys or self.config.autoindex is True: index_name = self.config.vertex_index return self.create_indexed_vertex(data, index_name, keys=keys) data = self._remove_null_values(data) return self.request.post(vertex_path, data) def get_vertex(self, _id): """ Gets the vertex with the _id and returns the Response. :param data: Vertex ID. :type data: int :rtype: RexsterResponse """ path = build_path(vertex_path, _id) return self.request.get(path, params=None) def get_all_vertices(self): """ Returns a Response containing all the vertices in the Graph. :rtype: RexsterResponse """ script = self.scripts.get("get_vertices") params = None return self.gremlin(script, params) def update_vertex(self, _id, data, keys=None): """ Updates the vertex with the _id and returns the Response. :param _id: Vertex ID. :type _id: dict :param data: Property data. :type data: dict :rtype: RexsterResponse """ data = self._remove_null_values(data) path = build_path(vertex_path, _id) return self.request.put(path, data) def delete_vertex(self, _id): """ Deletes a vertex with the _id and returns the Response. :param _id: Vertex ID. :type _id: dict :rtype: RexsterResponse """ path = build_path(vertex_path, _id) return self.request.delete(path, params=None) # Edge Proxy def create_edge(self, outV, label, inV, data={}, keys=None): """ Creates a edge and returns the Response. :param outV: Outgoing vertex ID. :type outV: int :param label: Edge label. :type label: str :param inV: Incoming vertex ID. :type inV: int :param data: Property data. :type data: dict or None :rtype: RexsterResponse """ if keys or self.config.autoindex is True: index_name = self.config.edge_index return self.create_indexed_edge(outV, label, inV, data, index_name, keys=keys) data = self._remove_null_values(data) edge_data = dict(_outV=outV, _label=label, _inV=inV) data.update(edge_data) return self.request.post(edge_path, data) def get_edge(self, _id): """ Gets the edge with the _id and returns the Response. :param data: Edge ID. :type data: int :rtype: RexsterResponse """ path = build_path(edge_path, _id) return self.request.get(path, params=None) def get_all_edges(self): """ Returns a Response containing all the edges in the Graph. :rtype: RexsterResponse """ script = self.scripts.get("get_edges") params = None return self.gremlin(script, params) def update_edge(self, _id, data, keys=None): """ Updates the edge with the _id and returns the Response. :param _id: Edge ID. :type _id: dict :param data: Property data. :type data: dict :rtype: RexsterResponse """ data = self._remove_null_values(data) path = build_path(edge_path, _id) return self.request.put(path, data) def delete_edge(self, _id): """ Deletes a edge with the _id and returns the Response. :param _id: Edge ID. :type _id: dict :rtype: RexsterResponse """ path = build_path(edge_path, _id) return self.request.delete(path, params=None) # Vertex Container def outE(self, _id, label=None, start=None, limit=None): """ Returns the outgoing edges of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: RexsterResponse """ script = self.scripts.get('outE') params = dict(_id=_id, label=label, start=start, limit=limit) return self.gremlin(script, params) def inE(self, _id, label=None, start=None, limit=None): """ Returns the incoming edges of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: RexsterResponse """ script = self.scripts.get('inE') params = dict(_id=_id, label=label, start=start, limit=limit) return self.gremlin(script, params) def bothE(self, _id, label=None, start=None, limit=None): """ Returns the incoming and outgoing edges of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: RexsterResponse """ script = self.scripts.get('bothE') params = dict(_id=_id, label=label, start=start, limit=limit) return self.gremlin(script, params) def outV(self, _id, label=None, start=None, limit=None): """ Returns the out-adjacent vertices of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: RexsterResponse """ script = self.scripts.get('outV') params = dict(_id=_id, label=label, start=start, limit=limit) return self.gremlin(script, params) def inV(self, _id, label=None, start=None, limit=None): """ Returns the in-adjacent vertices of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: RexsterResponse """ script = self.scripts.get('inV') params = dict(_id=_id, label=label, start=start, limit=limit) return self.gremlin(script, params) def bothV(self, _id, label=None, start=None, limit=None): """ Returns the incoming- and outgoing-adjacent vertices of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: RexsterResponse """ script = self.scripts.get('bothV') params = dict(_id=_id, label=label, start=start, limit=limit) return self.gremlin(script, params) # Index Proxy - General def get_all_indices(self): """Returns a list of all the element indices.""" return self.request.get(index_path, params=None) def get_index(self, name): path = build_path(index_path, name) return self.request.get(path, params=None) def delete_index(self, name): """Deletes the index with the index_name.""" path = build_path(index_path, name) return self.request.delete(path, params=None) # Index Proxy - Vertex def create_vertex_index(self, index_name, *args, **kwds): """ Creates a vertex index with the specified params. :param index_name: Name of the index to create. :type index_name: str :rtype: RexsterResponse """ path = build_path(index_path, index_name) index_type = kwds.get('index_type', 'manual') index_keys = kwds.get('index_keys', None) params = {'class': 'vertex', 'type': index_type} if index_keys: params.update({'keys': index_keys}) return self.request.post(path, params) def get_vertex_index(self, index_name): """ Returns the vertex index with the index_name. :param index_name: Name of the index. :type index_name: str :rtype: RexsterResponse """ return self.get_index(index_name) def get_or_create_vertex_index(self, index_name, index_params=None): script = self.scripts.get('get_or_create_vertex_index') params = dict(index_name=index_name, index_params=index_params) resp = self.gremlin(script, params) #assert "MANUAL" in resp.content['results'][0] result = {'name': index_name, 'type': 'manual', 'class': 'vertex'} resp.results = RexsterResult(result, self.config) return resp def delete_vertex_index(self, name): """ Deletes the vertex index with the index_name. :param index_name: Name of the index. :type index_name: str :rtype: RexsterResponse """ return self.delete_index(name) # Index Proxy - Edge def create_edge_index(self, name, *args, **kwds): """ Creates a edge index with the specified params. :param index_name: Name of the index. :type index_name: str :rtype: RexsterResponse """ path = build_path(index_path, name) index_type = kwds.get('index_type', 'manual') index_keys = kwds.get('index_keys', None) params = {'class': 'edge', 'type': index_type} if index_keys: params.update({'keys': index_keys}) return self.request.post(path, params) def get_edge_index(self, name): """ Returns the edge index with the index_name. :param index_name: Name of the index. :type index_name: str :rtype: RexsterResponse """ return self.get_index(name) def get_or_create_edge_index(self, index_name, index_params=None): script = self.scripts.get('get_or_create_edge_index') params = dict(index_name=index_name, index_params=index_params) resp = self.gremlin(script, params) #assert "MANUAL" in resp.content['results'][0] result = {'name': index_name, 'type': 'manual', 'class': 'edge'} resp.results = RexsterResult(result, self.config) return resp def delete_edge_index(self, name): """ Deletes the edge index with the index_name. :param index_name: Name of the index. :type index_name: str :rtype: RexsterResponse """ self.delete_index(name) #def create_automatic_vertex_index(self,index_name,element_class,keys=None): # keys = json.dumps(keys) if keys else "null" # params = dict(index_name=index_name,element_class=element_class,keys=keys) # script = self.scripts.get('create_automatic_vertex_index',params) # return self.gremlin(script) #def create_indexed_vertex_automatic(self,data,index_name): # data = json.dumps(data) # params = dict(data=data,index_name=index_name) # script = self.scripts.get('create_automatic_indexed_vertex',params) # return self.gremlin(script) # Index Container - General def index_count(self, index_name, key, value): path = build_path(index_path, index_name, "count") params = dict(key=key, value=value) return self.request.get(path, params) def index_keys(self, index_name): path = build_path(index_path, index_name, "keys") return self.request.get(path, params=None) # Index Container - Vertex def put_vertex(self, index_name, key, value, _id): """ Adds a vertex to the index with the index_name. :param index_name: Name of the index. :type index_name: str :param key: Name of the key. :type key: str :param value: Value of the key. :type value: str :param _id: Vertex ID :type _id: int :rtype: RexsterResponse """ # Rexster's API only supports string lookups so convert value to a string path = build_path(index_path, index_name) params = { 'key': key, 'value': str(value), 'class': 'vertex', 'id': _id } return self.request.put(path, params) def lookup_vertex(self, index_index_name, key, value): """ Returns the vertices indexed with the key and value. :param index_name: Name of the index. :type index_name: str :param key: Name of the key. :type key: str :param value: Value of the key. :type value: str :rtype: RexsterResponse """ path = build_path(index_path, index_index_name) params = dict(key=key, value=value) return self.request.get(path, params) def query_vertex(self, index_name, params): """Queries for an edge in the index and returns the Response.""" path = build_path(index_path, index_name) return self.request.get(path, params) def remove_vertex(self, index_name, _id, key=None, value=None): """ Removes a vertex from the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param key: Optional. Name of the key. :type key: str :param value: Optional. Value of the key. :type value: str :rtype: RexsterResponse """ # Can Rexster have None for key and value? path = build_path(index_path, index_name) params = {'key': key, 'value': value, 'class': 'vertex', 'id': _id} return self.request.delete(path, params) # Index Container - Edge def put_edge(self, index_name, key, value, _id): """ Adds an edge to the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param key: Name of the key. :type key: str :param value: Value of the key. :type value: str :param _id: Edge ID :type _id: int :rtype: RexsterResponse """ # Rexster's API only supports string lookups so convert value to a string path = build_path(index_path, index_name) params = {'key': key, 'value': str(value), 'class': 'edge', 'id': _id} return self.request.put(path, params) def lookup_edge(self, index_index_name, key, value): """ Looks up an edge in the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param key: Name of the key. :type key: str :param value: Value of the key. :type value: str :rtype: RexsterResponse """ path = build_path(index_path, index_index_name) params = dict(key=key, value=value) return self.request.get(path, params) def query_edge(self, index_name, params): """Queries for an edge in the index and returns the Response.""" path = build_path(index_path, index_name) return self.request.get(path, params) def remove_edge(self, index_name, _id, key=None, value=None): """ Removes an edge from the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param _id: Edge ID :type _id: int :param key: Optional. Name of the key. :type key: str :param value: Optional. Value of the key. :type value: str :rtype: RexsterResponse """ # Can Rexster have None for key and value? path = build_path(index_path, index_name) params = {'key': key, 'value': value, 'class': 'edge', 'id': _id} return self.request.delete(path, params) # Model Proxy - Vertex def create_indexed_vertex(self, data, index_name, keys=None): """ Creates a vertex, indexes it, and returns the Response. :param data: Property data. :type data: dict :param index_name: Name of the index. :type index_name: str :param keys: Property keys to index. :type keys: list :rtype: RexsterResponse """ data = self._remove_null_values(data) params = dict(data=data, index_name=index_name, keys=keys) script = self.scripts.get("create_indexed_vertex") resp = self.gremlin(script, params) resp.results = resp.one() return resp def update_indexed_vertex(self, _id, data, index_name, keys=None): """ Updates an indexed vertex and returns the Response. :param index_name: Name of the index. :type index_name: str :param data: Property data. :type data: dict :param index_name: Name of the index. :type index_name: str :param keys: Property keys to index. :type keys: list :rtype: RexsterResponse """ data = self._remove_null_values(data) params = dict(_id=_id, data=data, index_name=index_name, keys=keys) script = self.scripts.get("update_indexed_vertex") return self.gremlin(script, params) # Model Proxy - Edge def create_indexed_edge(self, outV, label, inV, data, index_name, keys=None): """ Creates a edge, indexes it, and returns the Response. :param outV: Outgoing vertex ID. :type outV: int :param label: Edge label. :type label: str :param inV: Incoming vertex ID. :type inV: int :param data: Property data. :type data: dict :param index_name: Name of the index. :type index_name: str :param keys: Property keys to index. Defaults to None (indexes all properties). :type keys: list :rtype: RexsterResponse """ data = self._remove_null_values(data) edge_params = dict(outV=outV, label=label, inV=inV, label_var=self.config.label_var) params = dict(data=data, index_name=index_name, keys=keys) params.update(edge_params) script = self.scripts.get("create_indexed_edge") resp = self.gremlin(script, params) resp.results = resp.one() return resp def update_indexed_edge(self, _id, data, index_name, keys=None): """ Updates an indexed edge and returns the Response. :param _id: Edge ID. :type _id: int :param data: Property data. :type data: dict :param index_name: Name of the index. :type index_name: str :param keys: Property keys to index. Defaults to None (indexes all properties). :type keys: list :rtype: RexsterResponse """ data = self._remove_null_values(data) params = dict(_id=_id, data=data, index_name=index_name, keys=keys) script = self.scripts.get("update_indexed_edge") return self.gremlin(script, params) # Utils def warm_cache(self): """Warms the server cache by loading elements into memory.""" script = self.scripts.get('warm_cache') return self.gremlin(script, params=None) # Rexster Specific Stuff def rebuild_vertex_index(self, index_name): params = dict(index_name=index_name) script = self.scripts.get('rebuild_vertex_index', params) return self.gremlin(script) def rebuild_edge_index(self, index_name): params = dict(index_name=index_name) script = self.scripts.get('rebuild_edge_index', params) return self.gremlin(script) # TODO: manual/custom index API def multi_get_vertices(self, id_list): path = build_path(multi_get_path, "vertices") idList = self._build_url_list(id_list) params = dict(idList=idList) return self.request.get(path, params) def multi_get_edges(self, id_list): path = build_path(multi_get_path, "edges") idList = self._build_url_list(id_list) params = dict(idList=idList) return self.request.get(path, params) def _build_url_list(self, items): items = [str(item) for item in items] url_list = "[%s]" % ",".join(items) return url_list def execute_transaction(self, transaction): params = dict(tx=transaction.actions) return self.request.post(self.transction_path, params) def _remove_null_values(self, data): """Removes null property values because they aren't valid in Neo4j.""" # using PUTs to overwrite all properties so no need # to worry about deleting props that are being set to null. clean_data = [(k, data[k]) for k in data if data[k] is not None] # Python 3 return dict(clean_data)
class Neo4jResource(Resource): vertex_path = "node" edge_path = "relationships" index_path = "index" gremlin_path = "ext/GremlinPlugin/graphdb/execute_script" cypher_path = "ext/CypherPlugin/graphdb/execute_query" #cypher_path = "cypher" def __init__(self,config): """ Initializes a resource object. :param root_uri: the base URL of Rexster. """ self.config = config self.registry = Registry(config) self.scripts = Scripts() dir_name = os.path.dirname(__file__) self.scripts.override(get_file_path(dir_name,"gremlin.groovy")) self.registry.add_scripts("gremlin",self.scripts) self.type_system = self._get_type_system() self.request = Neo4jRequest(config,self.type_system.content_type) # Gremlin def gremlin(self,script,params=None): params = dict(script=script,params=params) return self.request.post(self.gremlin_path,params) # Cypher def cypher(self,query,params=None): params = dict(query=query,params=params) return self.request.post(self.cypher_path,params) # Vertex Proxy def create_vertex(self,data): data = self._remove_null_values(data) if self.config.autoindex is True: index_name = self.config.vertex_autoindex return self.create_indexed_vertex(data,index_name,keys=None) return self.request.post(self.vertex_path,data) def get_vertex(self,_id): path = build_path(self.vertex_path,_id) return self.request.get(path,params=None) def update_vertex(self,_id,data): data = self._remove_null_values(data) if self.config.autoindex is True: index_name = self.config.vertex_autoindex return self.update_indexed_vertex(_id,data,index_name,keys=None) path = build_path(self.vertex_path,_id,"properties") return self.request.put(path,data) def delete_vertex(self,_id): # Neo4j requires you delete all adjacent edges first. # But the Neo4jServer DELETE URI doesn't do this so # I created a Gremlin method for it. - James params = dict(_id=_id) script = self.scripts.get("delete_vertex") return self.gremlin(script,params) # Edge Proxy def create_edge(self,outV,label,inV,data={}): data = self._remove_null_values(data) if self.config.autoindex is True: index_name = self.config.edge_autoindex return self.create_indexed_edge(outV,label,inV,data,index_name,keys=None) path = build_path(self.vertex_path,outV,self.edge_path) inV_uri = "%s/node/%s" % (self.config.root_uri.rstrip("/"), inV) params = {'to':inV_uri,'type':label, 'data':data} return self.request.post(path,params) def get_edge(self,_id): path = build_path("relationship",_id) return self.request.get(path,params=None) def update_edge(self,_id,data): data = self._remove_null_values(data) if self.config.autoindex is True: index_name = self.config.edge_autoindex return self.update_indexed_edge(_id,data,index_name,keys=None) path = build_path("relationship",_id,"properties") return self.request.put(path,data) def delete_edge(self,_id): # note relationship path is singular, not plural path = build_path("relationship",_id) return self.request.delete(path,params=None) # Vertex Container def outE(self,_id,label=None): """Return the outgoing edges of the vertex.""" script = self.scripts.get('outE') params = dict(_id=_id,label=label) return self.gremlin(script,params) def inE(self,_id,label=None): """Return the incoming edges of the vertex.""" script = self.scripts.get('inE') params = dict(_id=_id,label=label) return self.gremlin(script,params) def bothE(self,_id,label=None): """Return all incoming and outgoing edges of the vertex.""" script = self.scripts.get('bothE') params = dict(_id=_id,label=label) return self.gremlin(script,params) def outV(self,_id,label=None): """Return the out-adjacent vertices to the vertex.""" script = self.scripts.get('outV') params = dict(_id=_id,label=label) return self.gremlin(script,params) def inV(self,_id,label=None): """Return the in-adjacent vertices of the vertex.""" script = self.scripts.get('inV') params = dict(_id=_id,label=label) return self.gremlin(script,params) def bothV(self,_id,label=None): """Return all incoming- and outgoing-adjacent vertices of vertex.""" script = self.scripts.get('bothV') params = dict(_id=_id,label=label) return self.gremlin(script,params) # Index Proxy - Vertex def create_vertex_index(self,index_name,*args,**kwds): index_type = kwds.pop("index_type","exact") provider = kwds.pop("provider","lucene") #keys = kwds.pop("keys",None) #keys = json.dumps(keys) if keys else "null" #config = {'type':index_type,'provider':provider,'keys':str(keys)} config = {'type':index_type,'provider':provider} path = build_path(self.index_path,"node") params = dict(name=index_name,config=config) return self.request.post(path,params) def get_vertex_indices(self): # returns a map of indices path = build_path(self.index_path,"node") return self.request.get(path,params=None) def get_vertex_index(self,index_name): resp = self.get_vertex_indices() resp.results = self._get_index_results(index_name,resp) return resp def delete_vertex_index(self,name): path = build_path(self.index_path,"node",name) return self.request.delete(path,params=None) # Index Proxy - Edge def create_edge_index(self,index_name,*args,**kwds): path = build_path(self.index_path,"relationship") params = dict(name=index_name) return self.request.post(path,params) def get_edge_indices(self): path = build_path(self.index_path,"relationship") return self.request.get(path,params=None) def get_edge_index(self,index_name): resp = self.get_edge_indices() resp.results = self._get_index_results(index_name,resp) return resp def delete_edge_index(self,name): pass def _get_index_results(self,index_name,resp): # this is pretty much a hack becuase the way neo4j does this is inconsistent results = None # for clarity content = resp.content if content and index_name in content: result = content[index_name] result['name'] = index_name results = Neo4jResult(result) return results # Model Proxy - Vertex def create_indexed_vertex(self,data,index_name,keys=None): data = self._remove_null_values(data) params = dict(data=data,index_name=index_name,keys=keys) script = self.scripts.get("create_indexed_vertex") return self.gremlin(script,params) def update_indexed_vertex(self,_id,data,index_name,keys=None): data = self._remove_null_values(data) params = dict(_id=_id,data=data,index_name=index_name,keys=keys) script = self.scripts.get("update_indexed_vertex") return self.gremlin(script,params) # Model Proxy - Edge def create_indexed_edge(self,outV,label,inV,data,index_name,keys=None): data = self._remove_null_values(data) edge_params = dict(outV=outV,label=label,inV=inV) params = dict(data=data,index_name=index_name,keys=keys) params.update(edge_params) script = self.scripts.get("create_indexed_edge") return self.gremlin(script,params) def update_indexed_edge(self,_id,data,index_name,keys=None): data = self._remove_null_values(data) params = dict(_id=_id,data=data,index_name=index_name,keys=keys) script = self.scripts.get("update_indexed_edge") return self.gremlin(script,params) # Utils def warm_cache(self): script = self.scripts.get('warm_cache') return self.gremlin(script,params=None) def _remove_null_values(self,data): # You could do this at the Model._get_property_data(), # but this may not be needed for all databases. # Moreover, Neo4j Server uses PUTs to overwrite all properties so no need # to worry about deleting props that are being set to null. clean_data = [(k, v) for k, v in data.items() if v is not None] return dict(clean_data) # # Deprecated # # Indexed vertices def put_vertex(self,name,key,value,_id): path = build_path(self.index_path,"node",name) uri = "%s/%s/%d" % (self.config.root_uri,"node",_id) params = dict(key=key,value=value,uri=uri) return self.request.post(path,params) def lookup_vertex(self,name,key,value): path = build_path(self.index_path,"node",name,key,value) return self.request.get(path,params=None) def query_vertex(self,name,params): path = build_path(self.index_path,"node",name) return self.request.get(path,params) def remove_vertex_from_index(self,name,_id,key=None,value=None): #if key is not None and value is not None: # path = build_path("node",name,key,value,_id) #elif key is not None: # path = build_path("node",name,key,_id) #else: # path = build_path("node",name,_id) path = build_path("node",name,key,value,_id) return self.request.delete(path,params=None) def put_edge(self,name,key,value,_id): path = build_path(self.index_path,"relationship",name) uri = "%s/%s/%d" % (self.config.root_uri,"relationship",_id) params = dict(key=key,value=value,uri=uri) return self.request.post(path,params) def _get_type_system(self): type_system_map = dict(json=JSONTypeSystem) type_system = type_system_map[self.config.type_system] return type_system()
class Neo4jClient(Client): """ Low-level client that sends a request to Neo4j Server and returns a response. :param config: Optional Config object. Defaults to default Config. :type config: bulbs.config.Config :ivar config: Config object. :ivar registry: Registry object. :ivar scripts: GroovyScripts object. :ivar type_system: JSONTypeSystem object. :ivar request: Neo4jRequest object. Example: >>> from bulbs.neo4jserver import Neo4jClient >>> client = Neo4jClient() >>> response = client.get_all_vertices() >>> result = response.results.next() """ #: Default URI for the database. default_uri = NEO4J_URI #: Request class for the Client. request_class = Neo4jRequest def __init__(self, config=None): self.config = config or Config(self.default_uri) self.registry = Registry(self.config) self.type_system = JSONTypeSystem() self.request = self.request_class(self.config, self.type_system.content_type) # Neo4j supports Gremlin so include the Gremlin-Groovy script library self.scripts = GroovyScripts(self.config) # Also include the Neo4j Server-specific Gremlin-Groovy scripts scripts_file = get_file_path(__file__, "gremlin.groovy") self.scripts.update(scripts_file) # Add it to the registry. This allows you to have more than one scripts namespace. self.registry.add_scripts("gremlin", self.scripts) # Gremlin def gremlin(self, script, params=None): """ Executes a Gremlin script and returns the Response. :param script: Gremlin script to execute. :type script: str :param params: Param bindings for the script. :type params: dict :rtype: Neo4jResponse """ path = gremlin_path params = dict(script=script, params=params) return self.request.post(path, params) # Cypher def cypher(self, query, params=None): """ Executes a Cypher query and returns the Response. :param query: Cypher query to execute. :type query: str :param params: Param bindings for the query. :type params: dict :rtype: Neo4jResponse """ path = cypher_path params = dict(query=query, params=params) resp = self.request.post(path, params) # Cypher data hack resp.total_size = len(resp.results.data) resp.results = (Neo4jResult(result[0], self.config) for result in resp.results.data) return resp # Vertex Proxy def create_vertex(self, data, keys=None): """ Creates a vertex and returns the Response. :param data: Property data. :type data: dict :rtype: Neo4jResponse """ if keys or self.config.autoindex is True: index_name = self.config.vertex_index return self.create_indexed_vertex(data, index_name, keys=keys) path = vertex_path params = self._remove_null_values(data) return self.request.post(path, params) def get_vertex(self, _id): """ Gets the vertex with the _id and returns the Response. :param data: Vertex ID. :type data: int :rtype: Neo4jResponse """ path = build_path(vertex_path, _id) params = None return self.request.get(path, params) def get_all_vertices(self): """ Returns a Response containing all the vertices in the Graph. :rtype: Neo4jResponse """ script = self.scripts.get("get_vertices") params = None return self.gremlin(script, params) def update_vertex(self, _id, data, keys=None): """ Updates the vertex with the _id and returns the Response. :param _id: Vertex ID. :type _id: dict :param data: Property data. :type data: dict :rtype: Neo4jResponse """ if keys or self.config.autoindex is True: index_name = self.config.vertex_index return self.update_indexed_vertex(_id, data, index_name, keys=keys) path = self._build_vertex_path(_id, "properties") params = self._remove_null_values(data) return self.request.put(path, params) def delete_vertex(self, _id): """ Deletes a vertex with the _id and returns the Response. :param _id: Vertex ID. :type _id: dict :rtype: Neo4jResponse """ script = self.scripts.get("delete_vertex") params = dict(_id=_id) return self.gremlin(script, params) # Edge Proxy def create_edge(self, outV, label, inV, data=None, keys=None): """ Creates a edge and returns the Response. :param outV: Outgoing vertex ID. :type outV: int :param label: Edge label. :type label: str :param inV: Incoming vertex ID. :type inV: int :param data: Property data. :type data: dict or None :rtype: Neo4jResponse """ if keys or self.config.autoindex is True: index_name = self.config.edge_index return self.create_indexed_edge(outV, label, inV, data, index_name, keys=keys) data = self._remove_null_values(data) inV_uri = self._build_vertex_uri(inV) path = build_path(vertex_path, outV, "relationships") params = {'to': inV_uri, 'type': label, 'data': data} return self.request.post(path, params) def get_edge(self, _id): """ Gets the edge with the _id and returns the Response. :param data: Edge ID. :type data: int :rtype: Neo4jResponse """ path = build_path(edge_path, _id) params = None return self.request.get(path, params) def get_all_edges(self): """ Returns a Response containing all the edges in the Graph. :rtype: Neo4jResponse """ script = self.scripts.get("get_edges") params = None return self.gremlin(script, params) def update_edge(self, _id, data, keys=None): """ Updates the edge with the _id and returns the Response. :param _id: Edge ID. :type _id: dict :param data: Property data. :type data: dict :rtype: Neo4jResponse """ if keys or self.config.autoindex is True: index_name = self.config.edge_index return self.update_indexed_edge(_id, data, index_name, keys=keys) path = build_path(edge_path, _id, "properties") params = self._remove_null_values(data) return self.request.put(path, params) def delete_edge(self, _id): """ Deletes a edge with the _id and returns the Response. :param _id: Edge ID. :type _id: dict :rtype: Neo4jResponse """ path = build_path(edge_path, _id) params = None return self.request.delete(path, params) # Vertex Container def outE(self, _id, label=None, start=None, limit=None): """ Returns the outgoing edges of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: Neo4jResponse """ script = self.scripts.get('outE') params = dict(_id=_id, label=label, start=start, limit=limit) return self.gremlin(script, params) def inE(self, _id, label=None, start=None, limit=None): """ Returns the incoming edges of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: Neo4jResponse """ script = self.scripts.get('inE') params = dict(_id=_id, label=label, start=start, limit=limit) return self.gremlin(script, params) def bothE(self, _id, label=None, start=None, limit=None): """ Returns the incoming and outgoing edges of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: Neo4jResponse """ script = self.scripts.get('bothE') params = dict(_id=_id, label=label, start=start, limit=limit) return self.gremlin(script, params) def outV(self, _id, label=None, start=None, limit=None): """ Returns the out-adjacent vertices of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: Neo4jResponse """ script = self.scripts.get('outV') params = dict(_id=_id, label=label, start=start, limit=limit) return self.gremlin(script, params) def inV(self, _id, label=None, start=None, limit=None): """ Returns the in-adjacent vertices of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: Neo4jResponse """ script = self.scripts.get('inV') params = dict(_id=_id, label=label, start=start, limit=limit) return self.gremlin(script, params) def bothV(self, _id, label=None, start=None, limit=None): """ Returns the incoming- and outgoing-adjacent vertices of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: Neo4jResponse """ script = self.scripts.get('bothV') params = dict(_id=_id, label=label, start=start, limit=limit) return self.gremlin(script, params) #: Index Proxy - Vertex def create_vertex_index(self, index_name, *args, **kwds): """ Creates a vertex index with the specified params. :param index_name: Name of the index to create. :type index_name: str :rtype: Neo4jResponse """ default_config = {'type': "exact", 'provider': "lucene"} index_config = kwds.pop("index_config", default_config) path = build_path(index_path, vertex_path) params = dict(name=index_name, config=index_config) resp = self.request.post(path, params) resp._set_index_name(index_name) return resp def get_vertex_indices(self): """ Returns all the vertex indices. :rtype: Neo4jResponse """ path = build_path(index_path, vertex_path) params = None return self.request.get(path, params) def get_vertex_index(self, index_name): """ Returns the vertex index with the index_name. :param index_name: Name of the index. :type index_name: str :rtype: Neo4jResponse """ resp = self.get_vertex_indices() resp.results = self._get_index_results(index_name, resp) if resp.results: resp._set_index_name(index_name) return resp def get_or_create_vertex_index(self, index_name, *args, **kwds): """ Get a Vertex Index or create it if it doesn't exist. :param index_name: Index name. :type index_name: str :param index_config: Index configuration. :type index_config: dict :rtype: bulbs.neo4jserver.index.Index """ # Neo4j's create index endpoint returns the index if it already exists return self.create_vertex_index(index_name, *args, **kwds) def delete_vertex_index(self, index_name): """ Deletes the vertex index with the index_name. :param index_name: Name of the index. :type index_name: str :rtype: Neo4jResponse """ path = build_path(index_path, vertex_path, index_name) params = None return self.request.delete(path, params) # Index Proxy - Edge def create_edge_index(self, index_name, *args, **kwds): """ Creates a edge index with the specified params. :param index_name: Name of the index. :type index_name: str :rtype: Neo4jResponse """ default_config = {'type': "exact", 'provider': "lucene"} index_config = kwds.pop("index_config", default_config) path = build_path(index_path, edge_path) params = dict(name=index_name, config=index_config) resp = self.request.post(path, params) resp._set_index_name(index_name) return resp def get_edge_indices(self): """ Returns a dict of all the vertex indices. :rtype: Neo4jResponse """ path = build_path(index_path, edge_path) params = None return self.request.get(path, params) def get_edge_index(self, index_name): """ Returns the edge index with the index_name. :param index_name: Name of the index. :type index_name: str :rtype: Neo4jResponse """ resp = self.get_edge_indices() resp.results = self._get_index_results(index_name, resp) if resp.results: resp._set_index_name(index_name) return resp def get_or_create_edge_index(self, index_name, *args, **kwds): """ Get a Edge Index or create it if it doesn't exist. :param index_name: Index name. :type index_name: str :param index_config: Index configuration. :type index_config: dict :rtype: bulbs.neo4jserver.index.Index """ # Neo4j's create index endpoint returns the index if it already exists return self.create_edge_index(index_name, *args, **kwds) def delete_edge_index(self, index_name): """ Deletes the edge index with the index_name. :param index_name: Name of the index. :type index_name: str :rtype: Neo4jResponse """ path = build_path(index_path, edge_path, index_name) params = None return self.request.delete(path, params) # Index Container - Vertex def put_vertex(self, index_name, key, value, _id): """ Adds a vertex to the index with the index_name. :param index_name: Name of the index. :type index_name: str :param key: Name of the key. :type key: str :param value: Value of the key. :type value: str :param _id: Vertex ID :type _id: int :rtype: Neo4jResponse """ uri = "%s/%s/%d" % (self.config.root_uri, vertex_path, _id) path = build_path(index_path, vertex_path, index_name) params = dict(key=key, value=value, uri=uri) return self.request.post(path, params) def lookup_vertex(self, index_name, key, value): """ Returns the vertices indexed with the key and value. :param index_name: Name of the index. :type index_name: str :param key: Name of the key. :type key: str :param value: Value of the key. :type value: str :rtype: Neo4jResponse """ # converting all values to strings because that's how they're stored path = build_path(index_path, vertex_path, index_name, key, value) params = None return self.request.get(path, params) def create_unique_vertex(self, index_name, key, value, data=None): """ Create unique (based on the key / value pair) vertex with the properties described by data. :param index_name: Name of the index. :type index_name: str :param key: Name of the key. :type key: str :param value: Value of the key. :type value: str :param data: Properties of the new element. :type data: dict :rtype: Neo4jResponse """ data = {} if data is None else data data = self._remove_null_values(data) path = (build_path(index_path, vertex_path, index_name) + '?uniqueness=get_or_create') params = {'key': key, 'value': value, 'properties': data} return self.request.post(path, params) def query_vertex(self, index_name, query): """ Queries the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param query: Lucene query string :type query: str :rtype: Neo4jResponse """ path = build_path(index_path, vertex_path, index_name) params = dict(query=query) return self.request.get(path, params) def remove_vertex(self, index_name, _id, key=None, value=None): """ Removes a vertex from the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param key: Optional. Name of the key. :type key: str :param value: Optional. Value of the key. :type value: str :rtype: Neo4jResponse """ path = build_path(index_path, vertex_path, index_name, key, value, _id) params = None return self.request.delete(path, params) # Index Container - Edge def put_edge(self, index_name, key, value, _id): """ Adds an edge to the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param key: Name of the key. :type key: str :param value: Value of the key. :type value: str :param _id: Edge ID :type _id: int :rtype: Neo4jResponse """ uri = "%s/%s/%d" % (self.config.root_uri, edge_path, _id) path = build_path(index_path, edge_path, index_name) params = dict(key=key, value=value, uri=uri) return self.request.post(path, params) def lookup_edge(self, index_name, key, value): """ Looks up an edge in the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param key: Name of the key. :type key: str :param value: Value of the key. :type value: str :rtype: Neo4jResponse """ # converting all values to strings because that's how they're stored path = build_path(index_path, edge_path, index_name, key, value) params = None return self.request.get(path, params) def query_edge(self, index_name, query): """ Queries the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param query: Lucene query string :type query: str :rtype: Neo4jResponse """ path = build_path(index_path, edge_path, index_name) params = dict(query=query) return self.request.get(path, params) def remove_edge(self, index_name, _id, key=None, value=None): """ Removes an edge from the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param _id: Edge ID :type _id: int :param key: Optional. Name of the key. :type key: str :param value: Optional. Value of the key. :type value: str :rtype: Neo4jResponse """ path = build_path(index_path, edge_path, index_name, key, value, _id) params = None return self.request.delete(path, params) # Model Proxy - Vertex def create_indexed_vertex(self, data, index_name, keys=None): """ Creates a vertex, indexes it, and returns the Response. :param data: Property data. :type data: dict :param index_name: Name of the index. :type index_name: str :param keys: Property keys to index. :type keys: list :rtype: Neo4jResponse """ data = self._remove_null_values(data) params = dict(data=data, index_name=index_name, keys=keys) script = self.scripts.get("create_indexed_vertex") return self.gremlin(script, params) def update_indexed_vertex(self, _id, data, index_name, keys=None): """ Updates an indexed vertex and returns the Response. :param index_name: Name of the index. :type index_name: str :param data: Property data. :type data: dict :param index_name: Name of the index. :type index_name: str :param keys: Property keys to index. :type keys: list :rtype: Neo4jResponse """ data = self._remove_null_values(data) params = dict(_id=_id, data=data, index_name=index_name, keys=keys) script = self.scripts.get("update_indexed_vertex") return self.gremlin(script, params) # Model Proxy - Edge def create_indexed_edge(self, outV, label, inV, data, index_name, keys=None): """ Creates a edge, indexes it, and returns the Response. :param outV: Outgoing vertex ID. :type outV: int :param label: Edge label. :type label: str :param inV: Incoming vertex ID. :type inV: int :param data: Property data. :type data: dict :param index_name: Name of the index. :type index_name: str :param keys: Property keys to index. Defaults to None (indexes all properties). :type keys: list :rtype: Neo4jResponse """ data = self._remove_null_values(data) edge_params = dict(outV=outV, label=label, inV=inV, label_var=self.config.label_var) params = dict(data=data, index_name=index_name, keys=keys) params.update(edge_params) script = self.scripts.get("create_indexed_edge") return self.gremlin(script, params) def update_indexed_edge(self, _id, data, index_name, keys=None): """ Updates an indexed edge and returns the Response. :param _id: Edge ID. :type _id: int :param data: Property data. :type data: dict :param index_name: Name of the index. :type index_name: str :param keys: Property keys to index. Defaults to None (indexes all properties). :type keys: list :rtype: Neo4jResponse """ data = self._remove_null_values(data) params = dict(_id=_id, data=data, index_name=index_name, keys=keys) script = self.scripts.get("update_indexed_edge") return self.gremlin(script, params) # Metadata def set_metadata(self, key, value): """ Sets the metadata key to the supplied value. :param key: Metadata key :type key: str :param value: Metadata value. :type value: str, int, or list :rtype: Neo4jResponse """ script = self.scripts.get("set_metadata") params = dict(key=key, value=value) return self.gremlin(script, params) def get_metadata(self, key, default_value=None): """ Returns the value of metadata for the key. :param key: Metadata key :type key: str :param default_value: Default value to return if the key is not found. :type default_value: str, int, or list :rtype: Neo4jResponse """ script = self.scripts.get("get_metadata") params = dict(key=key, default_value=default_value) return self.gremlin(script, params) def remove_metadata(self, key): """ Removes the metadata key and value. :param key: Metadata key :type key: str :rtype: Neo4jResponse """ script = self.scripts.get("remove_metadata") params = dict(key=key) return self.gremlin(script, params) # Private def _remove_null_values(self, data): """Removes null property values because they aren't valid in Neo4j.""" # Neo4j Server uses PUTs to overwrite all properties so no need # to worry about deleting props that are being set to null. data = data or {} clean_data = [(k, data[k]) for k in data if data[k] is not None] # Python 3 return dict(clean_data) def _get_index_results(self, index_name, resp): """ Returns the index from a dict of indicies. """ if resp.content and index_name in resp.content: result = resp.content[index_name] return Neo4jResult(result, self.config) # Batch related def _placeholder(self, _id): pattern = "^{.*}$" match = re.search(pattern, str(_id)) if match: placeholder = match.group() return placeholder def _build_vertex_path(self, _id, *args): # if the _id is a placeholder, return the placeholder; # othewise, return a normal vertex path placeholder = self._placeholder(_id) if placeholder: segments = [placeholder] else: segments = [vertex_path, _id] segments = segments + list(args) return build_path(*segments) def _build_vertex_uri(self, _id, *args): placeholder = self._placeholder(_id) if placeholder: return placeholder root_uri = self.config.root_uri.rstrip("/") segments = [vertex_path, _id] + list(args) path = build_path(*segments) uri = "%s/%s" % (root_uri, path) return uri def _build_edge_path(self, _id): # if the _id is a placeholder, return the placeholder; # othewise, return a normal edge path return self._placeholder(_id) or build_path(edge_path, _id) def _build_edge_uri(self, _id): pass
class Neo4jResource(Resource): """Low-level client that connects to Neo4j Server and returns a response.""" def __init__(self,config): self.config = config self.registry = Registry(config) self.scripts = GroovyScripts() self.scripts.update(self._get_scripts_file("gremlin.groovy")) self.registry.add_scripts("gremlin",self.scripts) self.type_system = JSONTypeSystem() self.request = Neo4jRequest(config,self.type_system.content_type) self.message = Message(config, self.scripts) # Gremlin def gremlin(self,script,params=None): """Executes a Gremlin script and returns the Response.""" message = self.message.gremlin(script,params) return self.request.send(message) # Cypher def cypher(self,query,params=None): """Executes a Cypher query and returns the Response.""" message = self.message.cypher(query,params) return self.request.send(message) # Vertex Proxy def create_vertex(self,data): """Creates a vertex and returns the Response.""" if self.config.autoindex is True: index_name = self.config.vertex_autoindex return self.create_indexed_vertex(data,index_name,keys=None) message = self.message.create_vertex(data) return self.request.send(message) def get_vertex(self,_id): """Gets the vertex with the _id and returns the Response.""" message = self.message.get_vertex(_id) return self.request.send(message) def update_vertex(self,_id,data): """Updates the vertex with the _id and returns the Response.""" if self.config.autoindex is True: index_name = self.config.vertex_autoindex return self.update_indexed_vertex(_id,data,index_name,keys=None) message = self.message.update_vertex(_id,data) return self.request.send(message) def delete_vertex(self,_id): """Deletes a vertex with the _id and returns the Response.""" message = self.message.delete_vertex(_id) return self.request.send(message) # Edge Proxy def create_edge(self,outV,label,inV,data={}): """Creates a edge and returns the Response.""" if self.config.autoindex is True: index_name = self.config.edge_autoindex return self.create_indexed_edge(outV,label,inV,data,index_name,keys=None) message = self.message.create_edge(outV,label,inV,data) return self.request.send(message) def get_edge(self,_id): """Gets the edge with the _id and returns the Response.""" message = self.message.get_edge(_id) return self.request.send(message) def update_edge(self,_id,data): """Updates the edge with the _id and returns the Response.""" if self.config.autoindex is True: index_name = self.config.edge_autoindex return self.update_indexed_edge(_id,data,index_name,keys=None) message = self.message.update_edge(_id, data) return self.request.send(message) def delete_edge(self,_id): """Deletes a edge with the _id and returns the Response.""" message = self.message.delete_edge(_id) return self.request.send(message) # Vertex Container def outE(self,_id,label=None): """Return the outgoing edges of the vertex.""" message = self.message.outE(_id,label) return self.request.send(message) def inE(self,_id,label=None): """Return the incoming edges of the vertex.""" message = self.message.outE(_id,label) return self.request.send(message) def bothE(self,_id,label=None): """Return all incoming and outgoing edges of the vertex.""" message = self.message.bothE(_id,label) return self.request.send(message) def outV(self,_id,label=None): """Return the out-adjacent vertices to the vertex.""" message = self.message.outE(_id,label) return self.request.send(message) def inV(self,_id,label=None): """Return the in-adjacent vertices of the vertex.""" message = self.message.outE(_id,label) return self.request.send(message) def bothV(self,_id,label=None): """Return all incoming- and outgoing-adjacent vertices of vertex.""" message = self.message.outE(_id,label) return self.request.send(message) # Index Proxy - Vertex def create_vertex_index(self,index_name,*args,**kwds): """Creates a vertex index with the specified params.""" message = self.message.create_vertex_index(index_name,*args,**kwds) resp = self.request.send(message) resp.set_index_name(index_name) return resp def get_vertex_indices(self): """Returns a map of all the vertex indices.""" message = self.message.get_vertex_indices() return self.request.send(message) def get_vertex_index(self,index_name): """Returns the vertex index with the index_name.""" resp = self.get_vertex_indices() resp.results = self._get_index_results(index_name,resp) if resp.results: resp.set_index_name(index_name) return resp def delete_vertex_index(self,name): """Deletes the vertex index with the index_name.""" message = self.message.delete_vertex_index(name) return self.request.send(message) # Index Proxy - Edge def create_edge_index(self,index_name,*args,**kwds): """Creates a edge index with the specified params.""" message = self.message.create_edge_index(index_name,*args,**kwds) resp = self.request.send(message) resp.set_index_name(index_name) return resp def get_edge_indices(self): """Returns a map of all the vertex indices.""" message = self.message.get_edge_indices() return self.request.send(message) def get_edge_index(self,index_name): """Returns the edge index with the index_name.""" resp = self.get_edge_indices() resp.results = self._get_index_results(index_name,resp) if resp.results: resp.set_index_name(index_name) return resp def delete_edge_index(self,name): """Deletes the edge index with the index_name.""" message = self.message.delete_edge_index(name) return self.request.send(message) # Index Container - Vertex def put_vertex(self,name,key,value,_id): """Adds a vertex to the index with the index_name.""" message = self.message.put_vertex(name,key,value,_id) return self.request.send(message) def lookup_vertex(self,name,key,value): """Returns the vertices indexed with the key and value.""" message = self.message.lookup_vertex(name,key,value) return self.request.send(message) def query_vertex(self,name,params): """Returns the vertices for the index query.""" message = self.message.query_vertex(name,params) return self.request.send(message) def remove_vertex(self,name,_id,key=None,value=None): """Removes a vertex from the index and returns the Response.""" message = self.message.remove_vertex(name,_id,key,value) return self.request.send(message) # Index Container - Edge def put_edge(self,name,key,value,_id): """Adds an edge to the index and returns the Response.""" message = self.message.put_edge(name,key,value,_id) return self.request.send(message) def lookup_edge(self,name,key,value): """Looks up an edge in the index and returns the Response.""" message = self.message.lookup_edge(name,key,value) return self.request.send(message) def query_edge(self,name,params): """Queries for an edge in the index and returns the Response.""" message = self.message.query_edge(name,params) return self.request.send(message) def remove_edge(self,name,_id,key=None,value=None): """Removes an edge from the index and returns the Response.""" message = self.message.remove_edge(name,_id,key,value) return self.request.send(message) # Model Proxy - Vertex def create_indexed_vertex(self,data,index_name,keys=None): """Creates a vertex, indexes it, and returns the Response.""" message = self.message.create_indexed_vertex(data,index_name,keys) return self.request.send(message) # Batch try... #def create_indexed_vertex(self,data,index_name,keys=None): # """Creates a vertex, indexes it, and returns the Response.""" # batch = Neo4jBatch(self.resource) # placeholder = batch.add(self.message.create_vertex(data)) # for key in keys: # value = data.get(key) # if value is None: continue # batch.add(self.message.put_vertex(index_name,key,value,placeholder)) # resp = batch.send() # #for result in resp.results: def update_indexed_vertex(self,_id,data,index_name,keys=None): """Updates an indexed vertex and returns the Response.""" message = self.message.update_indexed_vertex(_id,data,index_name,keys) return self.request.send(message) # Model Proxy - Edge def create_indexed_edge(self,outV,label,inV,data,index_name,keys=None): """Creates a edge, indexes it, and returns the Response.""" message = self.message.create_indexed_edge(outV,label,inV,data,index_name,keys) return self.request.send(message) def update_indexed_edge(self,_id,data,index_name,keys=None): """Updates an indexed edge and returns the Response.""" message = self.message.update_indexed_edge(_id,data,index_name,keys) return self.request.send(message) # Utils def warm_cache(self): """Warms the server cache by loading elements into memory.""" message = self.message.warm_cache() return self.request.send(message) # Private def _get_scripts_file(self,file_name): """Returns the full file path for the scripts file.""" dir_name = os.path.dirname(__file__) file_path = get_file_path(dir_name,file_name) return file_path def _get_index_results(self,index_name,resp): """Returns the index from a map of indicies.""" if resp.content and index_name in resp.content: result = resp.content[index_name] return Neo4jResult(result, self.config)
class Neo4jClient(Client): """ Low-level client that sends a request to Neo4j Server and returns a response. :param config: Optional Config object. Defaults to default Config. :type config: bulbs.config.Config :ivar config: Config object. :ivar registry: Registry object. :ivar scripts: GroovyScripts object. :ivar type_system: JSONTypeSystem object. :ivar request: Neo4jRequest object. Example: >>> from bulbs.neo4jserver import Neo4jClient >>> client = Neo4jClient() >>> response = client.get_all_vertices() >>> result = response.results.next() """ #: Default URI for the database. default_uri = NEO4J_URI #: Request class for the Client. request_class = Neo4jRequest def __init__(self, config=None): self.config = config or Config(self.default_uri) self.registry = Registry(self.config) self.type_system = JSONTypeSystem() self.request = self.request_class(self.config, self.type_system.content_type) # Neo4j supports Gremlin so include the Gremlin-Groovy script library self.scripts = GroovyScripts() # Also include the Neo4j Server-specific Gremlin-Groovy scripts scripts_file = get_file_path(__file__, "gremlin.groovy") self.scripts.update(scripts_file) # Add it to the registry. This allows you to have more than one scripts namespace. self.registry.add_scripts("gremlin", self.scripts) # Gremlin def gremlin(self, script, params=None): """ Executes a Gremlin script and returns the Response. :param script: Gremlin script to execute. :type script: str :param params: Param bindings for the script. :type params: dict :rtype: Neo4jResponse """ path = gremlin_path params = dict(script=script, params=params) return self.request.post(path, params) # Cypher def cypher(self, query, params=None): """ Executes a Cypher query and returns the Response. :param query: Cypher query to execute. :type query: str :param params: Param bindings for the query. :type params: dict :rtype: Neo4jResponse """ path = cypher_path params = dict(query=query, params=params) resp = self.request.post(path, params) # Cypher data hack resp.total_size = len(resp.results.data) resp.results = (Neo4jResult(result[0], self.config) for result in resp.results.data) return resp # Vertex Proxy def create_vertex(self, data): """ Creates a vertex and returns the Response. :param data: Property data. :type data: dict :rtype: Neo4jResponse """ if self.config.autoindex is True: index_name = self.config.vertex_index return self.create_indexed_vertex(data, index_name, keys=None) path = vertex_path params = self._remove_null_values(data) return self.request.post(path, params) def get_vertex(self, _id): """ Gets the vertex with the _id and returns the Response. :param data: Vertex ID. :type data: int :rtype: Neo4jResponse """ path = build_path(vertex_path, _id) params = None return self.request.get(path, params) def get_all_vertices(self): """ Returns a Response containing all the vertices in the Graph. :rtype: Neo4jResponse """ script = self.scripts.get("get_vertices") params = None return self.gremlin(script, params) def update_vertex(self, _id, data): """ Updates the vertex with the _id and returns the Response. :param _id: Vertex ID. :type _id: dict :param data: Property data. :type data: dict :rtype: Neo4jResponse """ if self.config.autoindex is True: index_name = self.config.vertex_index return self.update_indexed_vertex(_id, data, index_name, keys=None) path = self._build_vertex_path(_id, "properties") params = self._remove_null_values(data) return self.request.put(path, params) def delete_vertex(self, _id): """ Deletes a vertex with the _id and returns the Response. :param _id: Vertex ID. :type _id: dict :rtype: Neo4jResponse """ script = self.scripts.get("delete_vertex") params = dict(_id=_id) return self.gremlin(script, params) # Edge Proxy def create_edge(self, outV, label, inV, data=None): """ Creates a edge and returns the Response. :param outV: Outgoing vertex ID. :type outV: int :param label: Edge label. :type label: str :param inV: Incoming vertex ID. :type inV: int :param data: Property data. :type data: dict or None :rtype: Neo4jResponse """ if self.config.autoindex is True: index_name = self.config.edge_index return self.create_indexed_edge(outV, label, inV, data, index_name, keys=None) data = self._remove_null_values(data) inV_uri = self._build_vertex_uri(inV) path = build_path(vertex_path, outV, "relationships") params = {"to": inV_uri, "type": label, "data": data} return self.request.post(path, params) def get_edge(self, _id): """ Gets the edge with the _id and returns the Response. :param data: Edge ID. :type data: int :rtype: Neo4jResponse """ path = build_path(edge_path, _id) params = None return self.request.get(path, params) def get_all_edges(self): """ Returns a Response containing all the edges in the Graph. :rtype: Neo4jResponse """ script = self.scripts.get("get_edges") params = None return self.gremlin(script, params) def update_edge(self, _id, data): """ Updates the edge with the _id and returns the Response. :param _id: Edge ID. :type _id: dict :param data: Property data. :type data: dict :rtype: Neo4jResponse """ if self.config.autoindex is True: index_name = self.config.edge_index return self.update_indexed_edge(_id, data, index_name, keys=None) path = build_path(edge_path, _id, "properties") params = self._remove_null_values(data) return self.request.put(path, params) def delete_edge(self, _id): """ Deletes a edge with the _id and returns the Response. :param _id: Edge ID. :type _id: dict :rtype: Neo4jResponse """ path = build_path(edge_path, _id) params = None return self.request.delete(path, params) # Vertex Container def outE(self, _id, label=None): """ Returns the outgoing edges of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: Neo4jResponse """ script = self.scripts.get("outE") params = dict(_id=_id, label=label) return self.gremlin(script, params) def inE(self, _id, label=None): """ Returns the incoming edges of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: Neo4jResponse """ script = self.scripts.get("inE") params = dict(_id=_id, label=label) return self.gremlin(script, params) def bothE(self, _id, label=None): """ Returns the incoming and outgoing edges of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: Neo4jResponse """ script = self.scripts.get("bothE") params = dict(_id=_id, label=label) return self.gremlin(script, params) def outV(self, _id, label=None): """ Returns the out-adjacent vertices of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: Neo4jResponse """ script = self.scripts.get("outV") params = dict(_id=_id, label=label) return self.gremlin(script, params) def inV(self, _id, label=None): """ Returns the in-adjacent vertices of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: Neo4jResponse """ script = self.scripts.get("inV") params = dict(_id=_id, label=label) return self.gremlin(script, params) def bothV(self, _id, label=None): """ Returns the incoming- and outgoing-adjacent vertices of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: Neo4jResponse """ script = self.scripts.get("bothV") params = dict(_id=_id, label=label) return self.gremlin(script, params) #: Index Proxy - Vertex def create_vertex_index(self, index_name, *args, **kwds): """ Creates a vertex index with the specified params. :param index_name: Name of the index to create. :type index_name: str :rtype: Neo4jResponse """ index_type = kwds.pop("index_type", "exact") provider = kwds.pop("provider", "lucene") # keys = kwds.pop("keys",None) # config = {'type':index_type,'provider':provider,'keys':str(keys)} config = {"type": index_type, "provider": provider} path = build_path(index_path, "node") params = dict(name=index_name, config=config) resp = self.request.post(path, params) resp._set_index_name(index_name) return resp def get_vertex_indices(self): """ Returns all the vertex indices. :rtype: Neo4jResponse """ path = build_path(index_path, "node") params = None return self.request.get(path, params) def get_vertex_index(self, index_name): """ Returns the vertex index with the index_name. :param index_name: Name of the index. :type index_name: str :rtype: Neo4jResponse """ resp = self.get_vertex_indices() resp.results = self._get_index_results(index_name, resp) if resp.results: resp._set_index_name(index_name) return resp def delete_vertex_index(self, index_name): """ Deletes the vertex index with the index_name. :param index_name: Name of the index. :type index_name: str :rtype: Neo4jResponse """ path = build_path(index_path, "node", index_name) params = None return self.request.delete(path, params) # Index Proxy - Edge def create_edge_index(self, index_name, *args, **kwds): """ Creates a edge index with the specified params. :param index_name: Name of the index. :type index_name: str :rtype: Neo4jResponse """ path = build_path(index_path, edge_path) params = dict(name=index_name) resp = self.request.post(path, params) resp._set_index_name(index_name) return resp def get_edge_indices(self): """ Returns a dict of all the vertex indices. :rtype: Neo4jResponse """ path = build_path(index_path, edge_path) params = None return self.request.get(path, params) def get_edge_index(self, index_name): """ Returns the edge index with the index_name. :param index_name: Name of the index. :type index_name: str :rtype: Neo4jResponse """ resp = self.get_edge_indices() resp.results = self._get_index_results(index_name, resp) if resp.results: resp._set_index_name(index_name) return resp def delete_edge_index(self, index_name): """ Deletes the edge index with the index_name. :param index_name: Name of the index. :type index_name: str :rtype: Neo4jResponse """ path = build_path(index_path, edge_path, index_name) params = None return self.request.delete(path, params) # Index Container - Vertex def put_vertex(self, index_name, key, value, _id): """ Adds a vertex to the index with the index_name. :param index_name: Name of the index. :type index_name: str :param key: Name of the key. :type key: str :param value: Value of the key. :type value: str :param _id: Vertex ID :type _id: int :rtype: Neo4jResponse """ uri = "%s/%s/%d" % (self.config.root_uri, "node", _id) path = build_path(index_path, "node", index_name) params = dict(key=key, value=value, uri=uri) return self.request.post(path, params) def lookup_vertex(self, index_name, key, value): """ Returns the vertices indexed with the key and value. :param index_name: Name of the index. :type index_name: str :param key: Name of the key. :type key: str :param value: Value of the key. :type value: str :rtype: Neo4jResponse """ # converting all values to strings because that's how they're stored key, value = quote(key), quote(str(value)) path = build_path(index_path, "node", index_name, key, value) params = None return self.request.get(path, params) def remove_vertex(self, index_name, _id, key=None, value=None): """ Removes a vertex from the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param key: Optional. Name of the key. :type key: str :param value: Optional. Value of the key. :type value: str :rtype: Neo4jResponse """ key, value = quote(key), quote(value) path = build_path(index_path, "node", index_name, key, value, _id) params = None return self.request.delete(path, params) # Index Container - Edge def put_edge(self, index_name, key, value, _id): """ Adds an edge to the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param key: Name of the key. :type key: str :param value: Value of the key. :type value: str :param _id: Edge ID :type _id: int :rtype: Neo4jResponse """ uri = "%s/%s/%d" % (self.config.root_uri, edge_path, _id) path = build_path(index_path, edge_path, index_name) params = dict(key=key, value=value, uri=uri) return self.request.post(path, params) def lookup_edge(self, index_name, key, value): """ Looks up an edge in the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param key: Name of the key. :type key: str :param value: Value of the key. :type value: str :rtype: Neo4jResponse """ # converting all values to strings because that's how they're stored key, value = quote(key), quote(str(value)) path = build_path(index_path, edge_path, index_name, key, value) params = None return self.request.get(path, params) def remove_edge(self, index_name, _id, key=None, value=None): """ Removes an edge from the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param _id: Edge ID :type _id: int :param key: Optional. Name of the key. :type key: str :param value: Optional. Value of the key. :type value: str :rtype: Neo4jResponse """ key, value = quote(key), quote(value) path = build_path(index_path, edge_path, index_name, key, value, _id) params = None return self.request.delete(path, params) # Model Proxy - Vertex def create_indexed_vertex(self, data, index_name, keys=None): """ Creates a vertex, indexes it, and returns the Response. :param data: Property data. :type data: dict :param index_name: Name of the index. :type index_name: str :param keys: Property keys to index. :type keys: list :rtype: Neo4jResponse """ data = self._remove_null_values(data) params = dict(data=data, index_name=index_name, keys=keys) script = self.scripts.get("create_indexed_vertex") return self.gremlin(script, params) def update_indexed_vertex(self, _id, data, index_name, keys=None): """ Updates an indexed vertex and returns the Response. :param index_name: Name of the index. :type index_name: str :param data: Property data. :type data: dict :param index_name: Name of the index. :type index_name: str :param keys: Property keys to index. :type keys: list :rtype: Neo4jResponse """ data = self._remove_null_values(data) params = dict(_id=_id, data=data, index_name=index_name, keys=keys) script = self.scripts.get("update_indexed_vertex") return self.gremlin(script, params) # Model Proxy - Edge def create_indexed_edge(self, outV, label, inV, data, index_name, keys=None): """ Creates a edge, indexes it, and returns the Response. :param outV: Outgoing vertex ID. :type outV: int :param label: Edge label. :type label: str :param inV: Incoming vertex ID. :type inV: int :param data: Property data. :type data: dict :param index_name: Name of the index. :type index_name: str :param keys: Property keys to index. Defaults to None (indexes all properties). :type keys: list :rtype: Neo4jResponse """ data = self._remove_null_values(data) edge_params = dict(outV=outV, label=label, inV=inV, label_var=self.config.label_var) params = dict(data=data, index_name=index_name, keys=keys) params.update(edge_params) script = self.scripts.get("create_indexed_edge") return self.gremlin(script, params) def update_indexed_edge(self, _id, data, index_name, keys=None): """ Updates an indexed edge and returns the Response. :param _id: Edge ID. :type _id: int :param data: Property data. :type data: dict :param index_name: Name of the index. :type index_name: str :param keys: Property keys to index. Defaults to None (indexes all properties). :type keys: list :rtype: Neo4jResponse """ data = self._remove_null_values(data) params = dict(_id=_id, data=data, index_name=index_name, keys=keys) script = self.scripts.get("update_indexed_edge") return self.gremlin(script, params) # Metadata def set_metadata(self, key, value): """ Sets the metadata key to the supplied value. :param key: Metadata key :type key: str :param value: Metadata value. :type value: str, int, or list :rtype: Neo4jResponse """ script = self.scripts.get("set_metadata") params = dict(key=key, value=value) return self.gremlin(script, params) def get_metadata(self, key, default_value=None): """ Returns the value of metadata for the key. :param key: Metadata key :type key: str :param default_value: Default value to return if the key is not found. :type default_value: str, int, or list :rtype: Neo4jResponse """ script = self.scripts.get("get_metadata") params = dict(key=key, default_value=default_value) return self.gremlin(script, params) def remove_metadata(self, key): """ Removes the metadata key and value. :param key: Metadata key :type key: str :rtype: Neo4jResponse """ script = self.scripts.get("remove_metadata") params = dict(key=key) return self.gremlin(script, params) # Private def _remove_null_values(self, data): """Removes null property values because they aren't valid in Neo4j.""" # Neo4j Server uses PUTs to overwrite all properties so no need # to worry about deleting props that are being set to null. data = data or {} clean_data = [(k, data[k]) for k in data if data[k] is not None] # Python 3 return dict(clean_data) def _get_index_results(self, index_name, resp): """ Returns the index from a dict of indicies. """ if resp.content and index_name in resp.content: result = resp.content[index_name] return Neo4jResult(result, self.config) # Batch related def _placeholder(self, _id): pattern = "^{.*}$" match = re.search(pattern, str(_id)) if match: placeholder = match.group() return placeholder def _build_vertex_path(self, _id, *args): # if the _id is a placeholder, return the placeholder; # othewise, return a normal vertex path placeholder = self._placeholder(_id) if placeholder: segments = [placeholder] else: segments = [vertex_path, _id] segments = segments + list(args) return build_path(*segments) def _build_vertex_uri(self, _id, *args): placeholder = self._placeholder(_id) if placeholder: return placeholder root_uri = self.config.root_uri.rstrip("/") segments = [vertex_path, _id] + list(args) path = build_path(*segments) uri = "%s/%s" % (root_uri, path) return uri def _build_edge_path(self, _id): # if the _id is a placeholder, return the placeholder; # othewise, return a normal edge path return self._placeholder(_id) or build_path(edge_path, _id) def _build_edge_uri(self, _id): pass
class Neo4jClient(Client): """ Low-level client that sends a request to Neo4j Server and returns a response. :param config: Optional Config object. Defaults to default Config. :type config: Config :ivar config: Config object. :ivar scripts: GroovyScripts object. :ivar type_system: JSONTypeSystem object. :ivar request: Neo4jRequest object. :ivar message: RequestMessage object. Example: >>> from bulbs.neo4jserver import Neo4jClient >>> client = Neo4jClient() >>> script = client.scripts.get("get_vertices") >>> response = client.gremlin(script, params=None) >>> result = response.results.next() """ #: Default URI for the database. default_uri = NEO4J_URI def __init__(self, config=None): self.config = config or Config(self.default_uri) self.registry = Registry(self.config) # Neo4j supports Gremlin so include the Gremlin-Groovy script library self.scripts = GroovyScripts() # Also include the Neo4j Server-specific Gremlin-Groovy scripts scripts_file = get_file_path(__file__, "gremlin.groovy") self.scripts.update(scripts_file) # Add it to the registry. This allows you to have more than one scripts namespace. self.registry.add_scripts("gremlin", self.scripts) self.type_system = JSONTypeSystem() self.request = Neo4jRequest(self.config, self.type_system.content_type) self.message = RequestMessage(self.config, self.scripts) #: Gremlin def gremlin(self, script, params=None): """ Executes a Gremlin script and returns the Response. :param script: Gremlin script to execute. :type script: str :param params: Param bindings for the script. :type params: dict :rtype: Neo4jResponse """ message = self.message.gremlin(script,params) return self.request.send(message) # Cypher def cypher(self, query, params=None): """ Executes a Cypher query and returns the Response. :param query: Cypher query to execute. :type query: str :param params: Param bindings for the query. :type params: dict :rtype: Neo4jResponse """ message = self.message.cypher(query,params) resp = self.request.send(message) # Cypher data hack resp.results = (self.result_class(result[0], self.config) for result in resp.results.data) resp.total_size = len(resp.results.data) return resp #: Vertex Proxy def create_vertex(self, data): """ Creates a vertex and returns the Response. :param data: Property data. :type data: dict :rtype: Neo4jResponse """ if self.config.autoindex is True: index_name = self.config.vertex_index return self.create_indexed_vertex(data,index_name,keys=None) message = self.message.create_vertex(data) return self.request.send(message) def get_vertex(self, _id): """ Gets the vertex with the _id and returns the Response. :param data: Vertex ID. :type data: int :rtype: Neo4jResponse """ message = self.message.get_vertex(_id) return self.request.send(message) def update_vertex(self, _id, data): """ Updates the vertex with the _id and returns the Response. :param _id: Vertex ID. :type _id: dict :param data: Property data. :type data: dict :rtype: Neo4jResponse """ if self.config.autoindex is True: index_name = self.config.vertex_index return self.update_indexed_vertex(_id,data,index_name,keys=None) message = self.message.update_vertex(_id,data) return self.request.send(message) def delete_vertex(self, _id): """ Deletes a vertex with the _id and returns the Response. :param _id: Vertex ID. :type _id: dict :rtype: Neo4jResponse """ message = self.message.delete_vertex(_id) return self.request.send(message) #: Edge Proxy def create_edge(self, outV, label, inV, data={}): """ Creates a edge and returns the Response. :param outV: Outgoing vertex ID. :type outV: int :param label: Edge label. :type label: str :param inV: Incoming vertex ID. :type inV: int :param data: Property data. :type data: dict :rtype: Neo4jResponse """ if self.config.autoindex is True: index_name = self.config.edge_index return self.create_indexed_edge(outV,label,inV,data,index_name,keys=None) message = self.message.create_edge(outV,label,inV,data) return self.request.send(message) def get_edge(self, _id): """ Gets the edge with the _id and returns the Response. :param data: Edge ID. :type data: int :rtype: Neo4jResponse """ message = self.message.get_edge(_id) return self.request.send(message) def update_edge(self, _id, data): """ Updates the edge with the _id and returns the Response. :param _id: Edge ID. :type _id: dict :param data: Property data. :type data: dict :rtype: Neo4jResponse """ if self.config.autoindex is True: index_name = self.config.edge_index return self.update_indexed_edge(_id,data,index_name,keys=None) message = self.message.update_edge(_id, data) return self.request.send(message) def delete_edge(self, _id): """ Deletes a edge with the _id and returns the Response. :param _id: Edge ID. :type _id: dict :rtype: Neo4jResponse """ message = self.message.delete_edge(_id) return self.request.send(message) # Vertex Container def outE(self, _id, label=None): """ Returns the outgoing edges of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: Neo4jResponse """ message = self.message.outE(_id,label) return self.request.send(message) def inE(self, _id, label=None): """ Returns the incoming edges of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: Neo4jResponse """ message = self.message.inE(_id,label) return self.request.send(message) def bothE(self, _id, label=None): """ Returns the incoming and outgoing edges of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: Neo4jResponse """ message = self.message.bothE(_id,label) return self.request.send(message) def outV(self, _id, label=None): """ Returns the out-adjacent vertices of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: Neo4jResponse """ message = self.message.outV(_id,label) return self.request.send(message) def inV(self, _id, label=None): """ Returns the in-adjacent vertices of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: Neo4jResponse """ message = self.message.inV(_id,label) return self.request.send(message) def bothV(self, _id, label=None): """ Returns the incoming- and outgoing-adjacent vertices of the vertex. :param _id: Vertex ID. :type _id: dict :param label: Optional edge label. Defaults to None. :type label: str :rtype: Neo4jResponse """ message = self.message.bothV(_id,label) return self.request.send(message) #: Index Proxy - Vertex def create_vertex_index(self, index_name, *args, **kwds): """ Creates a vertex index with the specified params. :param index_name: Name of the index to create. :type index_name: str :rtype: Neo4jResponse """ message = self.message.create_vertex_index(index_name,*args,**kwds) resp = self.request.send(message) resp._set_index_name(index_name) return resp def get_vertex_indices(self): """ Returns a map of all the vertex indices. :rtype: Neo4jResponse """ message = self.message.get_vertex_indices() return self.request.send(message) def get_vertex_index(self, index_name): """ Returns the vertex index with the index_name. :param index_name: Name of the index. :type index_name: str :rtype: Neo4jResponse """ resp = self.get_vertex_indices() resp.results = self._get_index_results(index_name,resp) if resp.results: resp._set_index_name(index_name) return resp def delete_vertex_index(self, index_name): """ Deletes the vertex index with the index_name. :param index_name: Name of the index. :type index_name: str :rtype: Neo4jResponse """ message = self.message.delete_vertex_index(index_name) return self.request.send(message) # Index Proxy - Edge def create_edge_index(self, index_name, *args, **kwds): """ Creates a edge index with the specified params. :param index_name: Name of the index. :type index_name: str :rtype: Neo4jResponse """ message = self.message.create_edge_index(index_name,*args,**kwds) resp = self.request.send(message) resp._set_index_name(index_name) return resp def get_edge_indices(self): """ Returns a map of all the vertex indices. :rtype: Neo4jResponse """ message = self.message.get_edge_indices() return self.request.send(message) def get_edge_index(self, index_name): """ Returns the edge index with the index_name. :param index_name: Name of the index. :type index_name: str :rtype: Neo4jResponse """ resp = self.get_edge_indices() resp.results = self._get_index_results(index_name, resp) if resp.results: resp._set_index_name(index_name) return resp def delete_edge_index(self, index_name): """ Deletes the edge index with the index_name. :param index_name: Name of the index. :type index_name: str :rtype: Neo4jResponse """ message = self.message.delete_edge_index(index_name) return self.request.send(message) # Index Container - Vertex def put_vertex(self, index_name, key, value, _id): """ Adds a vertex to the index with the index_name. :param index_name: Name of the index. :type index_name: str :param key: Name of the key. :type key: str :param value: Value of the key. :type value: str :param _id: Vertex ID :type _id: int :rtype: Neo4jResponse """ message = self.message.put_vertex(index_name,key,value,_id) return self.request.send(message) def lookup_vertex(self, index_name, key, value): """ Returns the vertices indexed with the key and value. :param index_name: Name of the index. :type index_name: str :param key: Name of the key. :type key: str :param value: Value of the key. :type value: str :rtype: Neo4jResponse """ message = self.message.lookup_vertex(index_name,key,value) return self.request.send(message) def query_vertex(self, index_name, params): """ Returns the vertices for the index query. :param index_name: Name of the index. :type index_name: str :param params: Query params. :type params: dict :rtype: Neo4jResponse """ message = self.message.query_vertex(index_name,params) return self.request.send(message) def remove_vertex(self, index_name, _id, key=None, value=None): """ Removes a vertex from the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param key: Optional. Name of the key. :type key: str :param value: Optional. Value of the key. :type value: str :rtype: Neo4jResponse """ message = self.message.remove_vertex(index_name,_id,key,value) return self.request.send(message) # Index Container - Edge def put_edge(self, index_name, key, value, _id): """ Adds an edge to the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param key: Name of the key. :type key: str :param value: Value of the key. :type value: str :param _id: Edge ID :type _id: int :rtype: Neo4jResponse """ message = self.message.put_edge(index_name,key,value,_id) return self.request.send(message) def lookup_edge(self, index_name, key, value): """ Looks up an edge in the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param key: Name of the key. :type key: str :param value: Value of the key. :type value: str :rtype: Neo4jResponse """ message = self.message.lookup_edge(index_name,key,value) return self.request.send(message) def query_edge(self, index_name, params): """ Queries for an edge in the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param params: Query params. :type params: dict :rtype: Neo4jResponse """ message = self.message.query_edge(index_name,params) return self.request.send(message) def remove_edge(self, index_name, _id, key=None, value=None): """ Removes an edge from the index and returns the Response. :param index_name: Name of the index. :type index_name: str :param _id: Edge ID :type _id: int :param key: Optional. Name of the key. :type key: str :param value: Optional. Value of the key. :type value: str :rtype: Neo4jResponse """ message = self.message.remove_edge(index_name,_id,key,value) return self.request.send(message) # Model Proxy - Vertex def create_indexed_vertex(self, data, index_name, keys=None): """ Creates a vertex, indexes it, and returns the Response. :param data: Property data. :type data: dict :param index_name: Name of the index. :type index_name: str :param keys: Property keys to index. :type keys: list :rtype: Neo4jResponse """ message = self.message.create_indexed_vertex(data,index_name,keys) return self.request.send(message) # Batch try... #def create_indexed_vertex(self,data,index_name,keys=None): # """Creates a vertex, indexes it, and returns the Response.""" # batch = Neo4jBatch(self.client) # placeholder = batch.add(self.message.create_vertex(data)) # for key in keys: # value = data.get(key) # if value is None: continue # batch.add(self.message.put_vertex(index_name,key,value,placeholder)) # resp = batch.send() # #for result in resp.results: def update_indexed_vertex(self, _id, data, index_name, keys=None): """ Updates an indexed vertex and returns the Response. :param index_name: Name of the index. :type index_name: str :param data: Property data. :type data: dict :param index_name: Name of the index. :type index_name: str :param keys: Property keys to index. :type keys: list :rtype: Neo4jResponse """ message = self.message.update_indexed_vertex(_id,data,index_name,keys) return self.request.send(message) # Model Proxy - Edge def create_indexed_edge(self, outV, label, inV, data, index_name, keys=None): """ Creates a edge, indexes it, and returns the Response. :param outV: Outgoing vertex ID. :type outV: int :param label: Edge label. :type label: str :param inV: Incoming vertex ID. :type inV: int :param data: Property data. :type data: dict :param index_name: Name of the index. :type index_name: str :param keys: Property keys to index. Defaults to None (indexes all properties). :type keys: list :rtype: Neo4jResponse """ message = self.message.create_indexed_edge(outV,label,inV,data,index_name,keys) return self.request.send(message) def update_indexed_edge(self, _id, data, index_name, keys=None): """ Updates an indexed edge and returns the Response. :param _id: Edge ID. :type _id: int :param data: Property data. :type data: dict :param index_name: Name of the index. :type index_name: str :param keys: Property keys to index. Defaults to None (indexes all properties). :type keys: list :rtype: Neo4jResponse """ message = self.message.update_indexed_edge(_id, data, index_name, keys) return self.request.send(message) # Batch def batch(self, messages): path = "batch" params = messages return self.request.post(path, params) # Metadata def get_metadata(self, key, default_value=None): script = self.scripts.get("get_metadata") params = dict(key=key, default_value=default_value) return self.gremlin(script, params) def set_metadata(self, key, value): script = self.scripts.get("set_metadata") params = dict(key=key, value=value) return self.gremlin(script, params) def remove_metadata(self, key): script = self.scripts.get("remove_metadata") params = dict(key=key) return self.gremlin(script, params) # Private def _get_index_results(self, index_name, resp): """ Returns the index from a map of indicies. """ if resp.content and index_name in resp.content: result = resp.content[index_name] return Neo4jResult(result, self.config)
class Neo4jResource(Resource): vertex_path = "node" edge_path = "relationships" index_path = "index" gremlin_path = "ext/GremlinPlugin/graphdb/execute_script" cypher_path = "ext/CypherPlugin/graphdb/execute_query" #cypher_path = "cypher" def __init__(self, config): """ Initializes a resource object. :param root_uri: the base URL of Rexster. """ self.config = config self.registry = Registry(config) self.scripts = Scripts() dir_name = os.path.dirname(__file__) self.scripts.override(get_file_path(dir_name, "gremlin.groovy")) self.registry.add_scripts("gremlin", self.scripts) self.type_system = self._get_type_system() self.request = Neo4jRequest(config, self.type_system.content_type) # Gremlin def gremlin(self, script, params=None): params = dict(script=script, params=params) return self.request.post(self.gremlin_path, params) # Cypher def cypher(self, query, params=None): params = dict(query=query, params=params) return self.request.post(self.cypher_path, params) # Vertex Proxy def create_vertex(self, data): data = self._remove_null_values(data) if self.config.autoindex is True: index_name = self.config.vertex_autoindex return self.create_indexed_vertex(data, index_name, keys=None) return self.request.post(self.vertex_path, data) def get_vertex(self, _id): path = build_path(self.vertex_path, _id) return self.request.get(path, params=None) def update_vertex(self, _id, data): data = self._remove_null_values(data) if self.config.autoindex is True: index_name = self.config.vertex_autoindex return self.update_indexed_vertex(_id, data, index_name, keys=None) path = build_path(self.vertex_path, _id, "properties") return self.request.put(path, data) def delete_vertex(self, _id): # Neo4j requires you delete all adjacent edges first. # But the Neo4jServer DELETE URI doesn't do this so # I created a Gremlin method for it. - James params = dict(_id=_id) script = self.scripts.get("delete_vertex") return self.gremlin(script, params) # Edge Proxy def create_edge(self, outV, label, inV, data={}): data = self._remove_null_values(data) if self.config.autoindex is True: index_name = self.config.edge_autoindex return self.create_indexed_edge(outV, label, inV, data, index_name, keys=None) path = build_path(self.vertex_path, outV, self.edge_path) inV_uri = "%s/node/%s" % (self.config.root_uri.rstrip("/"), inV) params = {'to': inV_uri, 'type': label, 'data': data} return self.request.post(path, params) def get_edge(self, _id): path = build_path("relationship", _id) return self.request.get(path, params=None) def update_edge(self, _id, data): data = self._remove_null_values(data) if self.config.autoindex is True: index_name = self.config.edge_autoindex return self.update_indexed_edge(_id, data, index_name, keys=None) path = build_path("relationship", _id, "properties") return self.request.put(path, data) def delete_edge(self, _id): # note relationship path is singular, not plural path = build_path("relationship", _id) return self.request.delete(path, params=None) # Vertex Container def outE(self, _id, label=None): """Return the outgoing edges of the vertex.""" script = self.scripts.get('outE') params = dict(_id=_id, label=label) return self.gremlin(script, params) def inE(self, _id, label=None): """Return the incoming edges of the vertex.""" script = self.scripts.get('inE') params = dict(_id=_id, label=label) return self.gremlin(script, params) def bothE(self, _id, label=None): """Return all incoming and outgoing edges of the vertex.""" script = self.scripts.get('bothE') params = dict(_id=_id, label=label) return self.gremlin(script, params) def outV(self, _id, label=None): """Return the out-adjacent vertices to the vertex.""" script = self.scripts.get('outV') params = dict(_id=_id, label=label) return self.gremlin(script, params) def inV(self, _id, label=None): """Return the in-adjacent vertices of the vertex.""" script = self.scripts.get('inV') params = dict(_id=_id, label=label) return self.gremlin(script, params) def bothV(self, _id, label=None): """Return all incoming- and outgoing-adjacent vertices of vertex.""" script = self.scripts.get('bothV') params = dict(_id=_id, label=label) return self.gremlin(script, params) # Index Proxy - Vertex def create_vertex_index(self, index_name, *args, **kwds): index_type = kwds.pop("index_type", "exact") provider = kwds.pop("provider", "lucene") #keys = kwds.pop("keys",None) #keys = json.dumps(keys) if keys else "null" #config = {'type':index_type,'provider':provider,'keys':str(keys)} config = {'type': index_type, 'provider': provider} path = build_path(self.index_path, "node") params = dict(name=index_name, config=config) return self.request.post(path, params) def get_vertex_indices(self): # returns a map of indices path = build_path(self.index_path, "node") return self.request.get(path, params=None) def get_vertex_index(self, index_name): resp = self.get_vertex_indices() resp.results = self._get_index_results(index_name, resp) return resp def delete_vertex_index(self, name): path = build_path(self.index_path, "node", name) return self.request.delete(path, params=None) # Index Proxy - Edge def create_edge_index(self, index_name, *args, **kwds): path = build_path(self.index_path, "relationship") params = dict(name=index_name) return self.request.post(path, params) def get_edge_indices(self): path = build_path(self.index_path, "relationship") return self.request.get(path, params=None) def get_edge_index(self, index_name): resp = self.get_edge_indices() resp.results = self._get_index_results(index_name, resp) return resp def delete_edge_index(self, name): pass def _get_index_results(self, index_name, resp): # this is pretty much a hack becuase the way neo4j does this is inconsistent results = None # for clarity content = resp.content if content and index_name in content: result = content[index_name] result['name'] = index_name results = Neo4jResult(result) return results # Model Proxy - Vertex def create_indexed_vertex(self, data, index_name, keys=None): data = self._remove_null_values(data) params = dict(data=data, index_name=index_name, keys=keys) script = self.scripts.get("create_indexed_vertex") return self.gremlin(script, params) def update_indexed_vertex(self, _id, data, index_name, keys=None): data = self._remove_null_values(data) params = dict(_id=_id, data=data, index_name=index_name, keys=keys) script = self.scripts.get("update_indexed_vertex") return self.gremlin(script, params) # Model Proxy - Edge def create_indexed_edge(self, outV, label, inV, data, index_name, keys=None): data = self._remove_null_values(data) edge_params = dict(outV=outV, label=label, inV=inV) params = dict(data=data, index_name=index_name, keys=keys) params.update(edge_params) script = self.scripts.get("create_indexed_edge") return self.gremlin(script, params) def update_indexed_edge(self, _id, data, index_name, keys=None): data = self._remove_null_values(data) params = dict(_id=_id, data=data, index_name=index_name, keys=keys) script = self.scripts.get("update_indexed_edge") return self.gremlin(script, params) # Utils def warm_cache(self): script = self.scripts.get('warm_cache') return self.gremlin(script, params=None) def _remove_null_values(self, data): # You could do this at the Model._get_property_data(), # but this may not be needed for all databases. # Moreover, Neo4j Server uses PUTs to overwrite all properties so no need # to worry about deleting props that are being set to null. clean_data = [(k, v) for k, v in data.items() if v is not None] return dict(clean_data) # # Deprecated # # Indexed vertices def put_vertex(self, name, key, value, _id): path = build_path(self.index_path, "node", name) uri = "%s/%s/%d" % (self.config.root_uri, "node", _id) params = dict(key=key, value=value, uri=uri) return self.request.post(path, params) def lookup_vertex(self, name, key, value): path = build_path(self.index_path, "node", name, key, value) return self.request.get(path, params=None) def query_vertex(self, name, params): path = build_path(self.index_path, "node", name) return self.request.get(path, params) def remove_vertex_from_index(self, name, _id, key=None, value=None): #if key is not None and value is not None: # path = build_path("node",name,key,value,_id) #elif key is not None: # path = build_path("node",name,key,_id) #else: # path = build_path("node",name,_id) path = build_path("node", name, key, value, _id) return self.request.delete(path, params=None) def put_edge(self, name, key, value, _id): path = build_path(self.index_path, "relationship", name) uri = "%s/%s/%d" % (self.config.root_uri, "relationship", _id) params = dict(key=key, value=value, uri=uri) return self.request.post(path, params) def _get_type_system(self): type_system_map = dict(json=JSONTypeSystem) type_system = type_system_map[self.config.type_system] return type_system()