def test_hydrating_unknown_structure_returns_same(): hydrant = DataHydrator() struct = Structure(b'?', "foo") mystery, = hydrant.hydrate([struct]) assert mystery == struct
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"
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"
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"
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
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()
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
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