Skip to content

Added missing system updates and missing profile statistics #496

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 43 additions & 70 deletions src/result-summary.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,24 @@ class ProfiledPlan {
this.operatorType = profile.operatorType
this.identifiers = profile.identifiers
this.arguments = profile.args
this.dbHits = intValue(profile.args.DbHits)
this.rows = intValue(profile.args.Rows)
this.dbHits = valueOrDefault('dbHits', profile)
this.rows = valueOrDefault('rows', profile)
this.pageCacheMisses = valueOrDefault('pageCacheMisses', profile)
this.pageCacheHits = valueOrDefault('pageCacheHits', profile)
this.pageCacheHitRatio = valueOrDefault('pageCacheHitRatio', profile)
this.time = valueOrDefault('time', profile)
this.children = profile.children
? profile.children.map(child => new ProfiledPlan(child))
: []
}

hasPageCacheStats () {
return (
this.pageCacheMisses > 0 ||
this.pageCacheHits > 0 ||
this.pageCacheHitRatio > 0
)
}
}

/**
Expand All @@ -206,12 +218,18 @@ class StatementStatistics {
constraintsAdded: 0,
constraintsRemoved: 0
}
this._systemUpdates = 0
Object.keys(statistics).forEach(index => {
// To camelCase
this._stats[index.replace(/(-\w)/g, m => m[1].toUpperCase())] = intValue(
statistics[index]
)
const camelCaseIndex = index.replace(/(-\w)/g, m => m[1].toUpperCase())
if (camelCaseIndex in this._stats) {
this._stats[camelCaseIndex] = intValue(statistics[index])
} else if (camelCaseIndex === 'systemUpdates') {
this._systemUpdates = intValue(statistics[index])
}
})

this._stats = Object.freeze(this._stats)
}

/**
Expand All @@ -227,80 +245,26 @@ class StatementStatistics {
}

/**
* @return {Number} - Number of nodes created.
*/
nodesCreated () {
return this._stats.nodesCreated
}

/**
* @return {Number} - Number of nodes deleted.
*/
nodesDeleted () {
return this._stats.nodesDeleted
}

/**
* @return {Number} - Number of relationships created.
*/
relationshipsCreated () {
return this._stats.relationshipsCreated
}

/**
* @return {Number} - Number of nodes deleted.
*/
relationshipsDeleted () {
return this._stats.relationshipsDeleted
}

/**
* @return {Number} - Number of properties set.
*/
propertiesSet () {
return this._stats.propertiesSet
}

/**
* @return {Number} - Number of labels added.
*/
labelsAdded () {
return this._stats.labelsAdded
}

/**
* @return {Number} - Number of labels removed.
*/
labelsRemoved () {
return this._stats.labelsRemoved
}

/**
* @return {Number} - Number of indexes added.
*/
indexesAdded () {
return this._stats.indexesAdded
}

