0

I'm trying to add a new nested Item document to a documents array in the List document. Instead of adding the Item document to the array, it overwrites the existing documents and replaces them with the new one.

Here's the code:

app.post('/', async (req, res) => {
        const item = req.body.item;

        const newItem = new Item({
            name: item,
        });

        // Takes the value from the submit button
        const list = req.body.list.toLocaleLowerCase();

        await List.updateOne({ name: list }, { items: newItem });

        res.redirect('/');
    });

This is the rest of the code that interacts with the List:

const itemsSchema = new Schema({
        name: { type: String, required: [true, `The field name is required.`] },
    });

const listsSchema = new Schema({
        name: {
            type: String,
            required: [true, `The name of the list is required.`],
        },
        items: [itemsSchema],
    });

    const List = mongoose.model('List', listsSchema);

app.get('/:list', async (req, res) => {
        // Get the requested title by the user and lower case it.
        const titleRequested = req.params.list.toLocaleLowerCase();

        // Upper case the first letter.
        const upperCasedTitle =
            titleRequested.at(0).toLocaleUpperCase() + titleRequested.slice(1);

        // Find if the list with the requested title exists in the database.
        await List.findOne({ name: titleRequested }).then(async list => {
            if (!list) {
                const newList = new List({
                    name: titleRequested,
                    items: new Item({
                        name: `You've created a ${titleRequested} list!`,
                    }),
                });

                await newList.save();

                await List.findOne({ name: titleRequested }).then(document => {
                    res.render('list', {
                        // De aca sale el value="<%= title %>"
                        title: upperCasedTitle,
                        items: document.items,
                    });
                    console.log(`${upperCasedTitle} list created succesfully.`);
                });
            } else if (list) {
                await List.findOne({ name: titleRequested }).then(document => {
                    res.render('list', {
                        // value="<%= title %>"
                        title: upperCasedTitle,
                        items: document.items,
                    });
                });
            }
        });
    });

Is there any way to update the List document found by the query and not overwrite the existing nested documents?

If there is a specific Model.prototype function that does this, I haven't found it :(

gonzaloku
  • 1
  • 2
  • why not just pushing the new item to the array field, then saving the document. Look up [this question](https://stackoverflow.com/questions/33049707/push-items-into-mongo-array-via-mongoose) – Abdelrahman May 01 '23 at 21:45

1 Answers1

0

Following the Adding Subdocs to Arrays documentation, we can add a sub-document to array using .push(subDoc) or .create(subSoc) methods on the sub-document field.

E.g.

model/list.ts:

import mongoose, { Schema } from 'mongoose';
import { itemSchema } from './item';

export const listsSchema = new Schema({
  name: {
    type: String,
    required: [true, `The name of the list is required.`],
  },
  items: [itemSchema],
});

export const List = mongoose.model('list', listsSchema);

models/item.ts:

import { Schema } from 'mongoose';

export const itemSchema = new Schema({
  name: { type: String, required: [true, `The field name is required.`] },
});

main.ts:

import { List } from './models/list';
import mongoose from 'mongoose';
import { config } from '../../src/config';

async function main() {
  mongoose.connect(config.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true });
  const db = mongoose.connection;
  db.on('error', console.error.bind(console, 'connection error:'));
  db.once('open', async () => {
    // seed
    const list = new List({ name: 'list-a', items: [{ name: 'item-a' }, { name: 'item-b' }] });
    await list.save();

    // query and add subdoc 
    const listDoc = await List.findOne({ name: 'list-a' });
    listDoc.items.push({ name: 'item-c' });
    await listDoc.save();
    
    // query and check
    console.log('list-a: ', await List.findOne({ name: 'list-a' }));

    await db.dropCollection('lists');
    db.close();
  })
}

main();

Execution result:

list-a:  {
  _id: 645387bae5d6004f6d3607f7,
  name: 'list-a',
  items: [
    { _id: 645387bae5d6004f6d3607f8, name: 'item-a' },
    { _id: 645387bae5d6004f6d3607f9, name: 'item-b' },
    { _id: 645387bae5d6004f6d3607fa, name: 'item-c' }
  ],
  __v: 1
}

As you can see, we add the item with item-c name to the list.items array successfully.

Lin Du
  • 88,126
  • 95
  • 281
  • 483