Example #1
0
    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)
Example #2
0
    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)
Example #3
0
 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)
Example #4
0
    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)
Example #5
0
    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)
Example #6
0
    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)
Example #7
0
    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)
Example #8
0
    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)
Example #9
0
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)
Example #10
0
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)
Example #11
0
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)
Example #12
0
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()
Example #13
0
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
Example #14
0
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)
Example #15
0
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
Example #16
0
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)
Example #17
0
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()