Skip to content

Commit dd85bad

Browse files
author
Zhen Li
committed
Merge pull request #6 from neo4j/result-summary
Result summary
2 parents cdae84a + 7f58230 commit dd85bad

File tree

3 files changed

+115
-52
lines changed

3 files changed

+115
-52
lines changed

neo4j/__main__.py

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ def main():
8383
parser.add_argument("-s", "--secure", action="store_true")
8484
parser.add_argument("-v", "--verbose", action="count")
8585
parser.add_argument("-x", "--times", type=int, default=1)
86+
parser.add_argument("-z", "--summarize", action="store_true")
8687
args = parser.parse_args()
8788

8889
if args.verbose:
@@ -102,24 +103,30 @@ def main():
102103

103104
driver = GraphDatabase.driver(args.url, secure=args.secure)
104105
session = driver.session()
105-
if session:
106-
for _ in range(args.times):
107-
for statement in args.statement:
108-
try:
109-
records = session.run(statement, parameters)
110-
except CypherError as error:
111-
stderr.write("%s: %s\r\n" % (error.code, error.message))
112-
else:
113-
if not args.quiet:
114-
has_results = False
115-
for i, record in enumerate(records):
116-
has_results = True
117-
if i == 0:
118-
stdout.write("%s\r\n" % "\t".join(record.__keys__))
119-
stdout.write("%s\r\n" % "\t".join(map(repr, record)))
120-
if has_results:
121-
stdout.write("\r\n")
122-
session.close()
106+
for _ in range(args.times):
107+
for statement in args.statement:
108+
try:
109+
result = session.run(statement, parameters)
110+
except CypherError as error:
111+
stderr.write("%s: %s\r\n" % (error.code, error.message))
112+
else:
113+
if not args.quiet:
114+
has_results = False
115+
for i, record in enumerate(result):
116+
has_results = True
117+
if i == 0:
118+
stdout.write("%s\r\n" % "\t".join(record.__keys__))
119+
stdout.write("%s\r\n" % "\t".join(map(repr, record)))
120+
if has_results:
121+
stdout.write("\r\n")
122+
if args.summarize:
123+
summary = result.summarize()
124+
stdout.write("Statement : %r\r\n" % summary.statement)
125+
stdout.write("Parameters : %r\r\n" % summary.parameters)
126+
stdout.write("Statement Type : %r\r\n" % summary.statement_type)
127+
stdout.write("Statistics : %r\r\n" % summary.statistics)
128+
stdout.write("\r\n")
129+
session.close()
123130

124131

125132
if __name__ == "__main__":

neo4j/session.py

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ class which can be used to obtain `Driver` instances that are used for
3636
from .typesystem import hydrated
3737

3838

39+
STATEMENT_TYPE_READ_ONLY = "r"
40+
STATEMENT_TYPE_READ_WRITE = "rw"
41+
STATEMENT_TYPE_WRITE_ONLY = "w"
42+
STATEMENT_TYPE_SCHEMA_WRITE = "sw"
43+
44+
3945
Latency = namedtuple("Latency", ["overall", "network", "wait"])
4046

4147

@@ -98,11 +104,14 @@ def session(self, **config):
98104

99105
class Result(list):
100106

101-
def __init__(self, session):
102-
self.session = session
107+
def __init__(self, session, statement, parameters):
103108
super(Result, self).__init__()
109+
self.session = session
110+
self.statement = statement
111+
self.parameters = parameters
104112
self.keys = None
105113
self.complete = False
114+
self.summary = None
106115
self.bench_test = None
107116

108117
def on_header(self, metadata):
@@ -115,6 +124,8 @@ def on_record(self, values):
115124

116125
def on_footer(self, metadata):
117126
self.complete = True
127+
self.summary = ResultSummary(self.statement, self.parameters,
128+
metadata.get("type"), metadata.get("stats"))
118129
if self.bench_test:
119130
self.bench_test.end_recv = perf_counter()
120131

@@ -126,6 +137,42 @@ def consume(self):
126137
while not self.complete:
127138
fetch_next()
128139

