add support for geoWithin.centerSphere queries via withJSON (#4825)
* add support for geoWithin.centerSphere queries via withJSON * added test for passing array of lat, lng instead of Parse.GeoPoint * added postgres support * added more tests * improved tests and validation * added more tests
This commit is contained in:
@@ -3985,4 +3985,106 @@ describe('Parse.Query testing', () => {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('withJSON supports geoWithin.centerSphere', (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('TestObject', {location: inbound});
|
||||||
|
const obj2 = new Parse.Object('TestObject', {location: onbound});
|
||||||
|
const obj3 = new Parse.Object('TestObject', {location: outbound});
|
||||||
|
const center = new Parse.GeoPoint(0, 0);
|
||||||
|
const distanceInKilometers = 1569 + 1; // 1569km is the approximate distance between {0, 0} and {10, 10}.
|
||||||
|
Parse.Object.saveAll([obj1, obj2, obj3]).then(() => {
|
||||||
|
const q = new Parse.Query(TestObject);
|
||||||
|
const jsonQ = q.toJSON();
|
||||||
|
jsonQ.where.location = {
|
||||||
|
'$geoWithin': {
|
||||||
|
'$centerSphere': [
|
||||||
|
center,
|
||||||
|
distanceInKilometers / 6371.0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
q.withJSON(jsonQ);
|
||||||
|
return q.find();
|
||||||
|
}).then(results => {
|
||||||
|
equal(results.length, 2);
|
||||||
|
const q = new Parse.Query(TestObject);
|
||||||
|
const jsonQ = q.toJSON();
|
||||||
|
jsonQ.where.location = {
|
||||||
|
'$geoWithin': {
|
||||||
|
'$centerSphere': [
|
||||||
|
[0, 0],
|
||||||
|
distanceInKilometers / 6371.0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
q.withJSON(jsonQ);
|
||||||
|
return q.find();
|
||||||
|
}).then(results => {
|
||||||
|
equal(results.length, 2);
|
||||||
|
done();
|
||||||
|
}).catch(error => {
|
||||||
|
fail(error);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('withJSON with geoWithin.centerSphere fails without parameters', (done) => {
|
||||||
|
const q = new Parse.Query(TestObject);
|
||||||
|
const jsonQ = q.toJSON();
|
||||||
|
jsonQ.where.location = {
|
||||||
|
'$geoWithin': {
|
||||||
|
'$centerSphere': [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
q.withJSON(jsonQ);
|
||||||
|
q.find(expectError(Parse.Error.INVALID_JSON, done));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('withJSON with geoWithin.centerSphere fails with invalid distance', (done) => {
|
||||||
|
const q = new Parse.Query(TestObject);
|
||||||
|
const jsonQ = q.toJSON();
|
||||||
|
jsonQ.where.location = {
|
||||||
|
'$geoWithin': {
|
||||||
|
'$centerSphere': [
|
||||||
|
[0, 0],
|
||||||
|
'invalid_distance'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
q.withJSON(jsonQ);
|
||||||
|
q.find(expectError(Parse.Error.INVALID_JSON, done));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('withJSON with geoWithin.centerSphere fails with invalid coordinate', (done) => {
|
||||||
|
const q = new Parse.Query(TestObject);
|
||||||
|
const jsonQ = q.toJSON();
|
||||||
|
jsonQ.where.location = {
|
||||||
|
'$geoWithin': {
|
||||||
|
'$centerSphere': [
|
||||||
|
[-190,-190],
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
q.withJSON(jsonQ);
|
||||||
|
q.find(expectError(undefined, done));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('withJSON with geoWithin.centerSphere fails with invalid geo point', (done) => {
|
||||||
|
const q = new Parse.Query(TestObject);
|
||||||
|
const jsonQ = q.toJSON();
|
||||||
|
jsonQ.where.location = {
|
||||||
|
'$geoWithin': {
|
||||||
|
'$centerSphere': [
|
||||||
|
{'longitude': 0, 'dummytude': 0},
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
q.withJSON(jsonQ);
|
||||||
|
q.find(expectError(undefined, done));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -294,11 +294,13 @@ function expectError(errorCode, callback) {
|
|||||||
error: function(obj, e) {
|
error: function(obj, e) {
|
||||||
// Some methods provide 2 parameters.
|
// Some methods provide 2 parameters.
|
||||||
e = e || obj;
|
e = e || obj;
|
||||||
|
if (errorCode !== undefined) {
|
||||||
if (!e) {
|
if (!e) {
|
||||||
fail('expected a specific error but got a blank error');
|
fail('expected a specific error but got a blank error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
expect(e.code).toEqual(errorCode, e.message);
|
expect(e.code).toEqual(errorCode, e.message);
|
||||||
|
}
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(e);
|
callback(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -905,6 +905,8 @@ function transformConstraint(constraint, field) {
|
|||||||
|
|
||||||
case '$geoWithin': {
|
case '$geoWithin': {
|
||||||
const polygon = constraint[key]['$polygon'];
|
const polygon = constraint[key]['$polygon'];
|
||||||
|
const centerSphere = constraint[key]['$centerSphere'];
|
||||||
|
if (polygon !== undefined) {
|
||||||
let points;
|
let points;
|
||||||
if (typeof polygon === 'object' && polygon.__type === 'Polygon') {
|
if (typeof polygon === 'object' && polygon.__type === 'Polygon') {
|
||||||
if (!polygon.coordinates || polygon.coordinates.length < 3) {
|
if (!polygon.coordinates || polygon.coordinates.length < 3) {
|
||||||
@@ -943,6 +945,30 @@ function transformConstraint(constraint, field) {
|
|||||||
answer[key] = {
|
answer[key] = {
|
||||||
'$polygon': points
|
'$polygon': points
|
||||||
};
|
};
|
||||||
|
} else if (centerSphere !== undefined) {
|
||||||
|
if (!(centerSphere instanceof Array) || centerSphere.length < 2) {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $geoWithin value; $centerSphere should be an array of Parse.GeoPoint and distance');
|
||||||
|
}
|
||||||
|
// Get point, convert to geo point if necessary and validate
|
||||||
|
let point = centerSphere[0];
|
||||||
|
if (point instanceof Array && point.length === 2) {
|
||||||
|
point = new Parse.GeoPoint(point[1], point[0]);
|
||||||
|
} else if (!GeoPointCoder.isValidJSON(point)) {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $geoWithin value; $centerSphere geo point invalid');
|
||||||
|
}
|
||||||
|
Parse.GeoPoint._validate(point.latitude, point.longitude);
|
||||||
|
// Get distance and validate
|
||||||
|
const distance = centerSphere[1];
|
||||||
|
if(isNaN(distance) || distance < 0) {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $geoWithin value; $centerSphere distance invalid');
|
||||||
|
}
|
||||||
|
answer[key] = {
|
||||||
|
'$centerSphere': [
|
||||||
|
[point.longitude, point.latitude],
|
||||||
|
distance
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case '$geoIntersects': {
|
case '$geoIntersects': {
|
||||||
|
|||||||
@@ -535,6 +535,30 @@ const buildWhereClause = ({ schema, query, index }): WhereClause => {
|
|||||||
index += 2;
|
index += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fieldValue.$geoWithin && fieldValue.$geoWithin.$centerSphere) {
|
||||||
|
const centerSphere = fieldValue.$geoWithin.$centerSphere;
|
||||||
|
if (!(centerSphere instanceof Array) || centerSphere.length < 2) {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $geoWithin value; $centerSphere should be an array of Parse.GeoPoint and distance');
|
||||||
|
}
|
||||||
|
// Get point, convert to geo point if necessary and validate
|
||||||
|
let point = centerSphere[0];
|
||||||
|
if (point instanceof Array && point.length === 2) {
|
||||||
|
point = new Parse.GeoPoint(point[1], point[0]);
|
||||||
|
} else if (!GeoPointCoder.isValidJSON(point)) {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $geoWithin value; $centerSphere geo point invalid');
|
||||||
|
}
|
||||||
|
Parse.GeoPoint._validate(point.latitude, point.longitude);
|
||||||
|
// Get distance and validate
|
||||||
|
const distance = centerSphere[1];
|
||||||
|
if(isNaN(distance) || distance < 0) {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad $geoWithin value; $centerSphere distance invalid');
|
||||||
|
}
|
||||||
|
const distanceInKM = distance * 6371 * 1000;
|
||||||
|
patterns.push(`ST_distance_sphere($${index}:name::geometry, POINT($${index + 1}, $${index + 2})::geometry) <= $${index + 3}`);
|
||||||
|
values.push(fieldName, point.longitude, point.latitude, distanceInKM);
|
||||||
|
index += 4;
|
||||||
|
}
|
||||||
|
|
||||||
if (fieldValue.$geoWithin && fieldValue.$geoWithin.$polygon) {
|
if (fieldValue.$geoWithin && fieldValue.$geoWithin.$polygon) {
|
||||||
const polygon = fieldValue.$geoWithin.$polygon;
|
const polygon = fieldValue.$geoWithin.$polygon;
|
||||||
let points;
|
let points;
|
||||||
@@ -1986,4 +2010,13 @@ function literalizeRegexPart(s: string) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var GeoPointCoder = {
|
||||||
|
isValidJSON(value) {
|
||||||
|
return (typeof value === 'object' &&
|
||||||
|
value !== null &&
|
||||||
|
value.__type === 'GeoPoint'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export default PostgresStorageAdapter;
|
export default PostgresStorageAdapter;
|
||||||
|
|||||||
Reference in New Issue
Block a user