Mongoose: Convertendo todos os ObjectIds em string usando o método Document.toObject()?

Criado em 25 mar. 2015  ·  24Comentários  ·  Fonte: Automattic/mongoose

Como descobri que _id no documento ainda é um ObjectId após toObject()

Existe uma maneira de conseguir isso?

help

Comentários muito úteis

Esse recurso do Mongo é realmente lamentável

Todos 24 comentários

Eu tenho o mesmo bug. Ambos toJSON() e toObject() não stringificam instâncias ObjectId .

Isso ocorre por design agora, porque ObjectIds tecnicamente não são strings. De cabeça, a única maneira que consigo pensar seria JSON.parse(JSON.stringify(doc)); Qual é o seu caso de uso para stringificar todos os ids de objeto?

então, a função toJSON() não converte o objeto em string JSON? O _id e a data não são uma string, mas um objeto?

a propósito, qual é a diferença entre toObject() e toJSON() ?
THX!

Sim, a data ainda deve ser uma data, não uma string, depois de toJSON() + toObject() .

toJSON() e toObject() são essencialmente idênticos, a única diferença é que quando você faz JSON.stringify(doc) JavaScript procura uma função toJSON() em doc e a usa para converter o objeto. Portanto, a razão para toJSON() é o açúcar sintático para JSON.stringify() .

@vkarpov15 Desculpe por comentar um problema encerrado, mas parecia o local mais relevante para postar, pois está diretamente relacionado a esse problema.

Estou tentando comparar profundamente dois documentos do mangusto para obter um objeto diff das mudanças entre eles.
No entanto, os ObjectIDs não podem ser comparados diretamente, porque eles sempre terminarão com um diff, mesmo que contenham exatamente os mesmos ids.

A única maneira que eu consegui fazer até agora, é fazer isso:

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

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

Essa parece ser uma maneira cara de obter uma diferença entre objetos.
Você conhece uma maneira melhor de comparar de forma confiável os documentos do mangusto?

Não tenho certeza de como o deep diff funciona, mas pode estar enfrentando alguns problemas com o mongodb ObjectIds. Tente converter _id e outros campos objectid em strings. Ou apenas use a verificação de igualdade profunda do lodash, acho que funciona com ObjectIds

@vkarpov15 deep diff usa lodash para a verificação de igualdade, então, infelizmente, isso não funcionará:
https://github.com/rbs392/object-deep-diff/blob/master/index.js

Converter todos os ObjectIds em strings funcionaria, mas como os objetos podem conter arrays e/ou outros objetos aninhados, isso também se torna uma tarefa bastante cara.. :/

Então aqui está outra pergunta, você está usando deep-diff em 2 documentos do mangusto ou 2 documentos que são o resultado de toObject() ? O último deve funcionar, o primeiro nem tanto.

@vkarpov15 geralmente é 1 doc mangusto (convertido em um objeto javscript com .toObject) e um objeto javascript regular.
O motivo é que preciso comparar os dados vindos do cliente, com os dados já existentes no banco de dados.
Assim, os dados provenientes do cliente já possuem os ObjectID's convertidos, mas os dados provenientes do banco de dados não.
Mas como estou fazendo essa verificação de diferenças profundas em um plug-in mangusto, preciso ter certeza absoluta de que ambos os documentos são construídos de forma idêntica, então é por isso que tenho que executar JSON.parse(JSON.stringify(doc.toObject()) ) em ambos os documentos.

Sim, nesse caso, você precisará fazer JSON.parse(JSON.stringify()) , essa é a maneira mais fácil de converter todos os ids de objeto em strings.

JSON.parse(JSON.stringify()) era muito computacionalmente intensivo para mim, então tive que criar essa lib de pensamento perf .

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'

Esse recurso do Mongo é realmente lamentável

@ZacharyRSmith Então, o que há sobre outros adereços, por exemplo. author_id(ObjectId refere-se a User Model)/author_ids(array de ObjectIds)?

@flight9 você pode dizer ao transformProps() para procurar várias chaves. veja este teste aqui: https://github.com/ZacharyRSmith/transform-props/blob/master/index.test.js#L67

eu acredito que você pode conseguir o que quer com isso:

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

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

@ZacharyRSmith Obrigado pela sua solução, é ótimo!

Acho que perdi uma coisa importante: não sei de antemão quais campos são ObjectId, pois a ferramenta que estou escrevendo deve ser aplicada a todos os modelos do nosso projeto, que podem ter vários campos ObjectId ou nenhum, e seus nomes de campo são indeterminados.
Então, depois de várias horas tentando, eu tenho minha solução:

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

Sim, nesse caso você precisará fazer JSON.parse(JSON.stringify()), essa é a maneira mais fácil de converter todos os ids de objetos em strings.

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

tldr; JSON.parse(JSON.stringify()) é uma tarefa síncrona e de bloqueio e pode causar sérios gargalos. Esta não deve ser a solução aceita/sugerida para este problema...

Você também deve ser capaz de fazer mongoose.Types.ObjectId.prototype.toJSON = function() { return this.toString(); . Mas mesmo esse artigo diz que JSON.parse(JSON.stringify) é uma das maneiras mais rápidas de fazer clones profundos, o problema deles era que eles estavam clonando desnecessariamente repetidamente

Então, aqui está 2019.
Ainda temos uma maneira agradável, limpa e óbvia de obter string hexadecimal de cada instância de ObjectId no 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 isso não é uma solução ao usar 'lean'.
Portanto, se o resultado desejado for retornar um objeto json simples com string objectIds, parece que a maneira mais eficiente de conseguir isso é ainda usar JSON.parse(JSON.stringify()) com a opção lean .

Ou possivelmente um plugin enxuto: https://www.npmjs.com/package/mongoose-lean-objectid-string

@nhitchins correto, você teria que usar um plugin como mongoose-lean-getters .

@vkarpov15 isso também deve funcionar com um esquema discriminador? Eu tenho um esquema base do qual crio variantes. Eu tentei definir getters no esquema base. Eu uso lean() e usei mangusto-lean-getters, mas não consegui fazer isso funcionar.

@pwrnrd você pode abrir um novo problema e seguir o modelo de problema? Deve funcionar bem com um esquema discriminador.

Esta página foi útil?
0 / 5 - 0 avaliações