Automattic / mongoose

MongoDB object modeling designed to work in an asynchronous environment.
https://mongoosejs.com
MIT License
26.9k stars 3.83k forks source link

prop in sub doc not updated if not replace entire sub doc #8601

Closed freemanlam closed 4 years ago

freemanlam commented 4 years ago

Do you want to request a feature or report a bug? Bug

What is the current behavior? Prop in sub doc not updated if not replace entire sub doc. As repro code, if I update doc2['subDoc'].a by doc2['subDoc'] = { a: false }, it can update successfully.

If the current behavior is a bug, please provide the steps to reproduce.

const mongoose = require('mongoose');

const testModel = mongoose.model(
  'TestModel',
  new mongoose.Schema({
    subDoc: {
      a: Boolean,
    },
  })
);

describe('Issue: sub prop in sub doc should update', () => {
  let _mongoose;
  beforeAll(async () => {
    _mongoose = await mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true });
    await testModel.deleteMany({});
  });

  it('should replace sub prop when save doc',  async () => {
    const doc1 = new testModel();
    doc1['subDoc'] = { a: true };

    const savedDoc1 = await doc1.save();
    const doc1Id = savedDoc1.id;

    const doc2 = await testModel.findById(doc1Id).exec();
    expect(doc2['subDoc'].a).toEqual(true);
    doc2['subDoc'].a = false;
    doc2.save();

    const doc3 = await testModel.findById(doc1Id).exec();
    expect(doc3['subDoc'].a).toEqual(false); // received true
  });

  afterAll(async () => {
    await _mongoose.disconnect();
  });
});

What is the expected behavior? Sub prop changed to false.

What are the versions of Node.js, Mongoose and MongoDB you are using? Note that "latest" is not a version. Node.js: 10.17 Mongoose: 5.9.1 MongoDB: 4.2 Jest: 25.1.0

vkarpov15 commented 4 years ago

You're missing an await before doc2.save(), the below script works fine:

'use strict';

const assert = require('assert');
const mongoose = require('mongoose');

mongoose.set('debug', true);
mongoose.set('useFindAndModify', false);

const { Schema } = mongoose;

run().catch(err => console.log(err));

async function run() {
  await mongoose.connect('mongodb://localhost:27017/test', {
    useNewUrlParser: true,
    useUnifiedTopology: true
  });

  await mongoose.connection.dropDatabase();

  const testModel = mongoose.model(
    'TestModel',
    new mongoose.Schema({
      subDoc: {
        a: Boolean,
      },
    })
  );

  const doc1 = new testModel();
  doc1['subDoc'] = { a: true };

  const savedDoc1 = await doc1.save();
  const doc1Id = savedDoc1.id;

  const doc2 = await testModel.findById(doc1Id).exec();
  assert.equal(doc2['subDoc'].a, true);
  doc2['subDoc'].a = false;
  await doc2.save(); // <--- added an `await` here

  const doc3 = await testModel.findById(doc1Id).exec();
  assert.equal(doc3['subDoc'].a, false); // received true
}