Puis-je interroger MongoDB ObjectId par date?

Réponses:

224

L'ajout d'horodatages dans ObjectIds couvre les requêtes basées sur les dates intégrées dans ObjectId de manière très détaillée.

Brièvement en code 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
la source
29
Très pratique .. Pour info, vous pouvez enregistrer cette fonction dans votre ~/.mongorc.jsfichier pour l'avoir disponible au démarrage du mongoshell.
Stennie
1
J'obtiens ReferenceError: ObjectId n'est pas défini. Comment résoudre ce problème?
peter
3
J'utilise nodejs avec mongodbnative. Correction de l '"erreur non définie" en incluant var ObjectId = require (' mongodb '). ObjectID;
peter
1
Si vous utilisez Mongoskin comme je le fais: Changez ObjectId(hexSeconds + "0000000000000000");pourdb.ObjectID.createFromHexString(hexSeconds + "0000000000000000");
Anders Östman
2
Ou, dans Mongoose, remplacez ObjectId()par: require('mongoose').Types.ObjectId()- où require('mongoose')est votre instance de Mongoose initialisée / configurée.
toblerpwn
34

L'utilisation de la fonction intégrée fournie par les pilotes mongodb dans Node.js vous permet d'interroger par n'importe quel horodatage:

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

Sinon, pour rechercher des enregistrements avant l'heure actuelle, vous pouvez simplement faire:

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

Source: http://mongodb.github.io/node-mongodb-native/api-bson-generated/objectid.html

jksdua
la source
3
C'est le moyen le meilleur / le plus simple de créer un ObjectId à partir d'un horodatage dans un env javascript. C'est ce que l'op demande pour ...
Anders Östman
34

Dans pymongo, cela peut être fait de cette façon:

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
la source
Notez que l'utilisation de datetime.datetime.utcnow () ou datetime.datetime.today () renverra le même résultat. La date / heure est gérée pour vous.
radtek le
Sinon, sans utiliser de pymongodépendance: epoch_time_hex = format(int(time.time()), 'x') (n'oubliez pas d'ajouter des zéros pour votre requête) Le package de temps a été utilisé ( import time).
VasiliNovikov
Réalisez que l'OP demandait du javascript, mais cela m'a vraiment aidé à simplifier mon code. Merci.
Fred S
14

Étant donné que les 4 premiers octets d'un ObjectId représentent un horodatage , pour interroger votre collection chronologiquement, triez simplement par id:

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

Après avoir obtenu les documents, vous pouvez obtenir le temps de génération de l'ObjectId comme suit:

id = some_object_id
generation_time = id.generation_time
atp
la source
1
J'espérais quelque chose qui permettrait en fait de faire des choses comme obtenir un nombre d'objets créés avant un certain temps en utilisant le temps intégré dans ObjectId, mais il semble que cela ne soit pas directement accessible. Merci.
Zach
1
Vous pouvez le faire, regardez la réponse de Leftium.
atp
14

Comment trouver la commande (de cette date [2015-1-12] à cette date [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 ()

Comptez la commande (de cette date [2015-1-12] à cette date [2015-1-15]):

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

Supprimez la commande (de cette date [2015-1-12] à cette date [2015-1-15]):

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

Karthickkumar Nagaraj
la source
10

Vous pouvez utiliser la $convertfonction pour extraire la date d'ObjectId à partir de la version 4.0.

Quelque chose comme

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

Vous pouvez interroger la date en comparant l'heure de début et de fin pour la 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")]}
    ]
  }
})

OU

Vous pouvez utiliser la sténographie $toDatepour obtenir la même chose.

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
la source
Je voulais juste demander en utilisant $ convert OU $ toDate, Mongo devra d'abord convertir, puis comparer si doc est présent dans la plage ou non, mais si j'utilisais l'approche de réponse acceptée, convertir la date en ObjectId, je n'aurais qu'à faire côté client et une seule fois, alors ne pensez-vous pas que cette solution sera plus efficace que cela? merci quand même de dire que ces deux opérateurs existent aussi :)
Sudhanshu Gaur
7

Pour obtenir des documents vieux de 60 jours dans la collection mongo, j'ai utilisé la requête ci-dessous dans le shell.

db.collection.find({_id: {$lt:new ObjectId( Math.floor(new Date(new Date()-1000*60*60*24*60).getTime()/1000).toString(16) + "0000000000000000" )}})
Murali
la source
1
Utilisez $ gt au lieu de $ lt. Sinon, il trouve les documents insérés avant (aujourd'hui-60 jours).
yoooshi
1
@Vivek Les 4 premiers octets d' ObjectId représentent le nombre de secondes depuis l'époque Unix (1970/1/1 00:00:00 UTC), et en tant que tel, vous pouvez l'utiliser avec plus de ($ gt) et moins de ($ lt) pour rechercher des objets créés dans une certaine fenêtre.
John
5

Si vous souhaitez effectuer une requête de plage, vous pouvez le faire comme dans cet article . Par exemple, interroger un jour spécifique (par exemple, le 4 avril 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
la source
2

En utilisant MongoObjectID, vous devriez également trouver les résultats comme indiqué ci-dessous

db.mycollection.find({ _id: { $gt: ObjectId("5217a543dd99a6d9e0f74702").getTimestamp().getTime()}});
Yogesh
la source
3
votre instruction de requête suppose que l'on connaît la valeur ObjectId pour commencer, ce qui n'est pas toujours le cas.
Dwight Spencer
0

Dans les rails, mongoidvous pouvez interroger en utilisant

  time = Time.utc(2010, 1, 1)
  time_id = ObjectId.from_time(time)
  collection.find({'_id' => {'$lt' => time_id}})
Lakhani Aliraza
la source