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

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


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

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

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


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

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

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


2

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

db.mycollection.find({ _id: { $gt: ObjectId("5217a543dd99a6d9e0f74702").getTimestamp().getTime()}});

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}})
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.