您基本上需要$elemMatch
和$exists
运算符,因为这将判断每个元素,以查看条件"field not 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".