diff --git a/src/Adapters/Storage/Mongo/MongoCollection.js b/src/Adapters/Storage/Mongo/MongoCollection.js index 78c90fef..6e1a73c1 100644 --- a/src/Adapters/Storage/Mongo/MongoCollection.js +++ b/src/Adapters/Storage/Mongo/MongoCollection.js @@ -57,6 +57,10 @@ export default class MongoCollection { }) } + insertOne(object) { + return this._mongoCollection.insertOne(object); + } + // Atomically updates data in the database for a single (first) object that matched the query // If there is nothing that matches the query - does insert // Postgres Note: `INSERT ... ON CONFLICT UPDATE` that is available since 9.5. @@ -83,8 +87,8 @@ export default class MongoCollection { return this._mongoCollection.deleteOne(query); } - remove(query) { - return this._mongoCollection.remove(query); + deleteMany(query) { + return this._mongoCollection.deleteMany(query); } drop() { diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 83c2703a..e2761bd5 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -54,6 +54,14 @@ function returnsTrue() { return true; } +DatabaseController.prototype.validateClassName = function(className) { + if (!Schema.classNameIsValid(className)) { + const error = new Parse.Error(Parse.Error.INVALID_CLASS_NAME, 'invalid className: ' + className); + return Promise.reject(error); + } + return Promise.resolve(); +}; + // Returns a promise for a schema object. // If we are provided a acceptor, then we run it on the schema. // If the schema isn't accepted, we reload it at most once. @@ -230,30 +238,28 @@ DatabaseController.prototype.handleRelationUpdates = function(className, // Adds a relation. // Returns a promise that resolves successfully iff the add was successful. -DatabaseController.prototype.addRelation = function(key, fromClassName, - fromId, toId) { - var doc = { +DatabaseController.prototype.addRelation = function(key, fromClassName, fromId, toId) { + let doc = { relatedId: toId, - owningId: fromId + owningId : fromId }; - var className = '_Join:' + key + ':' + fromClassName; - return this.collection(className).then((coll) => { - return coll.update(doc, doc, {upsert: true}); + let className = `_Join:${key}:${fromClassName}`; + return this.adaptiveCollection(className).then((coll) => { + return coll.upsertOne(doc, doc); }); }; // Removes a relation. // Returns a promise that resolves successfully iff the remove was // successful. -DatabaseController.prototype.removeRelation = function(key, fromClassName, - fromId, toId) { +DatabaseController.prototype.removeRelation = function(key, fromClassName, fromId, toId) { var doc = { relatedId: toId, owningId: fromId }; - var className = '_Join:' + key + ':' + fromClassName; - return this.collection(className).then((coll) => { - return coll.remove(doc); + let className = `_Join:${key}:${fromClassName}`; + return this.adaptiveCollection(className).then(coll => { + return coll.deleteOne(doc); }); }; @@ -269,40 +275,36 @@ DatabaseController.prototype.destroy = function(className, query, options = {}) var aclGroup = options.acl || []; var schema; - return this.loadSchema().then((s) => { - schema = s; - if (!isMaster) { - return schema.validatePermission(className, aclGroup, 'delete'); - } - return Promise.resolve(); - }).then(() => { - - return this.collection(className); - }).then((coll) => { - var mongoWhere = transform.transformWhere(schema, className, query); - - if (options.acl) { - var writePerms = [ - {_wperm: {'$exists': false}} - ]; - for (var entry of options.acl) { - writePerms.push({_wperm: {'$in': [entry]}}); + return this.loadSchema() + .then(s => { + schema = s; + if (!isMaster) { + return schema.validatePermission(className, aclGroup, 'delete'); } - mongoWhere = {'$and': [mongoWhere, {'$or': writePerms}]}; - } + return Promise.resolve(); + }) + .then(() => this.adaptiveCollection(className)) + .then(collection => { + let mongoWhere = transform.transformWhere(schema, className, query); - return coll.remove(mongoWhere); - }).then((resp) => { - //Check _Session to avoid changing password failed without any session. - if (resp.result.n === 0 && className !== "_Session") { - return Promise.reject( - new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, - 'Object not found.')); - - } - }, (error) => { - throw error; - }); + if (options.acl) { + var writePerms = [ + { _wperm: { '$exists': false } } + ]; + for (var entry of options.acl) { + writePerms.push({ _wperm: { '$in': [entry] } }); + } + mongoWhere = { '$and': [mongoWhere, { '$or': writePerms }] }; + } + return collection.deleteMany(mongoWhere); + }) + .then(resp => { + //Check _Session to avoid changing password failed without any session. + // TODO: @nlutsenko Stop relying on `result.n` + if (resp.result.n === 0 && className !== "_Session") { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); + } + }); }; // Inserts an object into the database. @@ -312,21 +314,21 @@ DatabaseController.prototype.create = function(className, object, options) { var isMaster = !('acl' in options); var aclGroup = options.acl || []; - return this.loadSchema().then((s) => { - schema = s; - if (!isMaster) { - return schema.validatePermission(className, aclGroup, 'create'); - } - return Promise.resolve(); - }).then(() => { - - return this.handleRelationUpdates(className, null, object); - }).then(() => { - return this.collection(className); - }).then((coll) => { - var mongoObject = transform.transformCreate(schema, className, object); - return coll.insert([mongoObject]); - }); + return this.validateClassName(className) + .then(() => this.loadSchema()) + .then(s => { + schema = s; + if (!isMaster) { + return schema.validatePermission(className, aclGroup, 'create'); + } + return Promise.resolve(); + }) + .then(() => this.handleRelationUpdates(className, null, object)) + .then(() => this.adaptiveCollection(className)) + .then(coll => { + var mongoObject = transform.transformCreate(schema, className, object); + return coll.insertOne(mongoObject); + }); }; // Runs a mongo query on the database. @@ -386,14 +388,14 @@ DatabaseController.prototype.owningIds = function(className, key, relatedIds) { // equal-to-pointer constraints on relation fields. // Returns a promise that resolves when query is mutated DatabaseController.prototype.reduceInRelation = function(className, query, schema) { - + // Search for an in-relation or equal-to-relation // Make it sequential for now, not sure of paralleization side effects if (query['$or']) { let ors = query['$or']; return Promise.all(ors.map((aQuery, index) => { return this.reduceInRelation(className, aQuery, schema).then((aQuery) => { - query['$or'][index] = aQuery; + query['$or'][index] = aQuery; }) })); } @@ -413,14 +415,14 @@ DatabaseController.prototype.reduceInRelation = function(className, query, schem relatedIds = [query[key].objectId]; } return this.owningIds(className, key, relatedIds).then((ids) => { - delete query[key]; + delete query[key]; this.addInObjectIdsIds(ids, query); return Promise.resolve(query); }); } return Promise.resolve(query); }) - + return Promise.all(promises).then(() => { return Promise.resolve(query); }) @@ -429,13 +431,13 @@ DatabaseController.prototype.reduceInRelation = function(className, query, schem // Modifies query so that it no longer has $relatedTo // Returns a promise that resolves when query is mutated DatabaseController.prototype.reduceRelationKeys = function(className, query) { - + if (query['$or']) { return Promise.all(query['$or'].map((aQuery) => { return this.reduceRelationKeys(className, aQuery); })); } - + var relatedTo = query['$relatedTo']; if (relatedTo) { return this.relatedIds( diff --git a/src/Controllers/HooksController.js b/src/Controllers/HooksController.js index fbc7e920..cbf26f8a 100644 --- a/src/Controllers/HooksController.js +++ b/src/Controllers/HooksController.js @@ -71,7 +71,7 @@ export class HooksController { _removeHooks(query) { return this.getCollection().then(collection => { - return collection.remove(query); + return collection.deleteMany(query); }).then(() => { return {}; });