Rechercher des objets entre deux dates MongoDB

406

J'ai essayé de stocker des tweets dans mongodb, chaque objet ressemble à ceci:

{
"_id" : ObjectId("4c02c58de500fe1be1000005"),
"contributors" : null,
"text" : "Hello world",
"user" : {
    "following" : null,
    "followers_count" : 5,
    "utc_offset" : null,
    "location" : "",
    "profile_text_color" : "000000",
    "friends_count" : 11,
    "profile_link_color" : "0000ff",
    "verified" : false,
    "protected" : false,
    "url" : null,
    "contributors_enabled" : false,
    "created_at" : "Sun May 30 18:47:06 +0000 2010",
    "geo_enabled" : false,
    "profile_sidebar_border_color" : "87bc44",
    "statuses_count" : 13,
    "favourites_count" : 0,
    "description" : "",
    "notifications" : null,
    "profile_background_tile" : false,
    "lang" : "en",
    "id" : 149978111,
    "time_zone" : null,
    "profile_sidebar_fill_color" : "e0ff92"
},
"geo" : null,
"coordinates" : null,
"in_reply_to_user_id" : 149183152,
"place" : null,
"created_at" : "Sun May 30 20:07:35 +0000 2010",
"source" : "web",
"in_reply_to_status_id" : {
    "floatApprox" : 15061797850
},
"truncated" : false,
"favorited" : false,
"id" : {
    "floatApprox" : 15061838001
}

Comment pourrais-je écrire une requête qui vérifie le created_at et trouve tous les objets entre 18:47 et 19:00? Dois-je mettre à jour mes documents pour que les dates soient stockées dans un format spécifique?

À M
la source
Vous ne dites pas quel champ vous souhaitez interroger?
shingara
Oups, je veux interroger le created_at et trouver tout entre deux dates.
Tom
Je suis curieux de savoir pourquoi ne pas utiliser l'horodatage, quels avantages en utilisant l'Obj Date?
Leo
4
@Leo Le plus grand avantage de l'objet Date sur les millisecondes depuis l'époque ou autre chose est la lisibilité humaine. Dans ce cas, il 2010-04-29T00:00:00.000Zest beaucoup plus facile de définir votre plage de départ que de calculer la même date / heure en millisecondes. Vous pouvez également effectuer une conversion de fuseau horaire assez facilement. De plus, les dates gèrent déjà des choses comme les jours bissextiles, les secondes bissextiles et d'autres bizarreries que vous ne voulez généralement pas gérer vous-même.
Thunderforge

Réponses:

620

Interroger une plage de dates (mois ou jour spécifique) dans le livre de recettes MongoDB a une très bonne explication à ce sujet, mais ci-dessous, j'ai essayé moi-même et cela semble fonctionner.

items.save({
    name: "example",
    created_at: ISODate("2010-04-30T00:00:00.000Z")
})
items.find({
    created_at: {
        $gte: ISODate("2010-04-29T00:00:00.000Z"),
        $lt: ISODate("2010-05-01T00:00:00.000Z")
    }
})
=> { "_id" : ObjectId("4c0791e2b9ec877893f3363b"), "name" : "example", "created_at" : "Sun May 30 2010 00:00:00 GMT+0300 (EEST)" }

Sur la base de mes expériences, vous devrez sérialiser vos dates dans un format pris en charge par MongoDB, car les éléments suivants ont donné des résultats de recherche indésirables.

items.save({
    name: "example",
    created_at: "Sun May 30 18.49:00 +0000 2010"
})
items.find({
    created_at: {
        $gte:"Mon May 30 18:47:00 +0000 2015",
        $lt: "Sun May 30 20:40:36 +0000 2010"
    }
})
=> { "_id" : ObjectId("4c079123b9ec877893f33638"), "name" : "example", "created_at" : "Sun May 30 18.49:00 +0000 2010" }

Dans le deuxième exemple, aucun résultat n'était attendu, mais il y en avait toujours un. En effet, une comparaison de chaîne de base est effectuée.

ponzao
la source
3
Cela semble intéressant, mais la date stockée doit-elle être dans un format spécifique. Je viens de stocker ce qui a été fourni par Twitter, cela doit-il être changé dans un format différent?
Tom
7
Vous avez probablement stocké les horodatages sous forme de chaînes, donc je suppose que MongoDB ne se rendra pas compte qu'il s'agit en fait de dates. Ainsi, une requête d'intervalle sur eux entraînerait une requête d'intervalle alphabétique (par exemple, "Jan Mon 01.01.2010" avant "Jan Sun 01.01.1000"). Il serait probablement judicieux de formater toutes les données de date au format MongoDB, qui, je pense, est simplement la date JavaScript.
ponzao
2
Je viens de l'utiliser pour convertir mes chaînes en objets de date stackoverflow.com/questions/2900674/…
Tom
D'accord cool! Je suppose que les requêtes de plage mentionnées dans le livre de cuisine devraient fonctionner alors, les avez-vous déjà essayées?
ponzao
Oui, une fois que je stockais les dates, les exemples de livres de cuisine fonctionnaient comme prévu.
Tom
35

Clarifier. Ce qu'il faut savoir, c'est que:

  • Oui, vous devez passer un objet Javascript Date.
  • Oui, il doit être compatible avec ISODate
  • Oui, d'après mon expérience pour que cela fonctionne, vous devez manipuler la date en ISO
  • Oui, travailler avec des dates est généralement toujours un processus fastidieux, et le mongo ne fait pas exception

Voici un extrait de code de travail, où nous faisons un peu de manipulation de date pour garantir que Mongo (ici j'utilise le module mangouste et souhaite des résultats pour les lignes dont l'attribut date est inférieur à (avant) la date donnée comme paramètre myDate) peut gérer correctement:

var inputDate = new Date(myDate.toISOString());
MyModel.find({
    'date': { $lte: inputDate }
})
arcseldon
la source
1
Plus un pour plus de clarté. Si vous utilisez le moment dans le backend, il conserve toujours la fonction toISOString (). J'utilise le moment pour ajouter et soustraire du temps pour mes requêtes.
VocoJax
17

MongoDB stocke en fait le millis d'une date en tant qu'int (64), comme prescrit par http://bsonspec.org/#/specification

Cependant, cela peut devenir assez déroutant lorsque vous récupérez des dates car le pilote client instanciera un objet date avec son propre fuseau horaire local. Le pilote JavaScript de la console mongo le fera certainement.

Donc, si vous vous souciez de vos fuseaux horaires, assurez-vous de savoir ce que c'est censé être lorsque vous le récupérez. Cela ne devrait pas avoir autant d'importance pour les requêtes, car cela équivaudra toujours au même int (64), quel que soit le fuseau horaire de votre objet date (j'espère). Mais je ferais certainement des requêtes avec des objets de date réels (pas des chaînes) et laisserais le pilote faire son travail.

Ben Smith
la source
16

Python et pymongo

Recherche d'objets entre deux dates en Python avec pymongoen collection posts(basé sur le tutoriel ):

from_date = datetime.datetime(2010, 12, 31, 12, 30, 30, 125000)
to_date = datetime.datetime(2011, 12, 31, 12, 30, 30, 125000)

for post in posts.find({"date": {"$gte": from_date, "$lt": to_date}}):
    print(post)

{"$gte": from_date, "$lt": to_date}spécifie la plage en termes de datetime.datetimetypes.

Anton Tarasenko
la source
Cela ne fonctionne pas. Chaque fois que j'exécute cette requête, j'obtiens par défaut une réponse complète et une réponse non filtrée
Abhay Bh
14
db.collection.find({"createdDate":{$gte:new ISODate("2017-04-14T23:59:59Z"),$lte:new ISODate("2017-04-15T23:59:59Z")}}).count();

Remplacer collectionpar le nom de la collection que vous souhaitez exécuter la requête

GSK
la source
3
Qu'est-ce que cela ajoute à la réponse acceptée (fournie 7 ans plus tôt)?
Dan Dascalescu
1
@DanDascalescu - peut-être n'a-t-il rien ajouté, mais quelle est votre préoccupation ici?
GSK
17
Les réponses en double font perdre du temps aux gens.
Dan Dascalescu
10

Utilisez ce code pour rechercher l'enregistrement entre deux dates à l'aide de $gteet $lt:

db.CollectionName.find({"whenCreated": {
    '$gte': ISODate("2018-03-06T13:10:40.294Z"),
    '$lt': ISODate("2018-05-06T13:10:40.294Z")
}});
Sunil Pal
la source
Qu'est-ce que cela ajoute à la réponse acceptée, fournie 8 ans plus tôt?
Dan Dascalescu
7

Utilisation avec Moment.js et les opérateurs de requête de comparaison

  var today = moment().startOf('day');
  // "2018-12-05T00:00:00.00
  var tomorrow = moment(today).endOf('day');
  // ("2018-12-05T23:59:59.999

  Example.find(
  {
    // find in today
    created: { '$gte': today, '$lte': tomorrow }
    // Or greater than 5 days
    // created: { $lt: moment().add(-5, 'days') },
  }), function (err, docs) { ... });
Tính Ngô Quang
la source
2

Convertissez vos dates en fuseau horaire GMT lorsque vous les placez dans Mongo. De cette façon, il n'y a jamais de problème de fuseau horaire. Ensuite, faites le calcul sur le champ Twitter / fuseau horaire lorsque vous retirez les données pour la présentation.

ici
la source
2

Pourquoi ne pas convertir la chaîne en un entier de la forme YYYYMMDDHHMMSS? Chaque incrément de temps créerait alors un entier plus grand, et vous pouvez filtrer sur les entiers au lieu de vous soucier de la conversion en temps ISO.

ZacharyST
la source
Parce que le temps ne se passe pas seulement dans mon fuseau horaire local.
Michael Cole
2
Cela devient un cauchemar une fois que vous commencez à convertir l'heure vers et depuis ce format partout. Si vous allez faire quelque chose comme ça, utilisez au moins la valeur de retour de .getTime () à partir d'un objet date JS.
nikk wong
1
c'est pourquoi nous stockons toujours les données en UTC
codewandler
2

utilisez $ gte et $ lte pour trouver entre les données de date dans mongodb

var tomorrowDate = moment(new Date()).add(1, 'days').format("YYYY-MM-DD");
db.collection.find({"plannedDeliveryDate":{ $gte: new Date(tomorrowDate +"T00:00:00.000Z"),$lte: new Date(tomorrowDate + "T23:59:59.999Z")}})
KARTHIKEYAN.A
la source
1
Légère faute de frappe dans votre réponse $ gte pas $ get :)
Bob
1
Désolé d'avoir répondu à un état très fatigué, j'ai donc fait une erreur. Merci de votre aide pour mettre à jour ma réponse. vous avez fait du bon travail :)
@Bob
2

