[英]Check If field exists in an sub-document of an Array
我有一個與此類似的架構。
{id: Number,
line_items: [{
id: String,
quantity: Number,
review_request_sent :Boolean
}],
total_price: String,
name: String,
order_number: Number
}
我想要找到的是所有沒有為數組中的所有項目設置字段 review_request_sent 的文檔。
例子
{
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
}
]
}
我需要有關查詢/查找的幫助,該查詢/查找將僅返回第二個文檔,因為它的數組中的所有項目中都沒有 review_request_sent 字段。
您基本上需要$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”。
你可以在沒有$map
情況下找到另一種方式, $unwind
使用$redact ,如下所示:
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()
在查詢上面首次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" : [ ] }
在最后一次匹配之后,只檢查那些line_items
數組包含值的文檔,即$size到 1
如果您只想獲取整個文檔,那么正如@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" ] }
} }
} }
]
注意: $type在與未定義的字段一起使用時返回“missing”。
let rankers = [
{
"_id": "5e4d4a13e0eb7b0733f27a18",
"totalMarks": 400,
"correct": 78,
"incorrect": 22,
"obtainMarks": 290,
"attemptCount": 100,
"timeTaken": 0,
"rank": 0,
"userRank": 1
},
{
"_id": "5e4d4a13e0eb7b0733f27a18",
"totalMarks": 400,
"correct": 77,
"incorrect": 21,
"obtainMarks": 287,
"attemptCount": 98,
"rank": 0,
"userRank": 2
},
{
"_id": "5e4d4a13e0eb7b0733f27a18",
"totalMarks": 400,
"correct": 76,
"incorrect": 21,
"obtainMarks": 283,
"attemptCount": 97,
"timeTaken": 0,
"userRank": 3
}
]
在 rankers 集合中,某些文檔中缺少 rank 和 timeTaken 鍵,所以請
Results.aggregate([
{
$project: {
"totalMarks": 1,
"correct": "$correct",
"incorrect": "$incorrect",
"obtainMarks": "$obtainMarks",
"attemptCount": "$attemptCount",
"timeTaken": {
$cond: {
if: { "$eq": [{ "$type": "$timeTaken" }, "missing"] },
then: 0,
else: "$timeTaken"
}
},
"rank": {
$cond: {
if: { "$eq": [{ "$type": "$rank" }, "missing"] },
then: 0,
else: "$rank"
}
}
}
}])
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.