【问题标题】:Set a document's field to an array of ObjectIDs matching a query将文档的字段设置为与查询匹配的 ObjectID 数组
【发布时间】:2015-12-03 05:29:31
【问题描述】:

假设我有一个带有一些文档的 Mongo 集合:

{ _id: ObjectId('idOfA'),
  name: 'a', type: 'vowel' },
{ _id: ObjectId('idOfB'),
  name: 'b', type: 'consonant' },
{ _id: ObjectId('idOfC'),
  name: 'c', type: 'consonant },
...

现在假设我有另一个文档,

fav = {
  name: 'my favourite letters',
  letters: []
}

我还有一个数组['a', 'c']

有没有一种单一的查询方式来更新fav,这样它是

{
  name: my favourite letters',
  letters: [
    ObjectId('idOfA'),
    ObjectId('idOfC')
  ]
}

?

我正在使用 Mongoose,但对类似的原始 Mongo 查询感到满意。我正在设想类似(伪mongo,别笑)

fav.set('letters', { $map: {
    input: {$find : ObjectID in ['a','c']},
    as: 'letterDoc',
    in: '$$letterDoc._id'
}});

【问题讨论】:

  • 不,一个查询是不可能的。最多两个查询。
  • @SergioTulentsev 感谢您对我的问题的直接回答,如果可以的话,我会接受它:)

标签: mongodb mongoose mongodb-query


【解决方案1】:

使用“node.js”的底线是您需要实现“回调”或“承诺”来解决来自先前查询操作的异步响应。这基本上是必需的,因为您首先需要从其他集合中“查询”您想要的项目,然后再将这些项目“添加”到您的相关对象。

这是async.waterfall 的示例,它可用于将先前查询的结果“传入”另一个参数:

var async = require('async'),
    mongoose = require('mongoose'),
    Schema = mongoose.Schema;

var letterSchema = new Schema({
  name: { type: String, required: true, unique: true },
  type: { type: String, enum: ['consonant','vowel'] }
});


var favoriteSchema = new Schema({
  name: { type: String, required: true },
  letters: [{ type: Schema.Types.ObjectId, ref: 'Letter' }]
});

var Letter = mongoose.model( 'Letter', letterSchema ),
    Favorite = mongoose.model( 'Favorite', favoriteSchema );

mongoose.connect('mongodb://localhost/test');

mongoose.set("debug",true);

async.series(
  [
    function(callback) {
      async.each([Letter,Favorite],function(model,callback) {
        model.remove({},callback);
      },callback);
    },
    function(callback) {
      async.each(
        [
          { "name": "a", "type": "vowel" },
          { "name": "b", "type": "consonant" },
          { "name": "c", "type": "consonant" }
        ],
        function(letter,callback) {
          Letter.create(letter,callback);
        },
        callback
      );
    },
    function(callback) {
      Favorite.create({ "name": "mine" },callback);
    },
    function(callback) {
      async.waterfall(
        [
          function(callback) {
            Letter.find({ "name": { "$in": ["a","c"] } },callback);
          },
          function(letters,callback) {
            Favorite.findOneAndUpdate(
              { "name": "mine" },
              { "$push": { "letters": { "$each": letters } } },
              { "new": true },
              callback
            );
          }
        ],
        callback
      )
    },
    function(callback) {
      Favorite.find().populate('letters').exec(function(err,docs) {
        console.log(docs);
        callback(err)
      });
    }
  ],
  function(err) {
    if (err) throw err;
    mongoose.disconnect();
  }
);

它提供了很好的输出,例如:

[ { _id: 55eed7dfc7b23b7c4838903b,
    name: 'mine',
    __v: 0,
    letters:
     [ { _id: 55eed7dfc7b23b7c48389038,
         name: 'a',
         type: 'vowel',
         __v: 0 },
       { _id: 55eed7dfc7b23b7c4838903a,
         name: 'c',
         type: 'consonant',
         __v: 0 } ] } ]

请注意列表中的行:

 { "$push": { "letters": { "$each": letters } } }

在使用的.findOneAndUpdate() 方法中,$push 运算符将“原子地”附加到文档中已经存在的数组,而$each 修饰符允许它接受参数的“数组”,而不是到要附加的单个对象。

Mongoose 还处理从先前的 .find() 返回的“对象”的“转换”,以插入架构所需的 _id 值。整个async.waterfall 进程也将先前结果回调的返回值传递到函数的下一个阶段。

那个“最后”基本上比写这个“更好”:

Letter.find({ "name": { "$in": ["a","c"] } },function(err,docs) {
    Favourite.findOneAndUpdate(
        { "name": "mine" },
        { "$push": { "letters": { "$each": letters } } },
        { "new": true },
        function(err,result) {
            console.log(result);
        }
    );
});

“缩进蠕变”令人讨厌。

所以“底线”再次是您需要先.find() 结果,然后在“更新”操作中“使用”这些结果以附加到数组。有多种方法可以做到这一点,但使用“原子”$push 是性能和可靠性的最佳选择。

除了创建一个集合来存储“字母”详细信息的一般“实用性”以及使用 ObjectId 而不是字母本身的自然“键”作为链接的东西之外,这是如何处理。

【讨论】:

  • 真可惜,我曾希望 Mongo 可以在内部执行此操作,就像它可以从聚合结果中填充完整集合一样。此外,您的代码看起来比我想出的解决方法要冗长得多——您是否有理由不使用本机承诺、map()s 和 then()s?感谢您的详细回复。 :)
  • @Jarrad 这里唯一的遗憾是你缺乏理解。 MongoDB 不做任何“内部操作”,例如“填充集合”。它是完全无模式的,并且按照设计基本上“除了存储之外什么都不做”。这是一件“好事”。至于“您想出的解决方法”,如果不以您的问题中提出的格式返回先前查询的结果,就不可能做任何事情。使用承诺或回调与它无关。您只需从查询的集合中返回结果,以便在下一条语句中使用这些结果。
  • @Jarrad 最好的“解决方法”或者实际上是“最佳实践”并不是做一些像尝试在集合中存储“字母表”那样愚蠢的事情,而是简单地实例化一些基本不变的东西记忆。只是想向您展示如何使用您描述的方法来完成。我以“基本上是一种非常愚蠢的方式来解决这个问题”开场。
  • 我没有在 Mongo 中保存字母表(或者实际上没有任何此类常量数据),这是一个愚蠢的简化示例,用于演示我所追求的功能。如果Mongo“除了存储什么都不做”,为什么他们会实施一个非常强大的聚合框架,我有点困惑。
  • @Jarrad 我认为您需要坐下来接受您问题的答案不是您想象的,而是您实际可用的。不发送到服务器并扩展“内联”的查询原则上称为“加入”,而 MongoDB 不这样做。吸取教训而不是坐等不存在的功能出现会更明智。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-10-31
  • 2018-11-24
  • 2020-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-12
相关资源
最近更新 更多