Skip to content

Support number props in spatial and temporal types #350

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 1 commit into from
Apr 12, 2018
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
17 changes: 16 additions & 1 deletion src/v1/internal/packstream-v1.js
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,15 @@ class Unpacker {
throw newError('Unknown packed value with marker ' + marker.toString(16));
}

unpackInteger(buffer) {
const marker = buffer.readUInt8();
const result = this._unpackInteger(marker, buffer);
if (result == null) {
throw newError('Unable to unpack integer value with marker ' + marker.toString(16));
}
return result;
}

_unpackBoolean(marker) {
if (marker == TRUE) {
return true;
Expand All @@ -413,7 +422,13 @@ class Unpacker {
_unpackNumberOrInteger(marker, buffer) {
if (marker == FLOAT_64) {
return buffer.readFloat64();
} else if (marker >= 0 && marker < 128) {
} else {
return this._unpackInteger(marker, buffer);
}
}

_unpackInteger(marker, buffer) {
if (marker >= 0 && marker < 128) {
return int(marker);
} else if (marker >= 240 && marker < 256) {
return int(marker - 256);
Expand Down
95 changes: 61 additions & 34 deletions src/v1/internal/packstream-v2.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
isTime,
Time
} from '../temporal-types';
import {int} from '../integer';
import {int, isInt} from '../integer';
import {
dateToEpochDay,
epochDayToDate,
Expand Down Expand Up @@ -126,19 +126,19 @@ export class Unpacker extends v1.Unpacker {
} else if (signature == DURATION) {
return unpackDuration(this, structSize, buffer);
} else if (signature == LOCAL_TIME) {
return unpackLocalTime(this, structSize, buffer);
return unpackLocalTime(this, structSize, buffer, this._disableLosslessIntegers);
} else if (signature == TIME) {
return unpackTime(this, structSize, buffer);
return unpackTime(this, structSize, buffer, this._disableLosslessIntegers);
} else if (signature == DATE) {
return unpackDate(this, structSize, buffer);
return unpackDate(this, structSize, buffer, this._disableLosslessIntegers);
} else if (signature == LOCAL_DATE_TIME) {
return unpackLocalDateTime(this, structSize, buffer);
return unpackLocalDateTime(this, structSize, buffer, this._disableLosslessIntegers);
} else if (signature == DATE_TIME_WITH_ZONE_OFFSET) {
return unpackDateTimeWithZoneOffset(this, structSize, buffer);
return unpackDateTimeWithZoneOffset(this, structSize, buffer, this._disableLosslessIntegers);
} else if (signature == DATE_TIME_WITH_ZONE_ID) {
return unpackDateTimeWithZoneId(this, structSize, buffer);
return unpackDateTimeWithZoneId(this, structSize, buffer, this._disableLosslessIntegers);
} else {
return super._unpackUnknownStruct(signature, structSize, buffer);
return super._unpackUnknownStruct(signature, structSize, buffer, this._disableLosslessIntegers);
}
}
}
Expand Down Expand Up @@ -284,13 +284,15 @@ function packLocalTime(value, packer, onError) {
* @param {Unpacker} unpacker the unpacker to use.
* @param {number} structSize the retrieved struct size.
* @param {BaseBuffer} buffer the buffer to unpack from.
* @param {boolean} disableLosslessIntegers if integer properties in the result local time should be native JS numbers.
* @return {LocalTime} the unpacked local time value.
*/
function unpackLocalTime(unpacker, structSize, buffer) {
function unpackLocalTime(unpacker, structSize, buffer, disableLosslessIntegers) {
unpacker._verifyStructSize('LocalTime', LOCAL_TIME_STRUCT_SIZE, structSize);

const nanoOfDay = unpacker.unpack(buffer);
return nanoOfDayToLocalTime(nanoOfDay);
const nanoOfDay = unpacker.unpackInteger(buffer);
const result = nanoOfDayToLocalTime(nanoOfDay);
return convertIntegerPropsIfNeeded(result, disableLosslessIntegers);
}

/**
Expand All @@ -315,16 +317,18 @@ function packTime(value, packer, onError) {
* @param {Unpacker} unpacker the unpacker to use.
* @param {number} structSize the retrieved struct size.
* @param {BaseBuffer} buffer the buffer to unpack from.
* @param {boolean} disableLosslessIntegers if integer properties in the result time should be native JS numbers.
* @return {Time} the unpacked time value.
*/
function unpackTime(unpacker, structSize, buffer) {
function unpackTime(unpacker, structSize, buffer, disableLosslessIntegers) {
unpacker._verifyStructSize('Time', TIME_STRUCT_SIZE, structSize);

const nanoOfDay = unpacker.unpack(buffer);
const offsetSeconds = unpacker.unpack(buffer);
const nanoOfDay = unpacker.unpackInteger(buffer);
const offsetSeconds = unpacker.unpackInteger(buffer);

const localTime = nanoOfDayToLocalTime(nanoOfDay);
return new Time(localTime.hour, localTime.minute, localTime.second, localTime.nanosecond, offsetSeconds);
const result = new Time(localTime.hour, localTime.minute, localTime.second, localTime.nanosecond, offsetSeconds);
return convertIntegerPropsIfNeeded(result, disableLosslessIntegers);
}

/**
Expand All @@ -347,13 +351,15 @@ function packDate(value, packer, onError) {
* @param {Unpacker} unpacker the unpacker to use.
* @param {number} structSize the retrieved struct size.
* @param {BaseBuffer} buffer the buffer to unpack from.
* @param {boolean} disableLosslessIntegers if integer properties in the result date should be native JS numbers.
* @return {Date} the unpacked neo4j date value.
*/
function unpackDate(unpacker, structSize, buffer) {
function unpackDate(unpacker, structSize, buffer, disableLosslessIntegers) {
unpacker._verifyStructSize('Date', DATE_STRUCT_SIZE, structSize);

const epochDay = unpacker.unpack(buffer);
return epochDayToDate(epochDay);
const epochDay = unpacker.unpackInteger(buffer);
const result = epochDayToDate(epochDay);
return convertIntegerPropsIfNeeded(result, disableLosslessIntegers);
}

/**
Expand All @@ -378,15 +384,16 @@ function packLocalDateTime(value, packer, onError) {
* @param {Unpacker} unpacker the unpacker to use.
* @param {number} structSize the retrieved struct size.
* @param {BaseBuffer} buffer the buffer to unpack from.
* @param {boolean} disableLosslessIntegers if integer properties in the result local date-time should be native JS numbers.
* @return {LocalDateTime} the unpacked local date time value.
*/
function unpackLocalDateTime(unpacker, structSize, buffer) {
function unpackLocalDateTime(unpacker, structSize, buffer, disableLosslessIntegers) {
unpacker._verifyStructSize('LocalDateTime', LOCAL_DATE_TIME_STRUCT_SIZE, structSize);

const epochSecond = unpacker.unpack(buffer);
const nano = unpacker.unpack(buffer);

return epochSecondAndNanoToLocalDateTime(epochSecond, nano);
const epochSecond = unpacker.unpackInteger(buffer);
const nano = unpacker.unpackInteger(buffer);
const result = epochSecondAndNanoToLocalDateTime(epochSecond, nano);
return convertIntegerPropsIfNeeded(result, disableLosslessIntegers);
}

/**
Expand All @@ -413,18 +420,20 @@ function packDateTimeWithZoneOffset(value, packer, onError) {
* @param {Unpacker} unpacker the unpacker to use.
* @param {number} structSize the retrieved struct size.
* @param {BaseBuffer} buffer the buffer to unpack from.
* @param {boolean} disableLosslessIntegers if integer properties in the result date-time should be native JS numbers.
* @return {DateTimeWithZoneOffset} the unpacked date time with zone offset value.
*/
function unpackDateTimeWithZoneOffset(unpacker, structSize, buffer) {
function unpackDateTimeWithZoneOffset(unpacker, structSize, buffer, disableLosslessIntegers) {
unpacker._verifyStructSize('DateTimeWithZoneOffset', DATE_TIME_WITH_ZONE_OFFSET_STRUCT_SIZE, structSize);

const epochSecond = unpacker.unpack(buffer);
const nano = unpacker.unpack(buffer);
const offsetSeconds = unpacker.unpack(buffer);
const epochSecond = unpacker.unpackInteger(buffer);
const nano = unpacker.unpackInteger(buffer);
const offsetSeconds = unpacker.unpackInteger(buffer);

const localDateTime = epochSecondAndNanoToLocalDateTime(epochSecond, nano);
return new DateTimeWithZoneOffset(localDateTime.year, localDateTime.month, localDateTime.day, localDateTime.hour,
localDateTime.minute, localDateTime.second, localDateTime.nanosecond, offsetSeconds);
const result = new DateTimeWithZoneOffset(localDateTime.year, localDateTime.month, localDateTime.day,
localDateTime.hour, localDateTime.minute, localDateTime.second, localDateTime.nanosecond, offsetSeconds);
return convertIntegerPropsIfNeeded(result, disableLosslessIntegers);
}

/**
Expand All @@ -451,16 +460,34 @@ function packDateTimeWithZoneId(value, packer, onError) {
* @param {Unpacker} unpacker the unpacker to use.
* @param {number} structSize the retrieved struct size.
* @param {BaseBuffer} buffer the buffer to unpack from.
* @param {boolean} disableLosslessIntegers if integer properties in the result date-time should be native JS numbers.
* @return {DateTimeWithZoneId} the unpacked date time with zone id value.
*/
function unpackDateTimeWithZoneId(unpacker, structSize, buffer) {
function unpackDateTimeWithZoneId(unpacker, structSize, buffer, disableLosslessIntegers) {
unpacker._verifyStructSize('DateTimeWithZoneId', DATE_TIME_WITH_ZONE_ID_STRUCT_SIZE, structSize);

const epochSecond = unpacker.unpack(buffer);
const nano = unpacker.unpack(buffer);
const epochSecond = unpacker.unpackInteger(buffer);
const nano = unpacker.unpackInteger(buffer);
const zoneId = unpacker.unpack(buffer);

const localDateTime = epochSecondAndNanoToLocalDateTime(epochSecond, nano);
return new DateTimeWithZoneId(localDateTime.year, localDateTime.month, localDateTime.day, localDateTime.hour,
localDateTime.minute, localDateTime.second, localDateTime.nanosecond, zoneId);
const result = new DateTimeWithZoneId(localDateTime.year, localDateTime.month, localDateTime.day,
localDateTime.hour, localDateTime.minute, localDateTime.second, localDateTime.nanosecond, zoneId);
return convertIntegerPropsIfNeeded(result, disableLosslessIntegers);
}

function convertIntegerPropsIfNeeded(obj, disableLosslessIntegers) {
if (!disableLosslessIntegers) {
return obj;
}

const clone = Object.create(Object.getPrototypeOf(obj));
for (let prop in obj) {
if (obj.hasOwnProperty(prop)) {
const value = obj[prop];
clone[prop] = isInt(value) ? value.toNumberOrInfinity() : value;
}
}
Object.freeze(clone);
return clone;
}
6 changes: 2 additions & 4 deletions src/v1/spatial-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
* limitations under the License.
*/

import {int} from './integer';

const POINT_IDENTIFIER_PROPERTY = '__isPoint__';

/**
Expand All @@ -29,13 +27,13 @@ export class Point {

/**
* @constructor
* @param {number|Integer} srid the coordinate reference system identifier.
* @param {Integer|number} srid the coordinate reference system identifier.
* @param {number} x the <code>x</code> coordinate of the point.
* @param {number} y the <code>y</code> coordinate of the point.
* @param {number} [z=undefined] the <code>y</code> coordinate of the point or <code>undefined</code> if point has 2 dimensions.
*/
constructor(srid, x, y, z) {
this.srid = int(srid);
this.srid = srid;
this.x = x;
this.y = y;
this.z = z;
Expand Down
24 changes: 24 additions & 0 deletions test/v1/spatial-types.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import neo4j from '../../src/v1';
import sharedNeo4j from '../internal/shared-neo4j';
import {ServerVersion, VERSION_3_4_0} from '../../src/v1/internal/server-version';
import {isPoint, Point} from '../../src/v1/spatial-types';
import _ from 'lodash';

const WGS_84_2D_CRS_CODE = neo4j.int(4326);
const CARTESIAN_2D_CRS_CODE = neo4j.int(7203);
Expand All @@ -31,11 +32,13 @@ const CARTESIAN_3D_CRS_CODE = neo4j.int(9157);
describe('spatial-types', () => {

let driver;
let driverWithNativeNumbers;
let session;
let serverVersion;

beforeAll(done => {
driver = neo4j.driver('bolt://localhost', sharedNeo4j.authToken);
driverWithNativeNumbers = neo4j.driver('bolt://localhost', sharedNeo4j.authToken, {disableLosslessIntegers: true});
ServerVersion.fromDriver(driver).then(version => {
serverVersion = version;
done();
Expand All @@ -47,6 +50,11 @@ describe('spatial-types', () => {
driver.close();
driver = null;
}

if (driverWithNativeNumbers) {
driverWithNativeNumbers.close();
driverWithNativeNumbers = null;
}
});

beforeEach(done => {
Expand Down Expand Up @@ -166,6 +174,22 @@ describe('spatial-types', () => {
testSendingAndReceivingOfPoints(done, arrayOfPoints);
});

it('should receive point with number srid when disableLosslessIntegers=true', done => {
session = driverWithNativeNumbers.session();

testReceivingOfPoints(done, 'RETURN point({x: 42.231, y: 176.938123})', point => {
expect(isPoint(point)).toBeTruthy();
expect(_.isNumber(point.srid)).toBeTruthy();
expect(point.srid).toEqual(CARTESIAN_2D_CRS_CODE.toNumber());
});
});

it('should send and receive point with number srid when disableLosslessIntegers=true', done => {
session = driverWithNativeNumbers.session();

testSendingAndReceivingOfPoints(done, new Point(CARTESIAN_3D_CRS_CODE.toNumber(), 12.87, 13.89, 14.901));
});

function testReceivingOfPoints(done, query, pointChecker) {
if (neo4jDoesNotSupportPoints(done)) {
return;
Expand Down
Loading