Trying to understand with either C#/Linq or even raw Mongodb query itself how to join multiple arrays as a cartesian product.
Say for example I had a collection that I filtered down to the following two documents:
[
{"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"}
]
How can I join each item in each array to produce the following type of result?
[{"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"}]
I am looking for a solution that could work with any number of documents. So for example if in this example there was a 3rd document that also had 3 object arrays that would produce a result set of 27 items in the array - or 27 rows as it were.
Hoping for a solution of how to use C# (Linq?) Mongodb Driver to query and return data like this but would be open to even a mongodb specific query as I can hopefully reverse the logic from there. thank you
C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...
In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.
C is a general-purpose language that most programmers learn before moving on to more complex languages. From Unix and Windows to Tic Tac Toe and Photoshop, several of the most commonly used applications today have been built on C. It is easy to learn because: A simple syntax with only 32 keywords.
Compared to C, C++ has significantly more libraries and functions to use. If you're working with complex software, C++ is a better fit because you have more libraries to rely on. Thinking practically, having knowledge of C++ is often a requirement for a variety of programming roles.
You can try below aggregation pipeline.
Note mergeObjects aggregation operator is available in the 3.5.6 +
development release which will be rolled into upcoming 3.6
release.
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"
}
]
]
}
Aggregation using conditional expression.
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
}
}
}]
}
}
}
}
}
}
});
Aggregation( using $setUnion
).
This solution was only added to suppress the conditional expression to give more readable pipeline.
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
}
}
}
}
}
}
}
}
});
Update
Both the above solutions will not work with repeating values across arrays as noted by Asya Kamsky's comments below because of the incorrect $cond
in the first solution and $setUnion
in second solution.
The correct fix is to
start with initialValue
of [ { } ]
Or
change input
to exclude the first element like input: {$slice:["$data", 1, {$subtract:[{$size:"$data"},1]}]},
Complete aggregation pipeline
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"]
}
}
}
}
}
}
}
}
});
Reference: Cartesian product of multiple arrays in JavaScript
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With