我有以下模型:Thre是incidents
。每个incident
都有time frames
,而time frame
每个objects
都有time frame
。每次事件都可以有数千的帧。
Incident
{
Id: "incident 1"
IncidetntFrames: [
{
Objects: [
{
id: 1
},
{
id: 2
}
]
},
{
Objects: [
{
id: 1
},
{
id: 3
}
]
}
]
}
我将事件存储在MongoDb数据库中。我想获得摘要:选择参与每个事件的不同 objects
计数。在这种情况下,它将是3
(1,2,3) - 3重复。
我试着这样做(C#LINQ):
_mongoDatabase.GetCollection<Incident>("Incident").AsQueryable()
.Select(incident => new IncidentDto {
Id = incident.Id;
ObjectsCount = incident
.IncidentFrames
.SelectMany(received => received
.Objects
.Select(dto => dto.Id))
.Distinct())
.Count()
});
而且我得到一个错误:
表达式树
不支持SelectMany方法
我提出的解决方案是对数据进行非规范化并将objects
计数存储为incident
中的数字。
这给我带来了非常有趣的问题。假设应用程序已经开始生产,现在我决定需要对象计数。我必须进行某种迁移来计算这个值并将其添加到现有记录中。
如何做这个数据库端?我还没有NoSQL的经验,所以我的想法可能会被类似SQL的apprach蒙上阴影。也许这种架构是完全错误的?
答案 0 :(得分:1)
嗯,你可以做的一件事就是首先调查你的查询,只加载你需要的数据,然后使用Linq对象处理这些数据:
var query = _mongoDatabase.GetCollection<Incident>("Incident")
.Aggregate()
.Project(i=>new{Id= i.Id,
ObjectIds= i.IncidentFrames.Select(f=>f.Objects.Select(o=>o.Id))}).ToList();
var result = query.Select(e => new IncidentDto { Id=e.Id, ObjectsCount = e.ObjectIds.SelectMany(l => l).Distinct().Count() });
也许使用aggregations有更好的方法,但这是我能找到的最佳解决方案。
我找到了另一种解决方案,你可以从数据库方面做同样的事情:
var Grouping = new BsonDocument { { "_id", "$Id" }, { "ObjectIds", new BsonDocument("$addToSet", "$ObjectIds") } };
var query = collection.Aggregate()
.Project(i => new { i.Id, ObjectIds = i.IncidentFrames.Select(f => f.Objects.Select(o => o.Id)) })
.Unwind(a => a.ObjectIds)
.Unwind(e => e["ObjectIds"])
.Group<IncidentDTO>(Grouping)
.ToList();
唯一需要稍微改变你的DTO:
public class IncidentDTO
{
public int Id { get; set; }
public int[] ObjectIds { get; set; }
}
如果您不想获取对象ID并且只想对象计数(作为原始DTO),那么您可以使用$size聚合运算符获得ObjectIds
数组的长度。如果您的DTO是这样的:
public class IncidentDTO
{
public int Id { get; set; }
public int ObjectsCount{ get; set; }
}
您可以执行以下操作:
var Grouping = new BsonDocument { { "_id", "$Id" }, { "ObjectIds", new BsonDocument("$addToSet", "$ObjectIds") } };
var projection = new BsonDocument { { "_id", "$_id" }, { "ObjectsCount", new BsonDocument("$size", "$ObjectIds") } };
var query = collection.Aggregate()
.Project(i => new { i.Id, ObjectIds = i.IncidentFrames.Select(f => f.Objects.Select(o => o.Id)) })
.Unwind(a => a.ObjectIds)
.Unwind(e => e["ObjectIds"])
.Group(Grouping)
.Project<IncidentDTO>(projection).ToList();