Exemplo n.º 1
0
def test_hydrating_unknown_structure_returns_same():
    hydrant = DataHydrator()

    struct = Structure(b'?', "foo")
    mystery, = hydrant.hydrate([struct])

    assert mystery == struct
Exemplo n.º 2
0
def test_can_hydrate_node_structure():
    hydrant = DataHydrator()

    struct = Structure(b'N', 123, ["Person"], {"name": "Alice"})
    alice, = hydrant.hydrate([struct])

    assert alice.id == 123
    assert alice.labels == {"Person"}
    assert set(alice.keys()) == {"name"}
    assert alice.get("name") == "Alice"
Exemplo n.º 3
0
def test_can_hydrate_in_dict():
    hydrant = DataHydrator()

    struct = Structure(b'N', 123, ["Person"], {"name": "Alice"})
    alice_in_dict, = hydrant.hydrate([{"foo": struct}])

    assert isinstance(alice_in_dict, dict)

    alice = alice_in_dict["foo"]

    assert alice.id == 123
    assert alice.labels == {"Person"}
    assert set(alice.keys()) == {"name"}
    assert alice.get("name") == "Alice"
Exemplo n.º 4
0
def test_can_hydrate_in_list():
    hydrant = DataHydrator()

    struct = Structure(b'N', 123, ["Person"], {"name": "Alice"})
    alice_in_list, = hydrant.hydrate([[struct]])

    assert isinstance(alice_in_list, list)

    alice, = alice_in_list

    assert alice.id == 123
    assert alice.labels == {"Person"}
    assert set(alice.keys()) == {"name"}
    assert alice.get("name") == "Alice"
Exemplo n.º 5
0
    def run(self, query, parameters=None, **kwparameters):
        """Run a Cypher query within an auto-commit transaction.

        The query is sent and the result header received
        immediately but the :class:`neo4j.Result` content is
        fetched lazily as consumed by the client application.

        If a query is executed before a previous
        :class:`neo4j.Result` in the same :class:`.Session` has
        been fully consumed, the first result will be fully fetched
        and buffered. Note therefore that the generally recommended
        pattern of usage is to fully consume one result before
        executing a subsequent query. If two results need to be
        consumed in parallel, multiple :class:`.Session` objects
        can be used as an alternative to result buffering.

        For more usage details, see :meth:`.Transaction.run`.

        :param query: cypher query
        :type query: str, neo4j.Query
        :param parameters: dictionary of parameters
        :type parameters: dict
        :param kwparameters: additional keyword parameters
        :returns: a new :class:`neo4j.Result` object
        :rtype: :class:`neo4j.Result`
        """
        if not query:
            raise ValueError("Cannot run an empty query")
        if not isinstance(query, (str, Query)):
            raise TypeError("query must be a string or a Query instance")

        if self._transaction:
            raise ClientError(
                "Explicit Transaction must be handled explicitly")

        if self._autoResult:
            self._autoResult._buffer_all(
            )  # This will buffer upp all records for the previous auto-transaction

        if not self._connection:
            self._connect(self._config.default_access_mode,
                          database=self._config.database)
        cx = self._connection
        protocol_version = cx.PROTOCOL_VERSION
        server_info = cx.server_info

        hydrant = DataHydrator()

        self._autoResult = Result(cx, hydrant, self._config.fetch_size,
                                  self._result_closed,
                                  self._result_network_error)
        self._autoResult._run(query, parameters, self._config.database,
                              self._config.default_access_mode,
                              self._bookmarks, **kwparameters)

        return self._autoResult
Exemplo n.º 6
0
    def run(self, query, parameters=None, **kwparameters):
        """ Run a Cypher query within the context of this transaction.

        The query is sent to the server lazily, when its result is
        consumed. To force the query to be sent to the server, use
        the :meth:`.Transaction.sync` method.

        Cypher is typically expressed as a query template plus a
        set of named parameters. In Python, parameters may be expressed
        through a dictionary of parameters, through individual parameter
        arguments, or as a mixture of both. For example, the `run`
        queries below are all equivalent::

            >>> query = "CREATE (a:Person { name: $name, age: $age })"
            >>> result = tx.run(query, {"name": "Alice", "age": 33})
            >>> result = tx.run(query, {"name": "Alice"}, age=33)
            >>> result = tx.run(query, name="Alice", age=33)

        Parameter values can be of any type supported by the Neo4j type
        system. In Python, this includes :class:`bool`, :class:`int`,
        :class:`str`, :class:`list` and :class:`dict`. Note however that
        :class:`list` properties must be homogenous.

        :param query: cypher query
        :type query: str
        :param parameters: dictionary of parameters
        :type parameters: dict
        :param kwparameters: additional keyword parameters
        :returns: a new :class:`neo4j.Result` object
        :rtype: :class:`neo4j.Result`
        :raise TransactionError: if the transaction is already closed
        """
        from neo4j.work.simple import Query
        if isinstance(query, Query):
            raise ValueError("Query object is only supported for session.run")

        if self._closed:
            raise TransactionError("Transaction closed")

        if (self._results
                and self._connection.supports_multiple_results is False):
            # Bolt 3 Support
            # Buffer up all records for the previous Result because it does not
            # have any qid to fetch in batches.
            self._results[-1]._buffer_all()

        result = Result(
            self._connection, DataHydrator(), self._fetch_size,
            self._result_on_closed_handler,
            self._result_on_network_error_handler
        )
        self._results.append(result)

        result._tx_ready_run(query, parameters, **kwparameters)

        return result
