MongoDB C# 驱动程序:嵌套查找 - 如何 "join" 嵌套关系?
MongoDB C# Driver: Nested Lookups - How do I "join" nested relations?
我有 3 个 MongoDB 个相互关联的集合:
- 公司
- 商店:一个公司可以有多个商店
- 产品:一个商店可以有多个产品
公司
{
"_id": { "$oid": "1388445c0000000000000001" },
"name": "Company A",
"stores": [
{ "$oid": "1388445c0000000000000011" },
{ "$oid": "1388445c0000000000000012" }
]
}
商店
{
"_id": { "$oid": "1388445c0000000000000011" },
"name": "Store A",
"products": [
{ "$oid": "1388445c0000000000000021" },
{ "$oid": "1388445c0000000000000022" },
{ "$oid": "1388445c0000000000000023" }
]
}
产品
{
"_id": { "$oid": "1388445c0000000000000021" },
"name": "Product A"
}
如果我使用 Lookup
来“连接”前两个集合,则 Store 的 ObjectId 将替换为 Store 集合中的相应对象:
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" }
]
},
...
]
}
但我正在努力“加入”嵌套商店中的产品。
首先我尝试了:
db.GetCollection<BsonDocument>("Company")
.Aggregate()
.Lookup("Store", "stores", "_id", "stores")
.Lookup("Product", "products", "_id", "products")
.ToList();
但显然,它并没有那么简单。因为字段 products
在公司中不存在,所以没有任何反应。
如果我尝试:
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"
},
...
]
}
}
然后产品被“加入”,但商店的所有其他字段都消失了。此外,字段 stores
不再是一个数组,而是一个对象。
如何使用 MongoDB C# 驱动程序正确设置聚合管道以“连接”3 个集合,以便收到以下结果:
{
"_id": { "$oid": "1388445c0000000000000001" },
"name": "Company A",
"stores": [
{
"_id": { "$oid": "1388445c0000000000000011" },
"name": "Store A",
"products": [
{
"_id": { "$oid": "1388445c0000000000000021" },
"name": "Product A"
},
...
]
}
]
}
旁注:
我正在使用 BsonDocument
而不是具体的 C# 类型。
我认为您应该使用嵌套 $lookup
管道实现如下:
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"
}
}
])
并使用 MongoDB Compass 将查询转换为 BsonDocument
。
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
感谢@Yong Shun 找到正确答案了
您还可以使用 MongoDB C# 类型构建查询,如下所示:
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();
我有 3 个 MongoDB 个相互关联的集合:
- 公司
- 商店:一个公司可以有多个商店
- 产品:一个商店可以有多个产品
公司
{
"_id": { "$oid": "1388445c0000000000000001" },
"name": "Company A",
"stores": [
{ "$oid": "1388445c0000000000000011" },
{ "$oid": "1388445c0000000000000012" }
]
}
商店
{
"_id": { "$oid": "1388445c0000000000000011" },
"name": "Store A",
"products": [
{ "$oid": "1388445c0000000000000021" },
{ "$oid": "1388445c0000000000000022" },
{ "$oid": "1388445c0000000000000023" }
]
}
产品
{
"_id": { "$oid": "1388445c0000000000000021" },
"name": "Product A"
}
如果我使用 Lookup
来“连接”前两个集合,则 Store 的 ObjectId 将替换为 Store 集合中的相应对象:
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" }
]
},
...
]
}
但我正在努力“加入”嵌套商店中的产品。
首先我尝试了:
db.GetCollection<BsonDocument>("Company")
.Aggregate()
.Lookup("Store", "stores", "_id", "stores")
.Lookup("Product", "products", "_id", "products")
.ToList();
但显然,它并没有那么简单。因为字段 products
在公司中不存在,所以没有任何反应。
如果我尝试:
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"
},
...
]
}
}
然后产品被“加入”,但商店的所有其他字段都消失了。此外,字段 stores
不再是一个数组,而是一个对象。
如何使用 MongoDB C# 驱动程序正确设置聚合管道以“连接”3 个集合,以便收到以下结果:
{
"_id": { "$oid": "1388445c0000000000000001" },
"name": "Company A",
"stores": [
{
"_id": { "$oid": "1388445c0000000000000011" },
"name": "Store A",
"products": [
{
"_id": { "$oid": "1388445c0000000000000021" },
"name": "Product A"
},
...
]
}
]
}
旁注:
我正在使用 BsonDocument
而不是具体的 C# 类型。
我认为您应该使用嵌套 $lookup
管道实现如下:
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"
}
}
])
并使用 MongoDB Compass 将查询转换为 BsonDocument
。
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
感谢@Yong Shun 找到正确答案了
您还可以使用 MongoDB C# 类型构建查询,如下所示:
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();