Mongodb 设置文档';将字段添加到与查询匹配的ObjectId数组

Mongodb 设置文档';将字段添加到与查询匹配的ObjectId数组,mongodb,mongoose,mongodb-query,Mongodb,Mongoose,Mongodb Query,假设我有一个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'

假设我有一个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查询也有类似的功能。我想象的是(伪蒙哥,别笑)

对于“node.js”,这里的底线是需要实现一个“回调”或“承诺”来解析来自先前查询操作的异步响应。这是基本要求,因为在将这些项“添加”到相关对象之前,首先需要从其他集合中“查询”所需的项

以下示例可用于将先前查询的结果“传入”到另一个查询的参数:

var async=require('async'),
mongoose=require('mongoose'),
Schema=mongoose.Schema;
var letterSchema=新模式({
名称:{type:String,必需:true,unique:true},
类型:{type:String,枚举:[‘辅音’,‘元音’]}
});
var favoriteSchema=新模式({
名称:{type:String,必需:true},
字母:[{type:Schema.Types.ObjectId,ref:'Letter'}]
});
var Letter=mongoose.model('Letter',letterSchema),
Favorite=mongoose.model('Favorite',favoriteSchema);
猫鼬mongodb://localhost/test');
mongoose.set(“调试”,true);
异步系列(
[
函数(回调){
async.each([Letter,Favorite],函数(model,callback){
删除({},回调);
},回调);
},
函数(回调){
async.each(
[
{“名称”:“a”,“类型”:“元音”},
{“名称”:“b”,“类型”:“辅音”},
{“名称”:“c”,“类型”:“辅音”}
],
函数(字母、回调){
创建(信函、回调);
},
回拨
);
},
函数(回调){
创建({“name”:“mine”},回调);
},
函数(回调){
异步瀑布(
[
函数(回调){
查找({“name”:{“$in”:[“a”,“c”]}},回调);
},
函数(字母、回调){
Favorite.findOneAndUpdate(
{“姓名”:“我的”},
{“$push”:{“字母”:{“$each”:字母}},
{“新”:正确},
回拨
);
}
],
回拨
)
},
函数(回调){
Favorite.find().populate('letters').exec(函数(err,docs){
console.log(文档);
回调(错误)
});
}
],
功能(err){
如果(错误)抛出错误;
mongoose.disconnect();
}
);
这会产生很好的输出,如:

[{u id:55eed7dfc7b23b7c4838903b,
名字:'我的',
__v:0,
信件:
[{u id:55eed7dfc7b23b7c48389038,
名称:‘a’,
类型:'元音',
__v:0},
{id:55eed7dfc7b23b7c4838903a,
名称:‘c’,
类型:'辅音',
__v:0}]}]
请注意列表中的行:

{“$push”:{“字母”:{“$each”:字母}}
在所使用的方法中,操作符将“原子地”附加到文档中已经存在的数组中,修饰符允许它接受参数的“数组”,而不是要附加的单个对象

Mongoose还处理从先前的
.find()
返回的“对象”的“转换”,以仅插入架构所需的
\u id
值。另外,整个
async.falter
过程将上一个结果回调返回的值传递到函数的下一个阶段

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

Letter.find({“name”:{“$in”:[“a”,“c”]}},函数(err,docs){
Favorite.findOneAndUpdate(
{“姓名”:“我的”},
{“$push”:{“字母”:{“$each”:字母}},
{“新”:正确},
函数(错误、结果){
控制台日志(结果);
}
);
});
这里的“缩进蠕变”非常糟糕

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


除了创建一个集合来存储“字母表”细节的一般“实用性”之外,还可以使用
ObjectId
而不是字母表本身的自然“键”作为链接对象,这是处理此问题的基本过程。

不,一个查询是不可能的。最多两个问题。@SergioTulentsev感谢您对我的问题的直接回答,如果可以的话我会接受:)真遗憾,我希望Mongo可以在内部这样做,就像它可以从聚合结果填充完整集合一样。此外,您的代码看起来比我提出的解决方案要详细得多——有没有理由不使用本机承诺、map()和then()呢?感谢您的详细回复。:)@贾拉德这里唯一的耻辱是你缺乏理解。MongoDB“内部不做任何事情”,比如“填充集合”。它是完全无模式的,基本上是按照设计“只存储”。这是一件“好事”。至于“您提出的解决方法”,如果不按照您问题中提出的格式返回先前查询的结果,显然不可能做任何事情。使用承诺或回调与此无关。您只需返回查询集合的结果,就可以在下一条语句中使用这些结果。@Jarrad最好的“解决方法”或实际上的“最佳实践”不是做一些像尝试在集合中存储“字母表”那样愚蠢的事情,而是
fav.set('letters', { $map: {
    input: {$find : ObjectID in ['a','c']},
    as: 'letterDoc',
    in: '$$letterDoc._id'
}});