Encontre objetos entre duas datas MongoDB

406

Venho brincando armazenando tweets dentro do mongodb, cada objeto fica assim:

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

Como eu escreveria uma consulta que verifica o created_at e encontra todos os objetos entre 18:47 e 19:00? Preciso atualizar meus documentos para que as datas sejam armazenadas em um formato específico?

Tom
fonte
Você não diz sobre qual campo deseja consultar?
Shingara
Ops, desejo consultar o created_at e encontrar todos entre duas datas.
Tom
Estou curioso para saber por que não usar o carimbo de data / hora, vantagens do uso do Obj. Data?
Leo
4
@Leo A maior vantagem do objeto Date em milissegundos desde a época ou o que quer que seja a legibilidade humana. Nesse caso, definir seu intervalo inicial para 2010-04-29T00:00:00.000Zé muito mais fácil do que calcular a mesma data / hora em milissegundos. Você também pode fazer a conversão de fuso horário com bastante facilidade. Além disso, as datas já lidam com coisas como dias bissextos, segundos bissextos e outras esquisitices que você normalmente não deseja.
Thunderforge 23/01

Respostas:

620

Consultar um intervalo de datas (mês ou dia específico) no MongoDB Cookbook tem uma explicação muito boa sobre o assunto, mas abaixo está algo que eu mesmo tentei e 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)" }

Com base em meus experimentos, você precisará serializar suas datas em um formato compatível com o MongoDB, porque o seguinte forneceu resultados de pesquisa indesejados.

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

No segundo exemplo, nenhum resultado era esperado, mas ainda havia um. Isso ocorre porque é feita uma comparação básica de cadeias.

ponzao
fonte
3
Parece interessante, mas a data armazenada precisa estar em um formato específico. Acabei de armazenar o que foi fornecido pelo twitter, isso precisa ser alterado para um formato diferente?
Tom
7
Você provavelmente armazenou os registros de data e hora como seqüências de caracteres, portanto, acho que o MongoDB não perceberá que são datas de fato. Assim, fazer uma consulta de intervalo neles resultaria em uma consulta de intervalo alfabético (por exemplo, "Jan Mon 01.01.2010" antes de "Jan Sun 01.01.1000"). Provavelmente faria sentido formatar todos os dados de data no formato MongoDB, que eu acho que é apenas uma data JavaScript simples.
Ponzao 31/05
2
Eu apenas usei isso para converter minhas cordas para datar objetos stackoverflow.com/questions/2900674/...
Tom
OK fixe! Eu acho que as consultas de intervalo mencionadas no livro de receitas devem funcionar então, você já as experimentou?
Ponzao 31/05
Sim, uma vez que eu estava armazenando as datas corretas, os exemplos de livros de receitas funcionaram como esperado.
Tom
35

Esclarecer. O que é importante saber é que:

  • Sim, você precisa passar um objeto Data Javascript.
  • Sim, tem que ser amigável ao ISODate
  • Sim, pela minha experiência em fazer isso funcionar, você precisa manipular a data para a ISO
  • Sim, trabalhar com datas geralmente é sempre um processo tedioso, e o mongo não é exceção

Aqui está um trecho de código de trabalho, onde fazemos um pouco de manipulação de data para garantir que o Mongo (aqui estou usando o módulo mongoose e deseje resultados para linhas cujo atributo date seja menor que (antes) a data fornecida como parâmetro myDate)) corretamente:

var inputDate = new Date(myDate.toISOString());
MyModel.find({
    'date': { $lte: inputDate }
})
arcseldon
fonte
11
Mais um para maior clareza. Se você estiver usando um momento no back-end, ele ainda manterá a função toISOString (). Eu uso o momento para adicionar e subtrair tempo para minhas consultas.
VocoJax 28/02/19
17

O MongoDB, na verdade, armazena os milésimos de uma data como int (64), conforme prescrito por http://bsonspec.org/#/specification

No entanto, pode ficar bastante confuso quando você recupera datas, pois o driver do cliente instancia um objeto de data com seu próprio fuso horário local. O driver JavaScript no console mongo certamente fará isso.

Portanto, se você se preocupa com o fuso horário, saiba o que deve ser quando recuperá-lo. Isso não deve importar muito para as consultas, pois ainda equivale à mesma int (64), independentemente do fuso horário em que o objeto de data está (espero). Mas eu definitivamente faria consultas com objetos de data reais (não seqüências de caracteres) e deixava o motorista fazer suas coisas.

Ben Smith
fonte
16

Python e pymongo

Localizando objetos entre duas datas no Python com pymongona coleção posts(com base no 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)

Onde {"$gte": from_date, "$lt": to_date}especifica o intervalo em termos de datetime.datetimetipos.

Anton Tarasenko
fonte
Isto não está a funcionar. Sempre que executo essa consulta, recebo, por padrão, resposta completa e não resposta 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();

Substitua collectionpelo nome da coleção que você deseja executar a consulta

GSK
fonte
3
O que isso acrescenta à resposta aceita (fornecida sete anos antes)?
Dan Dascalescu
11
@DanDascalescu -pode não ter acrescentado nada, mas qual é a sua preocupação aqui?
GSK
17
Respostas duplicadas desperdiçam o tempo das pessoas.
Dan Dascalescu 23/07
10

Use este código para encontrar o registro entre duas datas usando $gtee $lt:

db.CollectionName.find({"whenCreated": {
    '$gte': ISODate("2018-03-06T13:10:40.294Z"),
    '$lt': ISODate("2018-05-06T13:10:40.294Z")
}});
Sunil Pal
fonte
O que isso acrescenta à resposta aceita, fornecida 8 anos antes?
Dan Dascalescu
7

Usando com Moment.js e operadores de consulta de comparação

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

Converta suas datas no fuso horário GMT enquanto as coloca no Mongo. Dessa forma, nunca há um problema de fuso horário. Em seguida, basta fazer as contas no campo twitter / fuso horário quando você retirar os dados para apresentação.

heregear
fonte
2

Por que não converter a string em um número inteiro no formato YYYYMMDDHHMMSS? Cada incremento de tempo criaria um número inteiro maior e você poderá filtrar os números inteiros em vez de se preocupar em converter para o tempo ISO.

ZacharyST
fonte
Porque o tempo não está apenas acontecendo no meu fuso horário local.
Michael Cole
2
Isso se torna um pesadelo quando você começa a ter que converter a hora para esse formato em qualquer lugar. Se você for fazer algo assim, use pelo menos o valor de retorno de .getTime () de um objeto de data JS.
Nikk wong
11
é por isso que sempre armazenamos dados no UTC
codewandler
2

use $ gte e $ lte para encontrar dados da data no 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
fonte
11
Leve erro de digitação na sua resposta $ gte não $ get :)
Bob
11
Desculpe, eu respondi condição muito cansado, então eu cometi um erro. Obrigado por sua ajuda para atualizar minha resposta. você fez um bom trabalho :) @Bob
KARTHIKEYAN.A
2

Você também pode verificar isso. Se você estiver usando esse método, use a função de análise para obter valores do Mongo Database:

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

Você também pode verificar isso ou tentar, em vez 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
fonte
0

Eu tentei neste modelo de acordo com meus requisitos. Eu preciso armazenar uma data sempre que um objeto for criado posteriormente. Eu quero recuperar todos os registros (documentos) entre duas datas no meu arquivo html. Eu estava usando o seguinte 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>

no meu arquivo py (python) eu o converti em "iso fomate" da seguinte maneira

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

e salvei na minha coleção dbmongo com "SelectedDate" como campo na minha coleção

recuperar dados ou documentos entre 2 datas que eu usei após a consulta

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