Reputation: 351
I have 3 MongoDB collections that are related to each other:
Company
{
"_id": { "$oid": "1388445c0000000000000001" },
"name": "Company A",
"stores": [
{ "$oid": "1388445c0000000000000011" },
{ "$oid": "1388445c0000000000000012" }
]
}
Store
{
"_id": { "$oid": "1388445c0000000000000011" },
"name": "Store A",
"products": [
{ "$oid": "1388445c0000000000000021" },
{ "$oid": "1388445c0000000000000022" },
{ "$oid": "1388445c0000000000000023" }
]
}
Product
{
"_id": { "$oid": "1388445c0000000000000021" },
"name": "Product A"
}
If I use Lookup
to "join" the first two collections, then the ObjectIds of the Stores are replaced with their corresponding objects from the Store collection:
db.GetCollection<BsonDocument>("Company")
.Aggregate()
.Lookup("Store", "stores", "_id", "stores")
.ToList();
{
"_id": { "$oid": "1388445c0000000000000001" },
"name": "Company A",
"stores": [
{
"_id": { "$oid": "1388445c0000000000000011" },
"name": "Store A",
"products": [
{ "$oid": "1388445c0000000000000021" },
{ "$oid": "1388445c0000000000000022" },
{ "$oid": "1388445c0000000000000023" }
]
},
...
]
}
But I'm struggling to "join" the Products on the nested Stores.
First I tried:
db.GetCollection<BsonDocument>("Company")
.Aggregate()
.Lookup("Store", "stores", "_id", "stores")
.Lookup("Product", "products", "_id", "products")
.ToList();
but obviously, it doesn't work as simple as that. Because the field products
doesn't exist on Company, nothing happens.
If I try:
db.GetCollection<BsonDocument>("Company")
.Aggregate()
.Lookup("Store", "stores", "_id", "stores")
.Lookup("Product", "stores.products", "_id", "stores.products")
.ToList();
{
"_id": { "$oid": "1388445c0000000000000001" },
"name": "Company A",
"stores": {
"products": [
{
"_id": { "$oid": "1388445c0000000000000021" },
"name": "Product A"
},
...
]
}
}
then the products are "joined", but all other fields of the Store are gone. Furthermore the field stores
is not an array anymore, but an object.
How do I correctly setup the aggregate pipeline with the MongoDB C# Driver to get the 3 collections "joined" so that I receive the following result:
{
"_id": { "$oid": "1388445c0000000000000001" },
"name": "Company A",
"stores": [
{
"_id": { "$oid": "1388445c0000000000000011" },
"name": "Store A",
"products": [
{
"_id": { "$oid": "1388445c0000000000000021" },
"name": "Product A"
},
...
]
}
]
}
Side note:
I'm working with BsonDocument
and not a concrete C# type.
Upvotes: 0
Views: 1321
Reputation: 351
Thanx to @Yong Shun I have found the correct answer.
You can build the query also with MongoDB C# types as follows:
PipelineStageDefinition<BsonDocument, BsonDocument> stage = PipelineStageDefinitionBuilder.Lookup<BsonDocument, BsonDocument, BsonDocument, IEnumerable<BsonDocument>, BsonDocument>(
db.GetCollection<BsonDocument>("Store"),
new BsonDocument("stores", new BsonDocument("$ifNull", new BsonArray { "$stores", new BsonArray() })),
new PipelineStagePipelineDefinition<BsonDocument, BsonDocument>(new List<PipelineStageDefinition<BsonDocument, BsonDocument>>
{
PipelineStageDefinitionBuilder.Match(new BsonDocumentFilterDefinition<BsonDocument>(new BsonDocument("$expr", new BsonDocument("$in", new BsonArray { "$_id", "$$stores" })))),
PipelineStageDefinitionBuilder.Lookup<BsonDocument, BsonDocument, BsonDocument, IEnumerable<BsonDocument>, BsonDocument>(
db.GetCollection<BsonDocument>("Product"),
new BsonDocument("products", new BsonDocument("$ifNull", new BsonArray { "$products", new BsonArray() })),
new PipelineStagePipelineDefinition<BsonDocument, BsonDocument>(new List<PipelineStageDefinition<BsonDocument, BsonDocument>>
{
PipelineStageDefinitionBuilder.Match(new BsonDocumentFilterDefinition<BsonDocument>(new BsonDocument("$expr", new BsonDocument("$in", new BsonArray { "$_id", "$$products" })))),
}),
"products"
)
}),
"stores"
);
List<BsonDocument> result = db.GetCollection<BsonDocument>("Entity").Aggregate().AppendStage(stage).ToList();
Upvotes: 2
Reputation: 51195
I think you should achieve with nested $lookup
pipeline as below:
db.Company.aggregate([
{
"$lookup": {
"from": "Store",
"let": {
stores: "$stores"
},
"pipeline": [
{
$match: {
$expr: {
$in: [
"$_id",
"$$stores"
]
}
}
},
{
$lookup: {
"from": "Product",
let: {
products: { products: { $ifNull: [ "$products", [] ] } }
},
pipeline: [
{
$match: {
$expr: {
$in: [
"$_id",
"$$products"
]
}
}
}
],
as: "products"
}
}
],
"as": "stores"
}
}
])
And convert the query to BsonDocument
with MongoDB Compass.
var pipeline = new[]
{
new BsonDocument("$lookup",
new BsonDocument
{
{ "from", "Store" },
{ "let",
new BsonDocument("stores", "$stores")
},
{ "pipeline",
new BsonArray
{
new BsonDocument("$match",
new BsonDocument("$expr",
new BsonDocument("$in",
new BsonArray
{
"$_id",
"$$stores"
}
)
)
),
new BsonDocument("$lookup",
new BsonDocument
{
{ "from", "Product" },
{ "let",
new BsonDocument("products",
new BsonDocument("$ifNull",
new BsonArray
{
"$products",
new BsonArray()
}
)
)
},
{ "pipeline",
new BsonArray
{
new BsonDocument("$match",
new BsonDocument("$expr",
new BsonDocument("$in",
new BsonArray
{
"$_id",
"$$products"
}
)
)
)
}
},
{ "as", "products" }
}
)
}
},
{ "as", "stores" }
}
)
};
var result = _db.GetCollection<BsonDocument>("Company")
.Aggregate<BsonDocument>(pipeline)
.ToList();
Result
Upvotes: 1