Mongoose: .lean() doesn't convert subdocuments to POJOs, and other suggestions

Created on 23 May 2018  路  2Comments  路  Source: Automattic/mongoose

Do you want to request a feature or report a bug?
Report a bug.

What is the current behavior?
Now, when we use .lean() on a query, and populate on the same query, the main document is converted to a POJO, while the subdocs aren't.

What is the expected behavior?
.lean() should convert the document, and all subdocuments to POJO.

quoting @vkarpov15 on https://github.com/Automattic/mongoose/issues/4802#issuecomment-267864633

Just do .lean() on the query if you want to use lean and that'll make the top level doc and every subdoc lean.

also, I have other suggestions according to lean that can be sum up in:
The lean option precedence should be like this:
1- Is it set explicitly? Then use it.
2- not explicitly? find the closest parent that has a lean option set in, and take that as the setting.

The closest thing I can think of to this is css inheritance, I hope it's clear what I mean, let me know if I can elaborate more in any way.

If this behavior is accepted, let me know and I can work on it in a week or so.

repro script:

nodemon --exec mocha test.js -R min

// test.js
const mongoose = require('mongoose');
const { Schema } = mongoose;
const assert = require('assert');

mongoose.connect('mongodb://localhost:27017/test');


const userSchema = new Schema({
  name: String,
  roomId: { type: Schema.ObjectId, ref: 'Room' }
});
const officeSchema = new Schema();
const roomSchema = new Schema({ officeId: { type: Schema.ObjectId, ref: 'Office' } });

const User = mongoose.model('User', userSchema);
const Office = mongoose.model('Office', officeSchema);
const Room = mongoose.model('Room', roomSchema);

const user = new User();
const office = new Office();
const room = new Room();

user.roomId = room._id;
room.officeId = office._id;

const isLean = (doc) => !(doc instanceof mongoose.Document);

describe('lean', function () {
  this.timeout(10000);

  before(async () => {
    await Promise.all([User.remove(), Office.remove(), room.remove()]);
    await Promise.all([user.save(), office.save(), room.save()]);
  });

  it('document, and subdocuments are not lean by default', async () => {
    let user = await User.findOne()
      .populate({
        path: 'roomId',
        populate: {
          path: 'officeId'
        }
      });

    assert.equal(isLean(user), false);
    assert.equal(isLean(user.roomId), false);
    assert.equal(isLean(user.roomId.officeId), false);
  });

  describe('lean inheritance', () => {
    it('.lean() makes query result, and all populated fields lean', async () => {
      let user = await User.findOne()
        .populate({
          path: 'roomId',
          populate: {
            path: 'officeId'
          }
        })
        .lean();

      assert.equal(isLean(user), true);
      assert.equal(isLean(user.roomId), true);
      assert.equal(isLean(user.roomId.officeId), true);
    });


    it('disabling lean at some populating level reflects on it, and descendants', async () => {
      let user = await User.findOne()
        .populate({
          path: 'roomId',
          options: { lean: false },
          populate: {
            path: 'officeId'
          }
        })
        .lean();

      assert.equal(isLean(user), true);
      assert.equal(isLean(user.roomId), false);
      assert.equal(isLean(user.roomId.officeId), false);
    });

    it('enabling lean at some populating level reflects on it, and descendants', async () => {
      let user = await User.findOne()
        .populate({
          path: 'roomId',
          options: { lean: true },
          populate: {
            path: 'officeId'
          }
        });

      assert.equal(isLean(user), false);
      assert.equal(isLean(user.roomId), true);
      assert.equal(isLean(user.roomId.officeId), true);
    });


    it('disabling lean on nested population overwrites parent lean', async () => {
      let user = await User.findOne()
        .populate({
          path: 'roomId',
          options: { lean: true },
          populate: {
            options: { lean: false },
            path: 'officeId'
          }
        });

      assert.equal(isLean(user), false);
      assert.equal(isLean(user.roomId), true);
      assert.equal(isLean(user.roomId.officeId), false);
    });
  });
});

Node v10.1.0
MongoDB v3.2.7
Mongoose v5.1.2

confirmed-bug

Most helpful comment

Thank you for reporting and the quick fix.
was going to report this myself instead just ran npm update..! 馃憤 馃檪

All 2 comments

Thanks for reporting, will investigate asap :+1:

Thank you for reporting and the quick fix.
was going to report this myself instead just ran npm update..! 馃憤 馃檪

Was this page helpful?
0 / 5 - 0 ratings