Mongoose: ¿Convertir todos los ObjectIds en cadenas usando el método Document.toObject()?

Creado en 25 mar. 2015  ·  24Comentarios  ·  Fuente: Automattic/mongoose

Como descubrí que el _id en el documento sigue siendo un ObjectId después de toObject()

¿Hay una manera de lograr esto?

help

Comentario más útil

Esta característica de Mongo es realmente lamentable.

Todos 24 comentarios

Tengo el mismo error. Tanto toJSON() como toObject() no encadenan las instancias ObjectId .

Esto es así por diseño en este momento, porque los ObjectId técnicamente no son cadenas. En la parte superior de mi cabeza, la única forma en que se me ocurre sería JSON.parse(JSON.stringify(doc)); ¿Cuál es su caso de uso para clasificar todas las identificaciones de objetos?

Entonces, ¿la función toJSON() no convierte el objeto en una cadena JSON? ¿El _id y la fecha no son una cadena sino un objeto?

por cierto, ¿cuál es la diferencia entre toObject() y toJSON() ?
¡gracias!

Sí, la fecha debería seguir siendo una fecha, no una cadena, después toJSON() + toObject() .

toJSON() y toObject() son esencialmente idénticos, la única diferencia es que cuando haces JSON.stringify(doc) JavaScript busca una función toJSON() en el documento y la usa para convertir el objeto. Entonces, el motivo de toJSON() es el azúcar sintáctico para JSON.stringify() .

@vkarpov15 Perdón por comentar sobre un problema cerrado, pero parecía el lugar más relevante para publicar, ya que está directamente relacionado con este problema.

Estoy tratando de comparar en profundidad dos documentos de mangosta para obtener un objeto diferente de los cambios entre ellos.
Sin embargo, parece que los ObjectID no se pueden comparar directamente, porque siempre terminarán con una diferencia, incluso si contienen exactamente los mismos ID.

La única forma en que he podido hacerlo hasta ahora es hacer esto:

var objectdiff = require('object-deep-diff');

var diffresult = objectdiff(
  JSON.parse(JSON.stringify(doc1.toObject())),
  JSON.parse(JSON.stringify(doc2.toObject()))
);

Esa parece una forma costosa de obtener una diferencia entre los objetos.
¿Conoce una mejor manera de comparar documentos de mongoose de manera confiable?

No estoy seguro de qué tan profundo funciona la diferencia, pero es posible que tenga algunos problemas con mongodb ObjectIds. Intente convertir _id y otros campos de objectid en cadenas. O simplemente use la verificación de igualdad profunda de lodash, creo que uno funciona con ObjectIds

@vkarpov15 deep diff usa lodash para la verificación de igualdad, por lo que desafortunadamente eso no funcionará:
https://github.com/rbs392/object-deep-diff/blob/master/index.js

Convertir todos los ObjectId en cadenas funcionaría, pero dado que los objetos pueden contener matrices y/u otros objetos anidados, también se convierte en una tarea bastante costosa. :/

Así que aquí hay otra pregunta, ¿estás usando deep-diff en 2 documentos mongoose o 2 documentos que son el resultado de toObject() ? Este último debería funcionar, el primero no tanto.

@ vkarpov15 suele ser 1 documento mangosta (convertido en un objeto javscript con .toObject) y un objeto javascript normal.
El motivo es que necesito comparar los datos que provienen del cliente con los datos que ya están en la base de datos.
Por lo tanto, los datos que provienen del cliente ya tienen los ObjectID convertidos, pero los datos que provienen de la base de datos no.
Pero como estoy haciendo esta verificación de diferencias profundas en un complemento de mongoose, necesito asegurarme absolutamente de que ambos documentos estén construidos de manera idéntica, por eso tengo que ejecutar JSON.parse(JSON.stringify(doc.toObject()) ) en ambos documentos.

Sí, en ese caso necesitará hacer JSON.parse(JSON.stringify()) , esa es la forma más fácil de convertir todas las identificaciones de objetos en cadenas.

JSON.parse(JSON.stringify()) era demasiado computacionalmente intensivo para mí, así que tuve que crear esta lib de pensamiento de rendimiento .

var transformProps = require('transform-props');

function castToString(arg) {
    return String(arg);
}

var doc = new MongooseModel({ subDoc: { foo: 'bar' }});
var docObj = doc.toObject();

