Mongoose: document.toObject does not convert Mongoose Maps to POJOs

Created on 22 Nov 2018  路  5Comments  路  Source: Automattic/mongoose

I think there's still a bug similar to #6478:

What is the current behavior?
When converting a Mongoose document to an object via toObject, a Map still remains a Map. As from that point on I actually expect to be working with a POJO, this is problematic, especially since I'm converting it to JSON a bit later and will end up with an empty Object instead of a POJO representing my map.

The following, slightly modified script from #6486 should demonstrate the issue:

#!/usr/bin/env node
'use strict';

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
const conn = mongoose.connection;
const Schema = mongoose.Schema;
const Map = global.Map;

const schema = new Schema({
  test: {
    type: Map,
    of: String,
    default: new Map()
  }
});

const Test = mongoose.model('test', schema);

const mapTest = new Test({});

mapTest.test.set('key1', 'value1');

async function run() {
  await conn.dropDatabase();
  await mapTest.save();
  let found = await Test.findOne();
  let pojo = found.toObject();
  console.log(JSON.stringify(pojo));
  return conn.close();
}

run();

What is the expected behavior?
I'd expect toObject to map a MongooseMap to a POJO as well.

Please mention your node.js, mongoose and MongoDB version.
Node: v11.1.0
MongoDB: 3.6.5

Most helpful comment

If you don't use toObject(), this works fine:

async function run() {
  await conn.dropDatabase();
  await mapTest.save();
  let found = await Test.findOne();

  //let pojo = found.toObject();
  console.log(JSON.stringify(found));
  console.log(pojo.test)
  return conn.close();
}

That's because, in general, we try to make toObject() not do too much, but toJSON() actually converts maps to POJOs because JSON.stringify() doesn't handle maps. In 5.3.15 you'll be able to do this:

async function run() {
  await conn.dropDatabase();
  await mapTest.save();
  let found = await Test.findOne();

  let pojo = found.toObject({ flattenMaps: true });
  console.log(JSON.stringify(pojo));
  console.log(pojo.test)
  return conn.close();
}

All 5 comments

Thanks for reporting, will fix

If you don't use toObject(), this works fine:

async function run() {
  await conn.dropDatabase();
  await mapTest.save();
  let found = await Test.findOne();

  //let pojo = found.toObject();
  console.log(JSON.stringify(found));
  console.log(pojo.test)
  return conn.close();
}

That's because, in general, we try to make toObject() not do too much, but toJSON() actually converts maps to POJOs because JSON.stringify() doesn't handle maps. In 5.3.15 you'll be able to do this:

async function run() {
  await conn.dropDatabase();
  await mapTest.save();
  let found = await Test.findOne();

  let pojo = found.toObject({ flattenMaps: true });
  console.log(JSON.stringify(pojo));
  console.log(pojo.test)
  return conn.close();
}

Wonderful, thanks a lot!

It seems that flattenMaps can't be used by schema.set('toObject', { flattenMaps: true }) @vkarpov15

@liubog2008 thanks for reporting, I'll reopen this and take a look.

Was this page helpful?
0 / 5 - 0 ratings