web-dev-qa-db-ja.com

Mongo、DBRefで$ lookupする方法

問題があります(/(ㄒoㄒ)/ ~~)。コレクションAが

{ 
    "_id" : ObjectId("582abcd85d2dfa67f44127e1"), 
    "bid" : [
        DBRef("B", ObjectId("582abcd85d2dfa67f44127e0")),
        DBRef("B", ObjectId("582abcd85d2dfa67f44127e1"))
    ]
}


およびコレクションB:

{ 
    "_id" : ObjectId("582abcd85d2dfa67f44127e0"),  
    "status" : NumberInt(1), 
    "seq" : NumberInt(0)
},
{ 
    "_id" : ObjectId("582abcd85d2dfa67f44127e1"), 
    "status" : NumberInt(1), 
    "seq" : NumberInt(0)
} 


「入札」を$検索する方法がわかりません。私は試した

db.A.aggregate(
    [
        {$unwind: {path: "$bid"}},
        {$lookup: {from: "B", localField: "bid", foreignField: "_id", as: "bs"}},
    ]
) 


そして

db.A.aggregate(
    [
        {$unwind: {path: "$bid"}},
        {$lookup: {from: "B", localField: "bid.$id", foreignField: "_id", as: "bs"}},
    ]
)


しかし、それは機能しません。誰でも手伝ってくれる?ありがとう。

23
Latch Shun

実際、他の答えは間違っています。アグリゲーター内のDBrefフィールドでルックアップを実行することは可能であり、そのためにmapreduceは必要ありません。

解決

db.A.aggregate([
{
    $project: { 
        B_fk: {
          $map: { 
             input: { 
                  $map: {
                      input:"$bid",
                      in: {
                           $arrayElemAt: [{$objectToArray: "$$this"}, 1]
                      },
                  }
             },
             in: "$$this.v"}},
        }
}, 
{
    $lookup: {
        from:"B", 
        localField:"B_fk",
        foreignField:"_id", 
        as:"B"
    }
])

結果

{
    "_id" : ObjectId("59bb79df1e9c00162566f581"),
    "B_fk" : null,
    "B" : [ ]
},
{
    "_id" : ObjectId("582abcd85d2dfa67f44127e1"),
    "B_fk" : [
        ObjectId("582abcd85d2dfa67f44127e0"),
        ObjectId("582abcd85d2dfa67f44127e1")
    ],
    "B" : [
        {
            "_id" : ObjectId("582abcd85d2dfa67f44127e0"),
            "status" : NumberInt("1"),
            "seq" : NumberInt("0")
        }
    ]
}

簡単な説明

$ mapを使用してDBRefをループし、各DBrefを配列に分割し、$ idフィールドのみを保持し、$$ this.vを使用してk:v形式を削除し、ObjectIdのみを保持して残りをすべて削除します。 ObjectIdを検索できるようになりました。

ステップバイステップの説明

アグリゲーター内では、DBRef BSONタイプは、2つまたは3つのフィールド(ref、id、およびdb)を持つオブジェクトのように処理できます。

もしあなたがそうするなら:

db.A.aggregate([
    {
        $project: { 
            First_DBref_as_array: {$objectToArray:{$arrayElemAt:["$bid",0]}},
            Second_DBref_as_array: {$objectToArray:{$arrayElemAt:["$bid",1]}},
            }

    },

])

これが結果です:

{
"_id" : ObjectId("582abcd85d2dfa67f44127e1"),
"First_DBref_as_array : [
    {
        "k" : "$ref",
        "v" : "B"
    },
    {
        "k" : "$id",
        "v" : ObjectId("582abcd85d2dfa67f44127e0")
    }
],
"Second_DBref_as_array" : [
    {
        "k" : "$ref",
        "v" : "B"
    },
    {
        "k" : "$id",
        "v" : ObjectId("582abcd85d2dfa67f44127e0")
    }
]
}

Dbrefを配列に変換したら、次のように、インデックス1の値のみをクエリすることで、不要なフィールドを取り除くことができます。

