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?

Tomek
źródło
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.

ponzao
źródło
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 }
})
arcseldon
źródło
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.

Ben Smith
źródło
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.

Anton Tarasenko
źródło
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

GSK
źródło
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")
}});
Sunil Pal
źródło
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) { ... });
Tính Ngô Quang
źródło
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.

heregear
źródło
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.

ZacharyST
źródło
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")}})
KARTHIKEYAN.A
źródło
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")
    }
})
Kevin007
źródło
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
źródło
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")
    }
})
Kevin007
źródło
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}})
dla ciebie
źródło