-
Notifications
You must be signed in to change notification settings - Fork 189
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce driver.execute_query #833
Merged
Merged
Changes from 1 commit
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
44e0dd0
Introduce driver.execute_query
robsdedude 4125a1c
TestKit backend can handle omitted query params
robsdedude ef90e4a
Add tests for query parameter precedence
robsdedude bed428d
Merge branch '5.0' into driver-level-api
robsdedude cf78689
Docs: be more precise about retires
robsdedude cdd9010
Tested and improved examples in docstrings
robsdedude 610261c
API docs: explain auto-commit limitations
robsdedude d3a4aa5
API docs: fix missing ref
robsdedude bd8fcab
Leave extension points in execute_query API
robsdedude 7ff6803
Fix API docs: params have trailing underscore now
robsdedude 5ea762b
Merge branch '5.0' into driver-level-api
robsdedude 3cb8c09
Update versionadded, mark experimental, fix TestKit backend
robsdedude 530c0e4
Remove debug code
robsdedude 9619e53
Emit experimental warning for Driver.execute_query
robsdedude 44dac2a
Emit experimental warning for Driver.query_bookmark_manager
robsdedude fb0f2c3
Emit ExperimentalWarning for Result.to_eager_result
robsdedude aa5e7bd
Enable type checking for test that includes type annotations
robsdedude File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -151,7 +151,152 @@ Closing a driver will immediately shut down all connections in the pool. | |
query, use :meth:`neo4j.Driver.verify_connectivity`. | ||
|
||
.. autoclass:: neo4j.Driver() | ||
:members: session, encrypted, close, verify_connectivity, get_server_info | ||
:members: session, query_bookmark_manager, encrypted, close, | ||
verify_connectivity, get_server_info | ||
|
||
.. method:: execute_query(query, parameters=None,routing=neo4j.RoutingControl.WRITERS, database=None, impersonated_user=None, bookmark_manager=self.query_bookmark_manager, result_transformer=Result.to_eager_result, **kwargs) | ||
|
||
Execute a query inside a retired transaction and return all results. | ||
robsdedude marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
This method is a handy wrapper for lower-level driver APIs like | ||
sessions, transactions, and transaction functions. It is intended | ||
for simple use cases where there is no need for managing all possible | ||
options. | ||
|
||
The method is roughly equivalent to:: | ||
|
||
def execute_query( | ||
query, parameters, routing, database, impersonated_user, | ||
bookmark_manager, result_transformer, | ||
): | ||
def work(tx): | ||
result = tx.run(query, parameters) | ||
return some_transformer(result) | ||
|
||
with driver.session( | ||
database=database, | ||
impersonated_user=impersonated_user, | ||
bookmark_manager=bookmark_manager, | ||
) as session: | ||
if routing == RoutingControl.WRITERS: | ||
return session.execute_write(work) | ||
elif routing == RoutingControl.READERS: | ||
return session.execute_read(work) | ||
|
||
Usage example:: | ||
|
||
from typing import List | ||
|
||
import neo4j | ||
|
||
def example(driver: neo4j.Driver) -> List[str]: | ||
"""Get the name of all 42 year-olds.""" | ||
records, summary, keys = driver.execute_query( | ||
"MATCH (p:Person {age: $age}) RETURN p.name", | ||
{"age": 42}, | ||
routing=neo4j.RoutingControl.READERS, # or just "r" | ||
database="neo4j", | ||
) | ||
assert keys == ["p.name"] # not needed, just for illustration | ||
log.debug("some meta data: %s", summary) | ||
return [str(record["p.name"]) for record in records] | ||
# or: return [str(record[0]) for record in records] | ||
# or even: return list(map(lambda r: str(r[0]), records)) | ||
|
||
Another example:: | ||
|
||
import neo4j | ||
|
||
def example(driver: neo4j.Driver) -> int: | ||
"""Call all young people "My dear" and get their count.""" | ||
record = driver.execute_query( | ||
"MATCH (p:Person) WHERE n.age <= 15 " | ||
"SET p.nickname = 'My dear' " | ||
"RETURN count(*)", | ||
routing=neo4j.RoutingControl.WRITERS, # or just "w" | ||
database="neo4j", | ||
result_transformer=neo4j.Result.single, | ||
) | ||
count = record[0] | ||
assert isinstance(count, int) | ||
return count | ||
|
||
:param query: cypher query to execute | ||
:type query: typing.Optional[str] | ||
:param parameters: parameters to use in the query | ||
:type parameters: typing.Optional[typing.Dict[str, typing.Any]] | ||
:param routing: | ||
whether to route the query to a reader (follower/read replica) or | ||
a writer (leader) in the cluster. Default is to route to a writer. | ||
:type routing: neo4j.RoutingControl | ||
:param database: | ||
database to execute the query against. | ||
|
||
None (default) uses the database configured on the server side. | ||
|
||
.. Note:: | ||
It is recommended to always specify the database explicitly | ||
when possible. This allows the driver to work more efficiently, | ||
as it will not have to resolve the default database first. | ||
|
||
See also the Session config :ref:`database-ref`. | ||
:type database: typing.Optional[str] | ||
:param impersonated_user: | ||
Name of the user to impersonate. | ||
|
||
This means that all query will be executed in the security context | ||
of the impersonated user. For this, the user for which the | ||
:class:`Driver` has been created needs to have the appropriate | ||
permissions. | ||
|
||
See also the Session config | ||
:type impersonated_user: typing.Optional[str] | ||
:param result_transformer: | ||
A function that gets passed the :class:`neo4j.Result` object | ||
resulting from the query and converts it to a different type. The | ||
result of the transformer function is returned by this method. | ||
|
||
.. warning:: | ||
|
||
The transformer function must **not** return the | ||
:class:`neo4j.Result` itself. | ||
|
||
Example transformer that checks that exactly one record is in the | ||
result stream, then returns the record and the result summary:: | ||
|
||
from typing import Tuple | ||
|
||
import neo4j | ||
|
||
def transformer( | ||
result: neo4j.Result | ||
) -> Tuple[neo4j.Record, neo4j.ResultSummary]: | ||
record = result.single(strict=True) | ||
summary = result.consume() | ||
return record, summary | ||
|
||
:type result_transformer: | ||
typing.Callable[[neo4j.Result], typing.Union[T]] | ||
:param bookmark_manager: | ||
Specify a bookmark manager to use. | ||
|
||
If present, the bookmark manager is used to keep the query causally | ||
consistent with all work executed using the same bookmark manager. | ||
|
||
Defaults to the driver's :attr:`.query_bookmark_manager`. | ||
|
||
Pass :const:`None` to disable causal consistency. | ||
:type bookmark_manager: | ||
typing.Union[neo4j.BookmarkManager, neo4j.BookmarkManager, | ||
None] | ||
:param kwargs: additional keyword parameters. | ||
These take precedence over parameters passed as ``parameters``. | ||
:type kwargs: typing.Any | ||
|
||
:returns: the result of the ``result_transformer`` | ||
:rtype: T | ||
|
||
.. versionadded:: 5.2 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
|
||
.. _driver-configuration-ref: | ||
|
@@ -921,11 +1066,22 @@ A :class:`neo4j.Result` is attached to an active connection, through a :class:`n | |
|
||
.. automethod:: to_df | ||
|
||
.. automethod:: to_eager_result | ||
|
||
.. automethod:: closed | ||
|
||
See https://neo4j.com/docs/python-manual/current/cypher-workflow/#python-driver-type-mapping for more about type mapping. | ||
|
||
|
||
*********** | ||
EagerResult | ||
*********** | ||
|
||
.. autoclass:: neo4j.EagerResult | ||
:show-inheritance: | ||
:members: | ||
|
||
|
||
Graph | ||
===== | ||
|
||
|
@@ -1265,6 +1421,15 @@ BookmarkManager | |
:members: | ||
|
||
|
||
************************* | ||
Constants, Enums, Helpers | ||
************************* | ||
|
||
.. autoclass:: neo4j.RoutingControl | ||
:show-inheritance: | ||
:members: | ||
|
||
|
||
.. _errors-ref: | ||
|
||
****** | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have a
:raises:
field? Or does it raise too many different exceptions?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are plenty of errors this can raise. Here are the one's I can think of on the spot
Neo4jError
(or any sub-class) - anything the server complains aboutSessionExpired
,ServiceUnavailable
(or any sub-class) - lost/couldn't establish connectivity (after the retries this API includes)ConfigurationError
TypeError
ValueError
(maybe evenIndexError
?) - invalid configuration/parameters or trying to use a feature that's too new for the server you're connected toBoltError
(or any sub-class; internal error class you shouldn't ever encounter) - the server violated the protocolThere might be more that I can't think of right now. My conclusion:
So much for why I didn't include it. If you think it'd be helpful nonetheless, we can make it happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