Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

findOneAndUpdate creates subdocuments with timestamps in reverse order #12475

Closed
2 tasks done
justinpage opened this issue Sep 27, 2022 · 1 comment · Fixed by #12484
Closed
2 tasks done

findOneAndUpdate creates subdocuments with timestamps in reverse order #12475

justinpage opened this issue Sep 27, 2022 · 1 comment · Fixed by #12484
Labels
developer-experience This issue improves error messages, debugging, or reporting enhancement This issue is a user-facing general improvement that doesn't fix a bug or add a new feature
Milestone

Comments

@justinpage
Copy link

justinpage commented Sep 27, 2022

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Mongoose version

6.6.2

Node.js version

18.9.0

MongoDB server version

5.0.5

Description

When using Model.findOneAndUpdate, with a subdocument schema, that has timestamps enabled, the createdAt and updatedAt properties are inserted in reverse order.

In other words, updatedAt precedes createdAt when automatically inserted in the subdocument.

Steps to Reproduce

You can reproduce this behavior by running the following code.

import mongoose from 'mongoose';

const CONNECTION = 'mongodb://root:secret@0.0.0.0:27017/example?authSource=admin';

const user = {
  uuid: String,
};

const address = {
  location: String,
}

async function main() {
  let db = await mongoose.createConnection(CONNECTION);

  let uuid = '123';
  const home = { location: 'earth' }

  const userSchema = new mongoose.Schema({
    ...user,
    addresses: [
      new mongoose.Schema(address, { timestamps: true })
    ],
  }, { timestamps: true });

  const User = db.model('User', userSchema);

  const example = new User({ uuid, addresses: [ home ]});
  await example.save();

  uuid = '456';

  await User.findOneAndUpdate({ uuid },
    { uuid, '$push': { addresses: home }},
    { upsert: true, new: true, runValidators: true },
  );
}

main().catch(console.log)

In the example above, we create a new document using the traditional save method. Then, we create another document using the findOneAndUpdate method.

Looking at the database, we can see that the parent document has the timestamps in the expected order: createdAt and then updatedAt. However, when looking at the subdocument, created by the findOneAndUpdate method, we will see the timestamps in a reverse order: updatedAt and then createdAt:

example> db.users.find()
[
  {
    _id: ObjectId("6332450601a46a3a728b6153"),
    uuid: '123',
    addresses: [
      {
        location: 'earth',
        _id: ObjectId("6332450601a46a3a728b6154"),
        createdAt: ISODate("2022-09-27T00:34:14.301Z"),
        updatedAt: ISODate("2022-09-27T00:34:14.301Z")
      }
    ],
    createdAt: ISODate("2022-09-27T00:34:14.302Z"),
    updatedAt: ISODate("2022-09-27T00:34:14.302Z"),
    __v: 0
  },
  {
    _id: ObjectId("633245064ccd19e4172de839"),
    uuid: '456',
    __v: 0,
    addresses: [
      {
        location: 'earth',
        _id: ObjectId("6332450601a46a3a728b6157"),
        updatedAt: ISODate("2022-09-27T00:34:14.365Z"), // REVERSE
        createdAt: ISODate("2022-09-27T00:34:14.365Z") // ORDER
      }
    ],
    createdAt: ISODate("2022-09-27T00:34:14.365Z"),
    updatedAt: ISODate("2022-09-27T00:34:14.365Z")
  }
]
example>

Expected Behavior

When using Model.findOneAndUpdate, with a subdocument schema, that has timestamps enabled, the createdAt and updatedAt properties are inserted in the expected order:

example> db.users.find()
[
  {
    _id: ObjectId("6332450601a46a3a728b6153"),
    uuid: '123',
    addresses: [
      {
        location: 'earth',
        _id: ObjectId("6332450601a46a3a728b6154"),
        createdAt: ISODate("2022-09-27T00:34:14.301Z"), // SAME
        updatedAt: ISODate("2022-09-27T00:34:14.301Z")
      }
    ],
    createdAt: ISODate("2022-09-27T00:34:14.302Z"), // ORDER
    updatedAt: ISODate("2022-09-27T00:34:14.302Z"),
    __v: 0
  }
]
@hasezoey
Copy link
Collaborator