Vous pouvez également vérifier cela. Si vous utilisez cette méthode, utilisez la fonction d'analyse pour obtenir des valeurs de la base de données Mongo:

db.getCollection('user').find({
    createdOn: {
        $gt: ISODate("2020-01-01T00:00:00.000Z"),
        $lt: ISODate("2020-03-01T00:00:00.000Z")
    }
})
Kevin007
la source
1
mongoose.model('ModelName').aggregate([
    {
        $match: {
            userId: mongoose.Types.ObjectId(userId)
        }
    },
    {
        $project: {
            dataList: {
              $filter: {
                 input: "$dataList",
                 as: "item",
                 cond: { 
                    $and: [
                        {
                            $gte: [ "$$item.dateTime", new Date(`2017-01-01T00:00:00.000Z`) ]
                        },
                        {
                            $lte: [ "$$item.dateTime", new Date(`2019-12-01T00:00:00.000Z`) ]
                        },
                    ]
                 }
              }
           }
        }
     }
])
Jitendra
la source
1

Vous pouvez également vérifier cela ou essayer au lieu d'utiliser l'agrégat

db.getCollection('user').find({
    createdOn: {
        $gt: ISODate("2020-01-01T00:00:00.000Z"),
        $lt: ISODate("2020-03-01T00:00:00.000Z")
    }
})
Kevin007
la source
0

