-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Add support for async GRAPH module #2273
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
Changes from all commits
5e48aac
f5ee75e
db0b650
224778f
fb2c74b
439f2ea
d7e4ea1
13941b8
e993e2c
efe7c7a
b12b025
e9f8447
8529170
8e2ea28
20fa54f
a44b952
4a83d67
115c259
fe93f41
34c5177
24413b1
e60ad1c
2e9cab8
85732f4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,16 @@ | |
|
||
from .exceptions import VersionMismatchException | ||
from .execution_plan import ExecutionPlan | ||
from .query_result import QueryResult | ||
from .query_result import AsyncQueryResult, QueryResult | ||
|
||
PROFILE_CMD = "GRAPH.PROFILE" | ||
RO_QUERY_CMD = "GRAPH.RO_QUERY" | ||
QUERY_CMD = "GRAPH.QUERY" | ||
DELETE_CMD = "GRAPH.DELETE" | ||
SLOWLOG_CMD = "GRAPH.SLOWLOG" | ||
CONFIG_CMD = "GRAPH.CONFIG" | ||
LIST_CMD = "GRAPH.LIST" | ||
EXPLAIN_CMD = "GRAPH.EXPLAIN" | ||
|
||
|
||
class GraphCommands: | ||
|
@@ -52,33 +61,28 @@ def query(self, q, params=None, timeout=None, read_only=False, profile=False): | |
query = q | ||
|
||
# handle query parameters | ||
if params is not None: | ||
query = self._build_params_header(params) + query | ||
query = self._build_params_header(params) + query | ||
|
||
# construct query command | ||
# ask for compact result-set format | ||
# specify known graph version | ||
if profile: | ||
cmd = "GRAPH.PROFILE" | ||
cmd = PROFILE_CMD | ||
else: | ||
cmd = "GRAPH.RO_QUERY" if read_only else "GRAPH.QUERY" | ||
cmd = RO_QUERY_CMD if read_only else QUERY_CMD | ||
command = [cmd, self.name, query, "--compact"] | ||
|
||
# include timeout is specified | ||
if timeout: | ||
if not isinstance(timeout, int): | ||
raise Exception("Timeout argument must be a positive integer") | ||
command += ["timeout", timeout] | ||
if isinstance(timeout, int): | ||
command.extend(["timeout", timeout]) | ||
elif timeout is not None: | ||
raise Exception("Timeout argument must be a positive integer") | ||
|
||
# issue query | ||
try: | ||
response = self.execute_command(*command) | ||
return QueryResult(self, response, profile) | ||
except ResponseError as e: | ||
if "wrong number of arguments" in str(e): | ||
print( | ||
"Note: RedisGraph Python requires server version 2.2.8 or above" | ||
) # noqa | ||
if "unknown command" in str(e) and read_only: | ||
# `GRAPH.RO_QUERY` is unavailable in older versions. | ||
return self.query(q, params, timeout, read_only=False) | ||
|
@@ -106,7 +110,7 @@ def delete(self): | |
For more information see `DELETE <https://redis.io/commands/graph.delete>`_. # noqa | ||
""" | ||
self._clear_schema() | ||
return self.execute_command("GRAPH.DELETE", self.name) | ||
return self.execute_command(DELETE_CMD, self.name) | ||
|
||
# declared here, to override the built in redis.db.flush() | ||
def flush(self): | ||
|
@@ -146,7 +150,7 @@ def slowlog(self): | |
3. The issued query. | ||
4. The amount of time needed for its execution, in milliseconds. | ||
""" | ||
return self.execute_command("GRAPH.SLOWLOG", self.name) | ||
return self.execute_command(SLOWLOG_CMD, self.name) | ||
|
||
def config(self, name, value=None, set=False): | ||
""" | ||
|
@@ -170,14 +174,14 @@ def config(self, name, value=None, set=False): | |
raise DataError( | ||
"``value`` can be provided only when ``set`` is True" | ||
) # noqa | ||
return self.execute_command("GRAPH.CONFIG", *params) | ||
return self.execute_command(CONFIG_CMD, *params) | ||
|
||
def list_keys(self): | ||
""" | ||
Lists all graph keys in the keyspace. | ||
For more information see `GRAPH.LIST <https://redis.io/commands/graph.list>`_. # noqa | ||
""" | ||
return self.execute_command("GRAPH.LIST") | ||
return self.execute_command(LIST_CMD) | ||
|
||
def execution_plan(self, query, params=None): | ||
""" | ||
|
@@ -188,10 +192,9 @@ def execution_plan(self, query, params=None): | |
query: the query that will be executed | ||
params: query parameters | ||
""" | ||
if params is not None: | ||
query = self._build_params_header(params) + query | ||
query = self._build_params_header(params) + query | ||
|
||
plan = self.execute_command("GRAPH.EXPLAIN", self.name, query) | ||
plan = self.execute_command(EXPLAIN_CMD, self.name, query) | ||
if isinstance(plan[0], bytes): | ||
plan = [b.decode() for b in plan] | ||
return "\n".join(plan) | ||
|
@@ -206,8 +209,105 @@ def explain(self, query, params=None): | |
query: the query that will be executed | ||
params: query parameters | ||
""" | ||
if params is not None: | ||
query = self._build_params_header(params) + query | ||
query = self._build_params_header(params) + query | ||
|
||
plan = self.execute_command(EXPLAIN_CMD, self.name, query) | ||
return ExecutionPlan(plan) | ||
|
||
|
||
class AsyncGraphCommands(GraphCommands): | ||
async def query(self, q, params=None, timeout=None, read_only=False, profile=False): | ||
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. type hints 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. |
||
""" | ||
Executes a query against the graph. | ||
For more information see `GRAPH.QUERY <https://oss.redis.com/redisgraph/master/commands/#graphquery>`_. # noqa | ||
|
||
Args: | ||
|
||
q : str | ||
The query. | ||
params : dict | ||
Query parameters. | ||
timeout : int | ||
Maximum runtime for read queries in milliseconds. | ||
read_only : bool | ||
Executes a readonly query if set to True. | ||
profile : bool | ||
Return details on results produced by and time | ||
spent in each operation. | ||
""" | ||
|
||
# maintain original 'q' | ||
query = q | ||
|
||
# handle query parameters | ||
query = self._build_params_header(params) + query | ||
|
||
# construct query command | ||
# ask for compact result-set format | ||
# specify known graph version | ||
if profile: | ||
cmd = PROFILE_CMD | ||
else: | ||
cmd = RO_QUERY_CMD if read_only else QUERY_CMD | ||
command = [cmd, self.name, query, "--compact"] | ||
|
||
# include timeout is specified | ||
if isinstance(timeout, int): | ||
command.extend(["timeout", timeout]) | ||
elif timeout is not None: | ||
raise Exception("Timeout argument must be a positive integer") | ||
|
||
# issue query | ||
try: | ||
response = await self.execute_command(*command) | ||
return await AsyncQueryResult().initialize(self, response, profile) | ||
except ResponseError as e: | ||
if "unknown command" in str(e) and read_only: | ||
# `GRAPH.RO_QUERY` is unavailable in older versions. | ||
return await self.query(q, params, timeout, read_only=False) | ||
raise e | ||
except VersionMismatchException as e: | ||
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. Pattern for possible client side caching in the future... just a thought? |
||
# client view over the graph schema is out of sync | ||
# set client version and refresh local schema | ||
self.version = e.version | ||
self._refresh_schema() | ||
# re-issue query | ||
return await self.query(q, params, timeout, read_only) | ||
|
||
async def execution_plan(self, query, params=None): | ||
""" | ||
Get the execution plan for given query, | ||
GRAPH.EXPLAIN returns an array of operations. | ||
|
||
Args: | ||
query: the query that will be executed | ||
params: query parameters | ||
""" | ||
query = self._build_params_header(params) + query | ||
|
||
plan = self.execute_command("GRAPH.EXPLAIN", self.name, query) | ||
plan = await self.execute_command(EXPLAIN_CMD, self.name, query) | ||
if isinstance(plan[0], bytes): | ||
plan = [b.decode() for b in plan] | ||
dvora-h marked this conversation as resolved.
Show resolved
Hide resolved
dvora-h marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return "\n".join(plan) | ||
|
||
async def explain(self, query, params=None): | ||
""" | ||
Get the execution plan for given query, | ||
GRAPH.EXPLAIN returns ExecutionPlan object. | ||
|
||
Args: | ||
query: the query that will be executed | ||
params: query parameters | ||
""" | ||
query = self._build_params_header(params) + query | ||
|
||
plan = await self.execute_command(EXPLAIN_CMD, self.name, query) | ||
return ExecutionPlan(plan) | ||
|
||
async def flush(self): | ||
""" | ||
Commit the graph and reset the edges and the nodes to zero length. | ||
""" | ||
await self.commit() | ||
self.nodes = {} | ||
self.edges = [] |
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.
docstrings... every function please