.NETで参照される投稿として「正確なもの」を探している場合、実際にはそのように実装されることはおそらくないでしょう。あなたはそれを行うことができますが、私がする程度の「柔軟な間隔」の必要がない限り、あなたはおそらくすべての面倒に行き、実際に他の選択肢の1つに行くつもりはありません。
流暢な骨材
最新のMongoDB3.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();
サーバーに送信されたステートメント:
[
{ "$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 } }
]
その機能を利用できない場合は、それをオフのままにして日付を「分解」したままにして、カーソルを処理するときに再度アセンブルすることができます。リストでシミュレートするだけです:
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 }
}
);
}
サーバーに送信されたステートメント:
[
{ "$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 } }
]
コードに関しては、2つの間にほとんど違いはありません。ある場合には、DateTime
への「キャストバック」です。 実際には、サーバー上で$dateFromParts
を使用して発生します もう1つは、DateTime
を使用してまったく同じキャストを行うだけです。 各カーソル結果を反復する際のコード内のコンストラクター。
したがって、これらは実際にはほとんど同じですが、唯一の実際の違いは、「サーバー」がキャストを実行する場所で、返される日付がドキュメントごとに使用するバイト数がはるかに少ないことです。実際、ここでのすべての数値形式(BSON日付を含む)は64ビット整数に基づいているため、「5分の1」少なくなります。それでも、これらの数値はすべて、日付の「文字列」表現を送り返すよりも実際には「軽い」ものです。
LINQクエリ可能
これらは、これらの異なるフォームにマッピングするときに実際に同じままである基本的なフォームです:
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() };
サーバーに送信されたステートメント:
[
{ "$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" } }
]
または、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);
サーバーに送信されたステートメント:
[
{ "$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 } }
]
ご覧のとおり、基本的にすべて同じ形式です
オリジナルの変換
投稿された元の「日付計算」フォームを複製しようとしている場合、現在、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();
サーバーに送信されたリクエスト:
[
{ "$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 } }
]
現在これを実行できない大きな理由は、ステートメントの現在のシリアル化が、.NETFrameworkが2つのDateTime
を減算すると言っている点と基本的に一致しないためです。 値はTimeSpan
を返します 、および2つのBSON日付を減算するMongoDB構造は、「エポックからのミリ秒」を返します。これは、基本的に数学の仕組みです。
lamdba式の「直訳」は本質的に次のとおりです。
p => epoch.AddMilliseconds(
(p.Timestamp - epoch).TotalMilliseconds
- ((p.Timestamp - epoch).TotalMilliseconds % 1000 * 60 * 15))
ただし、ステートメントを認識したり、この目的で実際にどの種類のステートメントが意図されているかを形式化するために、マッピングにはまだいくつかの作業が必要です。
特に、MongoDB4.0では$convert
が導入されています 演算子と$toLong
の一般的なエイリアス および$toDate
、BSON日付を使用した「加算」および「減算」の現在の処理の代わりに、パイプラインですべて使用できます。これらは、その「加算」と「減算」のみに依存する示されている方法ではなく、そのような変換のより「正式な」仕様を形成し始めます。これはまだ有効ですが、そのような名前付き演算子は、コード内の意図がはるかに明確です。
{ "$group": {
"_id": {
"$toDate": {
"$subtract": [
{ "$toLong": "$Timestamp" },
{ "$mod": [{ "$toLong": "$Timestamp" }, 1000 * 60 * 15 ] }
]
}
},
"count": { "$sum": 1 }
}}
このような「DateToLong」および「LongToDate」関数のLINQを使用したステートメント構築の「形式化された」演算子を使用すると、「機能しない」ラムダ式に示される「強制」のタイプがなくても、ステートメントがはるかにクリーンになることがわかります。完了しました。