j'ai essayé dans ce modèle selon mes besoins j'ai besoin de stocker une date chaque fois qu'un objet est créé plus tard je veux récupérer tous les enregistrements (documents) entre deux dates dans mon fichier html j'utilisais le format suivant mm / jj / aaaa

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>
<head>

    <script>
//jquery
    $(document).ready(function(){  
    $("#select_date").click(function() { 
    $.ajax({
    type: "post",
    url: "xxx", 
    datatype: "html",
    data: $("#period").serialize(),  
    success: function(data){
    alert(data);
    } ,//success

    }); //event triggered

    });//ajax
    });//jquery  
    </script>

    <title></title>
</head>

<body>
    <form id="period" name='period'>
        from <input id="selecteddate" name="selecteddate1" type="text"> to 
        <input id="select_date" type="button" value="selected">
    </form>
</body>
</html>

dans mon fichier py (python) je l'ai converti en "iso fomate" de la manière suivante

date_str1   = request.POST["SelectedDate1"] 
SelectedDate1   = datetime.datetime.strptime(date_str1, '%m/%d/%Y').isoformat()

et enregistré dans ma collection dbmongo avec "SelectedDate" comme champ dans ma collection

récupérer des données ou des documents entre 2 dates que j'ai utilisées après la requête

db.collection.find( "SelectedDate": {'$gte': SelectedDate1,'$lt': SelectedDate2}})
ayu pour toi
la source