Skip to content

Commit e8be98d

Browse files
dplewisflovilmart
authored andcommitted
chore(Query): $withinPolygon: 3 point minimum and boundary testing. (#3889)
* added 3 point minimum constraint to within-polygon * test nit * test for open and closed paths
1 parent 9ad8697 commit e8be98d

File tree

3 files changed

+107
-21
lines changed

3 files changed

+107
-21
lines changed

spec/ParseGeoPoint.spec.js

Lines changed: 87 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const rp = require('request-promise');
55
var TestObject = Parse.Object.extend('TestObject');
66

77
describe('Parse.GeoPoint testing', () => {
8+
89
it('geo point roundtrip', (done) => {
910
var point = new Parse.GeoPoint(44.0, -11.0);
1011
var obj = new TestObject();
@@ -331,18 +332,20 @@ describe('Parse.GeoPoint testing', () => {
331332
});
332333

333334
it('works with geobox queries', (done) => {
334-
var inSF = new Parse.GeoPoint(37.75, -122.4);
335-
var southwestOfSF = new Parse.GeoPoint(37.708813, -122.526398);
336-
var northeastOfSF = new Parse.GeoPoint(37.822802, -122.373962);
337-
338-
var object = new TestObject();
339-
object.set('point', inSF);
340-
object.save().then(() => {
341-
var query = new Parse.Query(TestObject);
342-
query.withinGeoBox('point', southwestOfSF, northeastOfSF);
335+
const inbound = new Parse.GeoPoint(1.5, 1.5);
336+
const onbound = new Parse.GeoPoint(10, 10);
337+
const outbound = new Parse.GeoPoint(20, 20);
338+
const obj1 = new Parse.Object('TestObject', {location: inbound});
339+
const obj2 = new Parse.Object('TestObject', {location: onbound});
340+
const obj3 = new Parse.Object('TestObject', {location: outbound});
341+
Parse.Object.saveAll([obj1, obj2, obj3]).then(() => {
342+
const sw = new Parse.GeoPoint(0, 0);
343+
const ne = new Parse.GeoPoint(10, 10);
344+
const query = new Parse.Query(TestObject);
345+
query.withinGeoBox('location', sw, ne);
343346
return query.find();
344347
}).then((results) => {
345-
equal(results.length, 1);
348+
equal(results.length, 2);
346349
done();
347350
});
348351
});
@@ -407,13 +410,13 @@ describe('Parse.GeoPoint testing', () => {
407410
});
408411
});
409412

410-
it('supports withinPolygon', (done) => {
411-
const point1 = new Parse.GeoPoint(1.5, 1.5);
412-
const point2 = new Parse.GeoPoint(2, 8);
413-
const point3 = new Parse.GeoPoint(20, 20);
414-
const obj1 = new Parse.Object('Polygon', {location: point1});
415-
const obj2 = new Parse.Object('Polygon', {location: point2});
416-
const obj3 = new Parse.Object('Polygon', {location: point3});
413+
it('supports withinPolygon open path', (done) => {
414+
const inbound = new Parse.GeoPoint(1.5, 1.5);
415+
const onbound = new Parse.GeoPoint(10, 10);
416+
const outbound = new Parse.GeoPoint(20, 20);
417+
const obj1 = new Parse.Object('Polygon', {location: inbound});
418+
const obj2 = new Parse.Object('Polygon', {location: onbound});
419+
const obj3 = new Parse.Object('Polygon', {location: outbound});
417420
Parse.Object.saveAll([obj1, obj2, obj3]).then(() => {
418421
const where = {
419422
location: {
@@ -441,6 +444,41 @@ describe('Parse.GeoPoint testing', () => {
441444
}, done.fail);
442445
});
443446

447+
it('supports withinPolygon closed path', (done) => {
448+
const inbound = new Parse.GeoPoint(1.5, 1.5);
449+
const onbound = new Parse.GeoPoint(10, 10);
450+
const outbound = new Parse.GeoPoint(20, 20);
451+
const obj1 = new Parse.Object('Polygon', {location: inbound});
452+
const obj2 = new Parse.Object('Polygon', {location: onbound});
453+
const obj3 = new Parse.Object('Polygon', {location: outbound});
454+
Parse.Object.saveAll([obj1, obj2, obj3]).then(() => {
455+
const where = {
456+
location: {
457+
$geoWithin: {
458+
$polygon: [
459+
{ __type: 'GeoPoint', latitude: 0, longitude: 0 },
460+
{ __type: 'GeoPoint', latitude: 0, longitude: 10 },
461+
{ __type: 'GeoPoint', latitude: 10, longitude: 10 },
462+
{ __type: 'GeoPoint', latitude: 10, longitude: 0 },
463+
{ __type: 'GeoPoint', latitude: 0, longitude: 0 }
464+
]
465+
}
466+
}
467+
};
468+
return rp.post({
469+
url: Parse.serverURL + '/classes/Polygon',
470+
json: { where, '_method': 'GET' },
471+
headers: {
472+
'X-Parse-Application-Id': Parse.applicationId,
473+
'X-Parse-Javascript-Key': Parse.javaScriptKey
474+
}
475+
});
476+
}).then((resp) => {
477+
expect(resp.results.length).toBe(2);
478+
done();
479+
}, done.fail);
480+
});
481+
444482
it('invalid input withinPolygon', (done) => {
445483
const point = new Parse.GeoPoint(1.5, 1.5);
446484
const obj = new Parse.Object('Polygon', {location: point});
@@ -508,7 +546,8 @@ describe('Parse.GeoPoint testing', () => {
508546
$geoWithin: {
509547
$polygon: [
510548
{ __type: 'GeoPoint', latitude: 0, longitude: 0 },
511-
{ __type: 'GeoPoint', latitude: 181, longitude: 0 }
549+
{ __type: 'GeoPoint', latitude: 181, longitude: 0 },
550+
{ __type: 'GeoPoint', latitude: 0, longitude: 0 }
512551
]
513552
}
514553
}
@@ -539,7 +578,8 @@ describe('Parse.GeoPoint testing', () => {
539578
$geoWithin: {
540579
$polygon: [
541580
{ __type: 'GeoPoint', latitude: 0, longitude: 0 },
542-
{ __type: 'GeoPoint', latitude: 0, longitude: 181 }
581+
{ __type: 'GeoPoint', latitude: 0, longitude: 181 },
582+
{ __type: 'GeoPoint', latitude: 0, longitude: 0 }
543583
]
544584
}
545585
}
@@ -560,4 +600,32 @@ describe('Parse.GeoPoint testing', () => {
560600
done();
561601
});
562602
});
603+
604+
it('minimum 3 points withinPolygon', (done) => {
605+
const point = new Parse.GeoPoint(1.5, 1.5);
606+
const obj = new Parse.Object('Polygon', {location: point});
607+
obj.save().then(() => {
608+
const where = {
609+
location: {
610+
$geoWithin: {
611+
$polygon: []
612+
}
613+
}
614+
};
615+
return rp.post({
616+
url: Parse.serverURL + '/classes/Polygon',
617+
json: { where, '_method': 'GET' },
618+
headers: {
619+
'X-Parse-Application-Id': Parse.applicationId,
620+
'X-Parse-Javascript-Key': Parse.javaScriptKey
621+
}
622+
});
623+
}).then((resp) => {
624+
fail(`no request should succeed: ${JSON.stringify(resp)}`);
625+
done();
626+
}).catch((err) => {
627+
expect(err.error.code).toEqual(107);
628+
done();
629+
});
630+
});
563631
});

src/Adapters/Storage/Mongo/MongoTransform.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,16 @@ function transformConstraint(constraint, inArray) {
621621
case '$geoWithin': {
622622
const polygon = constraint[key]['$polygon'];
623623
if (!(polygon instanceof Array)) {
624-
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $geoWithin value');
624+
throw new Parse.Error(
625+
Parse.Error.INVALID_JSON,
626+
'bad $geoWithin value; $polygon should contain at least 3 GeoPoints'
627+
);
628+
}
629+
if (polygon.length < 3) {
630+
throw new Parse.Error(
631+
Parse.Error.INVALID_JSON,
632+
'bad $geoWithin value; $polygon should contain at least 3 GeoPoints'
633+
);
625634
}
626635
const points = polygon.map((point) => {
627636
if (!GeoPointCoder.isValidJSON(point)) {

src/Adapters/Storage/Postgres/PostgresStorageAdapter.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,16 @@ const buildWhereClause = ({ schema, query, index }) => {
348348
if (fieldValue.$geoWithin && fieldValue.$geoWithin.$polygon) {
349349
const polygon = fieldValue.$geoWithin.$polygon;
350350
if (!(polygon instanceof Array)) {
351-
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $geoWithin value');
351+
throw new Parse.Error(
352+
Parse.Error.INVALID_JSON,
353+
'bad $geoWithin value; $polygon should contain at least 3 GeoPoints'
354+
);
355+
}
356+
if (polygon.length < 3) {
357+
throw new Parse.Error(
358+
Parse.Error.INVALID_JSON,
359+
'bad $geoWithin value; $polygon should contain at least 3 GeoPoints'
360+
);
352361
}
353362
const points = polygon.map((point) => {
354363
if (typeof point !== 'object' || point.__type !== 'GeoPoint') {

0 commit comments

Comments
 (0)