-
Notifications
You must be signed in to change notification settings - Fork 149
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Main point here is to add a small layer of indirection for accessing fields in a record. Before this, we gave users a raw array instance which is useful - but also means we can *never* add additional functionality to a record, since we use up the "root" attribute space with fields. Adding any additional attribute to the record would break backwards compatibility. This introduces a really frustrating wart in the API, where most other access by key is done via JS object lookups (eg. node.properties['blah']). However, records have both indexed and keyed fields, meaning if a user ever did: RETURN 1, 0 It's now insane to figure out which value you get back when you ask for record[0]. With this implemntation, there's a strict separation between lookup by index (using JS number values) and lookup by key (using String): get(0) -> 1 get("0") -> 0 It also allows, as noted above, future extensions to the API, which the original design made very cumbersome.
- Loading branch information
Showing
11 changed files
with
264 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/** | ||
* Copyright (c) 2002-2016 "Neo Technology," | ||
* Network Engine for Objects in Lund AB [http://neotechnology.com] | ||
* | ||
* This file is part of Neo4j. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import {newError} from "./internal/error"; | ||
|
||
function generateFieldLookup( keys ) { | ||
let lookup = {}; | ||
keys.forEach( (name, idx) => { lookup[name] = idx; }); | ||
return lookup; | ||
} | ||
|
||
/** | ||
* Records make up the contents of the {@link Result}, and is how you access | ||
* the output of a statement. A simple statement might yield a result stream | ||
* with a single record, for instance: | ||
* | ||
* MATCH (u:User) RETURN u.name, u.age | ||
* | ||
* This returns a stream of records with two fields, named `u.name` and `u.age`, | ||
* each record represents one user found by the statement above. You can access | ||
* the values of each field either by name: | ||
* | ||
* record.get("n.name") | ||
* | ||
* Or by it's position: | ||
* | ||
* record.get(0) | ||
* | ||
* @access public | ||
*/ | ||
class Record { | ||
/** | ||
* Create a new record object. | ||
* @constructor | ||
* @access private | ||
* @param {Object} keys An array of field keys, in the order the fields appear | ||
* in the record | ||
* @param {Object} fields An array of field values | ||
* @param {Object} fieldLookup An object of fieldName -> value index, used to map | ||
* field names to values. If this is null, one will be | ||
* generated. | ||
*/ | ||
constructor(keys, fields, fieldLookup=null ) { | ||
this.keys = keys; | ||
this.length = keys.length; | ||
this._fields = fields; | ||
this._fieldLookup = fieldLookup || generateFieldLookup( keys ); | ||
} | ||
|
||
/** | ||
* Run the given function for each field in this record. The function | ||
* will get three arguments - the value, the key and this record, in that | ||
* order. | ||
* | ||
* @param visitor | ||
*/ | ||
forEach( visitor ) { | ||
for(let i=0;i<this.keys.length;i++) { | ||
visitor( this._fields[i], this.keys[i], this ); | ||
} | ||
} | ||
|
||
/** | ||
* Get a value from this record, either by index or by field key. | ||
* | ||
* @param {string|Number} key Field key, or the index of the field. | ||
* @returns {*} | ||
*/ | ||
get( key ) { | ||
let index; | ||
if( !(typeof key === "number") ) { | ||
index = this._fieldLookup[key]; | ||
if( index === undefined ) { | ||
throw newError("This record has no field with key '"+key+"', available key are: [" + this.keys + "]."); | ||
} | ||
} else { | ||
index = key; | ||
} | ||
|
||
if( index > this._fields.length - 1 || index < 0 ) { | ||
throw newError("This record has no field with index '"+index+"'. Remember that indexes start at `0`, " + | ||
"and make sure your statement returns records in the shape you meant it to."); | ||
} | ||
|
||
return this._fields[index]; | ||
} | ||
} | ||
|
||
export {Record} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
/** | ||
* Copyright (c) 2002-2016 "Neo Technology," | ||
* Network Engine for Objects in Lund AB [http://neotechnology.com] | ||
* | ||
* This file is part of Neo4j. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
var Record = require("../../lib/v1/record").Record; | ||
var Neo4jError = require("../../lib/v1/internal/error").Neo4jError; | ||
|
||
|
||
describe('Record', function() { | ||
it('should allow getting fields by name', function() { | ||
// Given | ||
var record = new Record( ["name"], ["Bob"] ); | ||
|
||
// When & Then | ||
expect(record.get("name")).toEqual("Bob"); | ||
}); | ||
|
||
it('should give helpful error on no such key', function() { | ||
// Given | ||
var record = new Record( ["name"], ["Bob"] ); | ||
|
||
// When & Then | ||
expect( function() { record.get("age") }).toThrow(new Neo4jError( | ||
"This record has no field with key 'age', available key are: [name].")); | ||
}); | ||
|
||
it('should allow getting fields by index', function() { | ||
// Given | ||
var record = new Record( ["name"], ["Bob"] ); | ||
|
||
// When & Then | ||
expect(record.get(0)).toEqual("Bob"); | ||
}); | ||
|
||
it('should give helpful error on no such index', function() { | ||
// Given | ||
var record = new Record( ["name"], ["Bob"] ); | ||
|
||
// When & Then | ||
expect( function() { record.get(1) }).toThrow(new Neo4jError( | ||
"This record has no field with index '1'. Remember that indexes start at `0`, " + | ||
"and make sure your statement returns records in the shape you meant it to.")); | ||
}); | ||
|
||
it('should have length', function() { | ||
// When & Then | ||
expect( new Record( [], []).length ).toBe(0); | ||
expect( new Record( ["name"], ["Bob"]).length ).toBe(1); | ||
expect( new Record( ["name", "age"], ["Bob", 45]).length ).toBe(2); | ||
}); | ||
|
||
it('should allow forEach through the record', function() { | ||
// Given | ||
var record = new Record( ["name", "age"], ["Bob", 45] ); | ||
var result = []; | ||
|
||
// When | ||
record.forEach( function( value, key, record ) { | ||
result.push( [value, key, record] ); | ||
}); | ||
|
||
// Then | ||
expect(result).toEqual([["Bob", "name", record], [45, "age", record]]); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.