¿Puedo determinar si una cadena es un ObjectID de MongoDB?

81

Estoy haciendo búsquedas de MongoDB convirtiendo una cadena a BSON. ¿Hay alguna forma de determinar si la cadena que tengo es un ObjectID válido para Mongo antes de realizar la conversión?

Aquí está el coffeescript de mi función findByID actual. Funciona muy bien, pero me gustaría buscar por un atributo diferente si determino que la cadena no es una ID.

db.collection "pages", (err, collection) ->
  collection.findOne
    _id: new BSON.ObjectID(id)
  , (err, item) ->
    if item
      res.send item
    else
      res.send 404
Será
fuente
Por ahora, el uso de un bloque try catch funciona. ¿Es esta la solución recomendada?
Será el

Respuestas:

145

Descubrí que el validador de ObjectId de mangosta funciona para validar objectIds válidos, pero encontré algunos casos en los que los identificadores no válidos se consideraron válidos. (por ejemplo: cualquier cadena de 12 caracteres)

var ObjectId = require('mongoose').Types.ObjectId;
ObjectId.isValid('microsoft123'); //true
ObjectId.isValid('timtomtamted'); //true
ObjectId.isValid('551137c2f9e1fac808a5f572'); //true

Lo que me ha funcionado es lanzar una cadena a un objectId y luego verificar que la cadena original coincida con el valor de la cadena del objectId.

new ObjectId('timtamtomted'); //616273656e6365576f726b73
new ObjectId('537eed02ed345b2e039652d2') //537eed02ed345b2e039652d2

Esto funciona porque los identificadores válidos no cambian cuando se convierten en un ObjectId, pero una cadena que obtiene un falso válido cambiará cuando se convierte en un objectId.

Andy Macleod
fuente
Teóricamente, podría agregar estos dos métodos para generar un validador de ObjectID bastante bueno, lo haré hoy.
Anthony
2
Entonces, ¿algo como esto? function checkObjectIdValid(id){ if(ObjectID.isValid(id)){ if(new ObjectID(id) === id){ return true } else { return false } } else { return false } }
Jackson Vaughan
Algo así funcionaría, o la comparación de cadenas usando la función toString de ObjetcId.
Andy Macleod
2
En realidad, la respuesta de @JacksonVaughan es casi correcta. Faltaba un String () para convertir el nuevo ObjectID (id) en una cadena, ya que lo estamos comparando con otra cadena. Aquí está la respuesta correcta completa: const ObjectId = require('mongoose').Types.ObjectId; function isObjectIdValid(id) { if (ObjectId.isValid(id)) { if (String(new ObjectId(id)) === id) { return true } else { return false } } else { return false } }
marcvander
1
@marcvander déjame decirte es6eso:isObjectIdValid = id => ObjectId.isValid(id) ? String(new ObjectId(id) === id) ? true : false : false;
Rod911
82

Puede usar una expresión regular para probar eso:

CoffeeScript

if id.match /^[0-9a-fA-F]{24}$/
    # it's an ObjectID
else
    # nope

JavaScript

if (id.match(/^[0-9a-fA-F]{24}$/)) {
    // it's an ObjectID    
} else {
    // nope    
}
JohnnyHK
fuente
1
Hmm, esto también podría coincidir con no objectIds, la mejor manera es construir un validador basado en la especificación y regex sus partes específicas o intentar hacer un nuevo objectid y albergar un bloque de captura para atrapar si puede hacerlo.
Sammaye
2
@Sammaye Es la misma validación que usa el constructor BSON ObjectID . ¿Puede darme un ejemplo de una cadena que no sea ObjectID con la que coincidiría?
JohnnyHK
Vaya, no lo vi venir. Bueno, cualquier cadena de 24 caracteres que tenga números y letras, es decirlol456712bbfghLLsdfr
Sammaye
13
@Sammaye Pero ese es un ObjectID válido, por lo que debería coincidir.
JohnnyHK
1
Probablemente la forma correcta, sugerida oficialmente por mongoose github.com/Automattic/mongoose/issues/…
Akarsh Satija
10

