mongodb 检查字段是否存在于数组的子文档中
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32344017/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Check If field exists in an sub-document of an Array
提问by user841818
I have a schema that is similar to this.
我有一个与此类似的架构。
{id: Number,
line_items: [{
id: String,
quantity: Number,
review_request_sent :Boolean
}],
total_price: String,
name: String,
order_number: Number
}
What I want to find is all documents that don't have the field review_request_sent set for all items in the array.
我想要找到的是所有没有为数组中的所有项目设置字段 review_request_sent 的文档。
Example
例子
{
id: 1,
line_items: [
{
id: 43,
review_request_sent: true
}
]
},
{
id: 2,
line_items: [
{
id: 1,
review_request_sent: false
},
{
id: 39
},
]
},
{
id: 3,
line_items: [
{
id: 23,
review_request_sent: true
},
{
id: 85,
review_request_sent: true
},
{
id: 12,
review_request_sent: false
}
]
}
I would want help with a query/find that will return only the second document because it does not have the review_request_sent field in all its items in its array.
我需要有关查询/查找的帮助,该查询/查找将仅返回第二个文档,因为它的数组中的所有项目中都没有 review_request_sent 字段。
回答by Blakes Seven
You basically want $elemMatch
and the $exists
operator, as this will inspect each element to see if the condition "field not exists" is true for any element:
您基本上想要$elemMatch
和$exists
操作符,因为这将检查每个元素以查看条件“字段不存在”对于任何元素是否为真:
Model.find({
"line_items": {
"$elemMatch": { "review_request_sent": { "$exists": false } }
}
},function(err,docs) {
});
That returns the second document only as the field is not present in one of the array sub-documents:
这仅返回第二个文档,因为该字段不存在于数组子文档之一中:
{
"id" : 2,
"line_items" : [
{
"id" : 1,
"review_request_sent" : false
},
{
"id" : 39
}
]
}
Note that this "differs" from this form:
请注意,这与此形式“不同”:
Model.find({
"line_items.review_request_sent": { "$exists": false }
},function(err,docs) {
})
Where that is asking does "all" of the array elements not contain this field, which is not true when a document has at least one element that has the field present. So the $eleMatch
makes the condition be tested against "each" array element, and thus you get the correct response.
那里要求的“所有”数组元素不包含此字段,当文档至少有一个具有该字段的元素时,情况并非如此。因此,$eleMatch
可以针对“每个”数组元素测试条件,从而获得正确的响应。
If you wanted to update this data so that any array element found that did not contain this field was to then receive that field with a value of false
( presumably ), then you could even write a statement like this:
如果您想更新此数据,以便找到的任何不包含此字段的数组元素都将接收值为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
}
);
}
);
Where the .aggregate()
result not only matches the documents, but filters out content from the array where the field was not present, so as to just return the "id" of that particular sub-document.
凡.aggregate()
结果不仅匹配的文件,但过滤掉从数组,其中场是不存在的内容,以便只返回特定的子文档的“ID”。
Then the looped .update()
statements match each found array element in each document and adds the missing field with a value at the matched position.
然后循环.update()
语句匹配每个文档中找到的每个数组元素,并在匹配位置添加缺失字段和值。
In that way you will have the field present in all sub-documents of every document where it was missing before.
这样,您将在之前丢失的每个文档的所有子文档中显示该字段。
If you want to do such a thing, then it would also be wise to change your schema to make sure the field is always there as well:
如果您想做这样的事情,那么更改您的架构以确保该字段也始终存在也是明智之举:
{id: Number,
line_items: [{
id: String,
quantity: Number,
review_request_sent: { type: Boolean, default: false }
}],
total_price: String,
name: String,
order_number: Number
}
So the next time you add new items to the array in your code the element will always exist with it's default value if not otherwise explicitly set. And it is probably a goood idea to do so, as well as setting required
on other fields you always want, such as "id".
因此,下次您将新项目添加到代码中的数组时,如果未明确设置,该元素将始终以其默认值存在。这样做可能是一个好主意,以及设置required
您一直想要的其他字段,例如“id”。
回答by Yogesh
You can find this another way without $map
and $unwind
used $redactlike this :
你可以找到没有这个另一种方式$map
,并$unwind
使用$纂是这样的:
db.collectionName.aggregate({
"$match": {
"line_items.review_request_sent": {
"$exists": true
}
}
}, {
"$redact": {
"$cond": {
"if": {
"$eq": [{
"$ifNull": ["$review_request_sent", "null"]
}, "null"]
},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}
}, {
"$match": {
"line_items": {
"$size": 1
}
}
}).pretty()
In above query first match
check whether review_request_sent
is presents or not in redact
$ifNulluse to check whether review_request_sent
presents or not if not then it replaced to "null"
and $eq
to check "null"
this will return documents like
在查询第一上述match
检查是否review_request_sent
是礼物或没有redact
$ IFNULL使用,以检查是否review_request_sent
呈现与否如果没有的话就更换,以"null"
和$eq
检查"null"
这将返回类似文件
{ "_id" : ObjectId("55e6ca322c33bb07ff2c163e"), "id" : 1, "line_items" : [ ] }
{ "_id" : ObjectId("55e6ca322c33bb07ff2c163f"), "id" : 2, "line_items" : [ { "id" : 39 } ] }
{ "_id" : ObjectId("55e6ca322c33bb07ff2c1640"), "id" : 3, "line_items" : [ ] }
and after that last match check only those documents whose line_items
array contains value i.e $sizeto 1
在最后一次匹配之后只检查那些line_items
数组包含值的文档, 即 $size到 1
回答by Abhinandan Kothari
If you just want to get the whole document then as mentioned by @Blakes Seven. You can query using $elemMatch.
But if you want to filter array element and get only those array elements which do not have a certain field then you can use $filteralong with $typein aggregation pipeline.
如果您只想获取整个文档,那么正如@Blakes Seven所提到的那样。您可以使用$elemMatch进行查询。
但是如果你想过滤数组元素并只获取那些没有特定字段的数组元素,那么你可以在聚合管道中使用$filter和$type。
[
{ $match: { "line_items": { "$elemMatch": { "review_request_sent": { "$exists": false } } } } },
{ $project: {
"line_items": { "$filter": {
"input": "$line_items",
"as": "item",
"cond": { "$eq": [ { "$type": "$$item.review_request_sent" }, "missing" ] }
} }
} }
]
Note:$typereturns "missing" when used with undefined fields.
注意:$type在与未定义的字段一起使用时返回“missing”。