louischatriot / nedb

The JavaScript Database, for Node.js, nw.js, electron and the browser
MIT License
13.51k stars 1.03k forks source link

Altering documents after they were updated in the database changes result of a find #619

Open bbosiljcic opened 4 years ago

bbosiljcic commented 4 years ago

We just found an issue where altering an already updated document changes that document in the database.

Steps to reproduce

I created a repo that illustrates the issue https://github.com/bbosiljcic/nedb-set-issue

A snipped from that repo

const originalDoc = { _id: 1, object: { a: 1, b: 2}};
db.insert(originalDoc, () => {});

db.update({ _id: 1 }, { $set: originalDoc }, {}, () => {
  db.find({}, (err, docs) => {
    console.log('find before edit', docs);
    // find before edit [ { _id: 1, object: { a: 1, b: 2 } } ]
    originalDoc.object.c = 3;
    db.find({}, (err, docs) => {
      console.log('find after edit', docs);
      // find after edit [ { _id: 1, object: { a: 1, b: 2, c: 3 } } ]
    });
  });
});

You can see that originalDoc.object has the c key that was added after the update in the second console.log.

The issue only occurs if $set is used and only nested objects are affected.

This only happens inMemory, when restarted the results are correced.

We currently fixed the issue by JSON.parse(JSON.stringify(originalDoc))

jeremy21212121 commented 4 years ago

That sounds like a JS object reference problem. The JSON.parse/stringify explicitly creates a new object, rather than a reference to the old one as is the norm. Object.assign({}, originalDoc) should work too, and it looks a bit better IMHO.

Part of your problem is possibly due to the param names for your nested db.find() calls. The second one uses the same param names in the same scope (err, docs), so that is going to cause some funky problems. I would be interested to see if the problem persists after changing those param names.