Skip to content

Commit 42a169b

Browse files
author
Zhen
committed
Added profile and explain
1 parent 70d0e37 commit 42a169b

File tree

2 files changed

+87
-10
lines changed

2 files changed

+87
-10
lines changed

neo4j/session.py

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ def on_footer(self, metadata):
139139
"""
140140
self.complete = True
141141
self.summary = ResultSummary(self.statement, self.parameters,
142-
metadata.get("type"), metadata.get("stats"))
142+
metadata.get("type"), metadata.get("stats"),
143+
metadata.get("plan"), metadata.get("profile"))
143144
if self.bench_test:
144145
self.bench_test.end_recv = perf_counter()
145146

@@ -181,11 +182,22 @@ class ResultSummary(object):
181182
#: A set of statistical information held in a :class:`.StatementStatistics` instance.
182183
statistics = None
183184

184-
def __init__(self, statement, parameters, statement_type, statistics):
185+
#: A :class:`.Plan` instance
186+
plan = None
187+
188+
#: A :class:`.ProfiledPlan` instance
189+
profile = None
190+
191+
def __init__(self, statement, parameters, statement_type, statistics, plan, profile):
185192
self.statement = statement
186193
self.parameters = parameters
187194
self.statement_type = statement_type
188195
self.statistics = StatementStatistics(statistics or {})
196+
if plan is not None:
197+
self.plan = Plan(plan)
198+
if profile is not None:
199+
self.profile = ProfiledPlan(profile)
200+
self.plan = self.profile
189201

190202

191203
class StatementStatistics(object):
@@ -237,6 +249,45 @@ def __repr__(self):
237249
return repr(vars(self))
238250

239251

252+
class Plan(object):
253+
""" This describes how the database will execute your statement.
254+
"""
255+
256+
#: The operation name performed by the plan
257+
operator_type = None
258+
259+
#: A list of identifiers used by this plan
260+
identifiers = None
261+
262+
#: A map of arguments used in the specific operation performed by the plan
263+
arguments = None
264+
265+
#: A list of sub plans
266+
children = None
267+
268+
def __init__(self, plan):
269+
self.operator_type = plan["operatorType"]
270+
self.identifiers = plan.get("identifiers", [])
271+
self.arguments = plan.get("args", [])
272+
self.children = [Plan(child) for child in plan.get("children", [])]
273+
274+
275+
class ProfiledPlan(Plan):
276+
""" This describes how the database excuted your statement.
277+
"""
278+
279+
#: The number of times this part of the plan touched the underlying data stores
280+
db_hits = 0
281+
282+
#: The number of records this part of the plan produced
283+
rows = 0
284+
285+
def __init__(self, profile):
286+
self.db_hits = profile.get("dbHits", 0);
287+
self.rows = profile.get("rows", 0);
288+
super(ProfiledPlan, self).__init__(profile)
289+
290+
240291
class Session(object):
241292
""" Logical session carried out over an established TCP connection.
242293
Sessions should generally be constructed using the :meth:`.Driver.session`

test/session_test.py

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525

2626

2727
class RunTestCase(TestCase):
28-
2928
def test_must_use_valid_url_scheme(self):
3029
try:
3130
GraphDatabase.driver("x://xxx")
@@ -169,9 +168,38 @@ def test_can_obtain_summary_info(self):
169168
assert summary.statement_type == "rw"
170169
assert summary.statistics.nodes_created == 1
171170

171+
def test_no_plan_info(self):
172+
with GraphDatabase.driver("bolt://localhost").session() as session:
173+
result = session.run("CREATE (n) RETURN n")
174+
assert result.summarize().plan is None
175+
assert result.summarize().profile is None
176+
177+
def test_can_obtain_plan_info(self):
178+
with GraphDatabase.driver("bolt://localhost").session() as session:
179+
result = session.run("EXPLAIN CREATE (n) RETURN n")
180+
plan = result.summarize().plan
181+
assert plan.operator_type == "ProduceResults"
182+
assert plan.identifiers == ["n"]
183+
assert plan.arguments == {"planner": "COST", "EstimatedRows": 1.0, "version": "CYPHER 3.0",
184+
"KeyNames": "n", "runtime-impl": "INTERPRETED", "planner-impl": "IDP",
185+
"runtime": "INTERPRETED"}
186+
assert len(plan.children) == 1
187+
188+
def test_can_obtain_profile_info(self):
189+
with GraphDatabase.driver("bolt://localhost").session() as session:
190+
result = session.run("PROFILE CREATE (n) RETURN n")
191+
profile = result.summarize().profile
192+
assert profile.db_hits == 0
193+
assert profile.rows == 1
194+
assert profile.operator_type == "ProduceResults"
195+
assert profile.identifiers == ["n"]
196+
assert profile.arguments == {"planner": "COST", "EstimatedRows": 1.0, "version": "CYPHER 3.0",
197+
"KeyNames": "n", "runtime-impl": "INTERPRETED", "planner-impl": "IDP",
198+
"runtime": "INTERPRETED", "Rows": 1, "DbHits": 0}
199+
assert len(profile.children) == 1
172200

173-
class TransactionTestCase(TestCase):
174201

202+
class TransactionTestCase(TestCase):
175203
def test_can_commit_transaction(self):
176204
with GraphDatabase.driver("bolt://localhost").session() as session:
177205
tx = session.new_transaction()
@@ -189,7 +217,7 @@ def test_can_commit_transaction(self):
189217

190218
# Check the property value
191219
result = session.run("MATCH (a) WHERE id(a) = {n} "
192-
"RETURN a.foo", {"n": node_id})
220+
"RETURN a.foo", {"n": node_id})
193221
foo = result[0][0]
194222
assert foo == "bar"
195223

@@ -210,13 +238,12 @@ def test_can_rollback_transaction(self):
210238

211239
# Check the property value
212240
result = session.run("MATCH (a) WHERE id(a) = {n} "
213-
"RETURN a.foo", {"n": node_id})
241+
"RETURN a.foo", {"n": node_id})
214242
assert len(result) == 0
215243

216244
def test_can_commit_transaction_using_with_block(self):
217245
with GraphDatabase.driver("bolt://localhost").session() as session:
218246
with session.new_transaction() as tx:
219-
220247
# Create a node
221248
result = tx.run("CREATE (a) RETURN id(a)")
222249
node_id = result[0][0]
@@ -230,14 +257,13 @@ def test_can_commit_transaction_using_with_block(self):
230257

231258
# Check the property value
232259
result = session.run("MATCH (a) WHERE id(a) = {n} "
233-
"RETURN a.foo", {"n": node_id})
260+
"RETURN a.foo", {"n": node_id})
234261
foo = result[0][0]
235262
assert foo == "bar"
236263

237264
def test_can_rollback_transaction_using_with_block(self):
238265
with GraphDatabase.driver("bolt://localhost").session() as session:
239266
with session.new_transaction() as tx:
240-
241267
# Create a node
242268
result = tx.run("CREATE (a) RETURN id(a)")
243269
node_id = result[0][0]
@@ -249,7 +275,7 @@ def test_can_rollback_transaction_using_with_block(self):
249275

250276
# Check the property value
251277
result = session.run("MATCH (a) WHERE id(a) = {n} "
252-
"RETURN a.foo", {"n": node_id})
278+
"RETURN a.foo", {"n": node_id})
253279
assert len(result) == 0
254280

255281

0 commit comments

Comments
 (0)