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

$lookup後の集約フィルター

    ここでの質問は、実際には別のことに関するものであり、$lookupは必要ありません。 まったく。しかし、純粋に「$ lookup後のフィルタリング」というタイトルからここに到着する人にとって、これらはあなたのためのテクニックです:

    MongoDB3.6-サブパイプライン

    db.test.aggregate([
        { "$match": { "id": 100 } },
        { "$lookup": {
          "from": "test",
          "let": { "id": "$id" },
          "pipeline": [
            { "$match": {
              "value": "1",
              "$expr": { "$in": [ "$$id", "$contain" ] }
            }}
          ],
          "as": "childs"
        }}
    ])
    

    以前-$lookup+ $ unwind +$match合体

    db.test.aggregate([
        { "$match": { "id": 100 } },
        { "$lookup": {
            "from": "test",
            "localField": "id",
            "foreignField": "contain",
            "as": "childs"
        }},
        { "$unwind": "$childs" },
        { "$match": { "childs.value": "1" } },
        { "$group": {
            "_id": "$_id",
            "id": { "$first": "$id" },
            "value": { "$first": "$value" },
            "contain": { "$first": "$contain" },
            "childs": { "$push": "$childs" }
         }}
    ])
    

    質問がある場合は、なぜ$unwind $filterを使用するのとは対照的に アレイで、Aggregate $ lookupを読み取ります。一致するパイプライン内のドキュメントの合計サイズが、これが一般的に必要であり、はるかに最適である理由のすべての詳細について、最大ドキュメントサイズを超えています。

    MongoDB 3.6以降のリリースでは、より表現力豊かな「サブパイプライン」は、通常、配列に何かが返される前に、外部コレクションの結果を「フィルタリング」したいものです。

    答えに戻ると、質問された質問に「参加しない」必要がある理由が実際に説明されています。...

    オリジナル

    $lookupを使用する このように、ここでやりたいことを行うための最も「効率的な」方法ではありません。しかし、これについては後で詳しく説明します。

    基本的な概念として、$filterを使用するだけです。 結果の配列:

    db.test.aggregate([ 
        { "$match": { "id": 100 } }, 
        { "$lookup": {
            "from": "test",
            "localField": "id",
            "foreignField": "contain",
            "as": "childs"
        }},
        { "$project": {
            "id": 1,
            "value": 1,
            "contain": 1,
            "childs": {
               "$filter": {
                   "input": "$childs",
                   "as": "child",
                   "cond": { "$eq": [ "$$child.value", "1" ] }
               }
            }
        }}
    ]);
    

    または、$redactを使用します 代わりに:

    db.test.aggregate([ 
        { "$match": { "id": 100 } }, 
        { "$lookup": {
            "from": "test",
            "localField": "id",
            "foreignField": "contain",
            "as": "childs"
        }},
        { "$redact": {
            "$cond": {
               "if": {
                  "$or": [
                    { "$eq": [ "$value", "0" ] },
                    { "$eq": [ "$value", "1" ] }
                  ]
               },
               "then": "$$DESCEND",
               "else": "$$PRUNE"
            }
        }}
    ]);
    

    どちらも同じ結果になります:

    {  
      "_id":ObjectId("570557d4094a4514fc1291d6"),
      "id":100,
      "value":"0",
      "contain":[ ],
      "childs":[ {  
          "_id":ObjectId("570557d4094a4514fc1291d7"),
          "id":110,
          "value":"1",
          "contain":[ 100 ]
        },
        {  
          "_id":ObjectId("570557d4094a4514fc1291d8"),
          "id":120,
          "value":"1",
          "contain":[ 100 ]
        }
      ]
    }
    

    つまり、$lookup それ自体は、特定のデータのみを選択するための「まだ」クエリを実行することはできません。したがって、すべての「フィルタリング」は$lookupの後に行う必要があります。

    しかし、実際には、このタイプの「自己参加」には、$lookupを使用しない方がよいでしょう。 まったく、追加の読み取りと「ハッシュマージ」のオーバーヘッドを完全に回避します。関連アイテムと$groupを取得するだけです 代わりに:

    db.test.aggregate([
      { "$match": { 
        "$or": [
          { "id": 100 },
          { "contain.0": 100, "value": "1" }
        ]
      }},
      { "$group": {
        "_id": {
          "$cond": {
            "if": { "$eq": [ "$value", "0" ] },
            "then": "$id",
            "else": { "$arrayElemAt": [ "$contain", 0 ] }
          }
        },
        "value": { "$first": { "$literal": "0"} },
        "childs": {
          "$push": {
            "$cond": {
              "if": { "$ne": [ "$value", "0" ] },
              "then": "$$ROOT",
              "else": null
            }
          }
        }
      }},
      { "$project": {
        "value": 1,
        "childs": {
          "$filter": {
            "input": "$childs",
            "as": "child",
            "cond": { "$ne": [ "$$child", null ] }
          }
        }
      }}
    ])
    

    余分なフィールドを意図的に削除したので、少し違うだけです。本当に必要な場合は、自分で追加してください:

    {
      "_id" : 100,
      "value" : "0",
      "childs" : [
        {
          "_id" : ObjectId("570557d4094a4514fc1291d7"),
          "id" : 110,
          "value" : "1",
          "contain" : [ 100 ]
        },
        {
          "_id" : ObjectId("570557d4094a4514fc1291d8"),
          "id" : 120,
          "value" : "1",
          "contain" : [ 100 ]
        }
      ]
    }
    

    したがって、ここでの唯一の実際の問題は、nullを「フィルタリング」することです。 現在のドキュメントがparentのときに作成された配列の結果 $pushへのアイテムの処理 。

    また、ここで欠落しているように見えるのは、探している結果に集計や「サブクエリ」がまったく必要ないということです。結論を出した、または他の場所で見つけた可能性のある構造は、1回のクエリリクエストで「ノード」とそのすべての「子」を取得できるように「設計」されています。

    つまり、実際に必要なのは「クエリ」だけであり、データ収集(コンテンツが実際に「削減」されていないために行われているのはこれだけです)は、カーソルの結果を繰り返す機能にすぎません。

    var result = {};
    
    db.test.find({
      "$or": [
        { "id": 100 },
        { "contain.0": 100, "value": "1" }
      ]
    }).sort({ "contain.0": 1 }).forEach(function(doc) {
      if ( doc.id == 100 ) {
        result = doc;
        result.childs = []
      } else {
        result.childs.push(doc)
      }
    })
    
    printjson(result);
    

    これはまったく同じことをします:

    {
      "_id" : ObjectId("570557d4094a4514fc1291d6"),
      "id" : 100,
      "value" : "0",
      "contain" : [ ],
      "childs" : [
        {
          "_id" : ObjectId("570557d4094a4514fc1291d7"),
          "id" : 110,
          "value" : "1",
          "contain" : [
                  100
          ]
        },
        {
          "_id" : ObjectId("570557d4094a4514fc1291d8"),
          "id" : 120,
          "value" : "1",
          "contain" : [
                  100
          ]
        }
      ]
    }
    

    そして、ここで本当に行う必要があるのは、親と子の両方を選択するための「単一」クエリを発行することだけであるという証拠として機能します。返されるデータはまったく同じであり、サーバーまたはクライアントのいずれかで行っているのは、収集された別の形式に「マッサージ」することだけです。

    これは、「リレーショナル」データベースでの作業方法を考えると「巻き込まれ」、データの保存方法が「変更」されたため、使用する必要がなくなったことに気付かない場合の1つです。同じアプローチ。

    これが、ドキュメントの例「子参照を使用したモデルツリー構造」のポイントです。 その構造では、1つのクエリ内で親と子を簡単に選択できます。




    1. StackExchange.Redisを使用したRedisへのSSL接続

    2. mongoimportを使用して、csvデータをmongodbの配列としてインポートします

    3. だからあなたのHBaseは壊れています

    4. redisテンプレートを使用してRedisからすべてのキーを取得する方法