Finden Sie Objekte zwischen zwei Daten MongoDB

406

Ich habe herumgespielt, um Tweets in Mongodb zu speichern. Jedes Objekt sieht folgendermaßen aus:

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

Wie würde ich eine Abfrage schreiben, die das created_at überprüft und alle Objekte zwischen 18:47 und 19:00 findet? Muss ich meine Dokumente aktualisieren, damit die Daten in einem bestimmten Format gespeichert werden?

Tom
quelle
Sie sagen nicht, welches Feld Sie abfragen möchten?
Shingara
Hoppla, ich möchte das created_at abfragen und alle zwischen zwei Daten finden.
Tom
Ich bin neugierig, warum nicht Zeitstempel verwenden, irgendwelche Vorteile durch die Verwendung des Datumsobjekts?
Leo
4
@Leo Der größte Vorteil mit dem Date-Objekt gegenüber Millisekunden seit der Epoche oder was auch immer ist die menschliche Lesbarkeit. In diesem Fall ist das Einstellen Ihres Startbereichs 2010-04-29T00:00:00.000Zviel einfacher als das Berechnen des gleichen Datums / der gleichen Uhrzeit in Millisekunden. Sie können auch ganz einfach Zeitzonen konvertieren. Außerdem behandeln Daten bereits Dinge wie Schalttage, Schaltsekunden und andere Kuriositäten, die Sie normalerweise nicht selbst behandeln möchten.
Thunderforge

Antworten:

619

Das Abfragen eines Datumsbereichs (bestimmter Monat oder Tag) im MongoDB-Kochbuch enthält eine sehr gute Erklärung zu diesem Thema, aber im Folgenden habe ich es selbst ausprobiert und es scheint zu funktionieren.

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

Basierend auf meinen Experimenten müssen Sie Ihre Daten in ein von MongoDB unterstütztes Format serialisieren, da das Folgende unerwünschte Suchergebnisse ergab.

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

Im zweiten Beispiel wurden keine Ergebnisse erwartet, aber es wurde immer noch eines erhalten. Dies liegt daran, dass ein grundlegender Zeichenfolgenvergleich durchgeführt wird.

ponzao
quelle
3
Sieht interessant aus, muss aber das gespeicherte Datum in einem bestimmten Format haben. Ich habe gerade gespeichert, was von Twitter bereitgestellt wurde. Muss dies in ein anderes Format geändert werden?
Tom
7
Sie haben die Zeitstempel wahrscheinlich als Zeichenfolgen gespeichert, daher wird MongoDB vermutlich nicht erkennen, dass es sich tatsächlich um Daten handelt. Eine Bereichsabfrage würde daher zu einer alphabetischen Bereichsabfrage führen (z. B. "Jan Mo 01.01.2010" vor "Jan So 01.01.1000"). Es wäre wahrscheinlich sinnvoll, alle Datumsdaten in das MongoDB-Format zu formatieren, das meiner Meinung nach nur ein einfaches JavaScript-Datum ist.
Ponzao
2
Ich habe dies nur verwendet, um meine Zeichenfolgen in Datumsobjekte
Tom
Okay cool! Ich würde vermuten, dass die im Kochbuch erwähnten Bereichsabfragen dann funktionieren sollten. Haben Sie sie bereits ausprobiert?
Ponzao
Ja, sobald ich die Daten korrekt gespeichert hatte, funktionierten die Kochbuchbeispiele wie erwartet.
Tom
35

Zu klären. Es ist wichtig zu wissen, dass:

  • Ja, Sie müssen ein Javascript Date-Objekt übergeben.
  • Ja, es muss ISODate-freundlich sein
  • Ja, meiner Erfahrung nach müssen Sie das Datum auf ISO ändern, damit dies funktioniert
  • Ja, das Arbeiten mit Daten ist im Allgemeinen immer ein langwieriger Prozess, und Mongo ist keine Ausnahme

Hier ist ein funktionierender Codeausschnitt, in dem wir ein wenig Datumsmanipulation durchführen, um sicherzustellen, dass Mongo (hier verwende ich das Mungo-Modul und möchte Ergebnisse für Zeilen, deren Datumsattribut kleiner als (vor) dem als myDate-Parameter angegebenen Datum ist) verarbeiten kann es richtig:

var inputDate = new Date(myDate.toISOString());
MyModel.find({
    'date': { $lte: inputDate }
})
arcseldon
quelle
1
Plus eins für Klarheit. Wenn Sie Moment im Backend verwenden, bleibt die Funktion toISOString () erhalten. Ich benutze den Moment, um Zeit für meine Abfragen zu addieren und zu subtrahieren.
VocoJax
17

MongoDB speichert die Millis eines Datums tatsächlich als int (64), wie von http://bsonspec.org/#/specification vorgeschrieben

Es kann jedoch ziemlich verwirrend werden, wenn Sie Daten abrufen, da der Client-Treiber ein Datumsobjekt mit seiner eigenen lokalen Zeitzone instanziiert. Der JavaScript-Treiber in der Mongo-Konsole wird dies sicherlich tun.