/**
* @return {Number} - Number of indexes removed.
* Returns the statement statistics updates in a dictionary.
* @returns {*}
*/
indexesRemoved () {
return this._stats.indexesRemoved
updates () {
return this._stats
}

/**
* @return {Number} - Number of constraints added.
* Return true if the system database get updated, otherwise false
* @returns {boolean} - If the system database get updated or not.
*/
constraintsAdded () {
return this._stats.constraintsAdded
containsSystemUpdates () {
return this._systemUpdates > 0
}

/**
* @return {Number} - Number of constraints removed.
* @returns {number} - Number of system updates
*/
constraintsRemoved () {
return this._stats.constraintsRemoved
systemUpdates () {
return this._systemUpdates
}
}

Expand Down Expand Up @@ -356,6 +320,15 @@ function intValue (value) {
return isInt(value) ? value.toInt() : value
}

function valueOrDefault (key, values, defaultValue = 0) {
if (key in values) {
const value = values[key]
return isInt(value) ? value.toInt() : value
} else {
return defaultValue
}
}

const statementType = {
READ_ONLY: 'r',
READ_WRITE: 'rw',
Expand Down
125 changes: 104 additions & 21 deletions test/rx/summary.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,43 @@ describe('#integration-rx summary', () => {
shouldReturnNotification(serverVersion, txc))
})

describe('system', () => {
let driver
/** @type {RxSession} */
let session
/** @type {ServerVersion} */
let serverVersion

beforeEach(async () => {
driver = neo4j.driver('bolt://localhost', sharedNeo4j.authToken)
session = driver.rxSession({ database: 'system' })
//

const normalSession = driver.session()
try {
const result = await normalSession.run('MATCH (n) DETACH DELETE n')
serverVersion = ServerVersion.fromString(result.summary.server.version)
} finally {
await normalSession.close()
}

await dropConstraintsAndIndices(driver)
})

afterEach(async () => {
if (session) {
await session.close().toPromise()
}
await driver.close()
})

it('session should return summary with correct system updates for create', () =>
shouldReturnSummaryWithSystemUpdates(serverVersion, session))

it('transaction should return summary with correct system updates for create', () =>
shouldReturnSummaryWithSystemUpdates(serverVersion, session, true))
})

/**
* @param {ServerVersion} version
* @param {RxSession|RxTransaction} runnable
Expand Down Expand Up @@ -269,6 +306,37 @@ describe('#integration-rx summary', () => {
await verifyStatementType(runnable, 'CREATE (n) RETURN n', 'rw')
}

/**
* @param {ServerVersion} version
* @param {RxSession} session
* @param {boolean} useTransaction
*/
async function shouldReturnSummaryWithSystemUpdates (
version,
session,
useTransaction = false
) {
if (version.compareTo(VERSION_4_0_0) < 0) {
return
}

let runnable = session
if (useTransaction) {
runnable = await session.beginTransaction().toPromise()
}

try {
await verifySystemUpdates(
runnable,
"CREATE USER foo SET PASSWORD 'bar'",
{},
1
)
} finally {
await verifySystemUpdates(runnable, 'DROP USER foo', {}, 1)
}
}

/**
* @param {ServerVersion} version
* @param {RxSession|RxTransaction} runnable
Expand All @@ -281,7 +349,7 @@ describe('#integration-rx summary', () => {
return
}

await verifyCounters(
await verifyUpdates(
runnable,
'CREATE (n:Label1 {id: $id1})-[:KNOWS]->(m:Label2 {id: $id2}) RETURN n, m',
{ id1: 10, id2: 20 },
Expand Down Expand Up @@ -316,7 +384,7 @@ describe('#integration-rx summary', () => {
// first create the to-be-deleted nodes
await shouldReturnSummaryWithUpdateStatisticsForCreate(version, runnable)

await verifyCounters(
await verifyUpdates(
runnable,
'MATCH (n:Label1)-[r:KNOWS]->(m:Label2) DELETE n, r',
null,
Expand Down Expand Up @@ -348,7 +416,7 @@ describe('#integration-rx summary', () => {
return
}

await verifyCounters(runnable, 'CREATE INDEX on :Label(prop)', null, {
await verifyUpdates(runnable, 'CREATE INDEX on :Label(prop)', null, {
nodesCreated: 0,
nodesDeleted: 0,
relationshipsCreated: 0,
Expand Down Expand Up @@ -384,7 +452,7 @@ describe('#integration-rx summary', () => {
await session.close()
}

await verifyCounters(runnable, 'DROP INDEX on :Label(prop)', null, {
await verifyUpdates(runnable, 'DROP INDEX on :Label(prop)', null, {
nodesCreated: 0,
nodesDeleted: 0,
relationshipsCreated: 0,
Expand All @@ -411,7 +479,7 @@ describe('#integration-rx summary', () => {
return
}

await verifyCounters(
await verifyUpdates(
runnable,
'CREATE CONSTRAINT ON (book:Book) ASSERT book.isbn IS UNIQUE',
null,
Expand Down Expand Up @@ -454,7 +522,7 @@ describe('#integration-rx summary', () => {
await session.close()
}

await verifyCounters(
await verifyUpdates(
runnable,
'DROP CONSTRAINT ON (book:Book) ASSERT book.isbn IS UNIQUE',
null,
Expand Down Expand Up @@ -628,27 +696,42 @@ describe('#integration-rx summary', () => {
* @param {RxSession|RxTransaction} runnable
* @param {string} statement
* @param {*} parameters
* @param {*} counters
* @param {*} stats
*/
async function verifyUpdates (runnable, statement, parameters, stats) {
const summary = await runnable
.run(statement, parameters)
.summary()
.toPromise()
expect(summary).toBeDefined()
expect(summary.counters.containsUpdates()).toBeTruthy()
expect(summary.counters.updates()).toEqual(stats)
expect(summary.counters.containsSystemUpdates()).toBeFalsy()
}

/**
*
* @param {RxSession|RxTransaction} runnable
* @param {string} statement
* @param {*} parameters
* @param {number} systemUpdates
* @returns {Promise<void>}
*/
async function verifyCounters (runnable, statement, parameters, counters) {
async function verifySystemUpdates (
runnable,
statement,
parameters,
systemUpdates
) {
const summary = await runnable
.run(statement, parameters)
.summary()
.toPromise()
expect(summary).toBeDefined()
expect({
nodesCreated: summary.counters.nodesCreated(),
nodesDeleted: summary.counters.nodesDeleted(),
relationshipsCreated: summary.counters.relationshipsCreated(),
relationshipsDeleted: summary.counters.relationshipsDeleted(),
propertiesSet: summary.counters.propertiesSet(),
labelsAdded: summary.counters.labelsAdded(),
labelsRemoved: summary.counters.labelsRemoved(),
indexesAdded: summary.counters.indexesAdded(),
indexesRemoved: summary.counters.indexesRemoved(),
constraintsAdded: summary.counters.constraintsAdded(),
constraintsRemoved: summary.counters.constraintsRemoved()
}).toEqual(counters)

expect(summary.counters.containsSystemUpdates()).toBeTruthy()
expect(summary.counters.systemUpdates()).toBe(systemUpdates)
expect(summary.counters.containsUpdates()).toBeFalsy()
}

async function dropConstraintsAndIndices (driver) {
Expand Down
Loading