尝试使用 C#/Linq 甚至原始 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 驱动程序来查询和返回这样的数据的解决方案,但即使是特定于 mongodb 的查询也将开放,因为我希望可以从那里反转逻辑。谢谢
最佳答案
您可以尝试以下聚合管道。
注意 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
和第二个解决方案。
正确的解决方法是
从 [ { } ]
的 initialValue
开始
或者
更改 input
以排除第一个元素,例如 input: {$slice:["$data", 1, {$subtract:[{$size:"$data"}, 1]}]},
完整的聚合管道
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"]
}
}
}
}
}
}
}
}
});
关于C# Mongodb 多对象数组文档的笛卡尔积,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43819186/