From df11182bf995e22ac46d596b57bbc5c97d84e050 Mon Sep 17 00:00:00 2001 From: Mads Bjerre Date: Mon, 31 Jul 2017 16:59:32 +0200 Subject: [PATCH 1/9] Whitespace --- src/vendor/mongodbUrl.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vendor/mongodbUrl.js b/src/vendor/mongodbUrl.js index 1616c3dfc0..4e3689f0c3 100644 --- a/src/vendor/mongodbUrl.js +++ b/src/vendor/mongodbUrl.js @@ -255,8 +255,8 @@ Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) { hostEnd = i; break; case 64: // '@' - // At this point, either we have an explicit point where the - // auth portion cannot go past, or the last @ char is the decider. + // At this point, either we have an explicit point where the + // auth portion cannot go past, or the last @ char is the decider. atSign = i; nonHost = -1; break; From cb18e4ada7ec698921dcfff7c1c68ad2472bef3f Mon Sep 17 00:00:00 2001 From: Mads Bjerre Date: Mon, 31 Jul 2017 17:25:18 +0200 Subject: [PATCH 2/9] Add Polygon type to $polygon query --- src/Adapters/Storage/Mongo/MongoTransform.js | 46 ++++++++++++-------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoTransform.js b/src/Adapters/Storage/Mongo/MongoTransform.js index f46c310e23..67a02bd7d0 100644 --- a/src/Adapters/Storage/Mongo/MongoTransform.js +++ b/src/Adapters/Storage/Mongo/MongoTransform.js @@ -670,26 +670,34 @@ function transformConstraint(constraint, inArray) { case '$geoWithin': { const polygon = constraint[key]['$polygon']; - if (!(polygon instanceof Array)) { - throw new Parse.Error( - Parse.Error.INVALID_JSON, - 'bad $geoWithin value; $polygon should contain at least 3 GeoPoints' - ); - } - if (polygon.length < 3) { - throw new Parse.Error( - Parse.Error.INVALID_JSON, - 'bad $geoWithin value; $polygon should contain at least 3 GeoPoints' - ); - } - const points = polygon.map((point) => { - if (!GeoPointCoder.isValidJSON(point)) { - throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $geoWithin value'); - } else { - Parse.GeoPoint._validate(point.latitude, point.longitude); + let points + if (polygon.__type === 'Polygon') { + points = polygon.coordinates; + } else { + if (!(polygon instanceof Array)) { + throw new Parse.Error( + Parse.Error.INVALID_JSON, + 'bad $geoWithin value; $polygon should contain at least 3 GeoPoints' + ); } - return [point.longitude, point.latitude]; - }); + if (polygon.length < 3) { + throw new Parse.Error( + Parse.Error.INVALID_JSON, + 'bad $geoWithin value; $polygon should contain at least 3 GeoPoints' + ); + } + points = polygon.map((point) => { + if (point instanceof Array && point.length === 2) { + return point; + } + if (!GeoPointCoder.isValidJSON(point)) { + throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $geoWithin value'); + } else { + Parse.GeoPoint._validate(point.latitude, point.longitude); + } + return [point.longitude, point.latitude]; + }); + } answer[key] = { '$polygon': points }; From c665516ee056b49ca02d5895d69d1a31c56d9ff3 Mon Sep 17 00:00:00 2001 From: Mads Bjerre Date: Mon, 31 Jul 2017 17:26:33 +0200 Subject: [PATCH 3/9] Add tests Polygon object in $polygon query $geoIntersects queries --- spec/ParseGeoPoint.spec.js | 93 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/spec/ParseGeoPoint.spec.js b/spec/ParseGeoPoint.spec.js index f2f8201e98..9bb1cbe62b 100644 --- a/spec/ParseGeoPoint.spec.js +++ b/spec/ParseGeoPoint.spec.js @@ -479,6 +479,45 @@ describe('Parse.GeoPoint testing', () => { }, done.fail); }); + it('supports withinPolygon Polygon object', (done) => { + const inbound = new Parse.GeoPoint(1.5, 1.5); + const onbound = new Parse.GeoPoint(10, 10); + const outbound = new Parse.GeoPoint(20, 20); + const obj1 = new Parse.Object('Polygon', {location: inbound}); + const obj2 = new Parse.Object('Polygon', {location: onbound}); + const obj3 = new Parse.Object('Polygon', {location: outbound}); + const polygon = { + __type: 'Polygon', + coordinates: [ + [0, 0], + [10, 0], + [10, 10], + [0, 10], + [0, 0] + ] + } + Parse.Object.saveAll([obj1, obj2, obj3]).then(() => { + const where = { + location: { + $geoWithin: { + $polygon: polygon + } + } + }; + return rp.post({ + url: Parse.serverURL + '/classes/Polygon', + json: { where, '_method': 'GET' }, + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey + } + }); + }).then((resp) => { + expect(resp.results.length).toBe(2); + done(); + }, done.fail); + }); + it('invalid input withinPolygon', (done) => { const point = new Parse.GeoPoint(1.5, 1.5); const obj = new Parse.Object('Polygon', {location: point}); @@ -628,4 +667,58 @@ describe('Parse.GeoPoint testing', () => { done(); }); }); + + it('support $geoIntersects queries', (done) => { + const polygon = { + __type: 'Polygon', + coordinates: [ + [-111.9250, 33.5746], + [-112.0002, 33.4769], + [-111.8391, 33.4761] + ] + } + const obj = new Parse.Object('Polygon', { polygon }); + obj.save().then(() => { + const pointInsidePolygon = new Parse.GeoPoint(33.51421, -111.92674); + const where = { + polygon: { + $geoIntersects: { + $point: pointInsidePolygon + } + } + }; + return rp.post({ + url: Parse.serverURL + '/classes/Polygon', + json: { where, '_method': 'GET' }, + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey + } + }); + }).then((resp) => { + equal(resp.results.length, 1); + const pointOutsidePolygon = new Parse.GeoPoint(33.52537, -112.17359); + const where = { + polygon: { + $geoIntersects: { + $point: pointOutsidePolygon + } + } + }; + return rp.post({ + url: Parse.serverURL + '/classes/Polygon', + json: { where, '_method': 'GET' }, + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey + } + }); + }).then((resp) => { + equal(resp.results.length, 0); + done(); + }).catch((err) => { + console.log(err) + fail(err); + }); + }); }); From 2a72e6b12d59bef3d0bb68c65f4119add5dd34df Mon Sep 17 00:00:00 2001 From: Mads Bjerre Date: Mon, 31 Jul 2017 18:06:49 +0200 Subject: [PATCH 4/9] Refactor --- src/Adapters/Storage/Mongo/MongoTransform.js | 39 ++++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoTransform.js b/src/Adapters/Storage/Mongo/MongoTransform.js index 67a02bd7d0..378ccf1be9 100644 --- a/src/Adapters/Storage/Mongo/MongoTransform.js +++ b/src/Adapters/Storage/Mongo/MongoTransform.js @@ -671,33 +671,40 @@ function transformConstraint(constraint, inArray) { case '$geoWithin': { const polygon = constraint[key]['$polygon']; let points - if (polygon.__type === 'Polygon') { - points = polygon.coordinates; - } else { - if (!(polygon instanceof Array)) { + if (typeof polygon === 'object' && polygon.__type === 'Polygon') { + if (!polygon.coordinates && polygon.coordinates.length < 3) { throw new Parse.Error( Parse.Error.INVALID_JSON, - 'bad $geoWithin value; $polygon should contain at least 3 GeoPoints' + 'bad $geoWithin value; Polygon.coordinates should contain at least 3 lon/lat pairs' ); } + points = polygon.coordinates + } else if (polygon instanceof Array) { if (polygon.length < 3) { throw new Parse.Error( Parse.Error.INVALID_JSON, 'bad $geoWithin value; $polygon should contain at least 3 GeoPoints' ); } - points = polygon.map((point) => { - if (point instanceof Array && point.length === 2) { - return point; - } - if (!GeoPointCoder.isValidJSON(point)) { - throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $geoWithin value'); - } else { - Parse.GeoPoint._validate(point.latitude, point.longitude); - } - return [point.longitude, point.latitude]; - }); + points = polygon; + } else { + throw new Parse.Error( + Parse.Error.INVALID_JSON, + 'bad $geoWithin value; $polygon should be Polygon object or Array of Parse.GeoPoint\'s' + ); } + points = points.map((point) => { + if (point instanceof Array && point.length === 2) { + Parse.GeoPoint._validate(point[1], point[0]); + return point; + } + if (!GeoPointCoder.isValidJSON(point)) { + throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $geoWithin value'); + } else { + Parse.GeoPoint._validate(point.latitude, point.longitude); + } + return [point.longitude, point.latitude]; + }); answer[key] = { '$polygon': points }; From 2c9466ac8ddfc43f4e11b07948c717172638bc8f Mon Sep 17 00:00:00 2001 From: Mads Bjerre Date: Mon, 31 Jul 2017 18:07:33 +0200 Subject: [PATCH 5/9] Postgres support --- .../Postgres/PostgresStorageAdapter.js | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js index 6cdac7ad79..ddc118ac34 100644 --- a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js +++ b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js @@ -413,19 +413,34 @@ const buildWhereClause = ({ schema, query, index }) => { if (fieldValue.$geoWithin && fieldValue.$geoWithin.$polygon) { const polygon = fieldValue.$geoWithin.$polygon; - if (!(polygon instanceof Array)) { - throw new Parse.Error( - Parse.Error.INVALID_JSON, - 'bad $geoWithin value; $polygon should contain at least 3 GeoPoints' - ); - } - if (polygon.length < 3) { + let points + if (typeof polygon === 'object' && polygon.__type === 'Polygon') { + if (!polygon.coordinates && polygon.coordinates.length < 3) { + throw new Parse.Error( + Parse.Error.INVALID_JSON, + 'bad $geoWithin value; Polygon.coordinates should contain at least 3 lon/lat pairs' + ); + } + points = polygon.coordinates; + } else if ((polygon instanceof Array)) { + if (polygon.length < 3) { + throw new Parse.Error( + Parse.Error.INVALID_JSON, + 'bad $geoWithin value; $polygon should contain at least 3 GeoPoints' + ); + } + points = polygon; + } else { throw new Parse.Error( Parse.Error.INVALID_JSON, - 'bad $geoWithin value; $polygon should contain at least 3 GeoPoints' + 'bad $geoWithin value; $polygon should be Polygon object or Array of Parse.GeoPoint\'s' ); } - const points = polygon.map((point) => { + points = points.map((point) => { + if (point instanceof Array && point.length === 2) { + Parse.GeoPoint._validate(point[1], point[0]); + return `(${point[0]}, ${point[1]})`; + } if (typeof point !== 'object' || point.__type !== 'GeoPoint') { throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $geoWithin value'); } else { From cba68794e5d5c1b052a27df251d3f7a6e679340c Mon Sep 17 00:00:00 2001 From: Mads Bjerre Date: Mon, 31 Jul 2017 18:07:45 +0200 Subject: [PATCH 6/9] More tests --- spec/ParseGeoPoint.spec.js | 83 +++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 5 deletions(-) diff --git a/spec/ParseGeoPoint.spec.js b/spec/ParseGeoPoint.spec.js index 9bb1cbe62b..b4c8229235 100644 --- a/spec/ParseGeoPoint.spec.js +++ b/spec/ParseGeoPoint.spec.js @@ -518,6 +518,77 @@ describe('Parse.GeoPoint testing', () => { }, done.fail); }); + it('invalid Polygon object withinPolygon', (done) => { + const point = new Parse.GeoPoint(1.5, 1.5); + const obj = new Parse.Object('Polygon', {location: point}); + const polygon = { + __type: 'Polygon', + coordinates: [ + [0, 0], + [10, 0], + ] + } + obj.save().then(() => { + const where = { + location: { + $geoWithin: { + $polygon: polygon + } + } + }; + return rp.post({ + url: Parse.serverURL + '/classes/Polygon', + json: { where, '_method': 'GET' }, + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey + } + }); + }).then((resp) => { + fail(`no request should succeed: ${JSON.stringify(resp)}`); + done(); + }).catch((err) => { + expect(err.error.code).toEqual(1); + done(); + }); + }); + + it('out of bounds Polygon object withinPolygon', (done) => { + const point = new Parse.GeoPoint(1.5, 1.5); + const obj = new Parse.Object('Polygon', {location: point}); + const polygon = { + __type: 'Polygon', + coordinates: [ + [0, 0], + [181, 0], + [0, 10] + ] + } + obj.save().then(() => { + const where = { + location: { + $geoWithin: { + $polygon: polygon + } + } + }; + return rp.post({ + url: Parse.serverURL + '/classes/Polygon', + json: { where, '_method': 'GET' }, + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey + } + }); + }).then((resp) => { + fail(`no request should succeed: ${JSON.stringify(resp)}`); + done(); + }).catch((err) => { + expect(err.error.code).toEqual(1); + done(); + }); + }); + it('invalid input withinPolygon', (done) => { const point = new Parse.GeoPoint(1.5, 1.5); const obj = new Parse.Object('Polygon', {location: point}); @@ -672,14 +743,16 @@ describe('Parse.GeoPoint testing', () => { const polygon = { __type: 'Polygon', coordinates: [ - [-111.9250, 33.5746], - [-112.0002, 33.4769], - [-111.8391, 33.4761] + [0, 0], + [10, 0], + [10, 10], + [0, 10], + [0, 0] ] } const obj = new Parse.Object('Polygon', { polygon }); obj.save().then(() => { - const pointInsidePolygon = new Parse.GeoPoint(33.51421, -111.92674); + const pointInsidePolygon = new Parse.GeoPoint(5, 5); const where = { polygon: { $geoIntersects: { @@ -697,7 +770,7 @@ describe('Parse.GeoPoint testing', () => { }); }).then((resp) => { equal(resp.results.length, 1); - const pointOutsidePolygon = new Parse.GeoPoint(33.52537, -112.17359); + const pointOutsidePolygon = new Parse.GeoPoint(20, 20); const where = { polygon: { $geoIntersects: { From 31913ecefa4a7c6a420aa31b8f8401526cce8d8b Mon Sep 17 00:00:00 2001 From: Mads Bjerre Date: Mon, 31 Jul 2017 22:09:53 +0200 Subject: [PATCH 7/9] Remove duplicate test --- spec/ParseGeoPoint.spec.js | 56 -------------------------------------- 1 file changed, 56 deletions(-) diff --git a/spec/ParseGeoPoint.spec.js b/spec/ParseGeoPoint.spec.js index b4c8229235..b1d30830d1 100644 --- a/spec/ParseGeoPoint.spec.js +++ b/spec/ParseGeoPoint.spec.js @@ -738,60 +738,4 @@ describe('Parse.GeoPoint testing', () => { done(); }); }); - - it('support $geoIntersects queries', (done) => { - const polygon = { - __type: 'Polygon', - coordinates: [ - [0, 0], - [10, 0], - [10, 10], - [0, 10], - [0, 0] - ] - } - const obj = new Parse.Object('Polygon', { polygon }); - obj.save().then(() => { - const pointInsidePolygon = new Parse.GeoPoint(5, 5); - const where = { - polygon: { - $geoIntersects: { - $point: pointInsidePolygon - } - } - }; - return rp.post({ - url: Parse.serverURL + '/classes/Polygon', - json: { where, '_method': 'GET' }, - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey - } - }); - }).then((resp) => { - equal(resp.results.length, 1); - const pointOutsidePolygon = new Parse.GeoPoint(20, 20); - const where = { - polygon: { - $geoIntersects: { - $point: pointOutsidePolygon - } - } - }; - return rp.post({ - url: Parse.serverURL + '/classes/Polygon', - json: { where, '_method': 'GET' }, - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey - } - }); - }).then((resp) => { - equal(resp.results.length, 0); - done(); - }).catch((err) => { - console.log(err) - fail(err); - }); - }); }); From 360bb1817311ac725f66e714b1688ff842a5b904 Mon Sep 17 00:00:00 2001 From: Mads Bjerre Date: Mon, 31 Jul 2017 22:40:59 +0200 Subject: [PATCH 8/9] Missing semicolon --- src/Adapters/Storage/Mongo/MongoTransform.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Adapters/Storage/Mongo/MongoTransform.js b/src/Adapters/Storage/Mongo/MongoTransform.js index 378ccf1be9..ed5e26abd1 100644 --- a/src/Adapters/Storage/Mongo/MongoTransform.js +++ b/src/Adapters/Storage/Mongo/MongoTransform.js @@ -678,7 +678,7 @@ function transformConstraint(constraint, inArray) { 'bad $geoWithin value; Polygon.coordinates should contain at least 3 lon/lat pairs' ); } - points = polygon.coordinates + points = polygon.coordinates; } else if (polygon instanceof Array) { if (polygon.length < 3) { throw new Parse.Error( From 8034f7386bd5a104c58bf0380d45347e32da3ce5 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Fri, 18 May 2018 19:13:04 -0500 Subject: [PATCH 9/9] fix tests --- spec/ParseGeoPoint.spec.js | 2 +- src/Adapters/Storage/Mongo/MongoTransform.js | 4 ++-- src/Adapters/Storage/Postgres/PostgresStorageAdapter.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/ParseGeoPoint.spec.js b/spec/ParseGeoPoint.spec.js index af839e0fa5..ec6ec79295 100644 --- a/spec/ParseGeoPoint.spec.js +++ b/spec/ParseGeoPoint.spec.js @@ -565,7 +565,7 @@ describe('Parse.GeoPoint testing', () => { fail(`no request should succeed: ${JSON.stringify(resp)}`); done(); }).catch((err) => { - expect(err.error.code).toEqual(1); + expect(err.error.code).toEqual(Parse.Error.INVALID_JSON); done(); }); }); diff --git a/src/Adapters/Storage/Mongo/MongoTransform.js b/src/Adapters/Storage/Mongo/MongoTransform.js index 52a7881158..78578865f9 100644 --- a/src/Adapters/Storage/Mongo/MongoTransform.js +++ b/src/Adapters/Storage/Mongo/MongoTransform.js @@ -905,9 +905,9 @@ function transformConstraint(constraint, field) { case '$geoWithin': { const polygon = constraint[key]['$polygon']; - let points + let points; if (typeof polygon === 'object' && polygon.__type === 'Polygon') { - if (!polygon.coordinates && polygon.coordinates.length < 3) { + if (!polygon.coordinates || polygon.coordinates.length < 3) { throw new Parse.Error( Parse.Error.INVALID_JSON, 'bad $geoWithin value; Polygon.coordinates should contain at least 3 lon/lat pairs' diff --git a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js index d1edde7a55..540516d37d 100644 --- a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js +++ b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js @@ -537,9 +537,9 @@ const buildWhereClause = ({ schema, query, index }): WhereClause => { if (fieldValue.$geoWithin && fieldValue.$geoWithin.$polygon) { const polygon = fieldValue.$geoWithin.$polygon; - let points + let points; if (typeof polygon === 'object' && polygon.__type === 'Polygon') { - if (!polygon.coordinates && polygon.coordinates.length < 3) { + if (!polygon.coordinates || polygon.coordinates.length < 3) { throw new Parse.Error( Parse.Error.INVALID_JSON, 'bad $geoWithin value; Polygon.coordinates should contain at least 3 lon/lat pairs'