From dffa8d1612f437c35f0a934693f302247a8135d5 Mon Sep 17 00:00:00 2001 From: Nigel Small Date: Wed, 14 Oct 2015 15:53:00 +0100 Subject: [PATCH 1/2] Included summary stats --- neo4j/__main__.py | 43 ++++++++++++++++++++++---------------- neo4j/session.py | 53 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 75 insertions(+), 21 deletions(-) diff --git a/neo4j/__main__.py b/neo4j/__main__.py index 97539f406..284be52a5 100644 --- a/neo4j/__main__.py +++ b/neo4j/__main__.py @@ -83,6 +83,7 @@ def main(): parser.add_argument("-s", "--secure", action="store_true") parser.add_argument("-v", "--verbose", action="count") parser.add_argument("-x", "--times", type=int, default=1) + parser.add_argument("-z", "--summarize", action="store_true") args = parser.parse_args() if args.verbose: @@ -102,24 +103,30 @@ def main(): driver = GraphDatabase.driver(args.url, secure=args.secure) session = driver.session() - if session: - for _ in range(args.times): - for statement in args.statement: - try: - records = session.run(statement, parameters) - except CypherError as error: - stderr.write("%s: %s\r\n" % (error.code, error.message)) - else: - if not args.quiet: - has_results = False - for i, record in enumerate(records): - has_results = True - if i == 0: - stdout.write("%s\r\n" % "\t".join(record.__keys__)) - stdout.write("%s\r\n" % "\t".join(map(repr, record))) - if has_results: - stdout.write("\r\n") - session.close() + for _ in range(args.times): + for statement in args.statement: + try: + result = session.run(statement, parameters) + except CypherError as error: + stderr.write("%s: %s\r\n" % (error.code, error.message)) + else: + if not args.quiet: + has_results = False + for i, record in enumerate(result): + has_results = True + if i == 0: + stdout.write("%s\r\n" % "\t".join(record.__keys__)) + stdout.write("%s\r\n" % "\t".join(map(repr, record))) + if has_results: + stdout.write("\r\n") + if args.summarize: + summary = result.summarize() + stdout.write("Statement : %r\r\n" % summary.statement) + stdout.write("Parameters : %r\r\n" % summary.parameters) + stdout.write("Statement Type : %r\r\n" % summary.statement_type) + stdout.write("Statistics : %r\r\n" % summary.statistics) + stdout.write("\r\n") + session.close() if __name__ == "__main__": diff --git a/neo4j/session.py b/neo4j/session.py index 90ca2c2c9..ea182299f 100644 --- a/neo4j/session.py +++ b/neo4j/session.py @@ -36,6 +36,12 @@ class which can be used to obtain `Driver` instances that are used for from .typesystem import hydrated +STATEMENT_TYPE_READ_ONLY = "r" +STATEMENT_TYPE_READ_WRITE = "rw" +STATEMENT_TYPE_WRITE_ONLY = "w" +STATEMENT_TYPE_SCHEMA_WRITE = "sw" + + Latency = namedtuple("Latency", ["overall", "network", "wait"]) @@ -98,11 +104,14 @@ def session(self, **config): class Result(list): - def __init__(self, session): - self.session = session + def __init__(self, session, statement, parameters): super(Result, self).__init__() + self.session = session + self.statement = statement + self.parameters = parameters self.keys = None self.complete = False + self.summary = None self.bench_test = None def on_header(self, metadata): @@ -115,6 +124,8 @@ def on_record(self, values): def on_footer(self, metadata): self.complete = True + self.summary = ResultSummary(self.statement, self.parameters, + metadata.get("type"), metadata.get("stats")) if self.bench_test: self.bench_test.end_recv = perf_counter() @@ -126,6 +137,42 @@ def consume(self): while not self.complete: fetch_next() + def summarize(self): + self.consume() + return self.summary + + +class ResultSummary(object): + + def __init__(self, statement, parameters, statement_type, statistics): + self.statement = statement + self.parameters = parameters + self.statement_type = statement_type + self.statistics = StatementStatistics(statistics or {}) + + +class StatementStatistics(object): + contains_updates = False + nodes_created = 0 + nodes_deleted = 0 + relationships_created = 0 + relationships_deleted = 0 + properties_set = 0 + labels_added = 0 + labels_removed = 0 + indexes_added = 0 + indexes_removed = 0 + constraints_added = 0 + constraints_removed = 0 + + def __init__(self, statistics): + for key, value in dict(statistics).items(): + key = key.replace("-", "_") + setattr(self, key, value) + + def __repr__(self): + return repr(vars(self)) + class Session(object): """ Logical session carried out over an established TCP connection. @@ -162,7 +209,7 @@ def run(self, statement, parameters=None): t = BenchTest() t.init = perf_counter() - result = Result(self) + result = Result(self, statement, parameters) result.bench_test = t run_response = Response(self.connection) From 7f58230e4674264c2afc979b43c3a17e03e7c85a Mon Sep 17 00:00:00 2001 From: Nigel Small Date: Wed, 14 Oct 2015 16:01:06 +0100 Subject: [PATCH 2/2] Added summary test --- test/session_test.py | 71 +++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/test/session_test.py b/test/session_test.py index f2632ca09..7397f535e 100644 --- a/test/session_test.py +++ b/test/session_test.py @@ -89,16 +89,16 @@ def test_can_run_statement_that_returns_multiple_records(self): def test_can_use_with_to_auto_close_session(self): with GraphDatabase.driver("bolt://localhost").session() as session: - records = session.run("RETURN 1") - assert len(records) == 1 - for record in records: + result = session.run("RETURN 1") + assert len(result) == 1 + for record in result: assert record[0] == 1 def test_can_return_node(self): with GraphDatabase.driver("bolt://localhost").session() as session: - records = session.run("MERGE (a:Person {name:'Alice'}) RETURN a") - assert len(records) == 1 - for record in records: + result = session.run("MERGE (a:Person {name:'Alice'}) RETURN a") + assert len(result) == 1 + for record in result: alice = record[0] assert isinstance(alice, Node) assert alice.labels == {"Person"} @@ -106,9 +106,9 @@ def test_can_return_node(self): def test_can_return_relationship(self): with GraphDatabase.driver("bolt://localhost").session() as session: - records = session.run("MERGE ()-[r:KNOWS {since:1999}]->() RETURN r") - assert len(records) == 1 - for record in records: + result = session.run("MERGE ()-[r:KNOWS {since:1999}]->() RETURN r") + assert len(result) == 1 + for record in result: rel = record[0] assert isinstance(rel, Relationship) assert rel.type == "KNOWS" @@ -116,9 +116,9 @@ def test_can_return_relationship(self): def test_can_return_path(self): with GraphDatabase.driver("bolt://localhost").session() as session: - records = session.run("MERGE p=({name:'Alice'})-[:KNOWS]->({name:'Bob'}) RETURN p") - assert len(records) == 1 - for record in records: + result = session.run("MERGE p=({name:'Alice'})-[:KNOWS]->({name:'Bob'}) RETURN p") + assert len(result) == 1 + for record in result: path = record[0] assert isinstance(path, Path) assert path.start.properties == {"name": "Alice"} @@ -138,9 +138,18 @@ def test_can_handle_cypher_error(self): def test_record_equality(self): with GraphDatabase.driver("bolt://localhost").session() as session: - records = session.run("unwind([1, 1]) AS a RETURN a") - assert records[0] == records[1] - assert records[0] != "this is not a record" + result = session.run("unwind([1, 1]) AS a RETURN a") + assert result[0] == result[1] + assert result[0] != "this is not a record" + + def test_can_obtain_summary_info(self): + with GraphDatabase.driver("bolt://localhost").session() as session: + result = session.run("CREATE (n) RETURN n") + summary = result.summarize() + assert summary.statement == "CREATE (n) RETURN n" + assert summary.parameters == {} + assert summary.statement_type == "rw" + assert summary.statistics.nodes_created == 1 class TransactionTestCase(TestCase): @@ -150,8 +159,8 @@ def test_can_commit_transaction(self): tx = session.new_transaction() # Create a node - records = tx.run("CREATE (a) RETURN id(a)") - node_id = records[0][0] + result = tx.run("CREATE (a) RETURN id(a)") + node_id = result[0][0] assert isinstance(node_id, int) # Update a property @@ -161,9 +170,9 @@ def test_can_commit_transaction(self): tx.commit() # Check the property value - records = session.run("MATCH (a) WHERE id(a) = {n} " + result = session.run("MATCH (a) WHERE id(a) = {n} " "RETURN a.foo", {"n": node_id}) - foo = records[0][0] + foo = result[0][0] assert foo == "bar" def test_can_rollback_transaction(self): @@ -171,8 +180,8 @@ def test_can_rollback_transaction(self): tx = session.new_transaction() # Create a node - records = tx.run("CREATE (a) RETURN id(a)") - node_id = records[0][0] + result = tx.run("CREATE (a) RETURN id(a)") + node_id = result[0][0] assert isinstance(node_id, int) # Update a property @@ -182,17 +191,17 @@ def test_can_rollback_transaction(self): tx.rollback() # Check the property value - records = session.run("MATCH (a) WHERE id(a) = {n} " + result = session.run("MATCH (a) WHERE id(a) = {n} " "RETURN a.foo", {"n": node_id}) - assert len(records) == 0 + assert len(result) == 0 def test_can_commit_transaction_using_with_block(self): with GraphDatabase.driver("bolt://localhost").session() as session: with session.new_transaction() as tx: # Create a node - records = tx.run("CREATE (a) RETURN id(a)") - node_id = records[0][0] + result = tx.run("CREATE (a) RETURN id(a)") + node_id = result[0][0] assert isinstance(node_id, int) # Update a property @@ -202,9 +211,9 @@ def test_can_commit_transaction_using_with_block(self): tx.success = True # Check the property value - records = session.run("MATCH (a) WHERE id(a) = {n} " + result = session.run("MATCH (a) WHERE id(a) = {n} " "RETURN a.foo", {"n": node_id}) - foo = records[0][0] + foo = result[0][0] assert foo == "bar" def test_can_rollback_transaction_using_with_block(self): @@ -212,8 +221,8 @@ def test_can_rollback_transaction_using_with_block(self): with session.new_transaction() as tx: # Create a node - records = tx.run("CREATE (a) RETURN id(a)") - node_id = records[0][0] + result = tx.run("CREATE (a) RETURN id(a)") + node_id = result[0][0] assert isinstance(node_id, int) # Update a property @@ -221,9 +230,9 @@ def test_can_rollback_transaction_using_with_block(self): "SET a.foo = {foo}", {"n": node_id, "foo": "bar"}) # Check the property value - records = session.run("MATCH (a) WHERE id(a) = {n} " + result = session.run("MATCH (a) WHERE id(a) = {n} " "RETURN a.foo", {"n": node_id}) - assert len(records) == 0 + assert len(result) == 0 if __name__ == "__main__":