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?


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.


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

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.


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.


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


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

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

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.


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.


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

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

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

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

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}})
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.