Skip to content

Commit 9aeafbe

Browse files
authored
Merge pull request #350 from lutovich/1.6-temporal-spatial-native-numbers
Support number props in spatial and temporal types
2 parents d9a6a2b + 4957f27 commit 9aeafbe

File tree

5 files changed

+174
-42
lines changed

5 files changed

+174
-42
lines changed

src/v1/internal/packstream-v1.js

+16-1
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,15 @@ class Unpacker {
400400
throw newError('Unknown packed value with marker ' + marker.toString(16));
401401
}
402402

403+
unpackInteger(buffer) {
404+
const marker = buffer.readUInt8();
405+
const result = this._unpackInteger(marker, buffer);
406+
if (result == null) {
407+
throw newError('Unable to unpack integer value with marker ' + marker.toString(16));
408+
}
409+
return result;
410+
}
411+
403412
_unpackBoolean(marker) {
404413
if (marker == TRUE) {
405414
return true;
@@ -413,7 +422,13 @@ class Unpacker {
413422
_unpackNumberOrInteger(marker, buffer) {
414423
if (marker == FLOAT_64) {
415424
return buffer.readFloat64();
416-
} else if (marker >= 0 && marker < 128) {
425+
} else {
426+
return this._unpackInteger(marker, buffer);
427+
}
428+
}
429+
430+
_unpackInteger(marker, buffer) {
431+
if (marker >= 0 && marker < 128) {
417432
return int(marker);
418433
} else if (marker >= 240 && marker < 256) {
419434
return int(marker - 256);

src/v1/internal/packstream-v2.js

+61-34
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import {
3333
isTime,
3434
Time
3535
} from '../temporal-types';
36-
import {int} from '../integer';
36+
import {int, isInt} from '../integer';
3737
import {
3838
dateToEpochDay,
3939
epochDayToDate,
@@ -126,19 +126,19 @@ export class Unpacker extends v1.Unpacker {
126126
} else if (signature == DURATION) {
127127
return unpackDuration(this, structSize, buffer);
128128
} else if (signature == LOCAL_TIME) {
129-
return unpackLocalTime(this, structSize, buffer);
129+
return unpackLocalTime(this, structSize, buffer, this._disableLosslessIntegers);
130130
} else if (signature == TIME) {
131-
return unpackTime(this, structSize, buffer);
131+
return unpackTime(this, structSize, buffer, this._disableLosslessIntegers);
132132
} else if (signature == DATE) {
133-
return unpackDate(this, structSize, buffer);
133+
return unpackDate(this, structSize, buffer, this._disableLosslessIntegers);
134134
} else if (signature == LOCAL_DATE_TIME) {
135-
return unpackLocalDateTime(this, structSize, buffer);
135+
return unpackLocalDateTime(this, structSize, buffer, this._disableLosslessIntegers);
136136
} else if (signature == DATE_TIME_WITH_ZONE_OFFSET) {
137-
return unpackDateTimeWithZoneOffset(this, structSize, buffer);
137+
return unpackDateTimeWithZoneOffset(this, structSize, buffer, this._disableLosslessIntegers);
138138
} else if (signature == DATE_TIME_WITH_ZONE_ID) {
139-
return unpackDateTimeWithZoneId(this, structSize, buffer);
139+
return unpackDateTimeWithZoneId(this, structSize, buffer, this._disableLosslessIntegers);
140140
} else {
141-
return super._unpackUnknownStruct(signature, structSize, buffer);
141+
return super._unpackUnknownStruct(signature, structSize, buffer, this._disableLosslessIntegers);
142142
}
143143
}
144144
}
@@ -284,13 +284,15 @@ function packLocalTime(value, packer, onError) {
284284
* @param {Unpacker} unpacker the unpacker to use.
285285
* @param {number} structSize the retrieved struct size.
286286
* @param {BaseBuffer} buffer the buffer to unpack from.
287+
* @param {boolean} disableLosslessIntegers if integer properties in the result local time should be native JS numbers.
287288
* @return {LocalTime} the unpacked local time value.
288289
*/
289-
function unpackLocalTime(unpacker, structSize, buffer) {
290+
function unpackLocalTime(unpacker, structSize, buffer, disableLosslessIntegers) {
290291
unpacker._verifyStructSize('LocalTime', LOCAL_TIME_STRUCT_SIZE, structSize);
291292

292-
const nanoOfDay = unpacker.unpack(buffer);
293-
return nanoOfDayToLocalTime(nanoOfDay);
293+
const nanoOfDay = unpacker.unpackInteger(buffer);
294+
const result = nanoOfDayToLocalTime(nanoOfDay);
295+
return convertIntegerPropsIfNeeded(result, disableLosslessIntegers);
294296
}
295297

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

323-
const nanoOfDay = unpacker.unpack(buffer);
324-
const offsetSeconds = unpacker.unpack(buffer);
326+
const nanoOfDay = unpacker.unpackInteger(buffer);
327+
const offsetSeconds = unpacker.unpackInteger(buffer);
325328

326329
const localTime = nanoOfDayToLocalTime(nanoOfDay);
327-
return new Time(localTime.hour, localTime.minute, localTime.second, localTime.nanosecond, offsetSeconds);
330+
const result = new Time(localTime.hour, localTime.minute, localTime.second, localTime.nanosecond, offsetSeconds);
331+
return convertIntegerPropsIfNeeded(result, disableLosslessIntegers);
328332
}
329333

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

355-
const epochDay = unpacker.unpack(buffer);
356-
return epochDayToDate(epochDay);
360+
const epochDay = unpacker.unpackInteger(buffer);
361+
const result = epochDayToDate(epochDay);
362+
return convertIntegerPropsIfNeeded(result, disableLosslessIntegers);
357363
}
358364

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

386-
const epochSecond = unpacker.unpack(buffer);
387-
const nano = unpacker.unpack(buffer);
388-
389-
return epochSecondAndNanoToLocalDateTime(epochSecond, nano);
393+
const epochSecond = unpacker.unpackInteger(buffer);
394+
const nano = unpacker.unpackInteger(buffer);
395+
const result = epochSecondAndNanoToLocalDateTime(epochSecond, nano);
396+
return convertIntegerPropsIfNeeded(result, disableLosslessIntegers);
390397
}
391398

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

421-
const epochSecond = unpacker.unpack(buffer);
422-
const nano = unpacker.unpack(buffer);
423-
const offsetSeconds = unpacker.unpack(buffer);
429+
const epochSecond = unpacker.unpackInteger(buffer);
430+
const nano = unpacker.unpackInteger(buffer);
431+
const offsetSeconds = unpacker.unpackInteger(buffer);
424432

425433
const localDateTime = epochSecondAndNanoToLocalDateTime(epochSecond, nano);
426-
return new DateTimeWithZoneOffset(localDateTime.year, localDateTime.month, localDateTime.day, localDateTime.hour,
427-
localDateTime.minute, localDateTime.second, localDateTime.nanosecond, offsetSeconds);
434+
const result = new DateTimeWithZoneOffset(localDateTime.year, localDateTime.month, localDateTime.day,
435+
localDateTime.hour, localDateTime.minute, localDateTime.second, localDateTime.nanosecond, offsetSeconds);
436+
return convertIntegerPropsIfNeeded(result, disableLosslessIntegers);
428437
}
429438

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

459-
const epochSecond = unpacker.unpack(buffer);
460-
const nano = unpacker.unpack(buffer);
469+
const epochSecond = unpacker.unpackInteger(buffer);
470+
const nano = unpacker.unpackInteger(buffer);
461471
const zoneId = unpacker.unpack(buffer);
462472

463473
const localDateTime = epochSecondAndNanoToLocalDateTime(epochSecond, nano);
464-
return new DateTimeWithZoneId(localDateTime.year, localDateTime.month, localDateTime.day, localDateTime.hour,
465-
localDateTime.minute, localDateTime.second, localDateTime.nanosecond, zoneId);
474+
const result = new DateTimeWithZoneId(localDateTime.year, localDateTime.month, localDateTime.day,
475+
localDateTime.hour, localDateTime.minute, localDateTime.second, localDateTime.nanosecond, zoneId);
476+
return convertIntegerPropsIfNeeded(result, disableLosslessIntegers);
477+
}
478+
479+
function convertIntegerPropsIfNeeded(obj, disableLosslessIntegers) {
480+
if (!disableLosslessIntegers) {
481+
return obj;
482+
}
483+
484+
const clone = Object.create(Object.getPrototypeOf(obj));
485+
for (let prop in obj) {
486+
if (obj.hasOwnProperty(prop)) {
487+
const value = obj[prop];
488+
clone[prop] = isInt(value) ? value.toNumberOrInfinity() : value;
489+
}
490+
}
491+
Object.freeze(clone);
492+
return clone;
466493
}

src/v1/spatial-types.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717
* limitations under the License.
1818
*/
1919

20-
import {int} from './integer';
21-
2220
const POINT_IDENTIFIER_PROPERTY = '__isPoint__';
2321

2422
/**
@@ -29,13 +27,13 @@ export class Point {
2927

3028
/**
3129
* @constructor
32-
* @param {number|Integer} srid the coordinate reference system identifier.
30+
* @param {Integer|number} srid the coordinate reference system identifier.
3331
* @param {number} x the <code>x</code> coordinate of the point.
3432
* @param {number} y the <code>y</code> coordinate of the point.
3533
* @param {number} [z=undefined] the <code>y</code> coordinate of the point or <code>undefined</code> if point has 2 dimensions.
3634
*/
3735
constructor(srid, x, y, z) {
38-
this.srid = int(srid);
36+
this.srid = srid;
3937
this.x = x;
4038
this.y = y;
4139
this.z = z;

test/v1/spatial-types.test.js

+24
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import neo4j from '../../src/v1';
2121
import sharedNeo4j from '../internal/shared-neo4j';
2222
import {ServerVersion, VERSION_3_4_0} from '../../src/v1/internal/server-version';
2323
import {isPoint, Point} from '../../src/v1/spatial-types';
24+
import _ from 'lodash';
2425

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

3334
let driver;
35+
let driverWithNativeNumbers;
3436
let session;
3537
let serverVersion;
3638

3739
beforeAll(done => {
3840
driver = neo4j.driver('bolt://localhost', sharedNeo4j.authToken);
41+
driverWithNativeNumbers = neo4j.driver('bolt://localhost', sharedNeo4j.authToken, {disableLosslessIntegers: true});
3942
ServerVersion.fromDriver(driver).then(version => {
4043
serverVersion = version;
4144
done();
@@ -47,6 +50,11 @@ describe('spatial-types', () => {
4750
driver.close();
4851
driver = null;
4952
}
53+
54+
if (driverWithNativeNumbers) {
55+
driverWithNativeNumbers.close();
56+
driverWithNativeNumbers = null;
57+
}
5058
});
5159

5260
beforeEach(done => {
@@ -166,6 +174,22 @@ describe('spatial-types', () => {
166174
testSendingAndReceivingOfPoints(done, arrayOfPoints);
167175
});
168176

177+
it('should receive point with number srid when disableLosslessIntegers=true', done => {
178+
session = driverWithNativeNumbers.session();
179+
180+
testReceivingOfPoints(done, 'RETURN point({x: 42.231, y: 176.938123})', point => {
181+
expect(isPoint(point)).toBeTruthy();
182+
expect(_.isNumber(point.srid)).toBeTruthy();
183+
expect(point.srid).toEqual(CARTESIAN_2D_CRS_CODE.toNumber());
184+
});
185+
});
186+
187+
it('should send and receive point with number srid when disableLosslessIntegers=true', done => {
188+
session = driverWithNativeNumbers.session();
189+
190+
testSendingAndReceivingOfPoints(done, new Point(CARTESIAN_3D_CRS_CODE.toNumber(), 12.87, 13.89, 14.901));
191+
});
192+
169193
function testReceivingOfPoints(done, query, pointChecker) {
170194
if (neo4jDoesNotSupportPoints(done)) {
171195
return;

0 commit comments

Comments
 (0)