我可以在 findOneAndUpdate 的投影中访问位置 $ 运算符吗 [英] Can I access the positional $ operator in projection of findOneAndUpdate

查看:33
本文介绍了我可以在 findOneAndUpdate 的投影中访问位置 $ 运算符吗的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个有效的查询,但我希望文档只显示 network.stations.$ 而不是整个数组.如果我写 fields: network.stations.$,我会得到一个错误.有没有办法让文档只从 [stations] 返回单个元素?

I have this query that works, but I want for the doc to only display network.stations.$ instead of the entire array. If I write fields: network.stations.$, I get an error. Is there a way for the doc only to return a single element from [stations]?

Network.findOneAndUpdate({
  "network.stations.id": req.params.station_Id
}, {
  "network.stations.$.free_bikes": req.body.free_bikes
}, {
  new: true,
  fields: "network.stations"
}, (err, doc) => console.log(doc)) 
// I want doc to somehow point only to a single station instead of 
// several stations like it currently does.

推荐答案

答案是肯定的,但并非如您所愿.正如您在问题中所指出的,将 network.stations.$ 放在 "fields" 选项中以按位置返回已修改"文档会引发特定错误:

The answer is "yes", but not in the way you are expecting. As you note in the question, putting network.stations.$ in the "fields" option to positionally return the "modified" document throws a specific error:

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

"cannot use a positional projection and return the new document"

然而,这应该是提示",因为当您知道正在修改的值时,您并不真正需要"新文档".简单的情况是不返回 "new" 文档,而是返回它的找到状态",即原子修改之前",并按照您的要求简单地对返回的数据进行相同的修改在声明中申请.

This however should be the "hint", because you don't really "need" the "new document" when you know what the value was you are modifying. The simple case then is to not return the "new" document, but instead return it's "found state" which was "before the atomic modification" and simply make the same modification to the returned data as you asked to apply in the statement.

作为一个小的包含演示:

As a small contained demo:

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
    }
  ]
}

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

So I'm doing the modifications in a loop so you can see that the update is actually applied on the server as the next iteration increments the already incremented value.

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

Merely by omitting the "new" option, what you get is the document in the state which it was "matched" and it then is perfectly valid to return that document state before modification. The modification still happens.

您需要做的就是依次对代码进行相同的修改.添加 .lean() 使这变得简单,而且它再次完全有效,因为您知道您要求服务器做什么".

All you need to do here is in turn make the same modification in code. Adding .lean() makes this simple, and again it's perfectly valid since you "know what you asked the server to do".

这比单独的查询要好,因为在您的修改和查询之间可以通过不同的更新单独"修改文档以仅返回投影匹配字段.

This is better than a separate query because "separately" the document can be modified by a different update in between your modification and the query to return just a projected matched field.

这比返回所有"元素并稍后过滤要好,因为当您真正想要的是匹配元素"时,潜在的可能是非常大的数组".这当然是真的.

And it's better than returning "all" the elements and filtering later, because the potential could be a "very large array" when all you really want is the "matched element". Which of course this actually does.

这篇关于我可以在 findOneAndUpdate 的投影中访问位置 $ 运算符吗的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