Ejemplo n.º 1
0
 def post(self, resource):
     self.__assert_unfinished()
     rs = resource.post({"statements": self.statements})
     location = rs.location
     if location:
         self.__execute = Resource(location)
     j = rs.content
     rs.close()
     self.statements = []
     if "commit" in j:
         self.__commit = Resource(j["commit"])
     if "errors" in j:
         errors = j["errors"]
         if len(errors) >= 1:
             error = errors[0]
             raise self.error_class.hydrate(error)
     out = []
     for result in j["results"]:
         producer = RecordProducer(result["columns"])
         out.append([
             producer.produce(
                 self.__begin.service_root.graph.hydrate(r["rest"]))
             for r in result["data"]
         ])
     return out
Ejemplo n.º 2
0
 def __init__(self, uri):
     self.statements = []
     self.__begin = Resource(uri)
     self.__begin_commit = Resource(uri + "/commit")
     self.__execute = None
     self.__commit = None
     self.__finished = False
Ejemplo n.º 3
0
class Loader(Bindable):

    def __init__(self, graph):
        Bindable.__init__(self, graph.service_root.uri.resolve("load2neo"))
        try:
            self.__load2neo_version = self.resource.metadata["load2neo_version"]
        except GraphError:
            raise NotImplementedError("Load2neo extension not available")
        self.__geoff_loader = Resource(self.resource.metadata["geoff_loader"])

    @property
    def load2neo_version(self):
        return self.__load2neo_version

    def load_geoff(self, geoff):
        """ Load Geoff data via the load2neo extension.

        >>> from py2neo import Graph
        >>> from py2neo.ext.geoff import Loader
        >>> graph = Graph()
        >>> loader = Loader(graph)
        >>> loader.load_geoff("(alice)<-[:KNOWS]->(bob)")
        [{'alice': (N1), 'bob': (N2)}]

        :param geoff: geoff data to load
        :return: list of node mappings

        """
        return [
            {key: self.graph.node(value) for key, value in json.loads(line).items()}
            for line in self.__geoff_loader.post(geoff).content.splitlines(keepends=False)
        ]
Ejemplo n.º 4
0
class GeoffLoader(UnmanagedExtension):
    """ A wrapper for the `load2neo <http://nigelsmall.com/load2neo>`_
    unmanaged extension.
    """

    DEFAULT_PATH = "/load2neo/"

    def __init__(self, graph, path=DEFAULT_PATH):
        UnmanagedExtension.__init__(self, graph, path)
        self.geoff_loader = Resource(self.resource.metadata["geoff_loader"])

    @property
    def load2neo_version(self):
        """ The version of server extension currently installed.
        """
        return version_tuple(self.resource.metadata["load2neo_version"])

    def load(self, string):
        """ Load Geoff data from a string.
        """
        rs = self.geoff_loader.post(string)
        return [
            NodeDictionary(self.graph, json.loads(line))
            for line in rs.content.splitlines(False)
        ]

    def load_xml(self, string):
        """ Load XML data by first converting to Geoff format.
        """
        return self.load(xml_to_geoff(string))
Ejemplo n.º 5
0
class GeoffLoader(UnmanagedExtension):
    """ A wrapper for the `load2neo <http://nigelsmall.com/load2neo>`_
    unmanaged extension.
    """

    DEFAULT_PATH = "/load2neo/"

    def __init__(self, graph, path=DEFAULT_PATH):
        UnmanagedExtension.__init__(self, graph, path)
        self.geoff_loader = Resource(self.resource.metadata["geoff_loader"])

    @property
    def load2neo_version(self):
        """ The version of server extension currently installed.
        """
        return version_tuple(self.resource.metadata["load2neo_version"])

    def load(self, string):
        """ Load Geoff data from a string.
        """
        rs = self.geoff_loader.post(string)
        return [NodeDictionary(self.graph, json.loads(line))
                for line in rs.content.splitlines(False)]

    def load_xml(self, string):
        """ Load XML data by first converting to Geoff format.
        """
        return self.load(xml_to_geoff(string))
