Skip to content

Commit bb80e5d

Browse files
committed
Merge pull request parse-community#759 from ParsePlatform/nlutsenko.storage.findAndDoMagic
Add findOneAndDelete, findOneAndModify to MongoCollection, move most of usages to it.
2 parents c7503fc + 4049ce4 commit bb80e5d

File tree

4 files changed

+101
-98
lines changed

4 files changed

+101
-98
lines changed

src/Adapters/Storage/Mongo/MongoCollection.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,29 @@ export default class MongoCollection {
4747
return this._mongoCollection.count(query, { skip, limit, sort });
4848
}
4949

50+
// Atomically finds and updates an object based on query.
51+
// The result is the promise with an object that was in the database !AFTER! changes.
52+
// Postgres Note: Translates directly to `UPDATE * SET * ... RETURNING *`, which will return data after the change is done.
53+
findOneAndUpdate(query, update) {
54+
// arguments: query, sort, update, options(optional)
55+
// Setting `new` option to true makes it return the after document, not the before one.
56+
return this._mongoCollection.findAndModify(query, [], update, { new: true }).then(document => {
57+
// Value is the object where mongo returns multiple fields.
58+
return document.value;
59+
})
60+
}
61+
62+
// Atomically find and delete an object based on query.
63+
// The result is the promise with an object that was in the database before deleting.
64+
// Postgres Note: Translates directly to `DELETE * FROM ... RETURNING *`, which will return data after delete is done.
65+
findOneAndDelete(query) {
66+
// arguments: query, sort
67+
return this._mongoCollection.findAndRemove(query, []).then(document => {
68+
// Value is the object where mongo returns multiple fields.
69+
return document.value;
70+
});
71+
}
72+
5073
drop() {
5174
return this._mongoCollection.drop();
5275
}

src/Controllers/DatabaseController.js

Lines changed: 36 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -142,51 +142,45 @@ DatabaseController.prototype.update = function(className, query, update, options
142142
var isMaster = !('acl' in options);
143143
var aclGroup = options.acl || [];
144144
var mongoUpdate, schema;
145-
return this.loadSchema(acceptor).then((s) => {
146-
schema = s;
147-
if (!isMaster) {
148-
return schema.validatePermission(className, aclGroup, 'update');
149-
}
150-
return Promise.resolve();
151-
}).then(() => {
152-
153-
return this.handleRelationUpdates(className, query.objectId, update);
154-
}).then(() => {
155-
return this.collection(className);
156-
}).then((coll) => {
157-
var mongoWhere = transform.transformWhere(schema, className, query);
158-
if (options.acl) {
159-
var writePerms = [
160-
{_wperm: {'$exists': false}}
161-
];
162-
for (var entry of options.acl) {
163-
writePerms.push({_wperm: {'$in': [entry]}});
145+
return this.loadSchema(acceptor)
146+
.then(s => {
147+
schema = s;
148+
if (!isMaster) {
149+
return schema.validatePermission(className, aclGroup, 'update');
150+
}
151+
return Promise.resolve();
152+
})
153+
.then(() => this.handleRelationUpdates(className, query.objectId, update))
154+
.then(() => this.adaptiveCollection(className))
155+
.then(collection => {
156+
var mongoWhere = transform.transformWhere(schema, className, query);
157+
if (options.acl) {
158+
var writePerms = [
159+
{_wperm: {'$exists': false}}
160+
];
161+
for (var entry of options.acl) {
162+
writePerms.push({_wperm: {'$in': [entry]}});
163+
}
164+
mongoWhere = {'$and': [mongoWhere, {'$or': writePerms}]};
165+
}
166+
mongoUpdate = transform.transformUpdate(schema, className, update);
167+
return collection.findOneAndUpdate(mongoWhere, mongoUpdate);
168+
})
169+
.then(result => {
170+
if (!result) {
171+
return Promise.reject(new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
172+
'Object not found.'));
164173
}
165-
mongoWhere = {'$and': [mongoWhere, {'$or': writePerms}]};
166-
}
167-
168-
mongoUpdate = transform.transformUpdate(schema, className, update);
169-
170-
return coll.findAndModify(mongoWhere, {}, mongoUpdate, {});
171-
}).then((result) => {
172-
if (!result.value) {
173-
return Promise.reject(new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
174-
'Object not found.'));
175-
}
176-
if (result.lastErrorObject.n != 1) {
177-
return Promise.reject(new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
178-
'Object not found.'));
179-
}
180174

181-
var response = {};
182-
var inc = mongoUpdate['$inc'];
183-
if (inc) {
184-
for (var key in inc) {
185-
response[key] = (result.value[key] || 0) + inc[key];
175+
let response = {};
176+
let inc = mongoUpdate['$inc'];
177+
if (inc) {
178+
Object.keys(inc).forEach(key => {
179+
response[key] = result[key];
180+
});
186181
}
187-
}
188-
return response;
189-
});
182+
return response;
183+
});
190184
};
191185

