web-dev-qa-db-fra.com

Filtrer uniquement par date à l'aide du pilote mongoDB c #

J'utilise mongoDB c # dernier pilote, c'est-à-dire 3. + dans mon projet. J'ai différents critères de filtre de date comme aujourd'hui, dernier jour, hier, ce mois, etc. en utilisant daterangepicker.

Voici mon modèle

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; }
    }

Voici le code du pilote

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

Ce code fonctionne bien, mais lorsque je sélectionne le filtre d'aujourd'hui, la date devient

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

Il n'a pas retourné d'enregistrements pour la journée en cours. C'est aussi le calcul du temps.

J'enregistre les dates au format DateTime.Now. Exemple de date ISO que j'interroge sont

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

C'est le filtre de dates que j'utilise. Dois-je avoir à soustraire -1 des dates de fin? enter image description here

Besoin d'aide pour ce que je fais mal.

9
Ghazanfar Khan

Je crois que vous vous confondez avec les fuseaux horaires, en particulier la partie décalée.

MongoDb enregistre toujours la date en heure UTC.

Ainsi, lorsque vous regardez l'heure de la date dans MongoDB, vous devez toujours prendre en compte le décalage par rapport à votre fuseau horaire local.

Vous enverrez toujours la date dans le fuseau horaire local. Le pilote Mongo C # change l'heure de locale à UTC avant de persister.

Par exemple

Lorsque j'enregistre le document avec CreatedOn = 2017-04-05 15:21:23.234 (Fuseau horaire local (Amérique/Chicago)) mais lorsque vous regardez les documents dans la base de données, vous verrez quelque chose ISODate("2017-04-05T20:21:23.234Z") c'est-à-dire le décalage de l'heure locale par rapport à UTC qui est -5 heures.

[BsonDateTimeOptions(Kind = DateTimeKind.Local)] indique au conducteur de convertir l'heure en heure locale à partir d'UTC lors du retrait du BSON vers votre POCO.

Voici le cas de test expliquant le comportement.

Code:

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: (vu à travers 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"
}

Mise à jour:

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

La raison pour laquelle votre comparaison ne fonctionne pas est

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

Ceci est envoyé au serveur en tant que $gte Que ISODate("2017-03-31T00:00:00.000+05:00") et $lte Que ISODate("2017-03-31T00:00:00.000+05:00") et il ne trouve pas l'entrée ci-dessus.

La bonne façon d'interroger la date today sera

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

et mettez à jour votre filtre

var filter = filterBuilder.Gte(x => x.CreatedOn, start) &
         filterBuilder.Lt(x => x.CreatedOn, end);

Alors maintenant, votre requête de plage est envoyée au serveur sous la forme $gte Que ISODate("2017-03-31T00:00:00.000+05:00") et $lt Que ISODate("2017-04-01T00:00:00.000+05:00") et vous devriez pouvoir trouver toutes les correspondances pour aujourd'hui .

mise à jour 2

Modifiez votre base de données pour stocker la date et l'heure avec une partie d'heure définie sur 00:00:00. Cela supprimera également la partie temps de l'équation de la base de données et vos anciennes requêtes de plage fonctionneront très bien dans tous les cas.

Modifiez votre méthode de sauvegarde à utiliser

var today = DateTime.Today; //2017-03-31 00:00:00.000

Vous pouvez revenir à l'ancienne définition de filtre.

Quelque chose comme

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

et mettez à jour votre filtre

var filter = filterBuilder.Gte(x => x.CreatedOn, start) &
         filterBuilder.Lte(x => x.CreatedOn, end);

Alors maintenant, votre requête de plage est envoyée au serveur sous la forme $gte Que ISODate("2017-03-31T00:00:00.000+05:00") et $lte Que ISODate("2017-03-31T00:00:00.000+05:00") et vous devriez pouvoir trouver toutes les correspondances pour aujourd'hui .

Mise à jour - Comparaison de la date uniquement à l'aide de BsonDocument.

L'idée ici est d'ajouter un décalage de fuseau horaire qui est +5:00 À la date UTC du serveur et de transformer le datetime calculé au format chaîne yyyy-MM-dd En utilisant l'opérateur $dateToSting Suivi d'une comparaison sur la date de la chaîne d'entrée dans le même format.

Cela fonctionnera dans votre fuseau horaire mais ne fonctionnera pas dans DST en observant les fuseaux horaires.

Mongo version 3.4

Vous pouvez utiliser l'étape $addFields Qui ajoute un nouveau champ CreatedOnDate tout en conservant toutes les propriétés existantes et dernier $project Pour supprimer le CreatedOnDate de la réponse finale après comparaison.

Requête 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
    }
}

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

Version Mongo = 3.2

Comme ci-dessus, mais ce pipeline utilise $project, Vous devrez donc ajouter tous les champs que vous souhaitez conserver dans la réponse finale.

Requête 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
    }
}

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

Mise à jour 4 - Comparaison de la date uniquement qui fonctionne avec les économies de lumière du jour.

Version Mongo = 3.6

Tout reste le même, attendez-vous à ce que $dateToString Prenne le fuseau horaire au lieu d'un décalage fixe qui devrait prendre en compte les changements d'heure d'été.

Mise à jour du shell:

{
    "$addFields": {
        "CreatedOnDate": {
            "$dateToString": {
                "format": "%Y-%m-%d",
                "date": "$CreatedOn",
                "timezone": "America/New_York"
            }
        }
    }
}

Mise à jour C #:

var addFields = BsonDocument.Parse("{$addFields: { CreatedOnDate: { $dateToString: { format: '%Y-%m-%d', date: "$CreatedOn", "timezone": "America/New_York"} }} }");
6
user2683814

Ce qui suit affichera votre accessoire POCO

    private DateTime _ShortDateOnly;
    [BsonElement("ShortDateOnly")]
    [BsonDateTimeOptions(DateOnly = true)]
    public DateTime ShortDateOnly {
        set { _ShortDateOnly = value; }
        get { return _ShortDateOnly.Date; }
    }

Je l'ai utilisé pour créer le DOCUMENT BSON suivant

{{ 
    "_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") 
}}

Souhaitez-vous bien vouloir ajouter ce qui suit à votre POCO et me faire savoir si cela a été au minimum suffisant pour que cela fonctionne pour vous?

0
UncleFifi