可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I would like to use findAndModify to atomically increment a field, using Mongoose.
However, the code below throws the error "TypeError: Object # has no method 'findAndModify'":
// defining schema for the "counters" table
var tableSchema = new Schema({
_id: String,
next: Number
});
// creating table object for the counters table
var counters_table = mongoose.model('counters', tableSchema);
var tableObj = new counters_table();
// increment the "next" field on the table object
var query = {_id: 'messagetransaction'};
var update = {'$inc': {next: 1}};
var ret = tableObj.findAndModify(query, [], update, true, true, function(err) {
if (err) {
throw err;
}
else {
console.log("updated!");
}
});
回答1:
The feature is not well (read: at all) documented, but after reading through the source code, I came up with the following solution.
Create your collection schema.
var Counters = new Schema({
_id: String,
next: Number
});
Create a static method on the schema which will expose the findAndModify method of the model's collection.
Counters.statics.findAndModify = function (query, sort, doc, options, callback) {
return this.collection.findAndModify(query, sort, doc, options, callback);
};
Create your model.
var Counter = mongoose.model('counters', Counters);
Find and modify!
Counter.findAndModify({ _id: 'messagetransaction' }, [], { $inc: { next: 1 } }, {}, function (err, counter) {
if (err) throw err;
console.log('updated, counter is ' + counter.next);
});
Bonus
Counters.statics.increment = function (counter, callback) {
return this.collection.findAndModify({ _id: counter }, [], { $inc: { next: 1 } }, callback);
};
Counter.increment('messagetransaction', callback);
回答2:
This is fully supported in Mongoose 3.x now, though the name is slightly different.
http://mongoosejs.com/docs/api.html#model_Model.findOneAndUpdate
http://mongoosejs.com/docs/api.html#model_Model.findByIdAndUpdate
http://mongoosejs.com/docs/api.html#model_Model.findOneAndRemove
http://mongoosejs.com/docs/api.html#model_Model.findByIdAndRemove
回答3:
Made working version increment for Mongoose 3.x
var mongoose = require('mongoose');
var CounterSchema = new mongoose.Schema({
_id: String,
next: {type: Number, default: 1}
});
CounterSchema.statics.increment = function (counter, callback) {
return this.findByIdAndUpdate(counter, { $inc: { next: 1 } }, {new: true, upsert: true, select: {next: 1}}, callback);
};
Use something like this:
Counter.increment('photo', function (err, result) {
if (err) {
console.error('Counter on photo save error: ' + err); return;
}
photo.cid = result.next;
photo.save();
});
I hope someone come in handy
回答4:
In version 3, the mongoose findOneAndUpdate method exposes mongodb's findAndModify operation. It works like so:
var query = { name: 'Sprinkls' };
var update = { name: 'Sprinkles' };
var options = { new: false };
Cat.findOneAndUpdate(query, update, options, function (err, cat) {
if (err) ..
render('cat', cat);
});
More info here: http://aaronheckmann.tumblr.com/post/48943524629/mongoose-v3-part-2-findandmodify
回答5:
I got findAndModify to
- Upsert a counter (create and initialize it if it doesn't exist)
- Increment the counter
- Call a callback with the incremented value
in a single DB roundtrip using the following code:
var Counters = new Schema({
_id:String, // the schema name
count: Number
});
Counters.statics.findAndModify = function (query, sort, doc, options, callback) {
return this.collection.findAndModify(query, sort, doc, options, callback);
};
var Counter = mongoose.model('Counter', Counters);
/**
* Increments the counter associated with the given schema name.
* @param {string} schemaName The name of the schema for which to
* increment the associated counter.
* @param {function(err, count)} The callback called with the updated
* count (a Number).
*/
function incrementCounter(schemaName, callback){
Counter.findAndModify({ _id: schemaName }, [],
{ $inc: { count: 1 } }, {"new":true, upsert:true}, function (err, result) {
if (err)
callback(err);
else
callback(null, result.count);
});
}
Enjoy!
- Curran
回答6:
a lot of answers but I find this simple solution.
Counter.findByIdAndUpdate(ID, {$inc: {next:1}}, function (err, data) {
});
回答7:
Taking the above response from @furf, this is my promised
solution:
// eslint-disable-next-line func-names
localeTypesSchema.statics.findAndModify = function (query, sort, update, opts, callback) {
const cb = callback || (() => { });
try {
const result = this.collection.findAndModify(query || {}, sort || [], update || {}, opts);
cb(null, result);
return Promise.resolve(result);
} catch (err) {
cb(err);
return Promise.reject(err);
}
};
回答8:
I would suggest using the direct command style shown at the bottom of http://www.mongodb.org/display/DOCS/findAndModify+Command. I'm not familiar enough with mongoose to know the method for running a command, but all drivers provide some way to do it. If mongoose doesn't, you can do it directly using the style described at the top of http://www.mongodb.org/display/DOCS/Commands.
That said, you should make sure that you really need findAndModify
and that update
won't do what you need it to do. To see what update
is capable of take a look at http://www.mongodb.org/display/DOCS/Updating.
回答9:
just adding to furf answer that if you use objectId in your query, mongoDB will not be able to find your document. The mongoose layer takes care of converting the Hex string object id you get from the routing params to the proper object id.
to solve this you need to:
var ObjectID = require('mongodb').ObjectID;
var itemId = req.params.itemId;
var objectId = ObjectID.createFromHexString(itemId);
Item.findAndModify({_id: objectId},