2017-05-06 19 views
6

C#/ Linqまたは生のMongodbのいずれかを理解しようとすると、複数の配列をデカルト積としてどのように結合するかがわかります。複数のオブジェクト配列ドキュメントのMongodbデカルト積

セイは、例えば、私は、次の二つの文書に絞り込まコレクションを持っていた:

[ 
{"movie":"starwars","showday":"monday"}, 
{"movie":"batman","showday":"thursday"}, 
{"movie":"sleepless","showday":"tuesday"} 
] 

[ 
{"actor":"angelina","location":"new york"}, 
{"actor":"jamie","location":"california"}, 
{"actor":"mcavoy","location":"arizona"} 
] 

はどのようにして、結果の次のタイプを生成するために、各配列内の各アイテムに参加することができますか?

[{"movie":"starwars","showday":"monday","actor":"angelina","location":"new york"}, 
{"movie":"batman","showday":"thursday","actor":"angelina","location":"new york"}, 
{"movie":"sleepless","showday":"tuesday","actor":"angelina","location":"new york"}, 
{"movie":"starwars","showday":"monday","actor":"jamie","location":"california"}, 
{"movie":"batman","showday":"thursday","actor":"jamie","location":"california"}, 
{"movie":"sleepless","showday":"tuesday","actor":"jamie","location":"california"}, 
{"movie":"starwars","showday":"monday","actor":"mcavoy","location":"arizona"}, 
{"movie":"batman","showday":"thursday","actor":"mcavoy","location":"arizona"}, 
{"movie":"sleepless","showday":"tuesday","actor":"mcavoy","location":"arizona"}] 

私はいくつでもドキュメントを扱うことができるソリューションを探しています。たとえば、この例では、3つのオブジェクト配列を持つ3番目のドキュメントがあり、配列内に27個のアイテムの結果セットを生成します。つまり、27個の行はそのままです。

C#(Linq?)Mongodb Driverを使用してこのようなデータをクエリして返す方法を願っていますが、mongodb固有のクエリであっても、そこから論理を戻すことができます。ありがとうございます

+0

2つの別々のコレクションは、1つのコレクションを反復しますか?あなたが望むものと試したものをもっと説明できますか? – Yogesh

+0

実際に、あなたのモデルが{映画、俳優}ではなく映画の{劇場、俳優、俳優}ではないのに、なぜ実際に質問がありますか トピック外です。 – Munzer

+1

データには全く関係がないので、「データベース」自体はこれを行うことができません。 MongoDBは、RDBMSのようなものではなく、複数のソースから任意にデータを取り込み、関数出力を実行することができます。実際、すべての操作は実際には '$ lookup'などを除いて一度に1つのコレクションにしかありませんが、これらの演算子には関連するキーが必要です。これをコードで行うか、 '.eval()'を使ってサーバ上でコードを実行します。しかし、** DONT **は 'eval()'を使います。これはコーディングのためのものであり、データベースソリューションではありません。 –

答えて

3

以下の集約パイプラインを試すことができます。

mergeObjectsアグリゲーション演算子は3.5.6 +開発リリースで公開されます。これは今後リリースされる3.6リリースになります。

db.collection.find(); 
{ 
"data" : [ 
    [ 
    { 
    "movie" : "starwars", 
    "showday" : "monday" 
    }, 
    { 
    "movie" : "batman", 
    "showday" : "thursday" 
    }, 
    { 
    "movie" : "sleepless", 
    "showday" : "tuesday" 
    } 
    ], 
    [ 
    { 
    "actor" : "angelina", 
    "location" : "new york" 
    }, 
    { 
    "actor" : "jamie", 
    "location" : "california" 
    }, 
    { 
    "actor" : "mcavoy", 
    "location" : "arizona" 
    } 
    ] 
] 
} 

条件式を使用した集約。

