Czy mogę wysyłać zapytania do obiektu MongoDB ObjectId według daty?

Odpowiedzi:

224

Umieszczanie znaczników czasu w ObjectId obejmuje zapytania oparte na datach osadzonych w ObjectId z dużą szczegółowością.

Krótko w kodzie JavaScript:

/* This function returns an ObjectId embedded with a given datetime */
/* Accepts both Date object and string input */

function objectIdWithTimestamp(timestamp) {
    /* Convert string date to Date object (otherwise assume timestamp is a date) */
    if (typeof(timestamp) == 'string') {
        timestamp = new Date(timestamp);
    }

    /* Convert date object to hex seconds since Unix epoch */
    var hexSeconds = Math.floor(timestamp/1000).toString(16);

    /* Create an ObjectId with that hex timestamp */
    var constructedObjectId = ObjectId(hexSeconds + "0000000000000000");

    return constructedObjectId
}


/* Find all documents created after midnight on May 25th, 1980 */
db.mycollection.find({ _id: { $gt: objectIdWithTimestamp('1980/05/25') } });
Leftium
źródło
29
Bardzo przydatne ... FYI, możesz zapisać tę funkcję w swoim ~/.mongorc.jspliku, aby była dostępna podczas mongouruchamiania powłoki.
Stennie
1
Otrzymuję ReferenceError: ObjectId nie jest zdefiniowany. Jak to naprawić?
Peter
3
Używam nodejs z mongodbnative. Naprawiono „niezdefiniowany błąd” przez dołączenie var ObjectId = require ('mongodb'). ObjectID;
Peter
1
Jeśli używasz Mongoskin tak jak ja: Zmień ObjectId(hexSeconds + "0000000000000000");nadb.ObjectID.createFromHexString(hexSeconds + "0000000000000000");
Anders Östman,
2
Lub w Mongoose zamień na ObjectId(): require('mongoose').Types.ObjectId()- gdzie require('mongoose')jest zainicjowana / skonfigurowana instancja Mongoose.
toblerpwn
34

Użycie wbudowanej funkcji dostarczanej przez sterowniki mongodb w Node.js umożliwia wykonywanie zapytań według dowolnego znacznika czasu:

var timestamp = Date.now();
var objectId = ObjectID.createFromTime(timestamp / 1000);

Alternatywnie, aby wyszukać rekordy przed bieżącą godziną, możesz po prostu wykonać:

var objectId = new ObjectID(); // or ObjectId in the mongo shell

Źródło: http://mongodb.github.io/node-mongodb-native/api-bson-generated/objectid.html

jksdua
źródło
3
Jest to najlepszy / najłatwiejszy sposób tworzenia ObjectId na podstawie sygnatury czasowej w javascript env. O to prosi
opera
34

W programie pymongomożna to zrobić w następujący sposób:

import datetime
from bson.objectid import ObjectId
mins = 15
gen_time = datetime.datetime.today() - datetime.timedelta(mins=mins) 
dummy_id = ObjectId.from_datetime(gen_time)
result = list(db.coll.find({"_id": {"$gte": dummy_id}}))
radtek
źródło
Uwaga, użycie datetime.datetime.utcnow () lub datetime.datetime.today () zwróci ten sam wynik. Data i godzina jest obsługiwana za Ciebie.
radtek
Alternatywnie, bez użycia pymongozależności: epoch_time_hex = format(int(time.time()), 'x') (nie zapomnij dodać zer w zapytaniu) Użyto pakietu czasu ( import time).
VasiliNovikov
Zrozum, że OP prosił o javascript, ale to naprawdę pomogło mi uprościć mój kod. Dzięki.
Fred S
14

Ponieważ pierwsze 4 bajty ObjectId reprezentują sygnaturę czasową , aby sprawdzić chronologicznie swoją kolekcję, po prostu uporządkuj według identyfikatora:

# oldest first; use pymongo.DESCENDING for most recent first
items = db.your_collection.find().sort("_id", pymongo.ASCENDING)

Po otrzymaniu dokumentów możesz uzyskać czas wygenerowania ObjectId w następujący sposób:

id = some_object_id
generation_time = id.generation_time
atp
źródło
1
Miałem nadzieję na coś, co faktycznie pozwoliłoby zrobić takie rzeczy, jak uzyskanie liczby obiektów utworzonych przed określonym czasem przy użyciu czasu osadzonego w ObjectId, ale wydaje się, że nie byłoby to bezpośrednio dostępne. Dzięki.
Zach,
1
Możesz to zrobić, spójrz na odpowiedź Leftium.
ATP
13

jak znaleźć polecenie Znajdź polecenie (od tej daty [2015-1-12] do tej daty [2015-1-15]):