transformProps(docObj, castToString, '_id');

console.log(typeof docObj._id); // 'string'
console.log(typeof docObj.subDoc._id); // 'string'

Esta característica de Mongo es realmente lamentable.

@ZacharyRSmith Entonces, ¿qué pasa con otros accesorios, por ejemplo? author_id(ObjectId se refiere al modelo de usuario)/author_ids(matriz de ObjectIds)?

@flight9 puede decirle a transformProps() que busque varias claves. vea esta prueba aquí: https://github.com/ZacharyRSmith/transform-props/blob/master/index.test.js#L67

Creo que puedes conseguir lo que quieres con esto:

function myToString(val) {
  if (Array.isArray(val)) {
    return val.map(item => String(item));
  }
  return String(val);
}

transformProps(obj, myToString, ['author_id', 'author_ids']);

@ZacharyRSmith Gracias por su solución, ¡es genial!

Creo que me perdí una cosa importante: no sé qué campos son ObjectId de antemano, porque la herramienta que estoy escribiendo debe aplicarse a todos los modelos de nuestro proyecto, que pueden tener varios campos ObjectId o no tener ninguno, y sus nombres de campo son indeterminados.
Entonces, después de varias horas de intentarlo, tengo mi solución:

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

/**
 * Convert ObjectId field values inside an object to String type values
 * (includes array of ObjectIds and nested ObjectId fields)
 * <strong i="9">@param</strong> obj Object - The Object to be converted.
 * <strong i="10">@return</strong> none
 */
function objectIdtoString(obj) {
  for(let k in obj) {
    let v = obj[k];
    if(typeof v == 'object') {
      if(k.endsWith('_id') && v instanceof ObjectID) {
        obj[k] = v.toString();
      }
      else if(
        k.endsWith('_ids') && Array.isArray(v) &&
        v.length > 0 && v[0] instanceof ObjectID
      ) {
        let vs = [];
        for(let iv of v) {
          vs.push(iv.toString());
        }
        obj[k] = vs;
      }
      else {
        objectIdtoString(v);
      }
    }
  }
}

// Call example
objectIdtoString(obj):

@vkarpov15

Sí, en ese caso, necesitará hacer JSON.parse (JSON.stringify()), esa es la forma más fácil de convertir todas las identificaciones de objetos en cadenas.

https://medium.com/ft-product-technology/this-one-line-of-javascript-made-ft-com-10-times-slower-5afb02bfd93f

tldr; JSON.parse(JSON.stringify()) es una tarea de bloqueo síncrona y puede causar cuellos de botella graves. Esta no debería ser la solución aceptada/sugerida para este problema...

También debería poder hacer mongoose.Types.ObjectId.prototype.toJSON = function() { return this.toString(); . Pero incluso ese artículo dice que JSON.parse (JSON.stringify) es una de las formas más rápidas de hacer una clonación profunda, su problema era que estaban clonando innecesariamente en profundidad repetidamente

Entonces, aquí está el 2019.
¿Todavía tenemos una forma agradable, limpia y obvia de obtener una cadena hexadecimal de cada instancia de ObjectId en el objeto?

@gitowiec

mongoose.ObjectId.get(v => v.toString());

Consulte: http://thecodebarbarian.com/whats-new-in-mongoose-54-global-schematype-configuration.html#schematype -getters

@ vkarpov15 parece que esta no es una solución cuando se usa 'lean'.
Entonces, si el resultado deseado es devolver un objeto json simple con ID de objeto de cadena, parece que la forma más eficiente de lograrlo es usar JSON.parse(JSON.stringify()) con la opción lean .

O posiblemente un complemento lean: https://www.npmjs.com/package/mongoose-lean-objectid-string

@nhitchins correcto, tendrías que usar un complemento como mongoose-lean-getters .

@ vkarpov15 ¿se supone que esto también funciona con un esquema discriminador? Tengo un esquema base a partir del cual creo variantes. Intenté establecer captadores en el esquema base. Yo uso lean() y usé mongoose-lean-getters pero no pude hacer que esto funcionara.

@pwrnrd , ¿puede abrir una nueva edición y seguir la plantilla de publicación? Debería funcionar bien con un esquema discriminador.

¿Fue útil esta página
0 / 5 - 0 calificaciones