Ejemplo n.º 6
0
 def __init__(self, graph):
     Bindable.__init__(self, graph.service_root.uri.resolve("load2neo"))
     try:
         self.__load2neo_version = self.resource.metadata["load2neo_version"]
     except GraphError:
         raise NotImplementedError("Load2neo extension not available")
     self.__geoff_loader = Resource(self.resource.metadata["geoff_loader"])
Ejemplo n.º 7
0
def test_implicit_authentication_through_resource_constructor():
    from py2neo.core import _headers, Resource
    _headers.clear()
    _headers.update({None: [("X-Stream", "true")]})
    resource = Resource("http://*****:*****@localhost:7474/")
    headers = _get_headers("localhost:7474")
    assert headers['Authorization'] == 'Basic YXJ0aHVyOmV4Y2FsaWJ1cg=='
    assert resource.headers['Authorization'] == 'Basic YXJ0aHVyOmV4Y2FsaWJ1cg=='
Ejemplo n.º 8
0
 def __init__(self, uri=None):
     if uri is None:
         service_root = ServiceRoot()
         manager = Resource(service_root.resource.metadata["management"])
         monitor = Monitor(manager.metadata["services"]["monitor"])
         uri = monitor.resource.uri
     Service.__init__(self)
     self.bind(uri)
Ejemplo n.º 9
0
 def __init__(self, content_type, uri, name=None):
     Service.__init__(self)
     self._content_type = content_type
     key_value_pos = uri.find("/{key}/{value}")
     if key_value_pos >= 0:
         self._searcher = ResourceTemplate(uri)
         self.bind(uri[:key_value_pos])
     else:
         self.bind(uri)
         self._searcher = ResourceTemplate(uri.string + "/{key}/{value}")
     uri = self.resource.uri
     if self.graph.neo4j_version >= (1, 9):
         self._create_or_fail = Resource(uri.resolve("?uniqueness=create_or_fail"))
         self._get_or_create = Resource(uri.resolve("?uniqueness=get_or_create"))
     else:
         self._create_or_fail = None
         self._get_or_create = Resource(uri.resolve("?unique"))
     self._query_template = ResourceTemplate(uri.string + "{?query,order}")
     self._name = name or uri.path.segments[-1]
     self.__searcher_stem_cache = {}
Ejemplo n.º 10
0
 def __init__(self, content_type, uri, name=None):
     Service.__init__(self)
     self._content_type = content_type
     key_value_pos = uri.find("/{key}/{value}")
     if key_value_pos >= 0:
         self._searcher = ResourceTemplate(uri)
         self.bind(uri[:key_value_pos])
     else:
         self.bind(uri)
         self._searcher = ResourceTemplate(uri.string + "/{key}/{value}")
     uri = self.resource.uri
     if self.graph.neo4j_version >= (1, 9):
         self._create_or_fail = Resource(
             uri.resolve("?uniqueness=create_or_fail"))
         self._get_or_create = Resource(
             uri.resolve("?uniqueness=get_or_create"))
     else:
         self._create_or_fail = None
         self._get_or_create = Resource(uri.resolve("?unique"))
     self._query_template = ResourceTemplate(uri.string + "{?query,order}")
     self._name = name or uri.path.segments[-1]
     self.__searcher_stem_cache = {}
Ejemplo n.º 11
0
    def _index_manager(self, content_type):
        """ Fetch the index management resource for the given `content_type`.

        :param content_type:
        :return:
        """
        if content_type is Node:
            uri = self.resource.metadata["node_index"]
        elif content_type is Relationship:
            uri = self.resource.metadata["relationship_index"]
        else:
            raise TypeError("Indexes can manage either Nodes or Relationships")
        return Resource(uri)
Ejemplo n.º 12
0
 def fetch_latest_stats(self):
     """ Fetch the latest server statistics as a list of 2-tuples, each
     holding a `datetime` object and a named tuple of node, relationship and
     property counts.
     """
     counts = namedtuple(
         "Stats", ("node_count", "relationship_count", "property_count"))
     uri = self.resource.metadata["resources"]["latest_data"]
     latest_data = Resource(uri).get().content
     timestamps = latest_data["timestamps"]
     data = latest_data["data"]
     data = zip(
         (datetime.fromtimestamp(t) for t in timestamps),
         (counts(*x) for x in zip(
             (numberise(n) for n in data["node_count"]),
             (numberise(n) for n in data["relationship_count"]),
             (numberise(n) for n in data["property_count"]),
         )),
     )
     return data