db.collection.find ({_ id: {$ gt: ObjectId (Math.floor ((new Date ('2015/1/12'))) / 1000) .toString (16) + "0000000000000000"), $ lt: ObjectId (Math.floor ((new Date ('2015/1/15')) / 1000) .toString (16) + "0000000000000000")}}). Pretty ()

Policz polecenie (od tej daty [2015-1-12] do tej daty [2015-1-15]):

db.collection.count ({_ id: {$ gt: ObjectId (Math.floor ((new Date ('2015/1/12'))) / 1000) .toString (16) + "0000000000000000"), $ lt: ObjectId (Math.floor ((new Date ('2015/1/15')) / 1000) .toString (16) + "0000000000000000")}})

Usuń polecenie (od tej daty [2015-1-12] do tej daty [2015-1-15]):

db.collection.remove ({_ id: {$ gt: ObjectId (Math.floor ((new Date ('2015/1/12'))) / 1000) .toString (16) + "0000000000000000"), $ lt: ObjectId (Math.floor ((new Date ('2015/1/15')) / 1000) .toString (16) + "0000000000000000")}})

Karthickkumar Nagaraj
źródło
10

Możesz użyć $convert funkcji do wyodrębnienia daty z ObjectId począwszy od wersji 4.0.

Coś jak

$convert: { input: "$_id", to: "date" } 

Możesz sprawdzić datę, porównując czas rozpoczęcia i zakończenia dla daty.

db.collectionname.find({
  "$expr":{
    "$and":[
      {"$gte":[{"$convert":{"input":"$_id","to":"date"}}, ISODate("2018-07-03T00:00:00.000Z")]},
      {"$lte":[{"$convert":{"input":"$_id","to":"date"}}, ISODate("2018-07-03T11:59:59.999Z")]}
    ]
  }
})

LUB

Możesz użyć skrótu, $toDateaby osiągnąć to samo.

db.collectionname.find({
  "$expr":{
    "$and":[
      {"$gte":[{"$toDate":"$_id"}, ISODate("2018-07-03T00:00:00.000Z")]},
      {"$lte":[{"$toDate":"$_id"},ISODate("2018-07-03T11:59:59.999Z")]}
    ]
  }
})
Sagar Veeram
źródło
Chciałem tylko zapytać, jak używa $ convert OR $ toDate, Mongo najpierw będzie musiało przekonwertować, a następnie porównać, czy dokument jest obecny w zakresie, czy nie, ale gdybym użył podejścia zaakceptowanej odpowiedzi, konwertując datę na ObjectId, musiałbym tylko zrobić po stronie klienta i tylko raz, więc nie sądzisz, że rozwiązanie będzie bardziej wydajne niż to? w każdym razie dzięki za poinformowanie, że ci dwaj operatorzy też istnieją :)
Sudhanshu Gaur
7

Aby pobrać dokumenty sprzed 60 dni w kolekcji mongo, użyłem poniższego zapytania w powłoce.

db.collection.find({_id: {$lt:new ObjectId( Math.floor(new Date(new Date()-1000*60*60*24*60).getTime()/1000).toString(16) + "0000000000000000" )}})
Murali
źródło
1
Użyj $ gt zamiast $ lt. W przeciwnym razie znajdzie dokumenty wstawione wcześniej (dziś-60 dni).
yoooshi,
1
@Vivek Pierwsze 4 bajty ObjectId reprezentują liczbę sekund od epoki unixowej (1970/1/1 00:00:00 UTC) i jako takie można go używać z większymi niż ($ gt) i mniejszymi niż ($ lt), aby znaleźć obiekty, które zostały utworzone w określonym oknie.
John
5

Jeśli chcesz wykonać zapytanie o zakres, możesz to zrobić tak, jak w tym poście . Na przykład zapytanie o konkretny dzień (np. 4 kwietnia 2015):

> var objIdMin = ObjectId(Math.floor((new Date('2015/4/4'))/1000).toString(16) + "0000000000000000")
> var objIdMax = ObjectId(Math.floor((new Date('2015/4/5'))/1000).toString(16) + "0000000000000000")
> db.collection.find({_id:{$gt: objIdMin, $lt: objIdMax}}).pretty()
AbdelHady
źródło
2

Korzystając z MongoObjectID powinieneś również znaleźć wyniki podane poniżej

db.mycollection.find({ _id: { $gt: ObjectId("5217a543dd99a6d9e0f74702").getTimestamp().getTime()}});
Yogesh
źródło
3
Twoja instrukcja zapytania zakłada, że ​​znasz wartość ObjectId na początku, co nie zawsze ma miejsce.
Dwight Spencer,
0

W szynach mongoidmożesz zapytać za pomocą

  time = Time.utc(2010, 1, 1)
  time_id = ObjectId.from_time(time)
  collection.find({'_id' => {'$lt' => time_id}})
Lakhani Aliraza
źródło