192186
// Processes relation-updating operations from a REST-format update.

src/Controllers/UserController.js

Lines changed: 37 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -40,49 +40,43 @@ export class UserController extends AdaptableController {
4040

4141

4242
verifyEmail(username, token) {
43-
44-
return new Promise((resolve, reject) => {
45-
43+
if (!this.shouldVerifyEmails) {
4644
// Trying to verify email when not enabled
47-
if (!this.shouldVerifyEmails) {
48-
reject();
49-
return;
50-
}
51-
52-
var database = this.config.database;
53-
54-
database.collection('_User').then(coll => {
45+
// TODO: Better error here.
46+
return Promise.reject();
47+
}
48+
49+
return this.config.database
50+
.adaptiveCollection('_User')
51+
.then(collection => {
5552
// Need direct database access because verification token is not a parse field
56-
return coll.findAndModify({
53+
return collection.findOneAndUpdate({
5754
username: username,
58-
_email_verify_token: token,
59-
}, null, {$set: {emailVerified: true}}, (err, doc) => {
60-
if (err || !doc.value) {
61-
reject(err);
62-
} else {
63-
resolve(doc.value);
64-
}
65-
});
55+
_email_verify_token: token
56+
}, {$set: {emailVerified: true}});
57+
})
58+
.then(document => {
59+
if (!document) {
60+
return Promise.reject();
61+
}
62+
return document;
6663
});
67-
68-
});
6964
}
7065

7166
checkResetTokenValidity(username, token) {
72-
return new Promise((resolve, reject) => {
73-
return this.config.database.collection('_User').then(coll => {
74-
return coll.findOne({
75-
username: username,
76-
_perishable_token: token,
77-
}, (err, doc) => {
78-
if (err || !doc) {
79-
reject(err);
80-
} else {
81-
resolve(doc);
82-
}
83-
});
67+
return this.config.database.adaptiveCollection('_User')
68+
.then(collection => {
69+
return collection.find({
70+
username: username,
71+
_perishable_token: token
72+
}, { limit: 1 });
73+
})
74+
.then(results => {
75+
if (results.length != 1) {
76+
return Promise.reject();
77+
}
78+
return results[0];
8479
});
85-
});
8680
}
8781

8882
getUserIfNeeded(user) {
@@ -130,24 +124,16 @@ export class UserController extends AdaptableController {
130124
}
131125

132126
setPasswordResetToken(email) {
133-
var database = this.config.database;
134-
var token = randomString(25);
135-
return new Promise((resolve, reject) => {
136-
return database.collection('_User').then(coll => {
127+
let token = randomString(25);
128+
return this.config.database
129+
.adaptiveCollection('_User')
130+
.then(collection => {
137131
// Need direct database access because verification token is not a parse field
138-
return coll.findAndModify({
139-
email: email,
140-
}, null, {$set: {_perishable_token: token}}, (err, doc) => {
141-
if (err || !doc.value) {
142-
console.error(err);
143-
reject(err);
144-
} else {
145-
doc.value._perishable_token = token;
146-
resolve(doc.value);
147-
}
148-
});
132+
return collection.findOneAndUpdate(
133+
{ email: email}, // query
134+
{ $set: { _perishable_token: token } } // update
135+
);
149136
});
150-
});
151137
}
152138

153139
sendPasswordResetEmail(email) {

src/Routers/SchemasRouter.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -164,14 +164,14 @@ function deleteSchema(req) {
164164
.then(() => {
165165
// We've dropped the collection now, so delete the item from _SCHEMA
166166
// and clear the _Join collections
167-
return req.config.database.collection('_SCHEMA')
168-
.then(coll => coll.findAndRemove({_id: req.params.className}, []))
169-
.then(doc => {
170-
if (doc.value === null) {
167+
return req.config.database.adaptiveCollection('_SCHEMA')
168+
.then(coll => coll.findOneAndDelete({_id: req.params.className}))
169+
.then(document => {
170+
if (document === null) {
171171
//tried to delete non-existent class
172172
return Promise.resolve();
173173
}
174-
return removeJoinTables(req.config.database, doc.value);
174+
return removeJoinTables(req.config.database, document);
175175
});
176176
})
177177
.then(() => {

0 commit comments

Comments
 (0)