问题描述
考虑此代码,在这里我需要创建或更新特定文档.
Inbox.model.findOneAndUpdate({ number: req.phone.number }, {
number: req.phone.number,
country: req.phone.country,
token: hat(),
appInstalled: true
}, { new: true, upsert: true }).then(function(inbox){
/*
do something here with inbox, but only if the inbox was created (not updated)
*/
});
猫鼬是否有能力区分文件是创建还是更新的?我需要new: true
,因为我需要在inbox
上调用函数.
对于 .findOneAndUpdate()
或用于猫鼬的任何.findAndModify()
核心驱动程序变体,实际的回调签名具有三个"参数:
function(err,result,raw)
第一个是任何错误响应,然后是修改后的文档或原始文档(取决于选项),第三个是已发布语句的写入结果.
第三个参数应返回如下数据:
{ lastErrorObject:
{ updatedExisting: false,
n: 1,
upserted: 55e12c65f6044f57c8e09a46 },
value: { _id: 55e12c65f6044f57c8e09a46,
number: 55555555,
country: 'US',
token: "XXX",
appInstalled: true,
__v: 0 },
ok: 1 }
其中的一致字段为true/false
或true/false
,这取决于是否发生了upsert.请注意,当此属性为false
时,还有一个"upserted"值,该值包含新文档的_id
响应,但当它为true
时,不包含该值.
这样,您随后将修改处理以考虑第三个条件,但这仅适用于回调而不是promise:
Inbox.model.findOneAndUpdate(
{ "number": req.phone.number },
{
"$set": {
"country": req.phone.country,
"token": hat(),
"appInstalled": true
}
},
{ "new": true, "upsert": true },
function(err,doc,raw) {
if ( !raw.lastErrorObject.updatedExitsing ) {
// do things with the new document created
}
}
);
强烈建议您使用更新运算符而不是原始对象,因为原始对象将始终覆盖整个文档,但 $set
仅会影响列出的字段.
还要注意,只要新值中未找到与该语句匹配的查询参数",它们就会自动在新文档中分配.
鉴于使用诺言由于某种原因似乎未返回其他信息,因此除了设置{ new: false}
之外,看不到诺言如何实现,基本上没有返回任何文档时,它是一个新的信息.
无论如何,您都希望插入所有文档数据,因此,您并不是真的需要返回任何数据.实际上,这是本机驱动程序方法如何在核心处进行处理,并且仅在发生upsert时才使用"upserted" _id
值进行响应.
这实际上归结为该网站上讨论的另一个问题,内容如下:
这实际上取决于承诺响应中多个对象的解析,这在本机规范中没有直接支持,但其中列出了一些方法.
因此,如果您实现Bluebird Promise并在其中使用.spread()
方法,那么一切都很好:
var async = require('async'),
Promise = require('bluebird'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/test');
var testSchema = new Schema({
name: String
});
var Test = mongoose.model('Test',testSchema,'test');
Promise.promisifyAll(Test);
Promise.promisifyAll(Test.prototype);
async.series(
[
function(callback) {
Test.remove({},callback);
},
function(callback) {
var promise = Test.findOneAndUpdateAsync(
{ "name": "Bill" },
{ "$set": { "name": "Bill" } },
{ "new": true, "upsert": true }
);
promise.spread(function(doc,raw) {
console.log(doc);
console.log(raw);
if ( !raw.lastErrorObject.updatedExisting ) {
console.log( "new document" );
}
callback();
});
}
],
function(err) {
if (err) throw err;
mongoose.disconnect();
}
);
当然会返回两个对象,然后您可以一致地访问它们:
{ _id: 55e14b7af6044f57c8e09a4e, name: 'Bill', __v: 0 }
{ lastErrorObject:
{ updatedExisting: false,
n: 1,
upserted: 55e14b7af6044f57c8e09a4e },
value: { _id: 55e14b7af6044f57c8e09a4e, name: 'Bill', __v: 0 },
ok: 1 }
以下是显示正常行为的完整列表:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/test');
var testSchema = new Schema({
name: String
});
var Test = mongoose.model('Test',testSchema,'test');
async.series(
[
function(callback) {
Test.remove({},callback);
},
function(callback) {
Test.findOneAndUpdate(
{ "name": "Bill" },
{ "$set": { "name": "Bill" } },
{ "new": true, "upsert": true }
).then(function(doc,raw) {
console.log(doc);
console.log(raw);
if ( !raw.lastErrorObject.updatedExisting ) {
console.log( "new document" );
}
callback();
});
}
],
function(err) {
if (err) throw err;
mongoose.disconnect();
}
);
出于记录,本机驱动程序本身没有此问题,因为响应对象实际上是除任何错误之外返回的唯一对象:
var async = require('async'),
mongodb = require('mongodb'),
MongoClient = mongodb.MongoClient;
MongoClient.connect('mongodb://localhost/test',function(err,db) {
var collection = db.collection('test');
collection.findOneAndUpdate(
{ "name": "Bill" },
{ "$set": { "name": "Bill" } },
{ "upsert": true, "returnOriginal": false }
).then(function(response) {
console.log(response);
});
});
所以总是这样:
{ lastErrorObject:
{ updatedExisting: false,
n: 1,
upserted: 55e13bcbf6044f57c8e09a4b },
value: { _id: 55e13bcbf6044f57c8e09a4b, name: 'Bill' },
ok: 1 }
Consider this code, where I need to either create or update a particular document.
Inbox.model.findOneAndUpdate({ number: req.phone.number }, {
number: req.phone.number,
country: req.phone.country,
token: hat(),
appInstalled: true
}, { new: true, upsert: true }).then(function(inbox){
/*
do something here with inbox, but only if the inbox was created (not updated)
*/
});
Does mongoose have a facility to be able to make the distinction between whether or not the documented was created or updated? I require new: true
because I need to call functions on the inbox
.
In the case of .findOneAndUpdate()
or any of the .findAndModify()
core driver variants for mongoose, the actual callback signature has "three" arguments:
function(err,result,raw)
With the first being any error response, then the modified or original document depending on options and the third which is a write result of the issued statement.
That third argument should return data much like this:
{ lastErrorObject:
{ updatedExisting: false,
n: 1,
upserted: 55e12c65f6044f57c8e09a46 },
value: { _id: 55e12c65f6044f57c8e09a46,
number: 55555555,
country: 'US',
token: "XXX",
appInstalled: true,
__v: 0 },
ok: 1 }
With the consistent field in there as lastErrorObject.updatedExisting
being either true/false
depending on the result of whether an upsert occured. Note that there is also a "upserted" value containing the _id
response for the new document when this property is false
, but not when it is true
.
As such you would then modify your handling to consider the third condition, but this only works with a callback and not a promise:
Inbox.model.findOneAndUpdate(
{ "number": req.phone.number },
{
"$set": {
"country": req.phone.country,
"token": hat(),
"appInstalled": true
}
},
{ "new": true, "upsert": true },
function(err,doc,raw) {
if ( !raw.lastErrorObject.updatedExitsing ) {
// do things with the new document created
}
}
);
Where I would also strongly suggest you use update operators rather than raw objects here, as a raw object will always overwrite the entire document, yet operators like $set
just affect the listed fields.
Also noting that any matching "query arguments" to the statement are automatically assigned in the new document as long as their value is an exact match that was not found.
Given that using a promise does not seem to return the additional information for some reason, then do not see how this is possible with a promise other than setting { new: false}
and basically when no document is returned then it's a new one.
You have all the document data expected to be inserted anyway, so it is not like you really need that data returned anyway. It is in fact how the native driver methods handle this at the core, and only respond with the "upserted" _id
value when an upsert occurs.
This really comes down to another issue discussed on this site, under:
Can promises have multiple arguments to onFulfilled?
Where this really comes down to the resolution of multiple objects in a promise response, which is something not directly supported in the native speicification but there are approaches listed there.
So if you implement Bluebird promises and use the .spread()
method there, then everything is fine:
var async = require('async'),
Promise = require('bluebird'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/test');
var testSchema = new Schema({
name: String
});
var Test = mongoose.model('Test',testSchema,'test');
Promise.promisifyAll(Test);
Promise.promisifyAll(Test.prototype);
async.series(
[
function(callback) {
Test.remove({},callback);
},
function(callback) {
var promise = Test.findOneAndUpdateAsync(
{ "name": "Bill" },
{ "$set": { "name": "Bill" } },
{ "new": true, "upsert": true }
);
promise.spread(function(doc,raw) {
console.log(doc);
console.log(raw);
if ( !raw.lastErrorObject.updatedExisting ) {
console.log( "new document" );
}
callback();
});
}
],
function(err) {
if (err) throw err;
mongoose.disconnect();
}
);
Which of course returns both objects and you can access then consistently:
{ _id: 55e14b7af6044f57c8e09a4e, name: 'Bill', __v: 0 }
{ lastErrorObject:
{ updatedExisting: false,
n: 1,
upserted: 55e14b7af6044f57c8e09a4e },
value: { _id: 55e14b7af6044f57c8e09a4e, name: 'Bill', __v: 0 },
ok: 1 }
Here is a full listing demonstrating the normal behaviour:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/test');
var testSchema = new Schema({
name: String
});
var Test = mongoose.model('Test',testSchema,'test');
async.series(
[
function(callback) {
Test.remove({},callback);
},
function(callback) {
Test.findOneAndUpdate(
{ "name": "Bill" },
{ "$set": { "name": "Bill" } },
{ "new": true, "upsert": true }
).then(function(doc,raw) {
console.log(doc);
console.log(raw);
if ( !raw.lastErrorObject.updatedExisting ) {
console.log( "new document" );
}
callback();
});
}
],
function(err) {
if (err) throw err;
mongoose.disconnect();
}
);
For the record, the native driver itself does not have this issue as the response object is in fact it's only object returned aside from any error:
var async = require('async'),
mongodb = require('mongodb'),
MongoClient = mongodb.MongoClient;
MongoClient.connect('mongodb://localhost/test',function(err,db) {
var collection = db.collection('test');
collection.findOneAndUpdate(
{ "name": "Bill" },
{ "$set": { "name": "Bill" } },
{ "upsert": true, "returnOriginal": false }
).then(function(response) {
console.log(response);
});
});
So it is always something like this:
{ lastErrorObject:
{ updatedExisting: false,
n: 1,
upserted: 55e13bcbf6044f57c8e09a4b },
value: { _id: 55e13bcbf6044f57c8e09a4b, name: 'Bill' },
ok: 1 }
这篇关于(猫鼬/承诺)如何检查使用findOneAndUpdate和upsert创建的文档的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!