Skip to content

Commit

Permalink
Avoid multiple $nears in one query (#3798)
Browse files Browse the repository at this point in the history
Mongo has a hard limit on 1 $near operation per query. Restructuring to
avoid SERVER-13732 should not invalidate a query by creating multiple
$near operations.

Additionally, queries with multiple $ors are now recursively handled,
whereas before, ops at the top level would only have been pushed one
level deeper.

#3767
  • Loading branch information
NotBobTheBuilder authored and flovilmart committed May 10, 2017
1 parent 64e6f40 commit 7b9ebc4
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 1 deletion.
23 changes: 23 additions & 0 deletions spec/DatabaseController.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,29 @@ describe('DatabaseController', function() {
done();
});

it('should not restructure SERVER-13732 queries with $nears', (done) => {
var query = {$or: [{a: 1}, {b: 1}], c: {$nearSphere: {}}};
validateQuery(query);
expect(query).toEqual({$or: [{a: 1}, {b: 1}], c: {$nearSphere: {}}});

query = {$or: [{a: 1}, {b: 1}], c: {$near: {}}};
validateQuery(query);
expect(query).toEqual({$or: [{a: 1}, {b: 1}], c: {$near: {}}});

done();
});


it('should push refactored keys down a tree for SERVER-13732', (done) => {
var query = {a: 1, $or: [{$or: [{b: 1}, {b: 2}]},
{$or: [{c: 1}, {c: 2}]}]};
validateQuery(query);
expect(query).toEqual({$or: [{$or: [{b: 1, a: 1}, {b: 2, a: 1}]},
{$or: [{c: 1, a: 1}, {c: 2, a: 1}]}]});

done();
});

it('should reject invalid queries', (done) => {
expect(() => validateQuery({$or: {'a': 1}})).toThrow();
done();
Expand Down
12 changes: 11 additions & 1 deletion src/Controllers/DatabaseController.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,27 @@ const validateQuery = query => {
* EG: {$or: [{a: 1}, {a: 2}], b: 2}
* Becomes: {$or: [{a: 1, b: 2}, {a: 2, b: 2}]}
*
* The only exceptions are $near and $nearSphere operators, which are
* constrained to only 1 operator per query. As a result, these ops
* remain at the top level
*
* https://jira.mongodb.org/browse/SERVER-13732
* https://github.com/parse-community/parse-server/issues/3767
*/
Object.keys(query).forEach(key => {
const noCollisions = !query.$or.some(subq => subq.hasOwnProperty(key))
if (key != '$or' && noCollisions) {
let hasNears = false
if (query[key] != null && typeof query[key] == 'object') {
hasNears = ('$near' in query[key] || '$nearSphere' in query[key])
}
if (key != '$or' && noCollisions && !hasNears) {
query.$or.forEach(subquery => {
subquery[key] = query[key];
});
delete query[key];
}
});
query.$or.forEach(validateQuery);
} else {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Bad $or format - use an array value.');
}
Expand Down

0 comments on commit 7b9ebc4

Please sign in to comment.