@@ -26,6 +26,7 @@
|
|||||||
"commander": "^2.9.0",
|
"commander": "^2.9.0",
|
||||||
"deepcopy": "^0.6.1",
|
"deepcopy": "^0.6.1",
|
||||||
"express": "^4.13.4",
|
"express": "^4.13.4",
|
||||||
|
"intersect": "^1.0.1",
|
||||||
"lru-cache": "^4.0.0",
|
"lru-cache": "^4.0.0",
|
||||||
"mailgun-js": "^0.7.7",
|
"mailgun-js": "^0.7.7",
|
||||||
"mime": "^1.3.4",
|
"mime": "^1.3.4",
|
||||||
|
|||||||
@@ -2228,4 +2228,22 @@ describe('Parse.Query testing', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('objectId containedIn with multiple large array', done => {
|
||||||
|
let obj = new Parse.Object('MyClass');
|
||||||
|
obj.save().then(obj => {
|
||||||
|
let longListOfStrings = [];
|
||||||
|
for (let i = 0; i < 130; i++) {
|
||||||
|
longListOfStrings.push(i.toString());
|
||||||
|
}
|
||||||
|
longListOfStrings.push(obj.id);
|
||||||
|
let q = new Parse.Query('MyClass');
|
||||||
|
q.containedIn('objectId', longListOfStrings);
|
||||||
|
q.containedIn('objectId', longListOfStrings);
|
||||||
|
return q.find();
|
||||||
|
}).then(results => {
|
||||||
|
expect(results.length).toEqual(1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -248,46 +248,50 @@ describe('Parse.Relation testing', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("queries on relation fields with multiple ins", (done) => {
|
it("queries on relation fields with multiple containedIn (regression test for #1271)", (done) => {
|
||||||
var ChildObject = Parse.Object.extend("ChildObject");
|
let ChildObject = Parse.Object.extend("ChildObject");
|
||||||
var childObjects = [];
|
let childObjects = [];
|
||||||
for (var i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
childObjects.push(new ChildObject({x: i}));
|
childObjects.push(new ChildObject({x: i}));
|
||||||
}
|
}
|
||||||
|
|
||||||
Parse.Object.saveAll(childObjects).then(() => {
|
Parse.Object.saveAll(childObjects).then(() => {
|
||||||
var ParentObject = Parse.Object.extend("ParentObject");
|
let ParentObject = Parse.Object.extend("ParentObject");
|
||||||
var parent = new ParentObject();
|
let parent = new ParentObject();
|
||||||
parent.set("x", 4);
|
parent.set("x", 4);
|
||||||
var relation = parent.relation("child");
|
let parent1Children = parent.relation("child");
|
||||||
relation.add(childObjects[0]);
|
parent1Children.add(childObjects[0]);
|
||||||
relation.add(childObjects[1]);
|
parent1Children.add(childObjects[1]);
|
||||||
relation.add(childObjects[2]);
|
parent1Children.add(childObjects[2]);
|
||||||
var parent2 = new ParentObject();
|
let parent2 = new ParentObject();
|
||||||
parent2.set("x", 3);
|
parent2.set("x", 3);
|
||||||
var relation2 = parent2.relation("child");
|
let parent2Children = parent2.relation("child");
|
||||||
relation2.add(childObjects[4]);
|
parent2Children.add(childObjects[4]);
|
||||||
relation2.add(childObjects[5]);
|
parent2Children.add(childObjects[5]);
|
||||||
relation2.add(childObjects[6]);
|
parent2Children.add(childObjects[6]);
|
||||||
|
|
||||||
var otherChild2 = parent2.relation("otherChild");
|
let parent2OtherChildren = parent2.relation("otherChild");
|
||||||
otherChild2.add(childObjects[0]);
|
parent2OtherChildren.add(childObjects[0]);
|
||||||
otherChild2.add(childObjects[1]);
|
parent2OtherChildren.add(childObjects[1]);
|
||||||
otherChild2.add(childObjects[2]);
|
parent2OtherChildren.add(childObjects[2]);
|
||||||
|
|
||||||
var parents = [];
|
return Parse.Object.saveAll([parent, parent2]);
|
||||||
parents.push(parent);
|
|
||||||
parents.push(parent2);
|
|
||||||
return Parse.Object.saveAll(parents);
|
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
var query = new Parse.Query(ParentObject);
|
let objectsWithChild0InBothChildren = new Parse.Query(ParentObject);
|
||||||
var objects = [];
|
objectsWithChild0InBothChildren.containedIn("child", [childObjects[0]]);
|
||||||
objects.push(childObjects[0]);
|
objectsWithChild0InBothChildren.containedIn("otherChild", [childObjects[0]]);
|
||||||
query.containedIn("child", objects);
|
return objectsWithChild0InBothChildren.find();
|
||||||
query.containedIn("otherChild", [childObjects[0]]);
|
}).then(objectsWithChild0InBothChildren => {
|
||||||
return query.find();
|
//No parent has child 0 in both it's "child" and "otherChild" field;
|
||||||
}).then((list) => {
|
expect(objectsWithChild0InBothChildren.length).toEqual(0);
|
||||||
equal(list.length, 2, "There should be 2 results");
|
}).then(() => {
|
||||||
|
let objectsWithChild4andOtherChild1 = new Parse.Query(ParentObject);
|
||||||
|
objectsWithChild4andOtherChild1.containedIn("child", [childObjects[4]]);
|
||||||
|
objectsWithChild4andOtherChild1.containedIn("otherChild", [childObjects[1]]);
|
||||||
|
return objectsWithChild4andOtherChild1.find();
|
||||||
|
}).then(objects => {
|
||||||
|
// parent2 has child 4 and otherChild 1
|
||||||
|
expect(objects.length).toEqual(1);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
// A database adapter that works with data exported from the hosted
|
// A database adapter that works with data exported from the hosted
|
||||||
// Parse database.
|
// Parse database.
|
||||||
|
|
||||||
|
import intersect from 'intersect';
|
||||||
|
|
||||||
var mongodb = require('mongodb');
|
var mongodb = require('mongodb');
|
||||||
var Parse = require('parse/node').Parse;
|
var Parse = require('parse/node').Parse;
|
||||||
|
|
||||||
@@ -492,18 +494,28 @@ DatabaseController.prototype.reduceRelationKeys = function(className, query) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
DatabaseController.prototype.addInObjectIdsIds = function(ids, query) {
|
DatabaseController.prototype.addInObjectIdsIds = function(ids = null, query) {
|
||||||
if (typeof query.objectId == 'string') {
|
let idsFromString = typeof query.objectId === 'string' ? [query.objectId] : null;
|
||||||
// Add equality op as we are sure
|
let idsFromEq = query.objectId && query.objectId['$eq'] ? [query.objectId['$eq']] : null;
|
||||||
// we had a constraint on that one
|
let idsFromIn = query.objectId && query.objectId['$in'] ? query.objectId['$in'] : null;
|
||||||
query.objectId = {'$eq': query.objectId};
|
|
||||||
|
let allIds = [idsFromString, idsFromEq, idsFromIn, ids].filter(list => list !== null);
|
||||||
|
let totalLength = allIds.reduce((memo, list) => memo + list.length, 0);
|
||||||
|
|
||||||
|
let idsIntersection = [];
|
||||||
|
if (totalLength > 125) {
|
||||||
|
idsIntersection = intersect.big(allIds);
|
||||||
|
} else {
|
||||||
|
idsIntersection = intersect(allIds);
|
||||||
}
|
}
|
||||||
query.objectId = query.objectId || {};
|
|
||||||
let queryIn = [].concat(query.objectId['$in'] || [], ids || []);
|
// Need to make sure we don't clobber existing $lt or other constraints on objectId.
|
||||||
// make a set and spread to remove duplicates
|
// Clobbering $eq, $in and shorthand $eq (query.objectId === 'string') constraints
|
||||||
// replace the $in operator as other constraints
|
// is expected though.
|
||||||
// may be set
|
if (!('objectId' in query) || typeof query.objectId === 'string') {
|
||||||
query.objectId['$in'] = [...new Set(queryIn)];
|
query.objectId = {};
|
||||||
|
}
|
||||||
|
query.objectId['$in'] = idsIntersection;
|
||||||
|
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
@@ -523,7 +535,7 @@ DatabaseController.prototype.addInObjectIdsIds = function(ids, query) {
|
|||||||
// anything about users, ideally. Then, improve the format of the ACL
|
// anything about users, ideally. Then, improve the format of the ACL
|
||||||
// arg to work like the others.
|
// arg to work like the others.
|
||||||
DatabaseController.prototype.find = function(className, query, options = {}) {
|
DatabaseController.prototype.find = function(className, query, options = {}) {
|
||||||
var mongoOptions = {};
|
let mongoOptions = {};
|
||||||
if (options.skip) {
|
if (options.skip) {
|
||||||
mongoOptions.skip = options.skip;
|
mongoOptions.skip = options.skip;
|
||||||
}
|
}
|
||||||
@@ -531,45 +543,39 @@ DatabaseController.prototype.find = function(className, query, options = {}) {
|
|||||||
mongoOptions.limit = options.limit;
|
mongoOptions.limit = options.limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
var isMaster = !('acl' in options);
|
let isMaster = !('acl' in options);
|
||||||
var aclGroup = options.acl || [];
|
let aclGroup = options.acl || [];
|
||||||
var acceptor = function(schema) {
|
let acceptor = schema => schema.hasKeys(className, keysForQuery(query))
|
||||||
return schema.hasKeys(className, keysForQuery(query));
|
let schema = null;
|
||||||
};
|
return this.loadSchema(acceptor).then(s => {
|
||||||
var schema;
|
|
||||||
return this.loadSchema(acceptor).then((s) => {
|
|
||||||
schema = s;
|
schema = s;
|
||||||
if (options.sort) {
|
if (options.sort) {
|
||||||
mongoOptions.sort = {};
|
mongoOptions.sort = {};
|
||||||
for (var key in options.sort) {
|
for (let key in options.sort) {
|
||||||
var mongoKey = transform.transformKey(schema, className, key);
|
let mongoKey = transform.transformKey(schema, className, key);
|
||||||
mongoOptions.sort[mongoKey] = options.sort[key];
|
mongoOptions.sort[mongoKey] = options.sort[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isMaster) {
|
if (!isMaster) {
|
||||||
var op = 'find';
|
let op = typeof query.objectId == 'string' && Object.keys(query).length === 1 ?
|
||||||
var k = Object.keys(query);
|
'get' :
|
||||||
if (k.length == 1 && typeof query.objectId == 'string') {
|
'find';
|
||||||
op = 'get';
|
|
||||||
}
|
|
||||||
return schema.validatePermission(className, aclGroup, op);
|
return schema.validatePermission(className, aclGroup, op);
|
||||||
}
|
}
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}).then(() => {
|
})
|
||||||
return this.reduceRelationKeys(className, query);
|
.then(() => this.reduceRelationKeys(className, query))
|
||||||
}).then(() => {
|
.then(() => this.reduceInRelation(className, query, schema))
|
||||||
return this.reduceInRelation(className, query, schema);
|
.then(() => this.adaptiveCollection(className))
|
||||||
}).then(() => {
|
.then(collection => {
|
||||||
return this.adaptiveCollection(className);
|
let mongoWhere = transform.transformWhere(schema, className, query);
|
||||||
}).then(collection => {
|
|
||||||
var mongoWhere = transform.transformWhere(schema, className, query);
|
|
||||||
if (!isMaster) {
|
if (!isMaster) {
|
||||||
var orParts = [
|
let orParts = [
|
||||||
{"_rperm" : { "$exists": false }},
|
{"_rperm" : { "$exists": false }},
|
||||||
{"_rperm" : { "$in" : ["*"]}}
|
{"_rperm" : { "$in" : ["*"]}}
|
||||||
];
|
];
|
||||||
for (var acl of aclGroup) {
|
for (let acl of aclGroup) {
|
||||||
orParts.push({"_rperm" : { "$in" : [acl]}});
|
orParts.push({"_rperm" : { "$in" : [acl]}});
|
||||||
}
|
}
|
||||||
mongoWhere = {'$and': [mongoWhere, {'$or': orParts}]};
|
mongoWhere = {'$and': [mongoWhere, {'$or': orParts}]};
|
||||||
|
|||||||
@@ -187,13 +187,12 @@ export function transformKeyValue(schema, className, restKey, restValue, options
|
|||||||
// Returns the mongo form of the query.
|
// Returns the mongo form of the query.
|
||||||
// Throws a Parse.Error if the input query is invalid.
|
// Throws a Parse.Error if the input query is invalid.
|
||||||
function transformWhere(schema, className, restWhere) {
|
function transformWhere(schema, className, restWhere) {
|
||||||
var mongoWhere = {};
|
let mongoWhere = {};
|
||||||
if (restWhere['ACL']) {
|
if (restWhere['ACL']) {
|
||||||
throw new Parse.Error(Parse.Error.INVALID_QUERY,
|
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Cannot query on ACL.');
|
||||||
'Cannot query on ACL.');
|
|
||||||
}
|
}
|
||||||
for (var restKey in restWhere) {
|
for (let restKey in restWhere) {
|
||||||
var out = transformKeyValue(schema, className, restKey, restWhere[restKey],
|
let out = transformKeyValue(schema, className, restKey, restWhere[restKey],
|
||||||
{query: true, validate: true});
|
{query: true, validate: true});
|
||||||
mongoWhere[out.key] = out.value;
|
mongoWhere[out.key] = out.value;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user