aggregate({ 
$project: { 
    cp: { 
    $reduce: { 
    input: "$data", 
    initialValue: { 
    $arrayElemAt: ["$data", 0] // Set the initial value to the first element of the arrays. 
    }, 
    in: { 
    $let: { 
     vars: { 
     currentr: "$$this", // Current processing element 
     currenta: "$$value" // Current accumulated value 
     }, 
     in: { 
     $cond: [{ // Conditional expression to return the accumulated value as initial value for first element 
     $eq: ["$$currentr", "$$currenta"] 
     }, 
     "$$currenta", 
     { // From second element onwards prepare the cartesian product 
     $reduce: { 
     input: { 
      $map: { 
      input: "$$currenta", 
      as: a"a", 
      in: { 
      $map: { 
      input: "$$currentr", 
      as: r"r", 
      in: { 
       $mergeObjects: ["$$a", "$$r"] // Merge accumulated value with the current processing element 
      } 
      } 
      } 
      } 
     }, 
     initialValue: [], 
     in: { 
     $concatArrays: ["$$value", "$$this"] // Reduce the merged values which will be used as accumulator for next element 
     } 
     } 
     }] 
     } 
    } 
    } 
    } 
    } 
} 
}); 

集約($setUnionを使用)。

このソリューションは、条件式を抑制してより読みやすいパイプラインを提供するために追加されました。

aggregate({ 
$project: { 
    cp: { 
    $reduce: { 
    input: "$data", 
    initialValue: { 
    $arrayElemAt: ["$data", 0] // Set the initial value to the first element of the arrays. 
    }, 
    in: { 
    $let: { 
     vars: { 
     currentr: "$$this", // Current processing element 
     currenta: "$$value" // Current accumulated value 
     }, 
     in:{ 
     $reduce: { 
     input: { 
     $map: { 
      input: "$$currenta", 
      as: "a", 
      in: { 
      $map: { 
      input: "$$currentr", 
      as: "r", 
      in: { 
      $mergeObjects: ["$$a", "$$r"] // Merge accumulated value with the current processing element 
      } 
      } 
      } 
     } 
     }, 
     initialValue: [], 
     in: { 
     $setUnion: ["$$value", "$$this"] // Reduce the merged values which will be used as accumulator for next element 
     } 
     } 
     } 
    } 
    } 
    } 
    } 
} 
}); 

更新

両方の上記溶液が原因で第二の溶液中の誤った最初の溶液中$cond$setUnionの下アスヤKamskyのコメントによって示されるようにアレイ間の値を反復して動作しません。

正しい修正がinput: {$slice:["$data", 1, {$subtract:[{$size:"$data"},1]}]},

完全集約パイプラインのような最初の要素を除外するよう[ { } ]

それとも

変更inputinitialValue

先頭にある

aggregate({ 
$project: { 
    cp: { 
    $reduce: { 
    input: {$slice:["$data", 1, {$subtract:[{$size:"$data"},1]}]}, 
    initialValue: {$arrayElemAt:["$data",0]}, 
    in: { 
    $let: { 
     vars: { 
     currentr: "$$this", 
     currenta: "$$value" 
     }, 
     in:{ 
     $reduce: { 
     input: { 
     $map: { 
      input: "$$currenta", 
      as: "a", 
      in: { 
      $map: { 
      input: "$$currentr", 
      as: "r", 
      in: { 
      $mergeObjects: ["$$a", "$$r"] 
      } 
      } 
      } 
     } 
     }, 
     initialValue: [], 
     in: { 
     $concatArrays: ["$$value", "$$this"] 
     } 
     } 
     } 
    } 
    } 
    } 
    } 
} 
}); 

参照: Cartesian product of multiple arrays in JavaScript

+0

フィールドのいずれかが配列全体に繰り返し値を持つ場合、これは機能しません。 –

+0

@AsyaKamskyはい、100%正しいです。私はそれを認識していましたが、条件式に代わるものを見つけることができませんでした。以前のリビジョンも追加しましたが、正しいと思います。 – Veeram

+0

いいえ、要素が繰り返されていると正しく動作しません。 –

関連する問題