class TemporalHydrationTestCase(TestCase):
    def setUp(self):
        self.hydrant = DataHydrator()

    def test_can_hydrate_date_time_structure(self):
        struct = Structure(b'd', 1539344261, 474716862)
        dt, = self.hydrant.hydrate([struct])
        self.assertEqual(dt.year, 2018)
        self.assertEqual(dt.month, 10)
        self.assertEqual(dt.day, 12)
        self.assertEqual(dt.hour, 11)
        self.assertEqual(dt.minute, 37)
        self.assertEqual(dt.second, 41.474716862)
class HydrationTestCase(TestCase):
    def setUp(self):
        self.hydrant = DataHydrator()

    def test_can_hydrate_node_structure(self):
        struct = Structure(b'N', 123, ["Person"], {"name": "Alice"})
        alice, = self.hydrant.hydrate([struct])
        self.assertEqual(alice.id, 123)
        self.assertEqual(alice.labels, {"Person"})
        self.assertEqual(set(alice.keys()), {"name"})
        self.assertEqual(alice.get("name"), "Alice")

    def test_hydrating_unknown_structure_returns_same(self):
        struct = Structure(b'?', "foo")
        mystery, = self.hydrant.hydrate([struct])
        self.assertEqual(mystery, struct)

    def test_can_hydrate_in_list(self):
        struct = Structure(b'N', 123, ["Person"], {"name": "Alice"})
        alice_in_list, = self.hydrant.hydrate([[struct]])
        self.assertIsInstance(alice_in_list, list)
        alice, = alice_in_list
        self.assertEqual(alice.id, 123)
        self.assertEqual(alice.labels, {"Person"})
        self.assertEqual(set(alice.keys()), {"name"})
        self.assertEqual(alice.get("name"), "Alice")

    def test_can_hydrate_in_dict(self):
        struct = Structure(b'N', 123, ["Person"], {"name": "Alice"})
        alice_in_dict, = self.hydrant.hydrate([{"foo": struct}])
        self.assertIsInstance(alice_in_dict, dict)
        alice = alice_in_dict["foo"]
        self.assertEqual(alice.id, 123)
        self.assertEqual(alice.labels, {"Person"})
        self.assertEqual(set(alice.keys()), {"name"})
        self.assertEqual(alice.get("name"), "Alice")
 def setUp(self):
     self.hydrant = DataHydrator()
Exemplo n.º 10
0
    def run(self, cypher, parameters=None, **kwparameters):
        """ Run a Cypher statement within an auto-commit transaction.

        The statement is sent and the result header received
        immediately but the :class:`.StatementResult` content is
        fetched lazily as consumed by the client application.

        If a statement is executed before a previous
        :class:`.StatementResult` in the same :class:`.Session` has
        been fully consumed, the first result will be fully fetched
        and buffered. Note therefore that the generally recommended
        pattern of usage is to fully consume one result before
        executing a subsequent statement. If two results need to be
        consumed in parallel, multiple :class:`.Session` objects
        can be used as an alternative to result buffering.

        For more usage details, see :meth:`.Transaction.run`.

        :param cypher: Cypher statement
        :param parameters: dictionary of parameters
        :param kwparameters: additional keyword parameters
        :returns: :class:`.StatementResult` object
        """
        if not cypher:
            raise ValueError("Cannot run an empty statement")
        if not isinstance(cypher, (str, Statement)):
            raise TypeError(
                "Statement must be a string or a Statement instance")

        if not self._connection:
            self._connect(self._config.default_access_mode)
        cx = self._connection
        protocol_version = cx.PROTOCOL_VERSION
        server = cx.server

        has_transaction = self.has_transaction()

        statement_text = str(cypher)
        statement_metadata = getattr(cypher, "metadata", None)
        statement_timeout = getattr(cypher, "timeout", None)
        parameters = DataDehydrator.fix_parameters(
            dict(parameters or {}, **kwparameters))

        def fail(_):
            self._close_transaction()

        hydrant = DataHydrator()
        result_metadata = {
            "statement": statement_text,
            "parameters": parameters,
            "server": server,
            "protocol_version": protocol_version,
        }
        run_metadata = {
            "metadata": statement_metadata,
            "timeout": statement_timeout,
            "on_success": result_metadata.update,
            "on_failure": fail,
        }

        def done(summary_metadata):
            result_metadata.update(summary_metadata)
            bookmark = result_metadata.get("bookmark")
            if bookmark:
                self._bookmarks_in = tuple([bookmark])
                self._bookmark_out = bookmark

        self._last_result = result = BoltStatementResult(
            self, hydrant, result_metadata)

        if has_transaction:
            if statement_metadata:
                raise ValueError(
                    "Metadata can only be attached at transaction level")
            if statement_timeout:
                raise ValueError("Timeouts only apply at transaction level")
            # TODO: fail if explicit database name has been set
        else:
            run_metadata["bookmarks"] = self._bookmarks_in

        # TODO: capture ValueError and surface as SessionError/TransactionError if
        # TODO: explicit database selection has been made
        cx.run(statement_text, parameters, **run_metadata)
        cx.pull(
            on_records=lambda records: result._records.extend(
                hydrant.hydrate_records(result.keys(), records)),
            on_success=done,
            on_failure=fail,
            on_summary=lambda: result.detach(sync=False),
        )

        if not has_transaction:
            self._connection.send_all()
            self._connection.fetch_message()

        return result
