簡體   English   中英

檢查字段是否存在於數組的子文檔中

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM