我可以在findOneAndUpdate的投影中访问位置$运算符

时间:2017-09-05 21:32:29

标签: mongodb mongoose mongodb-query

我有这个查询有效,但我希望doc只显示ü而不是整个数组。如果我写network.stations.$,我会收到错误。有没有办法让doc只返回fields: network.stations.$中的单个元素?

[stations]

2 个答案:

答案 0 :(得分:1)

答案是“是”,但不是你期望的方式。正如您在问题中所述,将network.stations.$放入"fields"选项以定位返回“已修改”文档会引发特定错误:

  

“不能使用位置投影并返回新文档”

然而,这应该是“提示”,因为当您知道要修改的值时,您并不真正“需要”“新文档”。那么简单的情况是不返回"new"文档,而是返回它的“找到状态”,这是“在原子修改之前”,并且只是对你在语句中要求应用的返回数据进行相同的修改

作为一个小小的演示:

const mongoose = require('mongoose'),
      Schema = mongoose.Schema;

mongoose.Promise = global.Promise;
mongoose.set('debug',true);

const uri = 'mongodb://localhost/test',
      options = { useMongoClient: true };

const testSchema = new Schema({},{ strict: false });

const Test = mongoose.model('Test', testSchema, 'collection');

function log(data) {
  console.log(JSON.stringify(data,undefined,2))
}

(async function() {

  try {

    const conn = await mongoose.connect(uri,options);

    await Test.remove();

    await Test.insertMany([{ a: [{ b: 1 }, { b: 2 }] }]);

    for ( let i of [1,2] ) {
      let result = await Test.findOneAndUpdate(
        { "a.b": { "$gte": 2 } },
        { "$inc": { "a.$.b": 1 } },
        { "fields": { "a.$": 1 } }
      ).lean();

      console.log('returned');
      log(result);

      result.a[0].b = result.a[0].b + 1;
      console.log('modified');
      log(result);

    }

  } catch(e) {
    console.error(e)
  } finally {
    mongoose.disconnect()
  }


})();

产生:

Mongoose: collection.remove({}, {})
Mongoose: collection.insertMany([ { __v: 0, a: [ { b: 1 }, { b: 2 } ], _id: 59af214b6fb3533d274928c9 } ])
Mongoose: collection.findAndModify({ 'a.b': { '$gte': 2 } }, [], { '$inc': { 'a.$.b': 1 } }, { new: false, upsert: false, fields: { 'a.$': 1 } })
returned
{
  "_id": "59af214b6fb3533d274928c9",
  "a": [
    {
      "b": 2
    }
  ]
}
modified
{
  "_id": "59af214b6fb3533d274928c9",
  "a": [
    {
      "b": 3
    }
  ]
}
Mongoose: collection.findAndModify({ 'a.b': { '$gte': 2 } }, [], { '$inc': { 'a.$.b': 1 } }, { new: false, upsert: false, fields: { 'a.$': 1 } })
returned
{
  "_id": "59af214b6fb3533d274928c9",
  "a": [
    {
      "b": 3
    }
  ]
}
modified
{
  "_id": "59af214b6fb3533d274928c9",
  "a": [
    {
      "b": 4
    }
  ]
}

所以我在循环中进行修改,以便您可以看到更新实际应用于服务器,因为下一次迭代会增加已经增加的值。

仅仅通过省略"new"选项,你得到的是处于“匹配”状态的文档,然后在修改之前返回该文档状态是完全有效的。修改仍然发生。

这里你需要做的就是在代码中进行相同的修改。添加.lean()会使这一点变得简单,并且它再次完全有效,因为您“知道您要求服务器执行的操作”。

这比单独的查询更好,因为“单独”文档可以通过修改和查询之间的不同更新进行修改,以仅返回投影匹配的字段。

并且它比返回“all”元素并稍后过滤更好,因为当你真正想要的只是“匹配元素”时,潜力可能是“非常大的数组”。当然这实际上是这样做的。

答案 1 :(得分:0)

尝试将fields更改为projection,然后使用您之前尝试过的network.stations.$

如果您的查询无效,那么这可能就足够了。如果它仍然不起作用,您可以尝试将第二个参数更改为显式$set

Network.findOneAndUpdate({
  "network.stations.id": req.params.station_Id
}, {
  "$set": {
    "network.stations.$.free_bikes": req.body.free_bikes
  }
}, {
  new: true,
  projection: "network.stations.$"
}, (err, doc) => console.log(doc))