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

timestamps options does not work as expect in subdocuments when using Model.bulkWrite to do updateMany operator. #13611

Closed
2 tasks done
mrdulin opened this issue Jul 14, 2023 · 1 comment · Fixed by #13649
Closed
2 tasks done
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Milestone

Comments

@mrdulin
Copy link

mrdulin commented Jul 14, 2023

Prerequisites

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

Mongoose version

7.3.4

Node.js version

v14.21.3

MongoDB server version

6.0.6

Typescript version (if applicable)

5.1.6

Description

Following the documentation Disabling Timestamps

There are two issues:

  1. Runtime issue. Try to disable updating the timestamps on the post subdocuments. But the updatedAt is still updated after setting the timestamps option to false.

  2. Compile time issue. When setting the timestamps option, TSC throws an error:

Type '{ filter: { isDeleted: false; }; update: { 'posts.$[post].isDeleted': true; }; arrayFilters: { 'post.category': { $eq: string; }; }[]; upsert: false; timestamps: false; }' is not assignable to type 'UpdateManyModel<any>'.
  Object literal may only specify known properties, and 'timestamps' does not exist in type 'UpdateManyModel<any>'.ts(2322)

When I checkout the type, there is no timestamps property on the UpdateManyModel interface.

export declare interface UpdateManyModel<TSchema extends Document = Document> {
    /** The filter to limit the updated documents. */
    filter: Filter<TSchema>;
    /** A document or pipeline containing update operators. */
    update: UpdateFilter<TSchema> | UpdateFilter<TSchema>[];
    /** A set of filters specifying to which array elements an update should apply. */
    arrayFilters?: Document[];
    /** Specifies a collation. */
    collation?: CollationOptions;
    /** The index to use. If specified, then the query system will only consider plans using the hinted index. */
    hint?: Hint;
    /** When true, creates a new document if no document matches the query. */
    upsert?: boolean;
}

Steps to Reproduce

import mongoose from 'mongoose';
import { config } from '../../config';

mongoose.set('debug', true);
console.log(mongoose.version);

const PostSchema = new mongoose.Schema(
	{
		category: { type: String, required: true },
		title: { type: String, required: true },
		isDeleted: { type: Boolean, default: false },
	},
	{ timestamps: true },
);
const Post = mongoose.model('post', PostSchema);

const UserSchema = new mongoose.Schema(
	{
		username: { type: String, required: true },
		posts: { type: [PostSchema] },
		isDeleted: { type: Boolean, default: false },
	},
	{ timestamps: true },
);
const User = mongoose.model('user', UserSchema);

(async function main() {
	try {
		await mongoose.connect(config.MONGODB_URI);
		// seed
		const [u1] = await User.create([
			{
				username: 'user a',
				posts: [
					{ title: 'post a', category: 'a', isDeleted: false },
					{ title: 'post b', category: 'b', isDeleted: false },
				],
				isDeleted: false,
			},
		]);

		console.log(u1?.posts[0].updatedAt);

		// test
		await User.bulkWrite([
			{
				updateMany: {
					filter: {
						isDeleted: false,
					},
					update: {
						'posts.$[post].isDeleted': true,
					},
					arrayFilters: [
						{
							'post.category': { $eq: 'a' },
						},
					],
					upsert: false,
					timestamps: false,
				},
			},
		]);
		const u2 = await User.findOne({ _id: u1._id });
		console.log(u2?.posts[0].updatedAt);
	} catch (error) {
		console.error(error);
	} finally {
		await mongoose.connection.close();
	}
})();

Logs:

Mongoose: users.insertOne({ username: 'user a', posts: [ { category: 'a', title: 'post a', isDeleted: false, _id: ObjectId("64b11d543062f023ceebd09e"), createdAt: new Date("Fri, 14 Jul 2023 10:03:00 GMT"), updatedAt: new Date("Fri, 14 Jul 2023 10:03:00 GMT") }, { category: 'b', title: 'post b', isDeleted: false, _id: ObjectId("64b11d543062f023ceebd09f"), createdAt: new Date("Fri, 14 Jul 2023 10:03:00 GMT"), updatedAt: new Date("Fri, 14 Jul 2023 10:03:00 GMT") } ], isDeleted: false, _id: ObjectId("64b11d543062f023ceebd09d"), createdAt: new Date("Fri, 14 Jul 2023 10:03:00 GMT"), updatedAt: new Date("Fri, 14 Jul 2023 10:03:00 GMT"), __v: 0}, {})
2023-07-14T10:03:00.285Z
Mongoose: users.bulkWrite([ { updateMany: { filter: { isDeleted: false }, update: { '$set': { 'posts.$[post].updatedAt': 2023-07-14T10:03:00.857Z, 'posts.$[post].isDeleted': true } }, arrayFilters: [ { 'post.category': { '$eq': 'a' } } ], upsert: false, timestamps: false } }], {})
Mongoose: users.findOne({ _id: ObjectId("64b11d543062f023ceebd09d") }, {})
2023-07-14T10:03:00.857Z

As you can see, 2023-07-14T10:03:00.285Z has been changed to 2023-07-14T10:03:00.857Z

Expected Behavior

The updatedAt should stay the same after updating.

@IslandRhythms IslandRhythms added the confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. label Jul 14, 2023
@IslandRhythms
Copy link
Collaborator

const mongoose = require('mongoose');

mongoose.set('debug', true);
console.log(mongoose.version);

const PostSchema = new mongoose.Schema(
	{
		category: { type: String, required: true },
		title: { type: String, required: true },
		isDeleted: { type: Boolean, default: false },
	},
	{ timestamps: true },
);
const Post = mongoose.model('post', PostSchema);

const UserSchema = new mongoose.Schema(
	{
		username: { type: String, required: true },
		posts: { type: [PostSchema] },
		isDeleted: { type: Boolean, default: false },
	},
	{ timestamps: true },
);
const User = mongoose.model('user', UserSchema);

(async function main() {
	try {
		await mongoose.connect('mongodb://localhost:27017');
    await mongoose.connection.dropDatabase();
		// seed
		const [u1] = await User.create([
			{
				username: 'user a',
				posts: [
					{ title: 'post a', category: 'a', isDeleted: false },
					{ title: 'post b', category: 'b', isDeleted: false },
				],
				isDeleted: false,
			},
		]);

		console.log(u1?.posts[0].updatedAt);

		// test
		await User.bulkWrite([
			{
				updateMany: {
					filter: {
						isDeleted: false,
					},
					update: {
						'posts.$[post].isDeleted': true,
					},
					arrayFilters: [
						{
							'post.category': { $eq: 'a' },
						},
					],
					upsert: false,
					timestamps: false,
				},
			},
		]);
		const u2 = await User.findOne({ _id: u1._id });
		console.log(u2?.posts[0].updatedAt);
	} catch (error) {
		console.error(error);
	} finally {
		await mongoose.connection.close();
	}
})();

@vkarpov15 vkarpov15 added this to the 7.4.2 milestone Jul 18, 2023
vkarpov15 added a commit that referenced this issue Jul 25, 2023
fix: timestamps:false on bulkWrite works
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants