Encuentra objetos entre dos fechas MongoDB

406

He estado jugando almacenando tweets dentro de mongodb, cada objeto se ve así:

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

¿Cómo escribiría una consulta que verifique el created_at y encuentre todos los objetos entre las 18:47 y las 19:00? ¿Necesito actualizar mis documentos para que las fechas se almacenen en un formato específico?

Tom
fuente
¿No dice sobre qué campo desea consultar?
shingara
Vaya, quiero consultar el created_at y encontrar todo entre dos fechas.
Tom
Tengo curiosidad por saber por qué no usar la marca de tiempo, ¿alguna ventaja al usar el Obj. Fecha?
Leo
44
@Leo La mayor ventaja con el objeto Date durante milisegundos desde la época o lo que sea es la legibilidad humana. En este caso, establecer su rango de inicio en 2010-04-29T00:00:00.000Zes mucho más fácil que calcular la misma fecha / hora en milisegundos. También puede hacer la conversión de zona horaria con bastante facilidad. Además, las fechas ya manejan cosas como días bisiestos, segundos bisiestos y otras rarezas que generalmente no desea manejar usted mismo.
Thunderforge el

Respuestas:

620

Consultar un intervalo de fechas (mes o día específico) en el libro de cocina de MongoDB tiene una muy buena explicación al respecto, pero a continuación hay algo que probé yo mismo y parece funcionar.

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

Según mis experimentos, necesitará serializar sus fechas en un formato compatible con MongoDB, porque lo siguiente dio resultados de búsqueda no deseados.

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

En el segundo ejemplo no se esperaban resultados, pero todavía se obtuvo uno. Esto se debe a que se realiza una comparación básica de cadenas.

ponzao
fuente
3
Parece interesante, pero la fecha almacenada debe estar en un formato específico. Acabo de almacenar lo que proporcionó Twitter. ¿Es necesario cambiarlo a un formato diferente?
Tom
77
Probablemente haya almacenado las marcas de tiempo como cadenas, por lo que supongo que MongoDB no se dará cuenta de que en realidad son fechas. Por lo tanto, hacer una consulta de rango en ellos daría como resultado una consulta de rango alfabético (por ejemplo, "Jan Mon 01.01.2010" antes de "Jan Sun 01.01.1000"). Probablemente tendría sentido formatear todos los datos de fecha en el formato MongoDB, que creo que es simplemente JavaScript Date.
ponzao
2
Acabo de usar esto para convertir mis cadenas en objetos de fecha stackoverflow.com/questions/2900674/…
Tom
Ok, genial! Supongo que las consultas de rango mencionadas en el libro de cocina deberían funcionar, ¿ya las probaste?
ponzao
Sí, una vez que estaba almacenando las fechas correctas, los ejemplos de libros de cocina funcionaron como se esperaba.
Tom
35

Para aclarar. Lo importante es saber que:

  • Sí, tiene que pasar un objeto de Fecha de Javascript.
  • Sí, tiene que ser ISODate amigable
  • Sí, desde mi experiencia haciendo que esto funcione, necesita manipular la fecha a ISO
  • Sí, trabajar con fechas generalmente es siempre un proceso tedioso, y Mongo no es una excepción

Aquí hay un fragmento de código que funciona, donde hacemos un poco de manipulación de la fecha para asegurar que Mongo (aquí estoy usando el módulo de mangosta y quiero resultados para las filas cuyo atributo de fecha es menor que (antes) la fecha dada como myDate param) puede manejar correctamente:

var inputDate = new Date(myDate.toISOString());
MyModel.find({
    'date': { $lte: inputDate }
})
arcseldon
fuente
1
Más uno para mayor claridad. Si está utilizando el momento en el back-end, aún conserva la función toISOString (). Utilizo momento para sumar y restar tiempo para mis consultas.
VocoJax
17

MongoDB en realidad almacena los millis de una fecha como int (64), según lo prescrito por http://bsonspec.org/#/specification

Sin embargo, puede ser bastante confuso cuando recupera fechas, ya que el controlador del cliente creará una instancia de un objeto de fecha con su propia zona horaria local. El controlador de JavaScript en la consola mongo ciertamente hará esto.