He utilizado el controlador mongodb de nodo nativo para hacer esto en el pasado. El método isValid comprueba que el valor sea un ObjectId BSON válido. Consulte la documentación aquí.

var ObjectID = require('mongodb').ObjectID;
console.log( ObjectID.isValid(12345) );
cbaigorri
fuente
no parece funcionar, lo anterior devuelve verdadero para un número aleatorio.
Dan Ochiana
1
Creo que es más probable porque debería ser así ObjectId, no ObjectID. :)
Ken Hoff
esto debe ser respuesta aceptada, sin necesidad de mangosta
FH
5

Si está utilizando Mongoose, podemos probar si una cadena tiene 12 bytes o una cadena de 24 caracteres hexadecimales mediante el uso de isValidObjectId integrado de mangosta .

mongoose.isValidObjectId (cadena) devolverá verdadero / falso

Esta es una solución mejorada proporcionada por una solución aceptada .

Alguien especial
fuente
en serio, votemos esto. La antigua respuesta aceptada era excelente cuando la biblioteca no brindaba soporte para esto, pero ahora sí. Evitarlo es un truco que depende de los detalles de implementación.
lance.dolan
4

Aquí hay un código que he escrito basado en la respuesta de @ andy-macleod.

Puede tomar un int o string o ObjectId y devuelve un ObjectId válido si el valor pasado es válido o nulo si no es válido:

var ObjectId= require('mongoose').Types.ObjectId;

function toObjectId(id) {

    var stringId = id.toString().toLowerCase();

    if (!ObjectId.isValid(stringId)) {
        return null;
    }

    var result = new ObjectId(stringId);
    if (result.toString() != stringId) {
        return null;
    }

    return result;
}
nzjoel
fuente
3

mongoose.Types.ObjectId.isValid (cadena) siempre devuelve Verdadero si la cadena contiene 12 letras

let firstUserID = '5b360fdea392d731829ded18';
let secondUserID = 'aaaaaaaaaaaa';

console.log(mongoose.Types.ObjectId.isValid(firstUserID)); // true
console.log(mongoose.Types.ObjectId.isValid(secondUserID)); // true

let checkForValidMongoDbID = new RegExp("^[0-9a-fA-F]{24}$");
console.log(checkForValidMongoDbID.test(firstUserID)); // true
console.log(checkForValidMongoDbID.test(secondUserID)); // false
Sajag Porwal
fuente
2

La única forma que encontré es crear un nuevo ObjectId con el valor que quiero verificar, si la entrada es igual a la salida, la identificación es válida:

function validate(id) {
    var valid = false;
    try
    {
        if(id == new mongoose.Types.ObjectId(""+id))
           valid = true;

    }
    catch(e)
    {
       valid = false;
    }
    return valid;
}

> validate(null)
false
> validate(20)
false
> validate("abcdef")
false
> validate("5ad72b594c897c7c38b2bf71")
true
Daphoque
fuente
1

La forma más fácil es básicamente envolver su método ObjectId en un servicio de prueba y captura. Entonces está usando este servicio para manejar los Id de Objecet, en lugar de usar el método directamente:

var ObjectId = REQUIRE OR IMPORT ...

// service
function oid(str) {
 try {   
   return ObjectId(str);
 } catch(err) {
   return false;
 }
}

// usage
if (oid(USER_INPUT)) {
  // continue
} else {
  // throw error
}

También puede enviar accesorios nulos o vacíos para obtener una nueva ID generada.

Raz
fuente
1

La forma más sencilla de comprobar si la cadena es un ObjectId de Mongo válido es mediante el módulo mongodb .

const ObjectID = require('mongodb').ObjectID;

if(ObjectID.isValid(777777777777777)){
   console.log("Valid ObjectID")
}
Pavneet Kaur
fuente
1