Wenn Sie sich also für Ihre Zeitzonen interessieren, stellen Sie sicher, dass Sie wissen, was es sein soll, wenn Sie es zurückerhalten. Dies sollte für die Abfragen nicht so wichtig sein, da es immer noch dem gleichen int (64) entspricht, unabhängig davon, in welcher Zeitzone sich Ihr Datumsobjekt befindet (ich hoffe). Aber ich würde definitiv Abfragen mit tatsächlichen Datumsobjekten (nicht mit Zeichenfolgen) durchführen und den Treiber seine Sache machen lassen.

Ben Smith
quelle
16

Python und pymongo

Suchen von Objekten zwischen zwei Daten in Python mit pymongoin der Sammlung posts(basierend auf dem 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)

Wobei {"$gte": from_date, "$lt": to_date}der Bereich in Bezug auf datetime.datetimeTypen angegeben wird.

Anton Tarasenko
quelle
Das funktioniert nicht. Immer wenn ich diese Abfrage ausführe, erhalte ich standardmäßig eine vollständige Antwort und keine gefilterte Antwort
Abhay Bh
14
db.collection.find({"createdDate":{$gte:new ISODate("2017-04-14T23:59:59Z"),$lte:new ISODate("2017-04-15T23:59:59Z")}}).count();

Ersetzen Sie collectiondurch den Namen der Sammlung, die Sie abfragen möchten

GSK
quelle
3
Was trägt dies zur akzeptierten Antwort bei (7 Jahre zuvor bereitgestellt)?
Dan Dascalescu
1
@ DanDascalescu - Vielleicht hat es nichts hinzugefügt, aber was ist Ihr Anliegen hier?
GSK
17
Doppelte Antworten verschwenden die Zeit der Menschen.
Dan Dascalescu
10

Verwenden Sie diesen Code, um den Datensatz zwischen zwei Daten mit $gteund zu finden $lt:

db.CollectionName.find({"whenCreated": {
    '$gte': ISODate("2018-03-06T13:10:40.294Z"),
    '$lt': ISODate("2018-05-06T13:10:40.294Z")
}});
Sunil Pal
quelle
Was trägt dies zu der akzeptierten Antwort bei, die 8 Jahre zuvor gegeben wurde?
Dan Dascalescu
7

Verwenden mit Moment.js und Vergleichsabfrageoperatoren

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

Konvertieren Sie Ihre Daten in die GMT-Zeitzone, während Sie sie in Mongo einfügen. Auf diese Weise gibt es nie ein Zeitzonenproblem. Berechnen Sie dann einfach das Feld Twitter / Zeitzone, wenn Sie die Daten zur Präsentation zurückziehen.

Heregear
quelle
2

Warum nicht die Zeichenfolge in eine Ganzzahl der Form JJJJMMTTHHMMSS konvertieren? Jedes Zeitinkrement würde dann eine größere Ganzzahl erzeugen, und Sie können nach den Ganzzahlen filtern, anstatt sich Gedanken über die Konvertierung in ISO-Zeit zu machen.

ZacharyST
quelle
Weil die Zeit nicht nur in meiner lokalen Zeitzone passiert.
Michael Cole
2
Dies wird zu einem Albtraum, sobald Sie die Zeit überall in und aus diesem Format konvertieren müssen. Wenn Sie mindestens so etwas tun möchten, verwenden Sie mindestens den Rückgabewert von .getTime () aus einem JS-Datumsobjekt.
Nikk Wong
1
Deshalb speichern wir Daten immer in UTC
Codewandler
2

Verwenden Sie $ gte und $ lte , um zwischen Datumsdaten in Mongodb zu suchen

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
quelle
1
Leichter Tippfehler in Ihrer Antwort $ gte not $ get :)
Bob
1
Entschuldigung, ich habe sehr müde geantwortet, also habe ich einen Fehler gemacht. Vielen Dank für Ihre Hilfe bei der Aktualisierung meiner Antwort. Sie haben gute Arbeit geleistet :) @ Bob
KARTHIKEYAN.A
2

Sie können dies auch überprüfen. Wenn Sie diese Methode verwenden, verwenden Sie die Analysefunktion, um Werte aus der Mongo-Datenbank abzurufen:

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

Sie können dies auch überprüfen oder versuchen, anstatt Aggregat zu verwenden

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

Ich habe in diesem Modell versucht, gemäß meinen Anforderungen ein Datum zu speichern, wenn ein Objekt später erstellt wird. Ich möchte alle Datensätze (Dokumente) zwischen zwei Daten in meiner HTML-Datei abrufen. Ich habe das folgende Format verwendet: MM / TT / JJJJ

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

in meiner py (python) datei habe ich es folgendermaßen in "iso fomate" konvertiert

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

und in meiner dbmongo-Sammlung mit "SelectedDate" als Feld in meiner Sammlung gespeichert

Um Daten oder Dokumente zwischen bis zu 2 Daten abzurufen, habe ich folgende Abfrage verwendet

db.collection.find( "SelectedDate": {'$gte': SelectedDate1,'$lt': SelectedDate2}})
Ayu für dich
quelle