hasezoey commented Sep 27, 2022

can reproduce locally, save is consistent order, where as findOneAndUpdate ($push) is in reversed order

Code
// NodeJS: 18.8.0
// MongoDB: 5.0 (Docker)
// Typescript 4.8.3
import * as mongoose from 'mongoose'; // mongoose@6.6.1

const userSchema = new mongoose.Schema(
  {
    uuid: String,
    addresses: [
      new mongoose.Schema(
        {
          location: String,
        },
        { timestamps: true }
      ),
    ],
  },
  { timestamps: true }
);

const User = mongoose.model('User', userSchema);

(async () => {
  await mongoose.connect(`mongodb://localhost:27017/`, {
    dbName: 'verifyMASTER',
  });

  let uuid = 'saveOp';
  const home = { location: 'earth' };

  const example = new User({ uuid, addresses: [home] });
  mongoose.set('debug', true);
  await example.save();

  uuid = 'upsertOp';

  const query = User.findOneAndUpdate({ uuid }, { uuid, $push: { addresses: home } }, { upsert: true, new: true, runValidators: true });
  await query;

  await mongoose.disconnect();
})();

Reproduction Repository / Branch: https://github.com/typegoose/typegoose-testing/tree/mongooseGh12475

mongoose debug log:

Original Log
Mongoose: users.findOneAndUpdate({ uuid: 'upsertOp' }, { '$set': { updatedAt: new Date("Tue, 27 Sep 2022 11:06:34 GMT"), uuid: 'upsertOp' }, '$setOnInsert': { __v: 0, createdAt: new Date("Tue, 27 Sep 2022 11:06:34 GMT") }, '$push': { addresses: { location: 'earth', _id: new ObjectId("6332d93a9999fe1024c129fd"), updatedAt: new Date("Tue, 27 Sep 2022 11:06:34 GMT"), createdAt: new Date("Tue, 27 Sep 2022 11:06:34 GMT") } }}, { upsert: true, runValidators: true, remove: false, projection: {}, returnDocument: 'after', returnOriginal: false})

Prettier log:

{
    $set: { updatedAt: new Date("Tue, 27 Sep 2022 11:06:34 GMT"), uuid: "upsertOp" },
    $setOnInsert: { __v: 0, createdAt: new Date("Tue, 27 Sep 2022 11:06:34 GMT") },
    $push: {
        addresses: {
            location: "earth",
            _id: new ObjectId("6332d93a9999fe1024c129fd"),
            updatedAt: new Date("Tue, 27 Sep 2022 11:06:34 GMT"),
            createdAt: new Date("Tue, 27 Sep 2022 11:06:34 GMT"),
        },
    },
}
{
    upsert: true,
    runValidators: true,
    remove: false,
    projection: {},
    returnDocument: "after",
    returnOriginal: false,
}
Resulting Document of findOneAndUpdate
{
  _id: new ObjectId("6332daed4b531a38e7fa53e9"),
  uuid: 'upsertOp',
  __v: 0,
  addresses: [
    {
      location: 'earth',
      _id: new ObjectId("6332daede100553d6e30330f"),
      updatedAt: 2022-09-27T11:13:49.047Z,
      createdAt: 2022-09-27T11:13:49.047Z
    }
  ],
  createdAt: 2022-09-27T11:13:49.047Z,
  updatedAt: 2022-09-27T11:13:49.047Z
}

@hasezoey hasezoey added enhancement This issue is a user-facing general improvement that doesn't fix a bug or add a new feature developer-experience This issue improves error messages, debugging, or reporting labels Sep 27, 2022
@justinpage justinpage changed the title findOneAndUpdate creates subdocuments with timestamps in reverse order findOneAndUpdate creates subdocuments with timestamps in reverse order Sep 27, 2022
lpizzinidev added a commit to lpizzinidev/mongoose that referenced this issue Sep 29, 2022
@vkarpov15 vkarpov15 added this to the 6.6.3 milestone Sep 30, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
developer-experience This issue improves error messages, debugging, or reporting enhancement This issue is a user-facing general improvement that doesn't fix a bug or add a new feature
Projects
None yet
3 participants