Skip to content

Commit a203f50

Browse files
author
Stefan Plantikow
committed
Merge pull request #6 from oskarhane/explain-profile
Add support for plan and profile
2 parents 51bea96 + 667e735 commit a203f50

File tree

3 files changed

+118
-8
lines changed

3 files changed

+118
-8
lines changed

lib/result.js

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ class Result {
9393
subscribe(observer) {
9494
let onCompletedOriginal = observer.onCompleted;
9595
let onCompletedWrapper = (metadata) => {
96-
this.summary = new ResultSummary(this._statement, this._parameters, metadata.type, metadata.stats);
96+
this.summary = new ResultSummary(this._statement, this._parameters, metadata);
9797
onCompletedOriginal(metadata);
9898
}
9999
observer.onCompleted = onCompletedWrapper;
@@ -121,12 +121,69 @@ class ResultSummary {
121121
* @param {string} statementType - How did the statement effect the database
122122
* @param {Object} statistics - Result statistics
123123
*/
124-
constructor(statement, parameters, statementType, statistics) {
124+
constructor(statement, parameters, metadata) {
125+
let {type: statementType, stats: statistics, plan, profile} = metadata;
125126
this.statement = {text: statement, parameters};
126127
this.statementType = statementType;
127128
this.statistics = new StatementStatistics(statistics || {});
129+
this.plan = plan || profile ? new Plan(plan || profile) : false;
130+
this.profile = profile ? new ProfiledPlan(profile) : false;
128131
}
129-
}
132+
133+
/**
134+
* Check if the result summary has a plan
135+
* @return {boolean}
136+
*/
137+
hasPlan() {
138+
return this.plan instanceof Plan
139+
}
140+
141+
/**
142+
* Check if the result summary has a profile
143+
* @return {boolean}
144+
*/
145+
hasProfile() {
146+
return this.profile instanceof ProfiledPlan
147+
}
148+
}
149+
150+
/**
151+
* Class for execution plan received by prepending Cypher with EXPLAIN.
152+
* @access public
153+
*/
154+
class Plan {
155+
/**
156+
* Create a Plan instance
157+
* @constructor
158+
* @param {Object} plan - Object with plan data
159+
*/
160+
constructor(plan) {
161+
this.operatorType = plan.operatorType;
162+
this.identifiers = plan.identifiers;
163+
this.arguments = plan.args;
164+
this.children = plan.children ? plan.children.map((child) => new Plan(child)) : [];
165+
}
166+
}
167+
168+
/**
169+
* Class for execution plan received by prepending Cypher with PROFILE.
170+
* @access public
171+
*/
172+
class ProfiledPlan {
173+
/**
174+
* Create a ProfiledPlan instance
175+
* @constructor
176+
* @param {Object} plan - Object with plan data
177+
*/
178+
constructor(profile) {
179+
this.operatorType = profile.operatorType;
180+
this.identifiers = profile.identifiers;
181+
this.arguments = profile.args;
182+
this.dbHits = profile.args.DbHits.toInt();
183+
this.rows = profile.args.Rows.toInt();
184+
this.children = profile.children ? profile.children.map((child) => new ProfiledPlan(child)) : [];
185+
}
186+
}
130187

131188
/**
132189
* Get statistical information for a {Result}.
@@ -247,4 +304,14 @@ class StatementStatistics {
247304
}
248305
}
249306

250-
export default Result;
307+
const statementType = {
308+
READ_ONLY: 'r',
309+
READ_WRITE: 'rw',
310+
WRITE_ONLY: 'w',
311+
SCHEMA_WRITE: 's'
312+
}
313+
314+
export default {
315+
Result,
316+
statementType
317+
}

lib/session.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*/
1919

2020
import StreamObserver from './internal/stream-observer';
21-
import Result from './result';
21+
import {Result} from './result';
2222

2323
/**
2424
* A Session instance is used for handling the connection and

test/session.test.js

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
var neo4j = require("../build/node/neo4j");
3+
var StatementType = require("../build/node/result").statementType;
34

45
describe('session', function() {
56
it('should expose basic run/subscribe ', function(done) {
@@ -10,7 +11,7 @@ describe('session', function() {
1011
var records = [];
1112
driver.session().run( "RETURN 1.0 AS a").subscribe( {
1213
onNext : function( record ) {
13-
records.push( record );
14+
records.push( record );
1415
},
1516
onCompleted : function( ) {
1617
expect( records.length ).toBe( 1 );
@@ -52,7 +53,7 @@ describe('session', function() {
5253
)
5354
});
5455

55-
it('should expose summarize method ', function(done) {
56+
it('should expose summarize method for basic metadata ', function(done) {
5657
// Given
5758
var driver = neo4j.driver("neo4j://localhost");
5859
var statement = "CREATE (n:Label {prop:{prop}}) RETURN n";
@@ -65,7 +66,49 @@ describe('session', function() {
6566
expect(sum.statement.parameters).toBe( params );
6667
expect(sum.statistics.containsUpdates()).toBe(true);
6768
expect(sum.statistics.nodesCreated()).toBe(1);
68-
expect(sum.statementType).toBe('rw');
69+
expect(sum.statementType).toBe(StatementType.READ_WRITE);
70+
driver.close();
71+
done();
72+
});
73+
});
74+
75+
it('should expose plan ', function(done) {
76+
// Given
77+
var driver = neo4j.driver("neo4j://localhost");
78+
var statement = "EXPLAIN CREATE (n:Label {prop:{prop}}) RETURN n";
79+
var params = {prop: "string"}
80+
// When & Then
81+
var result = driver.session().run( statement, params );
82+
result.then(function( records ) {
83+
var sum = result.summarize();
84+
expect(sum.hasPlan()).toBe(true);
85+
expect(sum.hasProfile()).toBe(false);
86+
expect(sum.plan.operatorType).toBe('ProduceResults');
87+
expect(sum.plan.arguments.runtime).toBe('INTERPRETED');
88+
expect(sum.plan.identifiers[0]).toBe('n');
89+
expect(sum.plan.children[0].operatorType).toBe('CreateNode');
90+
driver.close();
91+
done();
92+
});
93+
});
94+
95+
it('should expose profile ', function(done) {
96+
// Given
97+
var driver = neo4j.driver("neo4j://localhost");
98+
var statement = "PROFILE MATCH (n:Label {prop:{prop}}) RETURN n";
99+
var params = {prop: "string"}
100+
// When & Then
101+
var result = driver.session().run( statement, params );
102+
result.then(function( records ) {
103+
var sum = result.summarize();
104+
expect(sum.hasPlan()).toBe(true); //When there's a profile, there's a plan
105+
expect(sum.hasProfile()).toBe(true);
106+
expect(sum.profile.operatorType).toBe('ProduceResults');
107+
expect(sum.profile.arguments.runtime).toBe('INTERPRETED');
108+
expect(sum.profile.identifiers[0]).toBe('n');
109+
expect(sum.profile.children[0].operatorType).toBe('Filter');
110+
expect(sum.profile.rows).toBeGreaterThan(0);
111+
//expect(sum.profile.dbHits).toBeGreaterThan(0);
69112
driver.close();
70113
done();
71114
});

0 commit comments

Comments
 (0)