(mongoose / promises)如何检查文档是否是使用带有upsert的findOneAndUpdate创建的

时间:2015-08-29 01:41:41

标签: 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){
    /*
       do something here with inbox, but only if the inbox was created (not updated)
    */
});

mongoose是否有能力区分文件是否已创建或更新?我需要new: true,因为我需要调用inbox上的函数。

2 个答案:

答案 0 :(得分:4)

对于mongoose的.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 }

如果lastErrorObject.updatedExistingtrue/false,则_id中的一致字段取决于是否发生upsert的结果。请注意,还有一个" upserted"当此属性为false时,包含新文档的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}

我强烈建议你在这里使用update operators而不是原始对象,因为原始对象将始终覆盖整个文档,但像$set这样的运算符只会影响列出的字段。

还注意到任何匹配的"查询参数"只要它们的值与未找到的完全匹配,就会在新文档中自动分配该语句。

鉴于使用promise似乎由于某种原因似乎没有返回附加信息,那么除了设置_id之外的其他承诺并没有看到这是可能的,并且基本上没有返回任何文档然后它&#39一个新的。

无论如何,您都希望插入所有文档数据,因此您不一定非常需要返回的数据。实际上,本机驱动程序方法在核心处理这个问题,并且只响应" upserted"发生upsert时的.spread()值。

这真的归结为本网站讨论的另一个问题,在:

Can promises have multiple arguments to onFulfilled?

这实际上归结为承诺响应中多个对象的解析,这是本地特征中没有直接支持的东西,但是那里列出了一些方法。

因此,如果您实施Bluebird承诺并在那里使用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 }

所以总是这样:

class CompareRectangles(object):

    debugging = True

    def globals():
        global debugging
        debugging = True

    def __init__(self,r1,r2):
        # globals()
        self.r1 = r1
        self.r2 = r2
        self.initialise_boundary_tests()

    def method(self):
        if debugging:
            print("hello debugger")

答案 1 :(得分:0)

对于将其用于Promises的人们,根据this link from Mongoosejs.com,我们可以将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上访问文档