Ejemplo n.º 13
0
 def post(self, resource):
     self.__assert_unfinished()
     rs = resource.post({"statements": self.statements})
     location = dict(rs.headers).get("location")
     if location:
         self.__execute = Resource(location)
     j = rs.content
     rs.close()
     self.statements = []
     if "commit" in j:
         self.__commit = Resource(j["commit"])
     if "errors" in j:
         errors = j["errors"]
         if len(errors) >= 1:
             error = errors[0]
             raise TransactionError.new(error["code"], error["message"])
     out = []
     for result in j["results"]:
         producer = RecordProducer(result["columns"])
         out.append([
             producer.produce(self.__begin.service_root.graph.hydrate(r["rest"]))
             for r in result["data"]
         ])
     return out
Ejemplo n.º 14
0
class Index(Service):
    """ Searchable database index which can contain either nodes or
    relationships.

    .. seealso:: :py:func:`Graph.get_or_create_index`
    """

    __instances = {}

    def __new__(cls, content_type, uri, name=None):
        """ Fetch a cached instance if one is available, otherwise create,
        cache and return a new instance.

        :param uri: URI of the cached resource
        :return: a resource instance
        """
        inst = super(Index, cls).__new__(cls)
        return cls.__instances.setdefault(uri, inst)

    def __init__(self, content_type, uri, name=None):
        Service.__init__(self)
        self._content_type = content_type
        key_value_pos = uri.find("/{key}/{value}")
        if key_value_pos >= 0:
            self._searcher = ResourceTemplate(uri)
            self.bind(uri[:key_value_pos])
        else:
            self.bind(uri)
            self._searcher = ResourceTemplate(uri.string + "/{key}/{value}")
        uri = self.resource.uri
        if self.graph.neo4j_version >= (1, 9):
            self._create_or_fail = Resource(uri.resolve("?uniqueness=create_or_fail"))
            self._get_or_create = Resource(uri.resolve("?uniqueness=get_or_create"))
        else:
            self._create_or_fail = None
            self._get_or_create = Resource(uri.resolve("?unique"))
        self._query_template = ResourceTemplate(uri.string + "{?query,order}")
        self._name = name or uri.path.segments[-1]
        self.__searcher_stem_cache = {}

    def __repr__(self):
        return "{0}({1}, {2})".format(
            self.__class__.__name__,
            self._content_type.__name__,
            repr(self.uri.string)
        )

    def _searcher_stem_for_key(self, key):
        if key not in self.__searcher_stem_cache:
            stem = self._searcher.uri_template.string.partition("{key}")[0]
            self.__searcher_stem_cache[key] = stem + percent_encode(key) + "/"
        return self.__searcher_stem_cache[key]

    def add(self, key, value, entity):
        """ Add an entity to this index under the `key`:`value` pair supplied::

            # create a node and obtain a reference to the "People" node index
            alice, = graph.create({"name": "Alice Smith"})
            people = graph.get_or_create_index(neo4j.Node, "People")

            # add the node to the index
            people.add("family_name", "Smith", alice)

        Note that while Neo4j indexes allow multiple entities to be added under
        a particular key:value, the same entity may only be represented once;
        this method is therefore idempotent.
        """
        self.resource.post({
            "key": key,
            "value": value,
            "uri": entity.uri.string,
        })
        return entity

    def add_if_none(self, key, value, entity):
        """ Add an entity to this index under the `key`:`value` pair
        supplied if no entry already exists at that point::

            # obtain a reference to the "Rooms" node index and
            # add node `alice` to room 100 if empty
            rooms = graph.get_or_create_index(neo4j.Node, "Rooms")
            rooms.add_if_none("room", 100, alice)

        If added, this method returns the entity, otherwise :py:const:`None`
        is returned.
        """
        rs = self._get_or_create.post({
            "key": key,
            "value": value,
            "uri": entity.uri.string,
        })
        if rs.status_code == CREATED:
            return entity
        else:
            return None

    @property
    def content_type(self):
        """ Return the type of entity contained within this index. Will return
        either :py:class:`Node` or :py:class:`Relationship`.
        """
        return self._content_type

    @property
    def name(self):
        """ Return the name of this index.
        """
        return self._name

    def get(self, key, value):
        """ Fetch a list of all entities from the index which are associated
        with the `key`:`value` pair supplied::

            # obtain a reference to the "People" node index and
            # get all nodes where `family_name` equals "Smith"
            people = graph.get_or_create_index(neo4j.Node, "People")
            smiths = people.get("family_name", "Smith")

        ..
        """
        return [
            self.graph.hydrate(assembled(result))
            for i, result in grouped(self._searcher.expand(key=key, value=value).get())
        ]

    def create(self, key, value, abstract):
        """ Create and index a new node or relationship using the abstract
        provided.
        """
        batch = LegacyWriteBatch(self.graph)
        if self._content_type is Node:
            batch.create(abstract)
            batch.add_to_index(Node, self, key, value, 0)
        elif self._content_type is Relationship:
            batch.create(abstract)
            batch.add_to_index(Relationship, self, key, value, 0)
        else:
            raise TypeError(self._content_type)
        entity, index_entry = batch.submit()
        return entity

    def _create_unique(self, key, value, abstract):
        """ Internal method to support `get_or_create` and `create_if_none`.
        """
        if self._content_type is Node:
            body = {
                "key": key,
                "value": value,
                "properties": abstract
            }
        elif self._content_type is Relationship:
            body = {
                "key": key,
                "value": value,
                "start": abstract[0].uri.string,
                "type": abstract[1],
                "end": abstract[2].uri.string,
                "properties": abstract[3] if len(abstract) > 3 else None
            }
        else:
            raise TypeError(self._content_type)
        return self._get_or_create.post(body)

    def get_or_create(self, key, value, abstract):
        """ Fetch a single entity from the index which is associated with the
        `key`:`value` pair supplied, creating a new entity with the supplied
        details if none exists::

            # obtain a reference to the "Contacts" node index and
            # ensure that Alice exists therein
            contacts = graph.get_or_create_index(neo4j.Node, "Contacts")
            alice = contacts.get_or_create("name", "SMITH, Alice", {
                "given_name": "Alice Jane", "family_name": "Smith",
                "phone": "01234 567 890", "mobile": "07890 123 456"
            })

            # obtain a reference to the "Friendships" relationship index and
            # ensure that Alice and Bob's friendship is registered (`alice`
            # and `bob` refer to existing nodes)
            friendships = graph.get_or_create_index(neo4j.Relationship, "Friendships")
            alice_and_bob = friendships.get_or_create(
                "friends", "Alice & Bob", (alice, "KNOWS", bob)
            )

        ..
        """
        return self.graph.hydrate(assembled(self._create_unique(key, value, abstract)))

    def create_if_none(self, key, value, abstract):
        """ Create a new entity with the specified details within the current
        index, under the `key`:`value` pair supplied, if no such entity already
        exists. If creation occurs, the new entity will be returned, otherwise
        :py:const:`None` will be returned::

            # obtain a reference to the "Contacts" node index and
            # create a node for Alice if one does not already exist
            contacts = graph.get_or_create_index(neo4j.Node, "Contacts")
            alice = contacts.create_if_none("name", "SMITH, Alice", {
                "given_name": "Alice Jane", "family_name": "Smith",
                "phone": "01234 567 890", "mobile": "07890 123 456"
            })

        ..
        """
        rs = self._create_unique(key, value, abstract)
        if rs.status_code == CREATED:
            return self.graph.hydrate(assembled(rs))
        else:
            return None

    def remove(self, key=None, value=None, entity=None):
        """ Remove any entries from the index which match the parameters
        supplied. The allowed parameter combinations are:

        `key`, `value`, `entity`
            remove a specific entity indexed under a given key-value pair

        `key`, `value`
            remove all entities indexed under a given key-value pair

        `key`, `entity`
            remove a specific entity indexed against a given key but with
            any value

        `entity`
            remove all occurrences of a specific entity regardless of
            key and value

        """
        if key and value and entity:
            t = ResourceTemplate(self.resource.uri.string + "/{key}/{value}/{entity}")
            t.expand(key=key, value=value, entity=entity._id).delete()
        elif key and value:
            uris = [
                URI(entity.resource.metadata["indexed"])
                for entity in self.get(key, value)
            ]
            batch = LegacyWriteBatch(self.graph)
            for uri in uris:
                batch.append_delete(uri)
            batch.run()
        elif key and entity:
            t = ResourceTemplate(self.resource.uri.string + "/{key}/{entity}")
            t.expand(key=key, entity=entity._id).delete()
        elif entity:
            t = ResourceTemplate(self.resource.uri.string + "/{entity}")
            t.expand(entity=entity._id).delete()
        else:
            raise TypeError("Illegal parameter combination for index removal")

    def query(self, query):
        """ Query the index according to the supplied query criteria, returning
        a list of matched entities::

            # obtain a reference to the "People" node index and
            # get all nodes where `family_name` equals "Smith"
            people = graph.get_or_create_index(neo4j.Node, "People")
            s_people = people.query("family_name:S*")

        The query syntax used should be appropriate for the configuration of
        the index being queried. For indexes with default configuration, this
        should be Apache Lucene query syntax.
        """
        resource = self._query_template.expand(query=query)
        for i, result in grouped(resource.get()):
            yield self.graph.hydrate(assembled(result))

    def _query_with_score(self, query, order):
        resource = self._query_template.expand(query=query, order=order)
        for i, result in grouped(resource.get()):
            meta = assembled(result)
            yield self.graph.hydrate(meta), meta["score"]

    def query_by_index(self, query):
        return self._query_with_score(query, "index")

    def query_by_relevance(self, query):
        return self._query_with_score(query, "relevance")

    def query_by_score(self, query):
        return self._query_with_score(query, "score")
