Pra*_*n93 1 c# linq mongodb aggregation-framework mongodb-.net-driver
我是MongoDB的新手,想知道如何计算从UTC时间12 AM到当前UTC时间每15分钟间隔插入集合中的文档总数。
以下是样本文件
{
"_id" : ObjectId("5ade8bfc6b941c7726a54f01"),
"Country" : "US"
"Timestamp" : ISODate("2018-04-24T01:44:28.040Z"),
}
Run Code Online (Sandbox Code Playgroud)
这是预期的输出:
{
"Count": 245,
"ReceiveDateString": "5/2/2018 12:00:00 AM"
},
{
"Count": 239,
"ReceiveDateString": "5/2/2018 12:15:00 AM"
},
{
"Count": 252,
"ReceiveDateString": "5/2/2018 12:30:00 AM"
},
{
"Count": 255,
"ReceiveDateString": "5/2/2018 12:45:00 AM"
},
{
"Count": 242,
"ReceiveDateString": "5/2/2018 1:00:00 AM"
}
.
.
.
and so on until current UTC time.
Run Code Online (Sandbox Code Playgroud)
我可以按分钟分组,如下所示:
var filter = Builders<Model>.Filter.Where(r => r.Timestamp > startDate && r.Timestamp < endDate);
var result = Collection.Aggregate()
.Match(filter)
.Group(
r => r.Timestamp.Minute,
g => new
{
ReceiveDate = g.Select(x => x.Timestamp).First(),
Count = g.Count(),
}
).ToEnumerable();
Run Code Online (Sandbox Code Playgroud)
但是,我无法弄清楚如何将MongoDb中以15分钟为间隔的Group result中提供的解决方案转换为MongoDB C#驱动程序查询。
谢谢。
如果您将“确切的东西”视为与.NET相关的参考文章,则可能实际上并不会像这样实现。您可以这样做,但是除非您在我需要的范围内需要“灵活的间隔”,否则您可能不会费劲地去寻找其他选择之一。
如果您拥有现代化的MongoDB 3.6或更高版本的服务器,则可以使用$dateFromParts以便从从日期中提取的“四舍五入”部分中重建日期:
DateTime startDate = new DateTime(2018, 5, 1, 0, 0, 0, DateTimeKind.Utc);
DateTime endDate = new DateTime(2018, 6, 1, 0, 0, 0, DateTimeKind.Utc);
var result = Collection.Aggregate()
.Match(k => k.Timestamp >= startDate && k.Timestamp < endDate)
.Group(k =>
new DateTime(k.Timestamp.Year, k.Timestamp.Month, k.Timestamp.Day,
k.Timestamp.Hour, k.Timestamp.Minute - (k.Timestamp.Minute % 15), 0),
g => new { _id = g.Key, count = g.Count() }
)
.SortBy(d => d._id)
.ToList();
Run Code Online (Sandbox Code Playgroud)
发送到服务器的语句:
[
{ "$match" : {
"Timestamp" : {
"$gte" : ISODate("2018-05-01T00:00:00Z"),
"$lt" : ISODate("2018-06-01T00:00:00Z")
}
} },
{ "$group" : {
"_id" : {
"$dateFromParts" : {
"year" : { "$year" : "$Timestamp" },
"month" : { "$month" : "$Timestamp" },
"day" : { "$dayOfMonth" : "$Timestamp" },
"hour" : { "$hour" : "$Timestamp" },
"minute" : { "$subtract" : [
{ "$minute" : "$Timestamp" },
{ "$mod" : [ { "$minute" : "$Timestamp" }, 15 ] }
] },
"second" : 0
}
},
"count" : { "$sum" : 1 }
} },
{ "$sort": { "_id": 1 } }
]
Run Code Online (Sandbox Code Playgroud)
如果没有该功能,则可以简单地将其保留下来,并保留“反汇编”日期,然后在处理游标时再次将其汇编。只是为了模拟一个列表:
var result = Collection.Aggregate()
.Match(k => k.Timestamp >= startDate && k.Timestamp < endDate)
.Group(k => new
{
year = k.Timestamp.Year,
month = k.Timestamp.Month,
day = k.Timestamp.Day,
hour = k.Timestamp.Hour,
minute = k.Timestamp.Minute - (k.Timestamp.Minute % 15)
},
g => new { _id = g.Key, count = g.Count() }
)
.SortBy(d => d._id)
.ToList();
foreach (var doc in result)
{
//System.Console.WriteLine(doc.ToBsonDocument());
System.Console.WriteLine(
new BsonDocument {
{ "_id", new DateTime(doc._id.year, doc._id.month, doc._id.day,
doc._id.hour, doc._id.minute, 0) },
{ "count", doc.count }
}
);
}
Run Code Online (Sandbox Code Playgroud)
发送到服务器的语句:
[
{ "$match" : {
"Timestamp" : {
"$gte" : ISODate("2018-05-01T00:00:00Z"),
"$lt" : ISODate("2018-06-01T00:00:00Z")
}
} },
{ "$group" : {
"_id" : {
"year" : { "$year" : "$Timestamp" },
"month" : { "$month" : "$Timestamp" },
"day" : { "$dayOfMonth" : "$Timestamp" },
"hour" : { "$hour" : "$Timestamp" },
"minute" : { "$subtract" : [
{ "$minute" : "$Timestamp" },
{ "$mod" : [ { "$minute" : "$Timestamp" }, 15 ] }
] }
},
"count" : { "$sum" : 1 }
} },
{ "$sort" : { "_id" : 1 } }
]
Run Code Online (Sandbox Code Playgroud)
两者在代码方面几乎没有区别。只是在一种情况下,“投射回” DateTime实际上发生在服务器上,$dateFromParts而在另一种情况下,DateTime当您迭代每个游标结果时,我们使用代码中的构造函数进行了完全相同的转换。
因此,它们实际上几乎是相同的,唯一的区别是“服务器”强制转换返回的日期,每个文档使用的字节数要少得多。实际上,因为这里的所有数字格式(包括BSON Date)都是基于64位整数,所以减少了“ 5倍”。即便如此,所有这些数字实际上仍然比发送回日期的任何“字符串”表示“轻巧”。
这些是基本形式,当映射到这些不同形式时,它们实际上保持不变:
var query = from p in Collection.AsQueryable()
where p.Timestamp >= startDate && p.Timestamp < endDate
group p by new DateTime(p.Timestamp.Year, p.Timestamp.Month, p.Timestamp.Day,
p.Timestamp.Hour, p.Timestamp.Minute - (p.Timestamp.Minute % 15), 0) into g
orderby g.Key
select new { _id = g.Key, count = g.Count() };
Run Code Online (Sandbox Code Playgroud)
发送到服务器的语句:
[
{ "$match" : {
"Timestamp" : {
"$gte" : ISODate("2018-05-01T00:00:00Z"),
"$lt" : ISODate("2018-06-01T00:00:00Z")
}
} },
{ "$group" : {
"_id" : {
"$dateFromParts" : {
"year" : { "$year" : "$Timestamp" },
"month" : { "$month" : "$Timestamp" },
"day" : { "$dayOfMonth" : "$Timestamp" },
"hour" : { "$hour" : "$Timestamp" },
"minute" : { "$subtract" : [
{ "$minute" : "$Timestamp" },
{ "$mod" : [ { "$minute" : "$Timestamp" }, 15 ] }
] },
"second" : 0
}
},
"__agg0" : { "$sum" : 1 }
} },
{ "$sort" : { "_id" : 1 } },
{ "$project" : { "_id" : "$_id", "count" : "$__agg0" } }
]
Run Code Online (Sandbox Code Playgroud)
或使用 GroupBy()
var query = Collection.AsQueryable()
.Where(k => k.Timestamp >= startDate && k.Timestamp < endDate)
.GroupBy(k =>
new DateTime(k.Timestamp.Year, k.Timestamp.Month, k.Timestamp.Day,
k.Timestamp.Hour, k.Timestamp.Minute - (k.Timestamp.Minute % 15), 0),
(k, s) => new { _id = k, count = s.Count() }
)
.OrderBy(k => k._id);
Run Code Online (Sandbox Code Playgroud)
发送到服务器的语句:
[
{ "$match" : {
"Timestamp" : {
"$gte" : ISODate("2018-05-01T00:00:00Z"),
"$lt" : ISODate("2018-06-01T00:00:00Z")
}
} },
{ "$group" : {
"_id" : {
"$dateFromParts" : {
"year" : { "$year" : "$Timestamp" },
"month" : { "$month" : "$Timestamp" },
"day" : { "$dayOfMonth" : "$Timestamp" },
"hour" : { "$hour" : "$Timestamp" },
"minute" : { "$subtract" : [
{ "$minute" : "$Timestamp" },
{ "$mod" : [ { "$minute" : "$Timestamp" }, 15 ] }
] },
"second" : 0
}
},
"count" : { "$sum" : 1 }
} },
{ "$sort" : { "_id" : 1 } }
]
Run Code Online (Sandbox Code Playgroud)
如您所见,它们基本上都是相同的形式
如果您希望复制发布的原始“日期数学”表格,那么当前该表格超出了您可以使用LINQ或Fluent构建器实际执行的范围。获得相同序列的唯一方法是BsonDocument构造:
DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
var group = new BsonDocument { {
"$group",
new BsonDocument {
{ "_id",
new BsonDocument { {
"$add", new BsonArray
{
new BsonDocument { {
"$subtract",
new BsonArray {
new BsonDocument { { "$subtract", new BsonArray { "$Timestamp", epoch } } },
new BsonDocument { {
"$mod", new BsonArray
{
new BsonDocument { { "$subtract", new BsonArray { "$Timestamp", epoch } } },
1000 * 60 * 15
}
} }
}
} },
epoch
}
} }
},
{
"count", new BsonDocument("$sum", 1)
}
}
} };
var query = sales.Aggregate()
.Match(k => k.Timestamp >= startDate && k.Timestamp < endDate)
.AppendStage<BsonDocument>(group)
.Sort(new BsonDocument("_id", 1))
.ToList();
Run Code Online (Sandbox Code Playgroud)
请求已发送到服务器:
[
{ "$match" : {
"Timestamp" : {
"$gte" : ISODate("2018-05-01T00:00:00Z"),
"$lt" : ISODate("2018-06-01T00:00:00Z")
}
} },
{ "$group" : {
"_id" : {
"$add" : [
{ "$subtract" : [
{ "$subtract" : [ "$Timestamp", ISODate("1970-01-01T00:00:00Z") ] },
{ "$mod" : [
{ "$subtract" : [ "$Timestamp", ISODate("1970-01-01T00:00:00Z") ] },
900000
] }
] },
ISODate("1970-01-01T00:00:00Z")
]
},
"count" : { "$sum" : 1 }
} },
{ "$sort" : { "_id" : 1 } }
]
Run Code Online (Sandbox Code Playgroud)
我们现在无法执行此操作的主要原因是,当前的语句序列化基本上不同意.NET Framework的说法,即减去两个DateTime值将返回a TimeSpan,而减去两个BSON日期的MongoDB构造将返回“自纪元以来的毫秒数” ”,这实际上就是数学的工作原理。
lamdba表达式的“文字”翻译实质上是:
p => epoch.AddMilliseconds(
(p.Timestamp - epoch).TotalMilliseconds
- ((p.Timestamp - epoch).TotalMilliseconds % 1000 * 60 * 15))
Run Code Online (Sandbox Code Playgroud)
但是,映射仍然需要做一些工作,以便识别语句或正式确定针对此目的实际上打算使用哪种语句。
值得注意的是MongoDB的4.0引入了$convert运营商和共同的别名$toLong和$toDate,这都可以在管道中使用以代替目前的处理上“加法”和“减法”与BSON日期。这些开始为这种转换形成了一个更为“正式”的规范,而不是所示的仅依赖于“加法”和“减法”的方法,该方法仍然有效,但是这样的命名运算符在代码中的意图更加清晰:
{ "$group": {
"_id": {
"$toDate": {
"$subtract": [
{ "$toLong": "$Timestamp" },
{ "$mod": [{ "$toLong": "$Timestamp" }, 1000 * 60 * 15 ] }
]
}
},
"count": { "$sum": 1 }
}}
Run Code Online (Sandbox Code Playgroud)
显而易见,使用LINQ的“形式化”运算符对此类“ DateToLong”和“ LongToDate”函数使用LINQ进行构造,则该语句变得更加整洁,而无需使用“非工作” lambda表达式中所示的“强制”类型。完成。
| 归档时间: |
|
| 查看次数: |
1587 次 |
| 最近记录: |