您基本上需要$elemMatch 和$exists 运算符,因为这将检查每个元素以查看条件“字段不存在”是否适用于任何元素:
Model.find({
"line_items": {
"$elemMatch": { "review_request_sent": { "$exists": false } }
}
},function(err,docs) {
});
仅当该字段不存在于数组子文档之一中时才返回第二个文档:
{
"id" : 2,
"line_items" : [
{
"id" : 1,
"review_request_sent" : false
},
{
"id" : 39
}
]
}
请注意,这与此形式“不同”:
Model.find({
"line_items.review_request_sent": { "$exists": false }
},function(err,docs) {
})
在哪里询问“所有”数组元素是否不包含此字段,当文档至少有一个元素存在该字段时,这是不正确的。所以$eleMatch 使条件针对“每个”数组元素进行测试,从而得到正确的响应。
如果您想更新此数据,以便找到不包含此字段的任何数组元素然后接收值为false(大概)的该字段,那么您甚至可以编写如下语句:
Model.aggregate(
[
{ "$match": {
"line_items": {
"$elemMatch": { "review_request_sent": { "$exists": false } }
}
}},
{ "$project": {
"line_items": {
"$setDifference": [
{"$map": {
"input": "$line_items",
"as": "item",
"in": {
"$cond": [
{ "$eq": [
{ "$ifNull": [ "$$item.review_request_sent", null ] },
null
]},
"$$item.id",
false
]
}
}},
[false]
]
}
}}
],
function(err,docs) {
if (err) throw err;
async.each(
docs,
function(doc,callback) {
async.each(
doc.line_items,
function(item,callback) {
Model.update(
{ "_id": doc._id, "line_items.id": item },
{ "$set": { "line_items.$.review_request_sent": false } },
callback
);
},
callback
);
},
function(err) {
if (err) throw err;
// done
}
);
}
);
.aggregate() 结果不仅匹配文档,而且从数组中过滤掉该字段不存在的内容,以便仅返回该特定子文档的“id”。
然后循环的.update() 语句匹配每个文档中找到的每个数组元素,并在匹配位置添加缺失的字段。
通过这种方式,您将在之前丢失的每个文档的所有子文档中显示该字段。
如果您想做这样的事情,那么更改架构以确保该字段也始终存在也是明智之举:
{id: Number,
line_items: [{
id: String,
quantity: Number,
review_request_sent: { type: Boolean, default: false }
}],
total_price: String,
name: String,
order_number: Number
}
因此,下次您在代码中向数组中添加新项目时,如果未明确设置,该元素将始终以其默认值存在。这样做可能是一个好主意,同时将required 设置在您一直想要的其他字段上,例如“id”。