Ejemplo n.º 15
0
class Index(Service):
    """ Searchable database index which can contain either nodes or
    relationships.

    .. seealso:: :py:func:`Graph.get_or_create_index`
    """

    __instances = {}

    def __new__(cls, content_type, uri, name=None):
        """ Fetch a cached instance if one is available, otherwise create,
        cache and return a new instance.

        :param uri: URI of the cached resource
        :return: a resource instance
        """
        inst = super(Index, cls).__new__(cls)
        return cls.__instances.setdefault(uri, inst)

    def __init__(self, content_type, uri, name=None):
        Service.__init__(self)
        self._content_type = content_type
        key_value_pos = uri.find("/{key}/{value}")
        if key_value_pos >= 0:
            self._searcher = ResourceTemplate(uri)
            self.bind(uri[:key_value_pos])
        else:
            self.bind(uri)
            self._searcher = ResourceTemplate(uri.string + "/{key}/{value}")
        uri = self.resource.uri
        if self.graph.neo4j_version >= (1, 9):
            self._create_or_fail = Resource(
                uri.resolve("?uniqueness=create_or_fail"))
            self._get_or_create = Resource(
                uri.resolve("?uniqueness=get_or_create"))
        else:
            self._create_or_fail = None
            self._get_or_create = Resource(uri.resolve("?unique"))
        self._query_template = ResourceTemplate(uri.string + "{?query,order}")
        self._name = name or uri.path.segments[-1]
        self.__searcher_stem_cache = {}

    def __repr__(self):
        return "{0}({1}, {2})".format(self.__class__.__name__,
                                      self._content_type.__name__,
                                      repr(self.uri.string))

    def _searcher_stem_for_key(self, key):
        if key not in self.__searcher_stem_cache:
            stem = self._searcher.uri_template.string.partition("{key}")[0]
            self.__searcher_stem_cache[key] = stem + percent_encode(key) + "/"
        return self.__searcher_stem_cache[key]

    def add(self, key, value, entity):
        """ Add an entity to this index under the `key`:`value` pair supplied::

            # create a node and obtain a reference to the "People" node index
            alice, = graph.create({"name": "Alice Smith"})
            people = graph.get_or_create_index(neo4j.Node, "People")

            # add the node to the index
            people.add("family_name", "Smith", alice)

        Note that while Neo4j indexes allow multiple entities to be added under
        a particular key:value, the same entity may only be represented once;
        this method is therefore idempotent.
        """
        self.resource.post({
            "key": key,
            "value": value,
            "uri": entity.uri.string,
        })
        return entity

    def add_if_none(self, key, value, entity):
        """ Add an entity to this index under the `key`:`value` pair
        supplied if no entry already exists at that point::

            # obtain a reference to the "Rooms" node index and
            # add node `alice` to room 100 if empty
            rooms = graph.get_or_create_index(neo4j.Node, "Rooms")
            rooms.add_if_none("room", 100, alice)

        If added, this method returns the entity, otherwise :py:const:`None`
        is returned.
        """
        rs = self._get_or_create.post({
            "key": key,
            "value": value,
            "uri": entity.uri.string,
        })
        if rs.status_code == CREATED:
            return entity
        else:
            return None

    @property
    def content_type(self):
        """ Return the type of entity contained within this index. Will return
        either :py:class:`Node` or :py:class:`Relationship`.
        """
        return self._content_type

    @property
    def name(self):
        """ Return the name of this index.
        """
        return self._name

    def get(self, key, value):
        """ Fetch a list of all entities from the index which are associated
        with the `key`:`value` pair supplied::

            # obtain a reference to the "People" node index and
            # get all nodes where `family_name` equals "Smith"
            people = graph.get_or_create_index(neo4j.Node, "People")
            smiths = people.get("family_name", "Smith")

        ..
        """
        return [
            self.graph.hydrate(assembled(result)) for i, result in grouped(
                self._searcher.expand(key=key, value=value).get())
        ]

    def create(self, key, value, abstract):
        """ Create and index a new node or relationship using the abstract
        provided.
        """
        batch = LegacyWriteBatch(self.graph)
        if self._content_type is Node:
            batch.create(abstract)
            batch.add_to_index(Node, self, key, value, 0)
        elif self._content_type is Relationship:
            batch.create(abstract)
            batch.add_to_index(Relationship, self, key, value, 0)
        else:
            raise TypeError(self._content_type)
        entity, index_entry = batch.submit()
        return entity

    def _create_unique(self, key, value, abstract):
        """ Internal method to support `get_or_create` and `create_if_none`.
        """
        if self._content_type is Node:
            body = {"key": key, "value": value, "properties": abstract}
        elif self._content_type is Relationship:
            body = {
                "key": key,
                "value": value,
                "start": abstract[0].uri.string,
                "type": abstract[1],
                "end": abstract[2].uri.string,
                "properties": abstract[3] if len(abstract) > 3 else None
            }
        else:
            raise TypeError(self._content_type)
        return self._get_or_create.post(body)

    def get_or_create(self, key, value, abstract):
        """ Fetch a single entity from the index which is associated with the
        `key`:`value` pair supplied, creating a new entity with the supplied
        details if none exists::

            # obtain a reference to the "Contacts" node index and
            # ensure that Alice exists therein
            contacts = graph.get_or_create_index(neo4j.Node, "Contacts")
            alice = contacts.get_or_create("name", "SMITH, Alice", {
                "given_name": "Alice Jane", "family_name": "Smith",
                "phone": "01234 567 890", "mobile": "07890 123 456"
            })

            # obtain a reference to the "Friendships" relationship index and
            # ensure that Alice and Bob's friendship is registered (`alice`
            # and `bob` refer to existing nodes)
            friendships = graph.get_or_create_index(neo4j.Relationship, "Friendships")
            alice_and_bob = friendships.get_or_create(
                "friends", "Alice & Bob", (alice, "KNOWS", bob)
            )

        ..
        """
        return self.graph.hydrate(
            assembled(self._create_unique(key, value, abstract)))

    def create_if_none(self, key, value, abstract):
        """ Create a new entity with the specified details within the current
        index, under the `key`:`value` pair supplied, if no such entity already
        exists. If creation occurs, the new entity will be returned, otherwise
        :py:const:`None` will be returned::

            # obtain a reference to the "Contacts" node index and
            # create a node for Alice if one does not already exist
            contacts = graph.get_or_create_index(neo4j.Node, "Contacts")
            alice = contacts.create_if_none("name", "SMITH, Alice", {
                "given_name": "Alice Jane", "family_name": "Smith",
                "phone": "01234 567 890", "mobile": "07890 123 456"
            })

        ..
        """
        rs = self._create_unique(key, value, abstract)
        if rs.status_code == CREATED:
            return self.graph.hydrate(assembled(rs))
        else:
            return None

    def remove(self, key=None, value=None, entity=None):
        """ Remove any entries from the index which match the parameters
        supplied. The allowed parameter combinations are:

        `key`, `value`, `entity`
            remove a specific entity indexed under a given key-value pair

        `key`, `value`
            remove all entities indexed under a given key-value pair

        `key`, `entity`
            remove a specific entity indexed against a given key but with
            any value

        `entity`
            remove all occurrences of a specific entity regardless of
            key and value

        """
        if key and value and entity:
            t = ResourceTemplate(self.resource.uri.string +
                                 "/{key}/{value}/{entity}")
            t.expand(key=key, value=value, entity=entity._id).delete()
        elif key and value:
            uris = [
                URI(entity.resource.metadata["indexed"])
                for entity in self.get(key, value)
            ]
            batch = LegacyWriteBatch(self.graph)
            for uri in uris:
                batch.append_delete(uri)
            batch.run()
        elif key and entity:
            t = ResourceTemplate(self.resource.uri.string + "/{key}/{entity}")
            t.expand(key=key, entity=entity._id).delete()
        elif entity:
            t = ResourceTemplate(self.resource.uri.string + "/{entity}")
            t.expand(entity=entity._id).delete()
        else:
            raise TypeError("Illegal parameter combination for index removal")

    def query(self, query):
        """ Query the index according to the supplied query criteria, returning
        a list of matched entities::

            # obtain a reference to the "People" node index and
            # get all nodes where `family_name` equals "Smith"
            people = graph.get_or_create_index(neo4j.Node, "People")
            s_people = people.query("family_name:S*")

        The query syntax used should be appropriate for the configuration of
        the index being queried. For indexes with default configuration, this
        should be Apache Lucene query syntax.
        """
        resource = self._query_template.expand(query=query)
        for i, result in grouped(resource.get()):
            yield self.graph.hydrate(assembled(result))

    def _query_with_score(self, query, order):
        resource = self._query_template.expand(query=query, order=order)
        for i, result in grouped(resource.get()):
            meta = assembled(result)
            yield self.graph.hydrate(meta), meta["score"]

    def query_by_index(self, query):
        return self._query_with_score(query, "index")

    def query_by_relevance(self, query):
        return self._query_with_score(query, "relevance")

    def query_by_score(self, query):
        return self._query_with_score(query, "score")
