c# - 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 集合 - 没有运气
当每个实例文档与第一个集合 (customers.Projects.Instances) 提供的实例 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 (flatten) 客户。项目数组,所以我可以轻松地 $lookup 每个匹配的实例
现在我正在使用 $lookup 从实例集合中左外连接匹配的实例。此阶段的结果是 x(匹配实例数)文档,我需要将它们合并到单个文档中:
$group 帮助我实现了这一目标。
我想办法是通过我强烈推荐的官方 MongoDB 大学 ( https://university.mongodb.com/ ) 中的 M121 课程。
如果您考虑过如何改进管道 - 我会很高兴知道
推荐阅读
- java - 使用 selenium 和 cucumber 测试 java 类
- microsoft-graph-api - 未收到 MS Teams 的任何更改通知
- firebase - 如何等待 init 方法中的函数?
- express - Express 电子邮件服务器 Gatsby
- apache-spark - to_date 无法在 Spark 3.0 中解析日期
- c++ - 在我定义的类中释放分配的缓冲内存的问题
- vb.net - 尝试在 DataGridView 上添加新行时出现 System.InvalidOperationException
- swift - 致命错误:SwiftUI 中的索引超出范围
- c# - 在某些尺寸上生成动态图像失败
- amazon-web-services - 没有地址的 EKS 入口 - 尝试测试我的 EKS 集群