使用 mongoDB c# 驱动程序仅按日期过滤
Filter only by Date using mongoDB c# driver
我在我的项目中使用 mongoDB c# 最新驱动程序,即 3.+。我使用 daterangepicker 有不同的日期过滤条件,例如今天、最后一天、昨天、本月等。
这是我的模型
public class Student
{
public Student()
{
}
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
public DateTime CreatedOn { get; set; }
[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
public DateTime ModifiedOn { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
这是驱动代码
var server = new MongoClient(_connectionString);
var db = server.GetDatabase("Students");
var collection = db.GetCollection<Student>("student");
var filterBuilder = Builders<Student>.Filter;
var start = new DateTime(2017, 03, 29);
var end = new DateTime(2017, 03, 31);
var filter = filterBuilder.Gte(x => x.CreatedOn, new BsonDateTime(start)) &
filterBuilder.Lte(x => x.CreatedOn, new BsonDateTime(end));
List<Student> searchResult = collection.Find(filter).ToList();
此代码工作正常,但当我 select 今天筛选时,日期变为
var start = new DateTime(2017, 03, 31);
var end = new DateTime(2017, 03, 31);
当天没有 returns 记录。它也在计算时间。
我将日期保存为 DateTime.Now。我正在查询的示例 ISO 日期是
"CreatedOn": ISODate("2017-03-31T20:27:12.914+05:00"),
"ModifiedOn": ISODate("2017-03-31T20:27:12.914+05:00"),
这是我正在使用的日期过滤器。我应该从结束日期中减去 -1 吗?
我做错了什么需要帮助。
当您使用
new DateTime(2017, 03, 31);
你得到一个 DateTime 对象,这意味着它也计算时间。
因此,通过对开始和停止使用相同的解析,您实际上得到的结果等于:
var start = var end = new DateTime("31/03/2017 00:00:00.00");
当然,您可能没有在此特定时间范围内的记录。
如果你真的想获得今天的所有记录,你应该这样做:
var start = new DateTime("31/03/2017 00:00:00.00");
var end = new DateTime("31/03/2017 23:59:59.99");
我相信您对时区尤其是偏移部分感到困惑。
MongoDb 总是以 UTC 时间保存日期。
因此,当您查看 MongoDB 中的日期时间时,您总是必须考虑与当地时区的偏移量。
您将始终以当地时区发送日期。 Mongo C# 驱动程序在持久化之前将时间从本地更改为 UTC。
例如
当我用 CreatedOn = 2017-04-05 15:21:23.234
(本地时区 (America/Chicago) )保存文档时,但是
当您查看 DB 中的文档时,您会看到一些东西 ISODate("2017-04-05T20:21:23.234Z")
即本地时间与 UTC 的偏移量为 -5 小时。
[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
指示驱动程序在将 BSON 脱轨回您的 POCO 时将时间从 UTC 转换为本地时间。
这是解释行为的测试用例。
代码:
class Program
{
static void Main(string[] args)
{
var mongo = new MongoClient("mongodb://localhost:27017/test");
var db = mongo.GetDatabase("test");
db.DropCollection("students");
db.CreateCollection("students");
var collection = db.GetCollection<Student>("students");
var today = DateTime.Now; //2017-04-05 15:21:23.234
var yesterday = today.AddDays(-1);//2017-04-04 15:21:23.234
// Create 2 documents (yesterday & today)
collection.InsertMany(new[]
{
new Student{Description = "today", CreatedOn = today},
new Student{Description = "yesterday", CreatedOn = yesterday},
}
);
var filterBuilder1 = Builders<Student>.Filter;
var filter1 = filterBuilder1.Eq(x => x.CreatedOn, today);
List<Student> searchResult1 = collection.Find(filter1).ToList();
Console.Write(searchResult1.Count == 1);
var filterBuilder2 = Builders<Student>.Filter;
var filter2 = filterBuilder2.Eq(x => x.CreatedOn, yesterday);
List<Student> searchResult2 = collection.Find(filter2).ToList();
Console.Write(searchResult2.Count == 1);
}
}
public class Student
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
public DateTime CreatedOn { get; set; }
public string Description { get; set; }
}
Collection :(通过mongo shell查看时)
{
"_id" : ObjectId("58e559c76d3a9d2cb0449d84"),
"CreatedOn" : ISODate("2017-04-04T20:21:23.234Z"),
"Description" : "yesterday"
}
{
"_id" : ObjectId("58e559c76d3a9d2cb0449d85"),
"CreatedOn" : ISODate("2017-04-05T20:21:23.234Z"),
"Description" : "today"
}
更新:
"CreatedOn": ISODate("2017-03-31T20:27:12.914+05:00")
您的比较无效的原因是
var start = new DateTime(2017, 03, 31);
var end = new DateTime(2017, 03, 31);
发送到服务器的方式是 $gte
比 ISODate("2017-03-31T00:00:00.000+05:00")
和 $lte
比 ISODate("2017-03-31T00:00:00.000+05:00")
并且它没有找到上面的条目。
查询 today
日期的正确方法是
var start = new DateTime(2017, 03, 31);
var end = new DateTime(2017, 04, 01);
并将过滤器更新为
var filter = filterBuilder.Gte(x => x.CreatedOn, start) &
filterBuilder.Lt(x => x.CreatedOn, end);
现在您的范围查询以 $gte
比 ISODate("2017-03-31T00:00:00.000+05:00")
和 $lt
比 ISODate("2017-04-01T00:00:00.000+05:00")
发送到服务器,您应该能够找到今天的所有匹配项。
更新 2
更改数据库以存储时间部分设置为 00:00:00 的日期时间。这也会从 db 的等式中删除时间部分,并且您的旧范围查询在所有情况下都可以正常工作。
更改您的保存方法以使用
var today = DateTime.Today; //2017-03-31 00:00:00.000
您可以返回到旧的过滤器定义。
类似
var start = new DateTime(2017, 03, 31);
var end = new DateTime(2017, 03, 31);
并将过滤器更新为
var filter = filterBuilder.Gte(x => x.CreatedOn, start) &
filterBuilder.Lte(x => x.CreatedOn, end);
现在您的范围查询以 $gte
比 ISODate("2017-03-31T00:00:00.000+05:00")
和 $lte
比 ISODate("2017-03-31T00:00:00.000+05:00")
发送到服务器,您应该能够找到今天的所有匹配项。
更新 3 - 使用 BsonDocument
.
的仅日期比较
这里的想法是将 +5:00
的时区偏移添加到服务器的 UTC 日期,并使用 $dateToSting
运算符将计算的日期时间转换为字符串 yyyy-MM-dd
格式,然后比较输入相同格式的字符串日期。
这将适用于您的时区,但不适用于 DST 观察时区。
Mongo 版本 3.4
您可以使用 $addFields
阶段添加新字段 CreatedOnDate
同时保留所有现有属性,最后 $project
在比较后从最终响应中删除 CreatedOnDate
.
Shell 查询:
{
"$addFields": {
"CreatedOnDate": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": {
"$add": ["$CreatedOn", 18000000]
}
}
}
}
}, {
"$match": {
"CreatedOnDate": {
"$gte": "2017-03-31",
"$lte": "2017-03-31"
}
}
}, {
"$project": {
"CreatedOnDate": 0
}
}
C# 代码:
var start = new DateTime(2017, 03, 31);
var end = new DateTime(2017, 03, 31);
var addFields = BsonDocument.Parse("{$addFields: { CreatedOnDate: { $dateToString: { format: '%Y-%m-%d', date: {$add: ['$CreatedOn', 18000000] }} }} }");
var match = new BsonDocument("CreatedOnDate", new BsonDocument("$gte", start.ToString("yyyy-MM-dd")).Add("$lte", end.ToString("yyyy-MM-dd")));
var project = new BsonDocument
{
{ "CreatedOnDate", 0 }
};
var pipeline = collection.Aggregate().AppendStage<BsonDocument>(addFields)
.Match(match)
.Project(project);
var list = pipeline.ToList();
List<Student> searchResult = list.Select(doc => BsonSerializer.Deserialize<Student>(doc)).ToList();
Mongo版本=3.2
与上面相同,但此管道使用 $project
,因此您必须添加要在最终响应中保留的所有字段。
Shell 查询:
{
"$project": {
"CreatedOn": 1,
"Description": 1,
"CreatedOnDate": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": {
"$add": ["$CreatedOn", 18000000]
}
}
}
}
}, {
"$match": {
"CreatedOnDate": {
"$gte": "2017-03-31",
"$lte": "2017-03-31"
}
}
}, {
"$project": {
"CreatedOn": 1,
"Description": 1
}
}
C# 代码:
var start = new DateTime(2017, 03, 31);
var end = new DateTime(2017, 03, 31);
var project1 = new BsonDocument
{
{ "CreatedOn", 1 },
{ "Description", 1 },
{ "CreatedOnDate", new BsonDocument("$dateToString", new BsonDocument("format", "%Y-%m-%d")
.Add("date", new BsonDocument("$add", new BsonArray(new object[] { "$CreatedOn", 5 * 60 * 60 * 1000 }))))
}
};
var match = new BsonDocument("CreatedOnDate", new BsonDocument("$gte", start.ToString("yyyy-MM-dd")).Add("$lte", end.ToString("yyyy-MM-dd")));
var project2 = new BsonDocument
{
{ "CreatedOn", 1 },
{ "Description", 1 }
};
var pipeline = collection.Aggregate()
.Project(project1)
.Match(match)
.Project(project2);
var list = pipeline.ToList();
List<Student> searchResult = list.Select(doc => BsonSerializer.Deserialize<Student>(doc)).ToList();
更新 4 - 仅日期比较适用于夏令时。
Mongo版本=3.6
一切都保持不变,期望 $dateToString
将采用时区而不是固定偏移量,这应该考虑到夏令时的变化。
Shell更新:
{
"$addFields": {
"CreatedOnDate": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": "$CreatedOn",
"timezone": "America/New_York"
}
}
}
}
C# 更新:
var addFields = BsonDocument.Parse("{$addFields: { CreatedOnDate: { $dateToString: { format: '%Y-%m-%d', date: "$CreatedOn", "timezone": "America/New_York"} }} }");
下面会显示你的POCO道具
private DateTime _ShortDateOnly;
[BsonElement("ShortDateOnly")]
[BsonDateTimeOptions(DateOnly = true)]
public DateTime ShortDateOnly {
set { _ShortDateOnly = value; }
get { return _ShortDateOnly.Date; }
}
我用它创建了以下 BSON 文档
{{
"_id" : CSUUID("12ce2538-2921-4da0-8211-9202da92d7f3"),
"first" : "Felipe",
"New" : "Ferreira",
"PublicPassword" : null,
"netWorth" : "20000.99",
"netWorth2" : 20000.990000000002,
"UserType" : "Admin",
"BirthDate" : ISODate("2019-06-22T18:59:01.861Z"),
"ShortDateOnly" : ISODate("2019-06-22T00:00:00Z")
}}
请将以下内容添加到您的 POCO 中,让我知道这是否足以让您使用它?
将 Collection.Find() 与过滤器一起使用。在您的存储库模式或您的数据库查询代码所在的任何位置,添加:
public async Task<List<T>> FindAll(Expression<Func<T, bool>> filter)
{
try
{
//Fetch the filtered list from the Collection
List<T> documents = await Collection.Find(filter).ToListAsync();
//return the list
return documents;
}
catch (Exception ex)
{
return await Task.FromResult(new List<T>() { });
}
}
并这样称呼它:
_someClass.FindAll(x => x.UpdatedAt > DateTime.UtcNow.AddDays(-5))
_someClass 绝对应该替换为包含 FindAll 函数
的 class 的实例
我在我的项目中使用 mongoDB c# 最新驱动程序,即 3.+。我使用 daterangepicker 有不同的日期过滤条件,例如今天、最后一天、昨天、本月等。
这是我的模型
public class Student
{
public Student()
{
}
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
public DateTime CreatedOn { get; set; }
[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
public DateTime ModifiedOn { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
这是驱动代码
var server = new MongoClient(_connectionString);
var db = server.GetDatabase("Students");
var collection = db.GetCollection<Student>("student");
var filterBuilder = Builders<Student>.Filter;
var start = new DateTime(2017, 03, 29);
var end = new DateTime(2017, 03, 31);
var filter = filterBuilder.Gte(x => x.CreatedOn, new BsonDateTime(start)) &
filterBuilder.Lte(x => x.CreatedOn, new BsonDateTime(end));
List<Student> searchResult = collection.Find(filter).ToList();
此代码工作正常,但当我 select 今天筛选时,日期变为
var start = new DateTime(2017, 03, 31);
var end = new DateTime(2017, 03, 31);
当天没有 returns 记录。它也在计算时间。
我将日期保存为 DateTime.Now。我正在查询的示例 ISO 日期是
"CreatedOn": ISODate("2017-03-31T20:27:12.914+05:00"),
"ModifiedOn": ISODate("2017-03-31T20:27:12.914+05:00"),
这是我正在使用的日期过滤器。我应该从结束日期中减去 -1 吗?
我做错了什么需要帮助。
当您使用
new DateTime(2017, 03, 31);
你得到一个 DateTime 对象,这意味着它也计算时间。 因此,通过对开始和停止使用相同的解析,您实际上得到的结果等于:
var start = var end = new DateTime("31/03/2017 00:00:00.00");
当然,您可能没有在此特定时间范围内的记录。 如果你真的想获得今天的所有记录,你应该这样做:
var start = new DateTime("31/03/2017 00:00:00.00");
var end = new DateTime("31/03/2017 23:59:59.99");
我相信您对时区尤其是偏移部分感到困惑。
MongoDb 总是以 UTC 时间保存日期。
因此,当您查看 MongoDB 中的日期时间时,您总是必须考虑与当地时区的偏移量。
您将始终以当地时区发送日期。 Mongo C# 驱动程序在持久化之前将时间从本地更改为 UTC。
例如
当我用 CreatedOn = 2017-04-05 15:21:23.234
(本地时区 (America/Chicago) )保存文档时,但是
当您查看 DB 中的文档时,您会看到一些东西 ISODate("2017-04-05T20:21:23.234Z")
即本地时间与 UTC 的偏移量为 -5 小时。
[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
指示驱动程序在将 BSON 脱轨回您的 POCO 时将时间从 UTC 转换为本地时间。
这是解释行为的测试用例。
代码:
class Program
{
static void Main(string[] args)
{
var mongo = new MongoClient("mongodb://localhost:27017/test");
var db = mongo.GetDatabase("test");
db.DropCollection("students");
db.CreateCollection("students");
var collection = db.GetCollection<Student>("students");
var today = DateTime.Now; //2017-04-05 15:21:23.234
var yesterday = today.AddDays(-1);//2017-04-04 15:21:23.234
// Create 2 documents (yesterday & today)
collection.InsertMany(new[]
{
new Student{Description = "today", CreatedOn = today},
new Student{Description = "yesterday", CreatedOn = yesterday},
}
);
var filterBuilder1 = Builders<Student>.Filter;
var filter1 = filterBuilder1.Eq(x => x.CreatedOn, today);
List<Student> searchResult1 = collection.Find(filter1).ToList();
Console.Write(searchResult1.Count == 1);
var filterBuilder2 = Builders<Student>.Filter;
var filter2 = filterBuilder2.Eq(x => x.CreatedOn, yesterday);
List<Student> searchResult2 = collection.Find(filter2).ToList();
Console.Write(searchResult2.Count == 1);
}
}
public class Student
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
public DateTime CreatedOn { get; set; }
public string Description { get; set; }
}
Collection :(通过mongo shell查看时)
{
"_id" : ObjectId("58e559c76d3a9d2cb0449d84"),
"CreatedOn" : ISODate("2017-04-04T20:21:23.234Z"),
"Description" : "yesterday"
}
{
"_id" : ObjectId("58e559c76d3a9d2cb0449d85"),
"CreatedOn" : ISODate("2017-04-05T20:21:23.234Z"),
"Description" : "today"
}
更新:
"CreatedOn": ISODate("2017-03-31T20:27:12.914+05:00")
您的比较无效的原因是
var start = new DateTime(2017, 03, 31);
var end = new DateTime(2017, 03, 31);
发送到服务器的方式是 $gte
比 ISODate("2017-03-31T00:00:00.000+05:00")
和 $lte
比 ISODate("2017-03-31T00:00:00.000+05:00")
并且它没有找到上面的条目。
查询 today
日期的正确方法是
var start = new DateTime(2017, 03, 31);
var end = new DateTime(2017, 04, 01);
并将过滤器更新为
var filter = filterBuilder.Gte(x => x.CreatedOn, start) &
filterBuilder.Lt(x => x.CreatedOn, end);
现在您的范围查询以 $gte
比 ISODate("2017-03-31T00:00:00.000+05:00")
和 $lt
比 ISODate("2017-04-01T00:00:00.000+05:00")
发送到服务器,您应该能够找到今天的所有匹配项。
更新 2
更改数据库以存储时间部分设置为 00:00:00 的日期时间。这也会从 db 的等式中删除时间部分,并且您的旧范围查询在所有情况下都可以正常工作。
更改您的保存方法以使用
var today = DateTime.Today; //2017-03-31 00:00:00.000
您可以返回到旧的过滤器定义。
类似
var start = new DateTime(2017, 03, 31);
var end = new DateTime(2017, 03, 31);
并将过滤器更新为
var filter = filterBuilder.Gte(x => x.CreatedOn, start) &
filterBuilder.Lte(x => x.CreatedOn, end);
现在您的范围查询以 $gte
比 ISODate("2017-03-31T00:00:00.000+05:00")
和 $lte
比 ISODate("2017-03-31T00:00:00.000+05:00")
发送到服务器,您应该能够找到今天的所有匹配项。
更新 3 - 使用 BsonDocument
.
这里的想法是将 +5:00
的时区偏移添加到服务器的 UTC 日期,并使用 $dateToSting
运算符将计算的日期时间转换为字符串 yyyy-MM-dd
格式,然后比较输入相同格式的字符串日期。
这将适用于您的时区,但不适用于 DST 观察时区。
Mongo 版本 3.4
您可以使用 $addFields
阶段添加新字段 CreatedOnDate
同时保留所有现有属性,最后 $project
在比较后从最终响应中删除 CreatedOnDate
.
Shell 查询:
{
"$addFields": {
"CreatedOnDate": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": {
"$add": ["$CreatedOn", 18000000]
}
}
}
}
}, {
"$match": {
"CreatedOnDate": {
"$gte": "2017-03-31",
"$lte": "2017-03-31"
}
}
}, {
"$project": {
"CreatedOnDate": 0
}
}
C# 代码:
var start = new DateTime(2017, 03, 31);
var end = new DateTime(2017, 03, 31);
var addFields = BsonDocument.Parse("{$addFields: { CreatedOnDate: { $dateToString: { format: '%Y-%m-%d', date: {$add: ['$CreatedOn', 18000000] }} }} }");
var match = new BsonDocument("CreatedOnDate", new BsonDocument("$gte", start.ToString("yyyy-MM-dd")).Add("$lte", end.ToString("yyyy-MM-dd")));
var project = new BsonDocument
{
{ "CreatedOnDate", 0 }
};
var pipeline = collection.Aggregate().AppendStage<BsonDocument>(addFields)
.Match(match)
.Project(project);
var list = pipeline.ToList();
List<Student> searchResult = list.Select(doc => BsonSerializer.Deserialize<Student>(doc)).ToList();
Mongo版本=3.2
与上面相同,但此管道使用 $project
,因此您必须添加要在最终响应中保留的所有字段。
Shell 查询:
{
"$project": {
"CreatedOn": 1,
"Description": 1,
"CreatedOnDate": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": {
"$add": ["$CreatedOn", 18000000]
}
}
}
}
}, {
"$match": {
"CreatedOnDate": {
"$gte": "2017-03-31",
"$lte": "2017-03-31"
}
}
}, {
"$project": {
"CreatedOn": 1,
"Description": 1
}
}
C# 代码:
var start = new DateTime(2017, 03, 31);
var end = new DateTime(2017, 03, 31);
var project1 = new BsonDocument
{
{ "CreatedOn", 1 },
{ "Description", 1 },
{ "CreatedOnDate", new BsonDocument("$dateToString", new BsonDocument("format", "%Y-%m-%d")
.Add("date", new BsonDocument("$add", new BsonArray(new object[] { "$CreatedOn", 5 * 60 * 60 * 1000 }))))
}
};
var match = new BsonDocument("CreatedOnDate", new BsonDocument("$gte", start.ToString("yyyy-MM-dd")).Add("$lte", end.ToString("yyyy-MM-dd")));
var project2 = new BsonDocument
{
{ "CreatedOn", 1 },
{ "Description", 1 }
};
var pipeline = collection.Aggregate()
.Project(project1)
.Match(match)
.Project(project2);
var list = pipeline.ToList();
List<Student> searchResult = list.Select(doc => BsonSerializer.Deserialize<Student>(doc)).ToList();
更新 4 - 仅日期比较适用于夏令时。
Mongo版本=3.6
一切都保持不变,期望 $dateToString
将采用时区而不是固定偏移量,这应该考虑到夏令时的变化。
Shell更新:
{
"$addFields": {
"CreatedOnDate": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": "$CreatedOn",
"timezone": "America/New_York"
}
}
}
}
C# 更新:
var addFields = BsonDocument.Parse("{$addFields: { CreatedOnDate: { $dateToString: { format: '%Y-%m-%d', date: "$CreatedOn", "timezone": "America/New_York"} }} }");
下面会显示你的POCO道具
private DateTime _ShortDateOnly;
[BsonElement("ShortDateOnly")]
[BsonDateTimeOptions(DateOnly = true)]
public DateTime ShortDateOnly {
set { _ShortDateOnly = value; }
get { return _ShortDateOnly.Date; }
}
我用它创建了以下 BSON 文档
{{
"_id" : CSUUID("12ce2538-2921-4da0-8211-9202da92d7f3"),
"first" : "Felipe",
"New" : "Ferreira",
"PublicPassword" : null,
"netWorth" : "20000.99",
"netWorth2" : 20000.990000000002,
"UserType" : "Admin",
"BirthDate" : ISODate("2019-06-22T18:59:01.861Z"),
"ShortDateOnly" : ISODate("2019-06-22T00:00:00Z")
}}
请将以下内容添加到您的 POCO 中,让我知道这是否足以让您使用它?
将 Collection.Find() 与过滤器一起使用。在您的存储库模式或您的数据库查询代码所在的任何位置,添加:
public async Task<List<T>> FindAll(Expression<Func<T, bool>> filter)
{
try
{
//Fetch the filtered list from the Collection
List<T> documents = await Collection.Find(filter).ToListAsync();
//return the list
return documents;
}
catch (Exception ex)
{
return await Task.FromResult(new List<T>() { });
}
}
并这样称呼它:
_someClass.FindAll(x => x.UpdatedAt > DateTime.UtcNow.AddDays(-5))
_someClass 绝对应该替换为包含 FindAll 函数
的 class 的实例