C# 驱动程序聚合查找具有另一个集合的嵌入式数组



我有两个MongoDB集合,客户和实例:

客户:

[
{
id: 1,
name: 'Customer Name',
projects: [
{
name: 'Project 1',
description: 'Project description',
instances: [10],
},
{
name: 'Project 2',
description: 'Project description',
instances: [10, 20],
},
],
},
]

实例

[
{
id: 10,
operatingSystem: 'Microsoft Windows 2012R2',
version: '3.1.5',
product: {
name: 'Product 1',
vendor: 'Vendor A',
},
},
{
id: 20,
operatingSystem: 'Microsoft Windows 2016',
version: '4.1.0',
product: {
name: 'Product 2',
vendor: 'Vendor B',
},
},
]

我正在尝试使用 C# LINQ 聚合$lookup集合 - 没有运气

当每个实例文档与第一个集合(客户(提供的实例 ID 匹配时,预期结果是单个客户(与 id 匹配,即"1"(具有实例(第二个集合(详细信息。项目实例(:

{
id: 1,
name: 'Customer Name',
projects: [
{
name: 'Project 1',
description: 'Project description',
instances: [
{
id: 10,
operatingSystem: 'Microsoft Windows 2012R2',
version: '3.1.5',
product: {
name: 'Product 1',
vendor: 'Vendor A',
},
},
],
},
{
name: 'Project 2',
description: 'Project description',
instances: [
{
id: 10,
operatingSystem: 'Microsoft Windows 2012R2',
version: '3.1.5',
product: {
name: 'Product 1',
vendor: 'Vendor A',
},
},
{
id: 20,
operatingSystem: 'Microsoft Windows 2016',
version: '4.1.0',
product: {
name: 'Product 2',
vendor: 'Vendor B',
},
},
],
},
],
}

感谢您帮助查找适当的 LINQ 查询以匹配/联接两个集合,以最有效的方式匹配预期结果(到数据库的最小往返次数(

编辑

我尝试过的代码:

public async ValueTask<dynamic> GetAsync(string customerId)
{
var customer = await _customers.Aggregate()
.Match(c => c.Id == customerId)
.Lookup("instances", "Projects.Instances", "_id", "Results")
.FirstOrDefaultAsync();

return customer;
}

另一件事要提:

  • Instances.Id 和 Customers.Projects.Instances[] 都是 ObjectId 类型
  • 我尝试在查询中使用非强类型的原因是因为我找不到使用 C# 对象匹配"Projects.Instances"的方法。

在 Mongo shell 上使用相同的语法返回结果(没有完全符合预期,至少不会抛出错误(

上面的查询返回一个异常:

无法将类型为"MongoDB.Bson.BsonObjectId"的对象强制转换为类型 'MongoDB.Bson.BsonBoolean'.

再说一次,我更喜欢使用 LINQ 语法,但不幸的是没有成功

更新

我设法运行了以下返回匹配实例的代码。 现在我只需要找到一种方法将其附加到客户对象,因为这是我真正想要返回的实体:

var results = _customers.AsQueryable()
.Where(c => c.Id == customerId)
.SelectMany(i => i.Projects)
.FirstOrDefault()
.Instances
.Join(_instances.AsQueryable(), a => a, b => b.Id, (a, b) => new { Instance = b })
.ToList();

结果如下:

[
{
instance: {
id: 10,
operatingSystem: 'Microsoft Windows 2012R2',
version: '3.1.5',
product: {
name: 'Product 1',
vendor: 'Vendor A',
},
},
},
{
instance: {
id: 20,
operatingSystem: 'Microsoft Windows 2016',
version: '4.1.0',
product: {
name: 'Product 2',
vendor: 'Vendor B',
},
},
},
]

谢谢!

花了几个小时后,以下代码(使用令人惊叹的MongoDB聚合框架(解决了我的问题:

public async ValueTask<dynamic> GetAsync(string customerId)
{
var unwind = new BsonDocument("$unwind",
new BsonDocument("path", "$projects"));
var lookup = new BsonDocument("$lookup",
new BsonDocument("from", "instances")
.Add("localField", "projects.instances")
.Add("foreignField", "_id")
.Add("as", "projects.instances"));
var group = new BsonDocument("$group",
new BsonDocument("_id", "$_id")
.Add("name", new BsonDocument("$first", "$name"))
.Add("projects", new BsonDocument("$push", "$$ROOT.projects"))
);
return await _customers.Aggregate()
.Match(c => c.Id == customerId)
.AppendStage<dynamic>(unwind)
.AppendStage<dynamic>(lookup)
.AppendStage<CustomerDetailsModel>(group)
.FirstOrDefaultAsync();
}

代码解释:

我首先将提供的 customerId 与"客户"集合进行匹配

然后我$unwind(扁平化(客户。 项目数组,以便我可以轻松$lookup每个匹配的实例

现在,我正在使用$lookup从实例集合中左外联接匹配的实例。此阶段的结果是 x(匹配实例数(文档,我需要将它们合并到单个文档中:

$group帮助我实现这一目标。

我弄清楚的方法是在我强烈推荐的官方MongoDB大学(https://university.mongodb.com/(学习M121课程。

如果您想过如何改进管道 - 我将很高兴知道

最新更新