140+
def summarize(self):
141+
self.consume()
142+
return self.summary
143+
144+
145+
class ResultSummary(object):
146+
147+
def __init__(self, statement, parameters, statement_type, statistics):
148+
self.statement = statement
149+
self.parameters = parameters
150+
self.statement_type = statement_type
151+
self.statistics = StatementStatistics(statistics or {})
152+
153+
154+
class StatementStatistics(object):
155+
contains_updates = False
156+
nodes_created = 0
157+
nodes_deleted = 0
158+
relationships_created = 0
159+
relationships_deleted = 0
160+
properties_set = 0
161+
labels_added = 0
162+
labels_removed = 0
163+
indexes_added = 0
164+
indexes_removed = 0
165+
constraints_added = 0
166+
constraints_removed = 0
167+
168+
def __init__(self, statistics):
169+
for key, value in dict(statistics).items():
170+
key = key.replace("-", "_")
171+
setattr(self, key, value)
172+
173+
def __repr__(self):
174+
return repr(vars(self))
175+
129176

130177
class Session(object):
131178
""" Logical session carried out over an established TCP connection.
@@ -162,7 +209,7 @@ def run(self, statement, parameters=None):
162209
t = BenchTest()
163210
t.init = perf_counter()
164211

165-
result = Result(self)
212+
result = Result(self, statement, parameters)
166213
result.bench_test = t
167214

168215
run_response = Response(self.connection)

test/session_test.py

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -89,36 +89,36 @@ def test_can_run_statement_that_returns_multiple_records(self):
8989

9090
def test_can_use_with_to_auto_close_session(self):
9191
with GraphDatabase.driver("bolt://localhost").session() as session:
92-
records = session.run("RETURN 1")
93-
assert len(records) == 1
94-
for record in records:
92+
result = session.run("RETURN 1")
93+
assert len(result) == 1
94+
for record in result:
9595
assert record[0] == 1
9696

9797
def test_can_return_node(self):
9898
with GraphDatabase.driver("bolt://localhost").session() as session:
99-
records = session.run("MERGE (a:Person {name:'Alice'}) RETURN a")
100-
assert len(records) == 1
101-
for record in records:
99+
result = session.run("MERGE (a:Person {name:'Alice'}) RETURN a")
100+
assert len(result) == 1
101+
for record in result:
102102
alice = record[0]
103103
assert isinstance(alice, Node)
104104
assert alice.labels == {"Person"}
105105
assert alice.properties == {"name": "Alice"}
106106

107107
def test_can_return_relationship(self):
108108
with GraphDatabase.driver("bolt://localhost").session() as session:
109-
records = session.run("MERGE ()-[r:KNOWS {since:1999}]->() RETURN r")
110-
assert len(records) == 1
111-
for record in records:
109+
result = session.run("MERGE ()-[r:KNOWS {since:1999}]->() RETURN r")
110+
assert len(result) == 1
111+
for record in result:
112112
rel = record[0]
113113
assert isinstance(rel, Relationship)
114114
assert rel.type == "KNOWS"
115115
assert rel.properties == {"since": 1999}
116116

117117
def test_can_return_path(self):
118118
with GraphDatabase.driver("bolt://localhost").session() as session:
119-
records = session.run("MERGE p=({name:'Alice'})-[:KNOWS]->({name:'Bob'}) RETURN p")
120-
assert len(records) == 1
121-
for record in records:
119+
result = session.run("MERGE p=({name:'Alice'})-[:KNOWS]->({name:'Bob'}) RETURN p")
120+
assert len(result) == 1
121+
for record in result:
122122
path = record[0]
123123
assert isinstance(path, Path)
124124
assert path.start.properties == {"name": "Alice"}
@@ -138,9 +138,18 @@ def test_can_handle_cypher_error(self):
138138

139139
def test_record_equality(self):
140140
with GraphDatabase.driver("bolt://localhost").session() as session:
141-
records = session.run("unwind([1, 1]) AS a RETURN a")
142-
assert records[0] == records[1]
143-
assert records[0] != "this is not a record"
141+
result = session.run("unwind([1, 1]) AS a RETURN a")
142+
assert result[0] == result[1]
143+
assert result[0] != "this is not a record"
144+
145+
def test_can_obtain_summary_info(self):
146+
with GraphDatabase.driver("bolt://localhost").session() as session:
147+
result = session.run("CREATE (n) RETURN n")
148+
summary = result.summarize()
149+
assert summary.statement == "CREATE (n) RETURN n"
150+
assert summary.parameters == {}
151+
assert summary.statement_type == "rw"
152+
assert summary.statistics.nodes_created == 1
144153

145154

146155
class TransactionTestCase(TestCase):
@@ -150,8 +159,8 @@ def test_can_commit_transaction(self):
150159
tx = session.new_transaction()
151160

152161
# Create a node
153-
records = tx.run("CREATE (a) RETURN id(a)")
154-
node_id = records[0][0]
162+
result = tx.run("CREATE (a) RETURN id(a)")
163+
node_id = result[0][0]
155164
assert isinstance(node_id, int)
156165

157166
# Update a property
@@ -161,18 +170,18 @@ def test_can_commit_transaction(self):
161170
tx.commit()
162171

163172
# Check the property value
164-
records = session.run("MATCH (a) WHERE id(a) = {n} "
173+
result = session.run("MATCH (a) WHERE id(a) = {n} "
165174
"RETURN a.foo", {"n": node_id})
166-
foo = records[0][0]
175+
foo = result[0][0]
167176
assert foo == "bar"
168177

169178
def test_can_rollback_transaction(self):
170179
with GraphDatabase.driver("bolt://localhost").session() as session:
171180
tx = session.new_transaction()
172181

173182
# Create a node
174-
records = tx.run("CREATE (a) RETURN id(a)")
175-
node_id = records[0][0]
183+
result = tx.run("CREATE (a) RETURN id(a)")
184+
node_id = result[0][0]
176185
assert isinstance(node_id, int)
177186

178187
# Update a property
@@ -182,17 +191,17 @@ def test_can_rollback_transaction(self):
182191
tx.rollback()
183192

184193
# Check the property value
185-
records = session.run("MATCH (a) WHERE id(a) = {n} "
194+
result = session.run("MATCH (a) WHERE id(a) = {n} "
186195
"RETURN a.foo", {"n": node_id})
187-
assert len(records) == 0
196+
assert len(result) == 0
188197

189198
def test_can_commit_transaction_using_with_block(self):
190199
with GraphDatabase.driver("bolt://localhost").session() as session:
191200
with session.new_transaction() as tx:
192201

193202
# Create a node
194-
records = tx.run("CREATE (a) RETURN id(a)")
195-
node_id = records[0][0]
203+
result = tx.run("CREATE (a) RETURN id(a)")
204+
node_id = result[0][0]
196205
assert isinstance(node_id, int)
197206

198207
# Update a property
@@ -202,28 +211,28 @@ def test_can_commit_transaction_using_with_block(self):
202211
tx.success = True
203212

204213
# Check the property value
205-
records = session.run("MATCH (a) WHERE id(a) = {n} "
214+
result = session.run("MATCH (a) WHERE id(a) = {n} "
206215
"RETURN a.foo", {"n": node_id})
207-
foo = records[0][0]
216+
foo = result[0][0]
208217
assert foo == "bar"
209218

210219
def test_can_rollback_transaction_using_with_block(self):
211220
with GraphDatabase.driver("bolt://localhost").session() as session:
212221
with session.new_transaction() as tx:
213222

214223
# Create a node
215-
records = tx.run("CREATE (a) RETURN id(a)")
216-
node_id = records[0][0]
224+
result = tx.run("CREATE (a) RETURN id(a)")
225+
node_id = result[0][0]
217226
assert isinstance(node_id, int)
218227

219228
# Update a property
220229
tx.run("MATCH (a) WHERE id(a) = {n} "
221230
"SET a.foo = {foo}", {"n": node_id, "foo": "bar"})
222231

223232
# Check the property value
224-
records = session.run("MATCH (a) WHERE id(a) = {n} "
233+
result = session.run("MATCH (a) WHERE id(a) = {n} "
225234
"RETURN a.foo", {"n": node_id})
226-
assert len(records) == 0
235+
assert len(result) == 0
227236

228237

229238
if __name__ == "__main__":

0 commit comments

Comments
 (0)