teej
teej

Reputation: 125

Mongoose updateMany documents containing specific Array element

I've got a Mongoose schema set up as follows:

const mongoose = require('mongoose');

const TodoSchema = mongoose.Schema({
  id: {
    type: String,
    required: true,
  },
  todos: {
    type: Array,
    required: true,
  },
  date: {
    type: Date,
    default: Date.now(),
  },
});

module.exports = mongoose.model('todo', TodoSchema, 'todos');

Each of the elements in the todos Array is an Object and has the following format (example):

{
  id: 1,
  todo: "Do the dishes."
  category: "Kitchen"
}

There are multiple documents in my Todo collection and they all contain the same list of Todos. If I wanted to update a specific Todo across ALL documents, I figure I need to use updateMany. I'm using the following in my Todo Update route to update all instances of a Todo:

const { todo } = req.body; // todo.todo contains "Clean the dishes." as an update

Todo.updateMany(
  {
    todos: { $elemMatch: { id: todo.id } },
  },
  { $set: { todo: todo } }
);

I'm assigning the result of the above route code to a variable and console logging the result which comes back with:

{ ok: 0, n: 0, nModified: 0 }

What am I doing wrong? The passed todo id matches the id of a Todo in each of the Todos arrays.

Upvotes: 0

Views: 3321

Answers (1)

J.F.
J.F.

Reputation: 15187

First of all, for your object array, is recommendable create a schema too:

const subSchema = new mongoose.Schema({
    id: Number,
    todo: String,
    category: String
})
const MongooseModel = new mongoose.Schema({
    id: String,
    todos: [subSchema],
    date: Date
})

So now, your array object is defined.

And, the query question is something like that:

db.collection.update({
  "todos.id": todo.id
},
{
  "$set": {
    "todos.$": {newTodo}
  }
},
{
  "multi": true
})

First, you look for all elements that match your criteria; that is: todos.id = todo.id, then you use $ operator to set all element that match the criteria with your object. The last line multi is to updated all element that match.

Example playground here

Using moongoose, multi attribute is not neccessary because is set true by default using updateMany().

So moongose query should be something like that.

var update = await model.updateMany(
    {
        "todos.id": 1
    },
    {
    "$set": {
        "todos.$": {
            "id": 20,
            "todo": "newTodo",
            "category": "newCategory"
        }
    }
})

And for this example data the result is

{ n: 3, nModified: 3, ok: 1 }

Upvotes: 1

Related Questions