db.A.aggregate([
    {
        $project: { 
            First_DBref_as_array: {$arrayElemAt: [{$objectToArray:{$arrayElemAt:["$bid",0]}},1]},
            Second_DBref_as_array: {$arrayElemAt: [{$objectToArray:{$arrayElemAt:["$bid",0]}},1]},
            }

    },

])

結果:

{
    "_id" : ObjectId("582abcd85d2dfa67f44127e1"),
    "First_DBref_as_array" : {
        "k" : "$id",
        "v" : ObjectId("582abcd85d2dfa67f44127e0")
    },
    "Second_DBref_as_array" : {
        "k" : "$id",
        "v" : ObjectId("582abcd85d2dfa67f44127e0")
    }
}

次に、次のように、「$ myvalue.v」をポイントすることで、最終的に必要な値に到達できます。

db.A.aggregate([
    {
        $project: { 
            first_DBref_as_array: {$arrayElemAt: [{$objectToArray:{$arrayElemAt:["$bid",0]}},1]},
            second_DBref_as_array: {$arrayElemAt: [{$objectToArray:{$arrayElemAt:["$bid",0]}},1]},
            }

    },
    {
        $project: {
            first_DBref_as_ObjectId: "$first_DBref_as_array.v",
            second_DBref_as_ObjectId: "$second_DBref_as_array.v"
        }
    }

])

結果:

{
    "_id" : ObjectId("582abcd85d2dfa67f44127e1"),
    "first_DBref_as_ObjectId" : ObjectId("582abcd85d2dfa67f44127e0"),
    "second_DBref_as_ObjectId" : ObjectId("582abcd85d2dfa67f44127e0")
}

明らかに、通常のパイプラインでは、ネストされた$ mapを使用してこれらの冗長な手順をすべて行う必要はなく、一度に同じ結果を得ることができます。

db.A.aggregate([
    {
        $project: { 
            B_fk: { $map : {input: { $map: {    input:"$bid",
                                    in: { $arrayElemAt: [{$objectToArray: "$$this"}, 1 ]}, } },
                            in: "$$this.v"}},

            }
    }, 

])

結果:

{
    "_id" : ObjectId("582abcd85d2dfa67f44127e1"),
    "B_fk" : [
        ObjectId("582abcd85d2dfa67f44127e0"),
        ObjectId("582abcd85d2dfa67f44127e1")
    ]
}

お気軽にお尋ねください。

20
Olivier Maurel

MongoDB 3.4以降、これは不可能です$ match ステージを除いて、DBRefを集計パイプラインで使用することはできません。

DBRefを削除して、手動参照に切り替えることを強くお勧めします。ただし、本当にDBRefを維持する必要がある場合は、(醜い)解決策を次に示します。

最初に、「C」という名前の新しいコレクションを作成します。ここで、DBRefはmapReduceを使用してIDに置き換えられます。

db.A.mapReduce(
    function() {
        var key = this._id; 
        var value = [];  
        for ( var index = 0; index < this.bid.length; index++){
           value.Push(this.bid[index].$id); 
        }
        emit(key, value); 
    },
    function(key,values) {
        return  values;
    },
    {
        "query": {},
        "out": "C" 
    }
)

次に、新しい「C」コレクションで集計クエリを実行します。

db.C.aggregate([
   {
      $unwind:"$value"
   },
   {
      $lookup:{
         from:"B",
         localField:"value",
         foreignField:"_id",
         as:"bs"
      }
   }
]);

出力:

    {
       "_id":ObjectId("582abcd85d2dfa67f44127e1"),
       "value":ObjectId("582abcd85d2dfa67f44127e0"),
       "bs":[
          {
             "_id":ObjectId("582abcd85d2dfa67f44127e0"),
             "status":1,
             "seq":0
          }
       ]
    }{
       "_id":ObjectId("582abcd85d2dfa67f44127e1"),
       "value":ObjectId("582abcd85d2dfa67f44127e1"),
       "bs":[
          {
             "_id":ObjectId("582abcd85d2dfa67f44127e1"),
             "status":1,
             "seq":0
          }
       ]
    }
7
felix