我们有一个具有以下架构的文档集合,总结了日常交易。
{
"licenseId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"transactionDay":"2023-08-23",
"startTime": "2023-08-23T13:00:00Z",
"endTime": "2023-08-23T13:01:00Z",
"count": "1",
"size": "12345"
}
我们需要生成具有以下模式的报告,该报告提供由
count
值键入的字典结构中的 size
和 transactionDay
字段的每日总计。
{
"licenseId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"usage": {
"2023-08-23": {
"countTotal": "1",
"sizeTotal": "12345"
}
}
}
使用 C# MongoDB 驱动程序 v2.21,我们可以使用 Aggregator Fluent 语法生成包含每日摘要的报告,但不是我们要求的格式。
var usageReport = transactionCollection.Aggregate()
.Match(licenseFilter)
.Group(
e => e.transactionDay,
g => new
{
transactionDay= g.Key,
licenseId= g.First().licenseId,
sizeTotal= g.Sum(x=>x.size),
countTotal= g.Sum(x=>x.count)
})
.ToList();
目前尚不清楚我们是否缺少一个预测阶段,或者我们在小组中采取的方法是否不正确。
任何关于如何实现这一目标的指示或参考将不胜感激。
我在评论中提到的第一个问题是,根据提供的文档将数值作为字符串存储在集合中,这会降低在执行计算之前将字符串转换为数值所需的性能。
此外,从结果来看,您应该将数值转换回字符串。这将使查询变得复杂,并且认为使用 Aggregate Fluent 语法不容易实现这一点。
MongoDB 聚合管道应如下所示:
$match
- 过滤阶段
$group
- 按 transactionDay
分组并执行计算,确保您的值是否存储为字符串,您应该首先将它们转换为数值。
$group
- 按 licenseId
分组并构建 transactionDays
数组。
$project
- 装饰输出文档。通过 transactionDays
将 $arrayToObject
数组转换为键值对。
[
{
$match: {
"licenseId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
},
{
$group: {
_id: "$transactionDay",
licenseId: {
$first: "$licenseId"
},
countTotal: {
$sum: {
$toInt: "$count"
}
},
sizeTotal: {
$sum: {
$toInt: "$size"
}
}
}
},
{
$group: {
_id: "$licenseId",
transactionDays: {
$push: {
k: "$_id",
v: {
countTotal: {
$toString: "$countTotal"
},
sizeTotal: {
$toString: "$sizeTotal"
}
}
}
}
}
},
{
$project: {
_id: 0,
licenseId: "$_id",
usage: {
$arrayToObject: "$transactionDays"
}
}
}
]
您的预期结果的模型类应如下所示:
[BsonNoId]
public class UsageReport
{
public Guid licenseId { get; set; }
public Dictionary<string, UsageDate> usage { get; set; }
}
public class UsageDate
{
public string countTotal { get; set; }
public string sizeTotal { get; set; }
}
对于 Aggregate Fluent 语法,应如下所示:
PipelineStageDefinition<Transaction, Transaction> firstStage
= PipelineStageDefinitionBuilder.Match(licenseFilter);
PipelineStageDefinition<Transaction, BsonDocument> secondStage
= PipelineStageDefinitionBuilder.Group<Transaction, BsonDocument>(new BsonDocument
{
{ "_id", "$transactionDay" },
{ "licenseId", new BsonDocument
{
{ "$first", "$licenseId" }
}
},
{ "sizeTotal", new BsonDocument
{
{ "$sum", new BsonDocument("$toInt", "$size") }
}
},
{ "countTotal", new BsonDocument
{
{ "$sum", new BsonDocument("$toInt", "$count") }
}
}
});
PipelineStageDefinition<BsonDocument, BsonDocument> thirdStage
= PipelineStageDefinitionBuilder.Group<BsonDocument, BsonDocument>(new BsonDocument
{
{ "_id", "$licenseId" },
{ "transactionsDays", new BsonDocument
{
{ "$push", new BsonDocument
{
{ "k", "$_id" },
{ "v", new BsonDocument
{
{ "countTotal", new BsonDocument("$toString", "$countTotal") },
{ "sizeTotal", new BsonDocument("$toString", "$sizeTotal") }
}
}
}
}
}
}
});
PipelineStageDefinition<BsonDocument, UsageReport> forthStage
= PipelineStageDefinitionBuilder.Project<BsonDocument, UsageReport>(new BsonDocument
{
{ "_id", 0 },
{ "licenseId", "$_id" },
{ "usage", new BsonDocument("$arrayToObject", "$transactionsDays") }
});
var usageReport = _collection.Aggregate()
.AppendStage(firstStage)
.AppendStage(secondStage)
.AppendStage(thirdStage)
.AppendStage(forthStage)
.ToList();