11

集合操作の特別な目的の実装に出くわしましたが、一般的なケースでは何もありません。集合演算を実行する一般的なケースは何ですか (具体的には、交差、結合、対称差)。これは、$where または map reduce で JavaScript を使用すると簡単に理解できますが、ネイティブ パフォーマンスを得るために集約でこれを行う方法を知りたいです。

この質問を説明するより良い方法は、例を使用することです。2 つの配列/セットを持つレコードがあるとします。

db.colors.insert({
    _id: 1,
    left : ['red', 'green'],
    right : ['green', 'blue']
});

「左」配列と「右」配列の和、交点、差を見つけたい。さらに良いことに、私は見つけたいと思っています:

ユニオン--> ['red', 'green', 'blue']

連合

交差点--> ['緑']

ここに画像の説明を入力

対称差--> ['red', 'blue']

ここに画像の説明を入力

4

2 に答える 2

3

集約を使用するこれら 3 つの中で最も簡単なのは、交差 ** です。その一般的なケースは、次のような集計を使用して実行できます。

交差点:

db.colors.aggregate([
    {'$unwind' : "$left"},
    {'$unwind' : "$right"},
    {'$project': {  
                    value:"$left", 
                    same:{$cond:[{$eq:["$left","$right"]}, 1, 0]}
                 }
    },
    {'$group'  : { 
                    _id: {id:'$_id', val:'$value'}, 
                    doesMatch:{$max:"$same"}
                 }
    },
    {'$match'   :{doesMatch:1}},
]);

残りの 2 つはもう少しトリッキーになります。私の知る限り、同じドキュメント内の 2 つの別々のフィールドを組み合わせる方法はありません。$project パイプライン フェーズに $add、$combine、または $addToSet があると便利ですが、これは存在しません。したがって、私たちができる最善のことは、何かが交差したかどうかを言うことです. 両方の集計を次のように開始できます。

db.colors.aggregate([
    {'$unwind' : "$left"},
    {'$unwind' : "$right"},
    {'$project': {  
                    left:"$left",
                    right:'$right',
                    same:{$cond:[{$eq:["$left","$right"]}, 1, 0]}
                 }
    },
    {'$group'  : {
                    _id:{id:'$_id', left:'$left'},
                    right:{'$addToSet':'$right'},
                    sum: {'$sum':'$same'},
                 }
    },
    {'$project': {  
                    left:{val:"$_id.left",inter:"$sum"},
                    right:'$right',
                 }
    },
    {'$unwind' : "$right"},
    {'$project': {  
                    left:"$left",
                    right:'$right',
                    same:{$cond:[{$eq:["$left.val","$right"]}, 1, 0]}
                 }
    },
    {'$group'  : {
                    _id:{id:'$_id.id', right:'$right'},
                    left:{'$addToSet':'$left'},
                    sum: {'$sum':'$same'},
                 }
    },
    {'$project': {  
                    right:{val:"$_id.right",inter:"$sum"},
                    left:'$left',
                 }
    },
    {'$unwind' : "$left"},
    {'$group'  : {
                    _id:'$_id.id',
                    left:{'$addToSet':'$left'},
                    right: {'$addToSet':'$right'},
                 }
    },
]);

質問で提供されたサンプルを集計すると、次のような結果が得られます。

{
        "_id" : 1,
        "left" : [
                {
                        "val" : "green",
                        "inter" : 1
                },
                {
                        "val" : "red",
                        "inter" : 0
                }
        ],
        "right" : [
                {
                        "val" : "blue",
                        "inter" : 0
                },
                {
                        "val" : "green",
                        "inter" : 1
                }
        ]
}

ここから、以下を集計に追加することで交差を取得できます。

{'$project': {  
                    left:"$left"
                 }
    },
    {'$unwind' : "$left"},
    {'$match'  : {'left.inter': 1}},
    {'$group'  : {
                    _id:'$_id',
                    left:{'$addToSet':'$left'},
                 }
    },

ベース集約の最後に以下を追加することで、違いと相対的な補数を見つけることができます。

ここに画像の説明を入力

{'$unwind' : "$left"},
    {'$match'  : {'left.inter': 0}},
    {'$unwind' : "$right"},
    {'$match'  : {'right.inter': 0}},
    {'$group'  : {
                    _id:'$_id',
                    left:{'$addToSet':'$left'},
                    right:{'$addToSet':'$right'},
                 }
    },

残念ながら、異なる分野の異なるアイテムを組み合わせる良い方法はないようです。ユニオンを得るには、クライアントからそれを行うのが最善のようです。または、フィルタリングが必要な場合は、各セットで個別に行います。

于 2013-06-24T05:52:46.723 に答える