Могу ли я запросить MongoDB ObjectId по дате?

146

Я знаю, что ObjectIds содержит дату, когда они были созданы. Есть ли способ запросить этот аспект ObjectId?

Zach
источник
1
Возможный дубликат Mongodb: Выполните запрос диапазона дат из ObjectId в оболочке Монго
Мухаммед Эссехеми
Я получил свое решение на steveridout.github.io/mongo-object-time
bpedroso

Ответы:

224

Вставка меток времени в ObjectIds охватывает запросы, основанные на датах, встроенных в ObjectId, очень подробно.

Кратко в коде 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
источник
29
Очень удобно. К вашему сведению, вы можете сохранить эту функцию в своем ~/.mongorc.jsфайле, чтобы она была доступна при mongoзапуске оболочки.
Стенни
1
Я получаю ReferenceError: ObjectId не определен. Как мне это исправить?
Питер
3
Я использую nodejs с mongodbnative. Исправлена ​​ошибка «не определено» путем добавления var ObjectId = require ('mongodb'). ObjectID;
Питер
1
Если вы используете Mongoskin, как я: изменить ObjectId(hexSeconds + "0000000000000000");наdb.ObjectID.createFromHexString(hexSeconds + "0000000000000000");
Anders Östman
2
Или в Mongoose замените ObjectId()на: require('mongoose').Types.ObjectId()- где require('mongoose')находится ваш инициализированный / настроенный экземпляр Mongoose.
toblerpwn
34

Использование встроенной функции, предоставляемой драйверами mongodb в Node.js, позволяет выполнять запросы по любой временной отметке:

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

Кроме того, чтобы искать записи до текущего времени, вы можете просто сделать:

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

Источник: http://mongodb.github.io/node-mongodb-native/api-bson-generated/objectid.html

jksdua
источник
3
Это лучший / самый простой способ создать ObjectId из метки времени в javascript env. Это то, что оп просит ...
Андерс Остман
34

В pymongo, это можно сделать так:

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
источник
Обратите внимание, что использование datetime.datetime.utcnow () или datetime.datetime.today () вернет тот же результат. Дата и время обрабатываются для вас.
Радтек
В качестве альтернативы, без использования pymongoзависимости: epoch_time_hex = format(int(time.time()), 'x') (не забудьте добавить нули для вашего запроса) Был использован пакет времени ( import time).
Василий Новиков
Поймите, что OP требовал javascript, но это действительно помогло мне упростить мой код. Спасибо.
Фред С
14

Поскольку первые 4 байта ObjectId представляют временную метку , для хронологического запроса вашей коллекции просто упорядочите по id:

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

После того, как вы получите документы, вы можете получить время генерации ObjectId следующим образом:

id = some_object_id
generation_time = id.generation_time
ассоциация теннисистов-профессионалов
источник
1
Я надеялся на что-то, что на самом деле позволило бы сделать такие вещи, как получить количество объектов, созданных до определенного времени, используя время, встроенное в ObjectId, но кажется, что это не было бы напрямую доступно. Спасибо.
Зак
1
Вы можете сделать это, посмотрите на ответ Leftium.
АТП
14

как найти команду «Найти команду» (эта дата [2015-1-12] - эта дата [2015-1-15]):

db.collection.find ({_ id: {$ gt: ObjectId (Math.floor ((новая дата ('2015/1/12')) / 1000) .toString (16) + "0000000000000000"), $ lt: ObjectId (Math.floor ((новая дата ('2015/1/15')) / 1000) .toString (16) + "0000000000000000")}}). Pretty ()

Подсчитайте команду (эта дата [2015-1-12] до этой даты [2015-1-15]):

db.collection.count ({_ id: {$ gt: ObjectId (Math.floor ((новая дата ('2015/1/12')) / 1000) .toString (16) + "0000000000000000"), $ lt: ObjectId (Math.floor ((новая дата ('2015/1/15')) / 1000) .toString (16) + "0000000000000000")}})

Удалить команду (эта дата [2015-1-12] до этой даты [2015-1-15]):

db.collection.remove ({_ id: {$ gt: ObjectId (Math.floor ((новая дата ('2015/1/12')) / 1000) .toString (16) + "0000000000000000"), $ lt: ObjectId (Math.floor ((новая дата ('2015/1/15')) / 1000) .toString (16) + "0000000000000000")}})

Картиккумар Нагарадж
источник
10

Вы можете использовать $convertфункцию для извлечения даты из ObjectId, начиная с версии 4.0.

Что-то вроде

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

Вы можете запросить дату, сравнивая время начала и окончания даты.

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

ИЛИ

Вы можете использовать стенографию $toDateдля достижения того же.

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")]}
    ]
  }
})
Сагар Вирам
источник
Просто хотел спросить, как с использованием $ convert OR $ toDate, Mongo сначала должен будет конвертировать, а затем сравнить, присутствует ли документ в диапазоне или нет, но если бы я использовал подход принятого ответа, то для преобразования даты в ObjectId мне нужно было бы только сделать на стороне клиента и только один раз, так вы не думаете, что решение будет более эффективным, чем это? В любом случае, спасибо за сообщение, что эти два оператора также существуют :)
Судханшу Гаур
7

Чтобы получить последние 60 дней документы в коллекции монго, я использовал запрос ниже в оболочке.

db.collection.find({_id: {$lt:new ObjectId( Math.floor(new Date(new Date()-1000*60*60*24*60).getTime()/1000).toString(16) + "0000000000000000" )}})
Murali
источник
1
Используйте $ gt вместо $ lt. В противном случае он находит документы, вставленные ранее (сегодня - 60 дней).
yoooshi
1
@Vivek Первые 4 байта ObjectId представляют количество секунд с начала эпохи Unix (1970/1/1 00:00:00 UTC), и поэтому вы можете использовать его с значениями больше ($ gt) и меньше ($ lt) найти объекты, которые были созданы в определенном окне.
Джон
5

Если вы хотите сделать запрос диапазона, вы можете сделать это, как в этом посте . Например, запрос для определенного дня (т.е. 4 апреля 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()
Абдельхади
источник
2

Используя MongoObjectID, вы также должны найти результаты, как указано ниже

db.mycollection.find({ _id: { $gt: ObjectId("5217a543dd99a6d9e0f74702").getTimestamp().getTime()}});
Yogesh
источник
3
В вашем запросе предполагается, что для начала известно значение ObjectId, что не всегда так.
Дуайт Спенсер
0

В рельсах mongoidвы можете запросить, используя

  time = Time.utc(2010, 1, 1)
  time_id = ObjectId.from_time(time)
  collection.find({'_id' => {'$lt' => time_id}})
Лахани Алираза
источник