feat(ParseQuery): Added 'withinPolygon' support for GeoPoints (#3866)
* Added 'withinPolygon' to query * Unit test for withinPolygon * More Unit Test * withinPolygon fix for Postgres * Fix nit nit?
This commit is contained in:
committed by
Florent Vilmart
parent
a380fcf2c7
commit
c99fdea6fb
@@ -1,6 +1,7 @@
|
|||||||
// This is a port of the test suite:
|
// This is a port of the test suite:
|
||||||
// hungry/js/test/parse_geo_point_test.js
|
// hungry/js/test/parse_geo_point_test.js
|
||||||
|
|
||||||
|
const rp = require('request-promise');
|
||||||
var TestObject = Parse.Object.extend('TestObject');
|
var TestObject = Parse.Object.extend('TestObject');
|
||||||
|
|
||||||
describe('Parse.GeoPoint testing', () => {
|
describe('Parse.GeoPoint testing', () => {
|
||||||
@@ -374,4 +375,158 @@ describe('Parse.GeoPoint testing', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('supports withinPolygon', (done) => {
|
||||||
|
const point1 = new Parse.GeoPoint(1.5, 1.5);
|
||||||
|
const point2 = new Parse.GeoPoint(2, 8);
|
||||||
|
const point3 = new Parse.GeoPoint(20, 20);
|
||||||
|
const obj1 = new Parse.Object('Polygon', {location: point1});
|
||||||
|
const obj2 = new Parse.Object('Polygon', {location: point2});
|
||||||
|
const obj3 = new Parse.Object('Polygon', {location: point3});
|
||||||
|
Parse.Object.saveAll([obj1, obj2, obj3]).then(() => {
|
||||||
|
const where = {
|
||||||
|
location: {
|
||||||
|
$geoWithin: {
|
||||||
|
$polygon: [
|
||||||
|
{ __type: 'GeoPoint', latitude: 0, longitude: 0 },
|
||||||
|
{ __type: 'GeoPoint', latitude: 0, longitude: 10 },
|
||||||
|
{ __type: 'GeoPoint', latitude: 10, longitude: 10 },
|
||||||
|
{ __type: 'GeoPoint', latitude: 10, longitude: 0 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
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});
|
||||||
|
obj.save().then(() => {
|
||||||
|
const where = {
|
||||||
|
location: {
|
||||||
|
$geoWithin: {
|
||||||
|
$polygon: 1234
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
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(Parse.Error.INVALID_JSON);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invalid geoPoint withinPolygon', (done) => {
|
||||||
|
const point = new Parse.GeoPoint(1.5, 1.5);
|
||||||
|
const obj = new Parse.Object('Polygon', {location: point});
|
||||||
|
obj.save().then(() => {
|
||||||
|
const where = {
|
||||||
|
location: {
|
||||||
|
$geoWithin: {
|
||||||
|
$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(Parse.Error.INVALID_JSON);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invalid latitude withinPolygon', (done) => {
|
||||||
|
const point = new Parse.GeoPoint(1.5, 1.5);
|
||||||
|
const obj = new Parse.Object('Polygon', {location: point});
|
||||||
|
obj.save().then(() => {
|
||||||
|
const where = {
|
||||||
|
location: {
|
||||||
|
$geoWithin: {
|
||||||
|
$polygon: [
|
||||||
|
{ __type: 'GeoPoint', latitude: 0, longitude: 0 },
|
||||||
|
{ __type: 'GeoPoint', latitude: 181, longitude: 0 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
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 longitude withinPolygon', (done) => {
|
||||||
|
const point = new Parse.GeoPoint(1.5, 1.5);
|
||||||
|
const obj = new Parse.Object('Polygon', {location: point});
|
||||||
|
obj.save().then(() => {
|
||||||
|
const where = {
|
||||||
|
location: {
|
||||||
|
$geoWithin: {
|
||||||
|
$polygon: [
|
||||||
|
{ __type: 'GeoPoint', latitude: 0, longitude: 0 },
|
||||||
|
{ __type: 'GeoPoint', latitude: 0, longitude: 181 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -618,6 +618,24 @@ function transformConstraint(constraint, inArray) {
|
|||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case '$geoWithin': {
|
||||||
|
const polygon = constraint[key]['$polygon'];
|
||||||
|
if (!(polygon instanceof Array)) {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $geoWithin value');
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
return [point.longitude, point.latitude];
|
||||||
|
});
|
||||||
|
answer[key] = {
|
||||||
|
'$polygon': points
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
if (key.match(/^\$+/)) {
|
if (key.match(/^\$+/)) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
|
|||||||
@@ -344,6 +344,25 @@ const buildWhereClause = ({ schema, query, index }) => {
|
|||||||
index += 2;
|
index += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
const points = polygon.map((point) => {
|
||||||
|
if (typeof point !== 'object' || point.__type !== 'GeoPoint') {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $geoWithin value');
|
||||||
|
} else {
|
||||||
|
Parse.GeoPoint._validate(point.latitude, point.longitude);
|
||||||
|
}
|
||||||
|
return `(${point.longitude}, ${point.latitude})`;
|
||||||
|
}).join(', ');
|
||||||
|
|
||||||
|
patterns.push(`$${index}:name::point <@ $${index + 1}::polygon`);
|
||||||
|
values.push(fieldName, `(${points})`);
|
||||||
|
index += 2;
|
||||||
|
}
|
||||||
|
|
||||||
if (fieldValue.$regex) {
|
if (fieldValue.$regex) {
|
||||||
let regex = fieldValue.$regex;
|
let regex = fieldValue.$regex;
|
||||||
let operator = '~';
|
let operator = '~';
|
||||||
|
|||||||
Reference in New Issue
Block a user