Znajdź obiekty między dwiema datami MongoDB


406

Bawiłem się, przechowując tweety wewnątrz mongodb, każdy obiekt wygląda następująco:

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

Jak napisać kwerendę, która sprawdza created_at i znajduje wszystkie obiekty między 18:47 a 19:00? Czy muszę aktualizować dokumenty, aby daty były przechowywane w określonym formacie?


Nie mówisz o które pole chcesz zapytać?
shingara

Ups, chcę wysłać zapytanie o utworzone_at i znaleźć wszystkie między dwiema datami.
Tom

Jestem ciekawy, że dlaczego nie skorzystać ze znacznika czasu, jakieś korzyści wynikające z użycia Date Obj?
Leo

4
@Leo Największa zaleta obiektu Date w stosunku do milisekund od epoki lub czegokolwiek, co jest czytelnością dla człowieka. W takim przypadku ustawienie zakresu początkowego na 2010-04-29T00:00:00.000Zjest znacznie łatwiejsze niż obliczenie tej samej daty / godziny w milisekundach. Możesz również dość łatwo dokonać konwersji strefy czasowej. Ponadto Daty już obsługują takie dni przestępne, sekundy przestępne i inne osobliwości, z którymi zwykle nie chcesz sobie poradzić.
Thunderforge

Odpowiedzi:


619

Zapytanie o zakres dat (określony miesiąc lub dzień) w książce kucharskiej MongoDB ma bardzo dobre wytłumaczenie na ten temat, ale poniżej jest coś, co sam wypróbowałem i wydaje się, że działa.

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

Na podstawie moich eksperymentów będziesz musiał serializować swoje daty do formatu obsługiwanego przez MongoDB, ponieważ poniższe wyniki dały niepożądane wyniki wyszukiwania.

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

W drugim przykładzie nie oczekiwano żadnych rezultatów, ale wciąż był jeden. Wynika to z tego, że podstawowe porównanie ciągów zostało wykonane.


3
Wygląda interesująco, ale czy przechowywana data musi być w określonym formacie. Właśnie przechowałem to, co dostarczył Twitter, czy trzeba to zmienić na inny format?
Tom

7
Prawdopodobnie zapisałeś znaczniki czasu jako ciągi, więc zgaduję, że MongoDB nie zda sobie sprawy, że tak naprawdę są datami. Zatem wykonanie na nich zapytania o zakres skutkowałoby alfabetycznym zapytaniem o zakres (np. „Jan Mon 01.01.2010” znajdowałby się przed „Jan Sun 01.01.1000”). Prawdopodobnie sensowne byłoby sformatowanie wszystkich danych daty w formacie MongoDB, który moim zdaniem jest zwykłą datą JavaScript.
ponzao

2
Właśnie tego użyłem, aby przekonwertować moje ciągi na obiekty date stackoverflow.com/questions/2900674/…
Tom

Ok spoko! Sądzę, że zapytania o zakres wspomniane w książce kucharskiej powinny wtedy działać, czy już je wypróbowałeś?
ponzao

Tak, kiedy już zapisałem daty, przykłady książek kucharskich działały zgodnie z oczekiwaniami.
Tom

35

Wyjaśnić. Ważne jest, aby wiedzieć, że:

  • Tak, musisz przekazać obiekt Javascript Date.
  • Tak, musi być przyjazny ISODate
  • Tak, z mojego doświadczenia, że ​​to działa, musisz zmienić datę na ISO
  • Tak, praca z datami jest zazwyczaj zawsze żmudnym procesem, a mongo nie jest wyjątkiem

Oto działający fragment kodu, w którym dokonujemy drobnej manipulacji datą, aby zapewnić, że Mongo (tutaj używam modułu mangusty i chcę wyników dla wierszy, których atrybut daty jest krótszy niż (przed) datą podaną jako parametr myDate) to poprawnie:

var inputDate = new Date(myDate.toISOString());
MyModel.find({
    'date': { $lte: inputDate }
})

1
Plus jeden dla przejrzystości. Jeśli używasz momentu w backend, nadal zachowuje funkcję toISOString (). Wykorzystuję moment, aby dodać i odjąć czas na moje zapytania.
VocoJax

17

MongoDB faktycznie przechowuje millis daty jako int (64), zgodnie z zaleceniami http://bsonspec.org/#/specification

Jednak pobieranie danych może być dość mylące, ponieważ sterownik klienta utworzy obiekt daty z własną lokalną strefą czasową. Sterownik JavaScript w konsoli mongo na pewno to zrobi.