Exemplo n.º 11
0
    def run(self, query, parameters=None, **kwparameters):
        """ Run a Cypher query within an auto-commit transaction.

        The query is sent and the result header received
        immediately but the :class:`neo4j.Result` content is
        fetched lazily as consumed by the client application.

        If a query is executed before a previous
        :class:`neo4j.Result` in the same :class:`.Session` has
        been fully consumed, the first result will be fully fetched
        and buffered. Note therefore that the generally recommended
        pattern of usage is to fully consume one result before
        executing a subsequent query. If two results need to be
        consumed in parallel, multiple :class:`.Session` objects
        can be used as an alternative to result buffering.

        For more usage details, see :meth:`.Transaction.run`.

        :param query: Cypher query
        :param parameters: dictionary of parameters
        :param kwparameters: additional keyword parameters
        :returns: :class:`neo4j.Result` object
        """
        if not query:
            raise ValueError("Cannot run an empty query")
        if not isinstance(query, (str, Query)):
            raise TypeError("query must be a string or a Query instance")

        if not self._connection:
            self._connect(self._config.default_access_mode, database=self._config.database)
        cx = self._connection
        protocol_version = cx.PROTOCOL_VERSION
        server_info = cx.server_info

        has_transaction = self.has_transaction()

        query_text = str(query)
        query_metadata = getattr(query, "metadata", None)
        query_timeout = getattr(query, "timeout", None)
        parameters = DataDehydrator.fix_parameters(dict(parameters or {}, **kwparameters))

        def fail(_):
            self._close_transaction()

        hydrant = DataHydrator()
        result_metadata = {
            "query": query_text,
            "parameters": parameters,
            "server": server_info,
            "protocol_version": protocol_version,
        }
        run_metadata = {
            "metadata": query_metadata,
            "timeout": query_timeout,
            "on_success": result_metadata.update,
            "on_failure": fail,
        }

        def done(summary_metadata):
            result_metadata.update(summary_metadata)
            bookmark = result_metadata.get("bookmark")
            if bookmark:
                self._bookmarks_in = tuple([bookmark])
                self._bookmark_out = bookmark

        self._last_result = result = Result(self, hydrant, result_metadata)

        access_mode = None
        db = None
        bookmarks = None

        if has_transaction:
            # Explicit Transaction Run does not carry any extra values. RUN "query" {parameters} {extra}
            if query_metadata:
                raise ValueError("Metadata can only be attached at transaction level")
            if query_timeout:
                raise ValueError("Timeouts only apply at transaction level")
            access_mode = None
            db = None
            bookmarks = None
        else:
            run_metadata["bookmarks"] = self._bookmarks_in
            access_mode = self._config.default_access_mode
            db = self._config.database
            bookmarks = run_metadata.get("bookmarks", self._config.bookmarks)

        # BOLT RUN
        cx.run(
            query_text,
            parameters=parameters,
            mode=access_mode,
            bookmarks=bookmarks,
            metadata=run_metadata["metadata"],
            timeout=run_metadata["timeout"],
            db=db,
            on_success=run_metadata["on_success"],
            on_failure=run_metadata["on_failure"],
        )
        # BOLT PULL
        cx.pull(
            on_records=lambda records: result._records.extend(hydrant.hydrate_records(result.keys(), records)),
            on_success=done,
            on_failure=fail,
            on_summary=lambda: result.detach(sync=False),
        )

        if not has_transaction:
            self._connection.send_all()
            self._connection.fetch_message()

        return result