web-dev-qa-db-ja.com

mongoDB c#ドライバーを使用して日付のみでフィルター処理する

私はプロジェクトでmongoDB c#最新ドライバー、すなわち3. +を使用しています。 daterangepickerを使用して、Today、Last Day、Yesterday、This Monthなどの異なる日付フィルター条件を設定しています。

こちらが私のモデルです

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();

このコードは正常に機能しますが、今日のフィルターを選択すると、日付は

var start = new DateTime(2017, 03, 31);
var end = new DateTime(2017, 03, 31);

今日の記録を返しませんでした。時間も計算しています。

DateTime.Nowとして日付を保存しています。私が照会しているサンプルISO日付は

"CreatedOn": ISODate("2017-03-31T20:27:12.914+05:00"),
"ModifiedOn": ISODate("2017-03-31T20:27:12.914+05:00"),

これは私が使用している日付フィルターです。終了日から-1をサブラクトする必要がありますか? enter image description here

私が間違っていることを助けてください。

9
Ghazanfar Khan

タイムゾーン、特にオフセット部分と混同していると思います。

MongoDbは常に日付をUTC時間で保存します。

したがって、MongoDBで日付時刻を見るときは、常にローカルタイムゾーンからのオフセットを考慮する必要があります。

常にローカルタイムゾーンで日付を送信します。 Mongo C#ドライバーは、持続する前に時刻をローカルからUTCに変更します。

例えば

_CreatedOn = 2017-04-05 15:21:23.234_(ローカルタイムゾーン(アメリカ/シカゴ))でドキュメントを保存すると、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; }
}
_

コレクション:(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);
_

これは、ISODate("2017-03-31T00:00:00.000+05:00")より_$gte_およびISODate("2017-03-31T00:00:00.000+05:00")より__$lte_としてサーバーに送信され、上記のエントリが見つかりません。

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);
_

したがって、範囲クエリはISODate("2017-03-31T00:00:00.000+05:00")より__$gte_およびISODate("2017-04-01T00:00:00.000+05:00")より__$lt_としてサーバーに送信され、今日のすべての一致を見つけることができるはずです。

更新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);
_

したがって、範囲クエリはISODate("2017-03-31T00:00:00.000+05:00")より__$gte_およびISODate("2017-03-31T00:00:00.000+05:00")より__$lte_としてサーバーに送信され、今日のすべての一致を見つけることができるはずです。

更新-BsonDocumentを使用した日付のみの比較。

ここでの考え方は、サーバーのUTC日付に_+5:00_であるタイムゾーンオフセットを追加し、_yyyy-MM-dd_演算子とそれに続く入力文字列日付の比較を使用して、計算された日付時刻を文字列_$dateToSting_形式に変換することですフォーマット。

これはタイムゾーンで機能しますが、DSTでは機能しませんタイムゾーンを監視します。

Mongoバージョン3.4

_$addFields_ステージを使用して、既存のすべてのプロパティと最後の_$project_を保持しながら、新しいフィールドCreatedOnDateを追加して、比較後の最終応答からCreatedOnDateを削除できます。

シェルクエリ:

_{
    "$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_を使用するため、最終応答で保持するすべてのフィールドを追加する必要があります。

シェルクエリ:

_{
    "$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_は、夏時間の変更を考慮に入れる固定オフセットの代わりにタイムゾーンを取ることを期待して、すべてが同じままです。

シェルの更新:

_{
    "$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"} }} }");
_
6
user2683814

以下は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に親切に以下を追加して、これがあなたのためにこれを機能させるのに最低限十分であるかどうか私に知らせてください。

0
UncleFifi