Tak więc, jeśli zależy Ci na strefach czasowych, upewnij się, że wiesz, co to powinno być, kiedy je odzyskasz. Nie powinno to mieć większego znaczenia dla zapytań, ponieważ nadal będzie równe tej samej wartości int (64), niezależnie od strefy czasowej, w której znajduje się obiekt daty (mam nadzieję). Ale zdecydowanie robiłbym zapytania z rzeczywistymi obiektami daty (a nie ciągami znaków) i pozwalałbym sterownikowi robić swoje.


16

Python i pymongo

Znajdowanie obiektów między dwiema datami w Pythonie ze pymongozbiorami posts(na podstawie samouczka ):

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)

Gdzie {"$gte": from_date, "$lt": to_date}określa zakres pod względem datetime.datetimetypów.


To nie działa. Ilekroć uruchamiam to zapytanie, domyślnie otrzymuję pełną odpowiedź, a nie odpowiedź filtrowaną
Abhay Bh

14
db.collection.find({"createdDate":{$gte:new ISODate("2017-04-14T23:59:59Z"),$lte:new ISODate("2017-04-15T23:59:59Z")}}).count();

Zamień na collectionnazwę kolekcji, którą chcesz wykonać zapytanie


3
Co to dodaje do zaakceptowanej odpowiedzi (udzielonej 7 lat wcześniej)?
Dan Dascalescu

1
@ DanDascalescu - może nic nie dodało, ale o co się tutaj martwisz?
GSK,

17
Duplikaty odpowiedzi marnują czas ludzi.
Dan Dascalescu

10

Użyj tego kodu, aby znaleźć zapis między dwiema datami korzystania $gtei $lt:

db.CollectionName.find({"whenCreated": {
    '$gte': ISODate("2018-03-06T13:10:40.294Z"),
    '$lt': ISODate("2018-05-06T13:10:40.294Z")
}});

Co to dodaje do zaakceptowanej odpowiedzi udzielonej 8 lat wcześniej?
Dan Dascalescu

7

Korzystanie z Moment.js i operatorów zapytań porównawczych

  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) { ... });

2

Konwertuj daty na strefę czasową GMT, upychając je w Mongo. W ten sposób nigdy nie będzie problemu ze strefą czasową. Następnie po prostu wykonaj matematykę w polu Twitter / Strefa czasowa, gdy wyciągniesz dane z powrotem do prezentacji.


2

Dlaczego nie przekonwertować ciągu na liczbę całkowitą w postaci RRRRMMDDGGMMSS? Każdy przyrost czasu tworzyłby wówczas większą liczbę całkowitą i można filtrować liczby całkowite zamiast martwić się o konwersję do czasu ISO.


Ponieważ czas nie dzieje się tylko w mojej lokalnej strefie czasowej.
Michael Cole,

2
Staje się to koszmarem, gdy zaczniesz wszędzie konwertować czas do iz tego formatu. Jeśli zamierzasz zrobić coś takiego, przynajmniej użyj wartości zwracanej z .getTime () z obiektu daty JS.
nikk wong 10.04.17

1
dlatego zawsze przechowujemy dane w UTC
programista

2

użyj $ gte i $ lte, aby znaleźć dane między datami w 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")}})

1
Nieznaczna literówka w twojej odpowiedzi $ gte not $ get :)
Bob

1
Przepraszam, odpowiedziałem na bardzo zmęczony stan, więc popełniłem błąd. Dziękuję za pomoc w aktualizacji mojej odpowiedzi. wykonałeś dobrą robotę :) @Bob
KARTHIKEYAN.A

2

Możesz to również sprawdzić. Jeśli używasz tej metody, użyj funkcji parsowania, aby uzyskać wartości z bazy danych Mongo:

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

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`) ]
                        },
                    ]
                 }
              }
           }
        }
     }
])

1

Możesz to również sprawdzić lub spróbować zamiast używać agregacji

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

0

próbowałem w tym modelu zgodnie z moimi wymaganiami muszę zapisać datę, kiedy obiekt zostanie utworzony później Chcę pobrać wszystkie rekordy (dokumenty) między dwiema datami w moim pliku HTML Użyłem następującego formatu mm / dd / rrrr

<!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>

w moim pliku py (python) przekonwertowałem go na „iso fomate” w następujący sposób

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

i zapisane w mojej kolekcji dbmongo z „SelectedDate” jako polem w mojej kolekcji

aby pobrać dane lub dokumenty między 2 datami użyłem następującego zapytania

db.collection.find( "SelectedDate": {'$gte': SelectedDate1,'$lt': SelectedDate2}})
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.