Entonces, si te interesan tus zonas horarias, asegúrate de saber lo que se supone que debe ser cuando lo recuperes. Esto no debería importar tanto para las consultas, ya que aún equivaldrá al mismo int (64), independientemente de la zona horaria en la que se encuentre su objeto de fecha (espero). Pero definitivamente haría consultas con objetos de fecha reales (no cadenas) y dejaría que el controlador haga lo suyo.

Ben Smith
fuente
16

Python y pymongo

Encontrar objetos entre dos fechas en Python con una pymongocolección posts(basada en el tutorial ):

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)

Donde {"$gte": from_date, "$lt": to_date}especifica el rango en términos de datetime.datetimetipos.

Anton Tarasenko
fuente
Esto no está funcionando. Cada vez que ejecuto esta consulta obtengo por defecto una respuesta completa y no una respuesta filtrada
Abhay Bh
14
db.collection.find({"createdDate":{$gte:new ISODate("2017-04-14T23:59:59Z"),$lte:new ISODate("2017-04-15T23:59:59Z")}}).count();

Reemplace collectioncon el nombre de la colección que desea ejecutar la consulta

GSK
fuente
3
¿Qué agrega esto a la respuesta aceptada (proporcionada 7 años antes)?
Dan Dascalescu
1
@DanDascalescu -puede que no haya agregado nada pero ¿cuál es su preocupación aquí?
GSK
17
Las respuestas duplicadas pierden el tiempo de las personas.
Dan Dascalescu
10

Use este código para buscar el registro entre dos fechas usando $gtey $lt:

db.CollectionName.find({"whenCreated": {
    '$gte': ISODate("2018-03-06T13:10:40.294Z"),
    '$lt': ISODate("2018-05-06T13:10:40.294Z")
}});
Sunil Pal
fuente
¿Qué agrega esto a la respuesta aceptada, proporcionada 8 años antes?
Dan Dascalescu
7

Uso con Moment.js y operadores de consulta de comparación

  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
fuente
2

Convierta sus fechas a la zona horaria GMT a medida que las introduce en Mongo. De esa manera nunca hay un problema de zona horaria. Luego, haga los cálculos en el campo twitter / zona horaria cuando retire los datos para presentarlos.

heregear
fuente
2

¿Por qué no convertir la cadena a un entero de la forma AAAAMMDDHHMMSS? Cada incremento de tiempo crearía un número entero más grande, y puede filtrar los números enteros en lugar de preocuparse por la conversión al tiempo ISO.

ZacharyST
fuente
Porque el tiempo no solo está sucediendo en mi zona horaria local.
Michael Cole
2
Esto se convierte en una pesadilla una vez que comienza a tener que convertir el tiempo hacia y desde ese formato en todas partes. Si va a hacer algo como esto, al menos use el valor de retorno de .getTime () de un objeto de fecha JS.
Nikk Wong
1
es por eso que siempre almacenamos datos en UTC
codewandler
2

use $ gte y $ lte para buscar datos entre fechas en 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
fuente
1
Ligero error tipográfico en su respuesta $ gte not $ get :)
Bob
1
Lo siento, respondí muy cansado, así que me equivoqué. Gracias por su ayuda para actualizar mi respuesta. hiciste un buen trabajo :) @Bob
KARTHIKEYAN.A
2

También puedes ver esto. Si está utilizando este método, utilice la función de análisis para obtener valores de la base de datos Mongo:

db.getCollection('user').find({
    createdOn: {
        $gt: ISODate("2020-01-01T00:00:00.000Z"),
        $lt: ISODate("2020-03-01T00:00:00.000Z")
    }
})
Kevin007
fuente
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
fuente
1

También puede verificar esto o probar en lugar de usar agregado

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

Lo intenté en este modelo según mis requisitos. Necesito almacenar una fecha cada vez que se crea un objeto más tarde. Quiero recuperar todos los registros (documentos) entre dos fechas en mi archivo html. Estaba usando el siguiente formato mm / dd / 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>

en mi archivo py (python) lo convertí en "iso fomate" de la siguiente manera

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

y guardado en mi colección dbmongo con "SelectedDate" como campo en mi colección

para recuperar datos o documentos entre 2 fechas utilicé la siguiente consulta

db.collection.find( "SelectedDate": {'$gte': SelectedDate1,'$lt': SelectedDate2}})
ayu para ti
fuente