Node.js (mongoose/Promissions)如何检查文档是否是使用findOneAndUpdate和upsert创建的

Node.js (mongoose/Promissions)如何检查文档是否是使用findOneAndUpdate和upsert创建的,node.js,mongodb,mongoose,promise,mongodb-query,Node.js,Mongodb,Mongoose,Promise,Mongodb Query,考虑一下这段代码,我需要创建或更新一个特定的文档 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){ /*

考虑一下这段代码,我需要创建或更新一个特定的文档

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
,因为我需要调用
收件箱中的函数

对于mongoose的
.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 }
{ lastErrorObject:
   { updatedExisting: false,
     n: 1,
     upserted: 55e13bcbf6044f57c8e09a4b },
  value: { _id: 55e13bcbf6044f57c8e09a4b, name: 'Bill' },
  ok: 1 }
其中的一致字段为
lastErrorObject.updatedExisting
true/false
,具体取决于是否发生upsert的结果。请注意,当此属性为
false
时,也有一个“upserted”值,其中包含新文档的
\u id
响应,但当该属性为
true
时则没有

这样,你就可以修改你的操作来考虑第三个条件,但是这只能用回调而不是承诺:

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
      }
    }
);
我还强烈建议您在这里使用而不是原始对象,因为原始对象总是会覆盖整个文档,而像这样的操作符只会影响列出的字段

还要注意的是,任何与语句匹配的“查询参数”都会自动分配到新文档中,只要它们的值与未找到的值完全匹配

由于某种原因,使用承诺似乎不会返回附加信息,因此,除了设置
{new:false}
之外,不知道使用承诺如何实现这一点,基本上,当没有返回文档时,它就是一个新的文档

您已经拥有了所有预期插入的文档数据,因此并不需要返回这些数据。事实上,本机驱动程序方法是如何在核心处理这个问题的,并且只有在发生upsert时才使用“upserted”
\u id
值进行响应

这实际上涉及到本网站上讨论的另一个问题,即:

这实际上归结为一个promise响应中多个对象的解析,本机规范中不直接支持这一点,但这里列出了一些方法

因此,如果您实现Bluebird承诺并在那里使用
.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: 55e12c65f6044f57c8e09a46 },
  value: { _id: 55e12c65f6044f57c8e09a46, 
           number: 55555555, 
           country: 'US', 
           token: "XXX", 
           appInstalled: true,
           __v: 0 },
  ok: 1 }
{ lastErrorObject:
   { updatedExisting: false,
     n: 1,
     upserted: 55e13bcbf6044f57c8e09a4b },
  value: { _id: 55e13bcbf6044f57c8e09a4b, name: 'Bill' },
  ok: 1 }

对于承诺使用此功能的人,根据我们的建议,我们可以传递
rawQuery:true
以及其他选项

const filter = { name: 'Will Riker' };
const update = { age: 29 };

await Character.countDocuments(filter); // 0

let res = await Character.findOneAndUpdate(filter, update, {
  new: true,
  upsert: true,
  rawResult: true // Return the raw result from the MongoDB driver
});

res.value instanceof Character; // true
// The below property will be `false` if MongoDB upserted a new
// document, and `true` if MongoDB updated an existing object.
res.lastErrorObject.updatedExisting; /

此查询的结果的格式为

{ lastErrorObject:
   { n: 1,
     updatedExisting: false,
     upserted: 5e6a9e5ec6e44398ae2ac16a },
  value:
   { _id: 5e6a9e5ec6e44398ae2ac16a,
     name: 'Will Riker',
     __v: 0,
     age: 29 },
  ok: 1 }

因此,可以在
data.value

处访问该文档。我已尝试查看
raw
,但它在最新版本中似乎不再存在(始终未定义)。我还使用Promissions,它只为
then(函数(项)提供一个参数…
我最终通过创建自己的
createOrUpdate
static解决了这个问题。@MattWay这有点可疑。使用简单的回调可以很好地工作,似乎是关于如何将事情作为承诺传递。我想我会花几分钟来看看这里出了什么问题。根据这一发现,修改只提供了doc(不是raw)。@MattWay关于这个问题的陈述不完全正确。我已经添加了一些关于本机驱动程序方法实际做什么的信息,并用一个工作示例来澄清这一点。这里的问题是驱动程序方法的承诺,而不是功能。