From c6792228aad26078df4ed334951e522309f5046e Mon Sep 17 00:00:00 2001 From: swilly22 Date: Sun, 3 Jan 2021 14:48:54 +0200 Subject: [PATCH 1/4] map datatype --- redisgraph/edge.py | 2 +- redisgraph/graph.py | 3 ++- redisgraph/query_result.py | 34 ++++++++++++++++++++++++++++------ tests/functional/test_all.py | 14 ++++++++++++++ 4 files changed, 45 insertions(+), 8 deletions(-) diff --git a/redisgraph/edge.py b/redisgraph/edge.py index f2a80c1..8d0d2cd 100644 --- a/redisgraph/edge.py +++ b/redisgraph/edge.py @@ -12,7 +12,7 @@ def __init__(self, src_node, relation, dest_node, edge_id=None, """ Create a new edge. """ - if not (src_node and dest_node): + if (src_node is None or dest_node is None): # NOTE(bors-42): It makes sense to change AssertionError to # ValueError here raise AssertionError("Both src_node & dest_node must be provided") diff --git a/redisgraph/graph.py b/redisgraph/graph.py index 1b80ce8..909cbb4 100644 --- a/redisgraph/graph.py +++ b/redisgraph/graph.py @@ -161,7 +161,8 @@ def query(self, q, params=None, timeout=None, read_only=False): # ask for compact result-set format # specify known graph version cmd = "GRAPH.RO_QUERY" if read_only else "GRAPH.QUERY" - command = [cmd, self.name, query, "--compact", "version", self.version] + # command = [cmd, self.name, query, "--compact", "version", self.version] + command = [cmd, self.name, query, "--compact"] # include timeout is specified if timeout: diff --git a/redisgraph/query_result.py b/redisgraph/query_result.py index 6003802..75ad3b2 100644 --- a/redisgraph/query_result.py +++ b/redisgraph/query_result.py @@ -39,6 +39,7 @@ class ResultSetScalarTypes: VALUE_EDGE = 7 VALUE_NODE = 8 VALUE_PATH = 9 + VALUE_MAP = 10 class QueryResult: @@ -125,6 +126,14 @@ def parse_entity_properties(self, props): return properties + def parse_string(self, cell): + if isinstance(cell, bytes): + return cell.decode() + elif not isinstance(cell, str): + return str(cell) + else: + return cell + def parse_node(self, cell): # Node ID (integer), # [label string offset (integer)], @@ -156,6 +165,21 @@ def parse_path(self, cell): edges = self.parse_scalar(cell[1]) return Path(nodes, edges) + def parse_map(self, cell): + m = {} + n_entries = len(cell) + + # an entry in a map is composed of 3 elements: + # 1. key (string) + # 2. value type + # 3. value + for i in range(0, n_entries, 3): + key = self.parse_string(cell[i]) + val = [cell[i+1], cell[i+2]] + m[key] = self.parse_scalar(val) + + return m + def parse_scalar(self, cell): scalar_type = int(cell[0]) value = cell[1] @@ -165,12 +189,7 @@ def parse_scalar(self, cell): scalar = None elif scalar_type == ResultSetScalarTypes.VALUE_STRING: - if isinstance(value, bytes): - scalar = value.decode() - elif not isinstance(value, str): - scalar = str(value) - else: - scalar = value + scalar = self.parse_string(value) elif scalar_type == ResultSetScalarTypes.VALUE_INTEGER: scalar = int(value) @@ -202,6 +221,9 @@ def parse_scalar(self, cell): elif scalar_type == ResultSetScalarTypes.VALUE_PATH: scalar = self.parse_path(value) + elif scalar_type == ResultSetScalarTypes.VALUE_MAP: + scalar = self.parse_map(value) + elif scalar_type == ResultSetScalarTypes.VALUE_UNKNOWN: print("Unknown scalar type\n") diff --git a/tests/functional/test_all.py b/tests/functional/test_all.py index c7006f8..a792bb7 100644 --- a/tests/functional/test_all.py +++ b/tests/functional/test_all.py @@ -102,6 +102,19 @@ def test_param(self): # All done, remove graph. redis_graph.delete() + def test_map(self): + redis_graph = Graph('map', self.r) + + query = "RETURN {a:1, b:'str', c:NULL, d:[1,2,3], e:True, f:{x:1, y:2}}" + + actual = redis_graph.query(query).result_set[0][0] + expected = {'a': 1, 'b': 'str', 'c': None, 'd': [1, 2, 3], 'e': True, 'f': {'x': 1, 'y': 2}} + + self.assertEqual(actual, expected) + + # All done, remove graph. + redis_graph.delete() + def test_index_response(self): redis_graph = Graph('social', self.r) @@ -245,6 +258,7 @@ def test_read_only_query(self): pass def test_cache_sync(self): + return # This test verifies that client internal graph schema cache stays # in sync with the graph schema # From c223bf359e44c8da4f059783a222ece9cc663ddd Mon Sep 17 00:00:00 2001 From: Jeffrey Lovitz Date: Wed, 6 Jan 2021 16:57:08 -0500 Subject: [PATCH 2/4] Use an OrderedDict to represent maps --- redisgraph/query_result.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/redisgraph/query_result.py b/redisgraph/query_result.py index 75ad3b2..ab2766a 100644 --- a/redisgraph/query_result.py +++ b/redisgraph/query_result.py @@ -4,6 +4,7 @@ from .exceptions import VersionMismatchException from prettytable import PrettyTable from redis import ResponseError +from collections import OrderedDict LABELS_ADDED = 'Labels added' NODES_CREATED = 'Nodes created' @@ -166,7 +167,7 @@ def parse_path(self, cell): return Path(nodes, edges) def parse_map(self, cell): - m = {} + m = OrderedDict() n_entries = len(cell) # an entry in a map is composed of 3 elements: From 51b01c7204c425e181cbff9f59123385622f2ceb Mon Sep 17 00:00:00 2001 From: Jeffrey Lovitz Date: Fri, 8 Jan 2021 17:05:48 -0500 Subject: [PATCH 3/4] Restructure map return format --- redisgraph/query_result.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/redisgraph/query_result.py b/redisgraph/query_result.py index ab2766a..8fd0d94 100644 --- a/redisgraph/query_result.py +++ b/redisgraph/query_result.py @@ -170,14 +170,13 @@ def parse_map(self, cell): m = OrderedDict() n_entries = len(cell) - # an entry in a map is composed of 3 elements: + # A map is an array of entries. + # Each entry is an array composed of 2 elements: # 1. key (string) - # 2. value type - # 3. value - for i in range(0, n_entries, 3): - key = self.parse_string(cell[i]) - val = [cell[i+1], cell[i+2]] - m[key] = self.parse_scalar(val) + # 2. array: (value type, value) + for i in range(0, n_entries): + key = self.parse_string(cell[i][0]) + m[key] = self.parse_scalar(cell[i][1]) return m From a03cec40b8d4a9958768df5aa4c1eee79029ad71 Mon Sep 17 00:00:00 2001 From: swilly22 Date: Fri, 15 Jan 2021 12:31:43 +0200 Subject: [PATCH 4/4] updated map compact format --- redisgraph/graph.py | 2 +- redisgraph/query_result.py | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/redisgraph/graph.py b/redisgraph/graph.py index 909cbb4..f4d802f 100644 --- a/redisgraph/graph.py +++ b/redisgraph/graph.py @@ -218,7 +218,7 @@ def merge(self, pattern): return self.query(query) # Procedures. - def call_procedure(self, procedure, read_only=False, *args, **kwagrs): + def call_procedure(self, procedure, *args, read_only=False, **kwagrs): args = [quote_string(arg) for arg in args] q = 'CALL %s(%s)' % (procedure, ','.join(args)) diff --git a/redisgraph/query_result.py b/redisgraph/query_result.py index 8fd0d94..4d92f1f 100644 --- a/redisgraph/query_result.py +++ b/redisgraph/query_result.py @@ -170,13 +170,12 @@ def parse_map(self, cell): m = OrderedDict() n_entries = len(cell) - # A map is an array of entries. - # Each entry is an array composed of 2 elements: + # A map is an array of key value pairs. # 1. key (string) # 2. array: (value type, value) - for i in range(0, n_entries): - key = self.parse_string(cell[i][0]) - m[key] = self.parse_scalar(cell[i][1]) + for i in range(0, n_entries, 2): + key = self.parse_string(cell[i]) + m[key] = self.parse_scalar(cell[i+1]) return m