Ejemplo n.º 16
0
 def __init__(self, graph, path=DEFAULT_PATH):
     UnmanagedExtension.__init__(self, graph, path)
     self.geoff_loader = Resource(self.resource.metadata["geoff_loader"])
Ejemplo n.º 17
0
 def __init__(self, graph, path=DEFAULT_PATH):
     UnmanagedExtension.__init__(self, graph, path)
     self.geoff_loader = Resource(self.resource.metadata["geoff_loader"])
Ejemplo n.º 18
0
class Transaction(object):
    """ A transaction is a transient resource that allows multiple Cypher
    statements to be executed within a single server transaction.
    """

    def __init__(self, uri):
        self.statements = []
        self.__begin = Resource(uri)
        self.__begin_commit = Resource(uri + "/commit")
        self.__execute = None
        self.__commit = None
        self.__finished = False

    def __assert_unfinished(self):
        if self.__finished:
            raise TransactionFinished()

    @property
    def finished(self):
        """ Indicates whether or not this transaction has been completed or is
        still open.

        :return: :py:const:`True` if this transaction has finished,
                 :py:const:`False` otherwise
        """
        return self.__finished

    def append(self, statement, parameters=None):
        """ Append a statement to the current queue of statements to be
        executed.

        :param statement: the statement to execute
        :param parameters: a dictionary of execution parameters
        """
        self.__assert_unfinished()
        # OrderedDict is used here to avoid statement/parameters ordering bug
        self.statements.append(OrderedDict([
            ("statement", statement),
            ("parameters", dict(parameters or {})),
            ("resultDataContents", ["REST"]),
        ]))

    def post(self, resource):
        self.__assert_unfinished()
        rs = resource.post({"statements": self.statements})
        location = dict(rs.headers).get("location")
        if location:
            self.__execute = Resource(location)
        j = rs.content
        rs.close()
        self.statements = []
        if "commit" in j:
            self.__commit = Resource(j["commit"])
        if "errors" in j:
            errors = j["errors"]
            if len(errors) >= 1:
                error = errors[0]
                raise TransactionError.new(error["code"], error["message"])
        out = []
        for result in j["results"]:
            producer = RecordProducer(result["columns"])
            out.append([
                producer.produce(self.__begin.service_root.graph.hydrate(r["rest"]))
                for r in result["data"]
            ])
        return out

    def execute(self):
        """ Send all pending statements to the server for execution, leaving
        the transaction open for further statements.

        :return: list of results from pending statements
        """
        return self.post(self.__execute or self.__begin)

    def commit(self):
        """ Send all pending statements to the server for execution and commit
        the transaction.

        :return: list of results from pending statements
        """
        try:
            return self.post(self.__commit or self.__begin_commit)
        finally:
            self.__finished = True

    def rollback(self):
        """ Rollback the current transaction.
        """
        self.__assert_unfinished()
        try:
            if self.__execute:
                self.__execute.delete()
        finally:
            self.__finished = True
