Search code examples
typescriptmongodbmongoose

Using markModified in a class without extending mongoose.Document


I am using a simple queue type in Typescript using mongoose ODM. As mongoose has no way of knowing when you directly mutate an array rather then assigning a value to it, I need use the Document's markModified function to mark the array as dirty so it will be saved on the next save() call.

According to mongoose documentation, extending mongoose.Document is strongly discouraged and is no longer supported in versions 7+. I'd like to keep the management of marking items as modified within the class itself, as it may have more complex logic on how and when the item array is modified. However, since the class does not directly inherit from mongoose.Document, I do not have access to the markModified function to do so.

Is there a way to make sure the array is marked as modified while also adhering to mongoose's strong suggestion of not extending Document?

It would be possible to do so in the code that fetches the queue, but the logic of when the queue may be modified may be complex, and I wouldn't like to have to add a markModified whenever an outside code touches the queue.

This is a stripped down example of the code as it exists today:

import { itemSchema, Item } from "./items";

const schemaKey = "player_item_queue";
const schema = new mongoose.Schema(
    {
        user_id: { type: String, required: true },
        items: {type: [itemSchema], default: [] },
    });

export class PlayerItemQueue extends mongoose.Document<PlayerItemQueue>
{
    user_id: string;
    items: Item[];

    public add(item: Item): void
    {
        this.items.push(item);
        this.markModified('items');
    }

    public pop(): Item
    {
        return this.items.pop();
        this.markModified('items');
    }
}

schema.loadClass(PlayerItemQueue);
export const PlayerItemQueueModel = mongoose.model<PlayerItemQueue>(schemaKey, schema);
export { schema as PlayerItemQueueSchema };

Solution

  • You can add methods to the schema and get access to the markModified method, like so -

    import mongoose from "mongoose";
    
    // Stub
    const itemSchema = new mongoose.Schema();
    // Stub
    type Item = {};
    
    const schemaKey = "player_item_queue";
    const schema = new mongoose.Schema(
        {
            user_id: { type: String, required: true },
            items: { type: [itemSchema], default: [] },
        }, {
        methods: {
            add(item: Item): void { this.items.push(item); this.markModified("items"); }, pop(): Item | undefined {
                const item = this.items.pop();
                this.markModified("items");
                return item;
            }
        }
    });
    
    export class PlayerItemQueue {
        user_id!: string;
        items!: Item[];
    }
    
    schema.loadClass(PlayerItemQueue);
    export const PlayerItemQueueModel = mongoose.model(schemaKey, schema);
    
    export { schema as PlayerItemQueueSchema };
    

    Here's a link to the playground. Let me know if this serves the purpose of solving the problem that you're trying to solve.