sql >> データベース >  >> NoSQL >> MongoDB

ドキュメント全体とともに$lookupから配列内の要素のフィルタリングされた数を取得します

    探している人のための注釈-外国人カウント

    最初に回答されたものよりも少し良いのは、実際に新しい形式の $ lookup MongoDB3.6から。これにより、後続のフィルタリングとカウントのために「配列」を返すのではなく、「サブパイプライン」式内で「カウント」を実際に実行できます。また、 $ unwind

    db.emailGroup.aggregate([
      { "$lookup": {
        "from": "link",
        "let": { "id": "$_id" },
        "pipeline": [
          { "$match": {
            "originalLink": "",
            "$expr": { "$eq": [ "$$id", "$_id" ] }
          }},
          { "$count": "count" }
        ],
        "as": "linkCount"    
      }},
      { "$addFields": {
        "linkCount": { "$sum": "$linkCount.count" }
      }}
    ])
    

    元の質問が求めていたものではなく、以下の回答の一部が現在最も最適な形式になっています。もちろん、 $ lookup 「一致した数」に減らされますのみ 「一致したすべてのドキュメント」の代わりに。

    オリジナル

    これを行う正しい方法は、 "linkCount"を追加することです。 $ group ステージと $ first $ unwind $lookup<の結果である配列で処理されました。 / code>

    すべての詳細

    db.emailGroup.aggregate([
      { "$lookup": {
        "from": "link",
        "localField": "_id",
        "foreignField": "emailGroupId",
        "as": "link"    
      }},
      { "$unwind": "$link" },
      { "$match": { "link.originalLink": "" } },
      { "$group": {
        "_id": "$_id",
        "partId": { "$first": "$partId" },
        "link": { "$push": "$link" },
        "linkCount": {
          "$sum": {
            "$size": {
              "$ifNull": [ "$link.linkHistory", [] ]
            } 
          }   
        }
      }}
    ])
    

    生産物:

    {
        "_id" : ObjectId("594a6c47f51e075db713ccb6"),
        "partId" : "f56c7c71eb14a20e6129a667872f9c4f",
        "link" : [ 
            {
                "_id" : ObjectId("594b96d6f51e075db67c44c9"),
                "originalLink" : "",
                "emailGroupId" : ObjectId("594a6c47f51e075db713ccb6"),
                "linkHistory" : [ 
                    {
                        "_id" : ObjectId("594b96f5f51e075db713ccdf")
                    }, 
                    {
                        "_id" : ObjectId("594b971bf51e075db67c44ca")
                    }
                ]
            }
        ],
        "linkCount" : 2
    }
    

    partIdでグループ化

    db.emailGroup.aggregate([
      { "$lookup": {
        "from": "link",
        "localField": "_id",
        "foreignField": "emailGroupId",
        "as": "link"    
      }},
      { "$unwind": "$link" },
      { "$match": { "link.originalLink": "" } },
      { "$group": {
        "_id": "$partId",
        "linkCount": {
          "$sum": {
            "$size": {
              "$ifNull": [ "$link.linkHistory", [] ]
            } 
          }   
        }
      }}
    ])
    

    生産

    {
        "_id" : "f56c7c71eb14a20e6129a667872f9c4f",
        "linkCount" : 2
    }
    

    $ unwind 次に、 $ match これは、MongoDBがこの順序で発行されたときにパイプラインを実際に処理する方法が原因です。これは、 $ lookupに発生することです。 示されているように、 "explain" 操作からの出力:

        {
            "$lookup" : {
                "from" : "link",
                "as" : "link",
                "localField" : "_id",
                "foreignField" : "emailGroupId",
                "unwinding" : {
                    "preserveNullAndEmptyArrays" : false
                },
                "matching" : {
                    "originalLink" : {
                        "$eq" : ""
                    }
                }
            }
        }, 
        {
            "$group" : {
    

    $ groupでパーツを残します その出力では、他の2つのパイプラインステージが「消える」ことを示しています。これは、それらがに「ロールアップ」されているためです。 $ lookup 示されているパイプラインステージ。これは実際、MongoDBが $ lookup 親ドキュメントの配列に。

    または、次のように操作を記述できます。

    すべての詳細

    db.emailGroup.aggregate([
      { "$lookup": {
        "from": "link",
        "localField": "_id",
        "foreignField": "emailGroupId",
        "as": "link"    
      }},
      { "$addFields": {
        "link": {
          "$filter": {
            "input": "$link",
            "as": "l",
            "cond": { "$eq": [ "$$l.originalLink", "" ] }    
          }
        },
        "linkCount": {
          "$sum": {
            "$map": {
              "input": {
                "$filter": {
                  "input": "$link",
                  "as": "l",
                  "cond": { "$eq": [ "$$l.originalLink", "" ] }
                }
              },
              "as": "l",
              "in": { "$size": { "$ifNull": [ "$$l.linkHistory", [] ] } }
            }     
          }
        }    
      }}
    ])
    

    partIdでグループ化

    db.emailGroup.aggregate([
      { "$lookup": {
        "from": "link",
        "localField": "_id",
        "foreignField": "emailGroupId",
        "as": "link"    
      }},
      { "$addFields": {
        "link": {
          "$filter": {
            "input": "$link",
            "as": "l",
            "cond": { "$eq": [ "$$l.originalLink", "" ] }    
          }
        },
        "linkCount": {
          "$sum": {
            "$map": {
              "input": {
                "$filter": {
                  "input": "$link",
                  "as": "l",
                  "cond": { "$eq": [ "$$l.originalLink", "" ] }
                }
              },
              "as": "l",
              "in": { "$size": { "$ifNull": [ "$$l.linkHistory", [] ] } }
            }     
          }
        }    
      }},
      { "$unwind": "$link" },
      { "$group": {
        "_id": "$partId",
        "linkCount": { "$sum": "$linkCount" } 
      }}
    ])
    

    これは同じ出力ですが、という点で、最初のクエリとは「異なります」 $ filter ここでは、すべての「後に」適用されます $ lookupの結果 親ドキュメントの新しい配列に返されます。

    したがって、パフォーマンスの観点からは、実際には最初の方法で実行する方が効果的であり、「フィルタリング前」に16MBのBSON制限を超える可能性のある大きな結果セットに移植することもできます。

    興味のある方への補足として、MongoDBの将来のリリース(おそらく3.6以降)では、 $ replaceRoot $ addFieldsの代わりに 新しい$mergeObjectsを使用します パイプライン演算子。これの利点は「ブロック」として、「filtered」を宣言できることです。 $ letを介した変数としてのコンテンツ 、つまり、同じ $filterを記述する必要はありません。 「2回」:

    db.emailGroup.aggregate([
      { "$lookup": {
        "from": "link",
        "localField": "_id",
        "foreignField": "emailGroupId",
        "as": "link"    
      }},
      { "$replaceRoot": {
        "newRoot": {
          "$mergeObjects": [
            "$$ROOT",
            { "$let": {
              "vars": {
                "filtered": {
                  "$filter": {
                    "input": "$link",
                    "as": "l",
                    "cond": { "$eq": [ "$$l.originalLink", "" ] }    
                  }
                }
              },
              "in": {
                "link": "$$filtered",
                "linkCount": {
                  "$sum": {
                    "$map": {
                      "input": "$$filtered.linkHistory",
                      "as": "lh",
                      "in": { "$size": { "$ifNull": [ "$$lh", [] ] } } 
                    }   
                  } 
                }  
              }
            }}
          ]
        }
      }}
    ])
    

    それでも、このような「フィルタリングされた」 $lookup<を実行するための最良の方法/ code> 現時点では、 $unwind<を使用した操作は「まだ」です。 / code> 次に、 $ match パターン、$にクエリ引数を提供できるようになるまでルックアップ 直接。




    1. mongoDBから受け取ったjsonの二重引用符をトリミングします

    2. モンゴ:外部の重みで並べ替える方法

    3. MongoDBが日付を挿入すると、UTCに変換されます

    4. 文字列としての二重引用符の使用