Ejemplo n.º 19
0
class CypherTransaction(object):
    """ A transaction is a transient resource that allows multiple Cypher
    statements to be executed within a single server transaction.
    """

    error_class = CypherTransactionError

    def __init__(self, uri):
        self.statements = []
        self.__begin = Resource(uri)
        self.__begin_commit = Resource(uri + "/commit")
        self.__execute = None
        self.__commit = None
        self.__finished = False

    def __assert_unfinished(self):
        if self.__finished:
            raise TransactionFinished()

    @property
    def _id(self):
        """ The internal server ID of this transaction, if available.
        """
        if self.__execute is None:
            return None
        else:
            return int(self.__execute.uri.path.segments[-1])

    @property
    def finished(self):
        """ Indicates whether or not this transaction has been completed or is
        still open.

        :return: :py:const:`True` if this transaction has finished,
                 :py:const:`False` otherwise
        """
        return self.__finished

    def append(self, statement, parameters=None):
        """ Append a statement to the current queue of statements to be
        executed.

        :param statement: the statement to execute
        :param parameters: a dictionary of execution parameters
        """
        self.__assert_unfinished()
        # OrderedDict is used here to avoid statement/parameters ordering bug
        self.statements.append(
            OrderedDict([
                ("statement", statement),
                ("parameters", dict(parameters or {})),
                ("resultDataContents", ["REST"]),
            ]))

    def post(self, resource):
        self.__assert_unfinished()
        rs = resource.post({"statements": self.statements})
        location = rs.location
        if location:
            self.__execute = Resource(location)
        j = rs.content
        rs.close()
        self.statements = []
        if "commit" in j:
            self.__commit = Resource(j["commit"])
        if "errors" in j:
            errors = j["errors"]
            if len(errors) >= 1:
                error = errors[0]
                raise self.error_class.hydrate(error)
        out = []
        for result in j["results"]:
            producer = RecordProducer(result["columns"])
            out.append([
                producer.produce(
                    self.__begin.service_root.graph.hydrate(r["rest"]))
                for r in result["data"]
            ])
        return out

    def execute(self):
        """ Send all pending statements to the server for execution, leaving
        the transaction open for further statements.

        :return: list of results from pending statements
        """
        return self.post(self.__execute or self.__begin)

    def commit(self):
        """ Send all pending statements to the server for execution and commit
        the transaction.

        :return: list of results from pending statements
        """
        try:
            return self.post(self.__commit or self.__begin_commit)
        finally:
            self.__finished = True

    def rollback(self):
        """ Rollback the current transaction.
        """
        self.__assert_unfinished()
        try:
            if self.__execute:
                self.__execute.delete()
        finally:
            self.__finished = True