A continuación se muestra una función que verifica con el isValidmétodo ObjectId y si new ObjectId(id)devuelve el mismo valor o no . isValidAndy Macleod describe muy bien la razón de no ser suficiente solo en la respuesta elegida.

const ObjectId = require('mongoose').Types.ObjectId;

/**
 * True if provided object ID valid
 * @param {string} id 
 */
function isObjectIdValid(id){ 
  return ObjectId.isValid(id) && new ObjectId(id) == id;
}
AliAvci
fuente
Gracias por la reseña.
Actualicé
2
Esto no funcionará ya que está haciendo una comparación estricta entre la cadena y la identificación del objeto. Actualice a dobles iguales.
Mattia Rasulo
0

Si tiene la cadena hexadecimal, puede usar esto:

ObjectId.isValid(ObjectId.createFromHexString(hexId));
pkarc
fuente
0

Me tomó un tiempo obtener una solución válida, ya que la propuesta por @Andy Macleod de comparar el valor de objectId con su propia cadena estaba bloqueando el servidor Express.js en:

var view_task_id_temp=new mongodb.ObjectID("invalid_id_string"); //this crashed

Solo utilicé un simple intento de captura para resolver esto.

var mongodb = require('mongodb');
var id_error=false;
try{
    var x=new mongodb.ObjectID("57d9a8b310b45a383a74df93");
    console.log("x="+JSON.stringify(x));
}catch(err){
    console.log("error="+err);
    id_error=true;
}

if(id_error==false){
   // Do stuff here
}
vibhu
fuente
0

Para mangosta, use la función isValid () para verificar si objectId es válido o no

Ejemplo:

var ObjectId = mongoose.Types.ObjectId;
if(ObjectId.isValid(req.params.documentId)){
   console.log('Object id is valid'); 
}else{
   console.log('Invalid Object id');
}
Om Sharma
fuente
0

En la respuesta de la solución, Andy Macleod dijo:

Lo que me ha funcionado es lanzar una cadena a un objectId y luego verificar que la cadena original coincida con el valor de la cadena del objectId.

new ObjectId('timtamtomted'); //616273656e6365576f726b73
new ObjectId('537eed02ed345b2e039652d2') //537eed02ed345b2e039652d2

Esto funciona porque los identificadores válidos no cambian cuando se convierten en un ObjectId, pero una cadena que obtiene un falso válido cambiará cuando se convierte en un objectId.

Una implementación de este enfoque sería una función que verifica si el valor pasado es válido ObjectIdy funciona con los valores stringy ObjectId(objeto). Se vería así:

var ObjectId = require("mongoose");.Types.ObjectId;

function isValidObjectId(value) {
  // If value is an object (ObjectId) cast it to a string
  var valueString = typeof value === "string" ? value : String(value); 

  // Cast the string to ObjectId
  var idInstance = new ObjectId(valueString); 

  return String(idInstance) === valueString;
}
ross-u
fuente
0

La respuesta de @ ross-u es simplemente increíble.

He encadenado los métodos para hacer una validación completa en línea:

documentId = id && isValid(id) && new ObjectId(id) == id ? id : null

Tenga en cuenta el doble signo igual, que es MUY importante ya new ObjectId()que no devuelve una cadena y la comparación estricta devolverá falso en comparación con una cadena normal (que tenía en mi lógica).

Los métodos se han desestructurado del mongooseobjeto expuesto por el requisito:

const {
  Types: {
    ObjectId: { isValid },
    ObjectId
  }
} = require("mongoose");
Mattia Rasulo
fuente
-2

Advertencia: isValid devolverá verdadero para cadenas arbitrarias de 12/24 de longitud que comiencen con un dígito hexadecimal válido. Actualmente creo que esta es una mejor verificación:

((thing.length === 24 || thing.length === 12) && isNaN(parseInt(thing,16)) !== true)

iss42
fuente
3
Esto evaluaría truepara 'funky string'. Cualquier cadena que tenga la longitud correcta y comience con un dígito hexadecimal la satisfará.
JohnnyHK