0

Schema

{
    chapter: {
        required: true,
        type: Schema.Types.ObjectId,
        ref: "Chapter",
    },
    questions: {
        type: [Number]
    },
};

Here is an example document

{
   "_id":{
      "$oid":"5ff4b728b6af610f0851d2a6"
   },
   "chapters":[
      {
         "chapter":{
            "$oid":"611478ab34dde61f28dbe4d3"
         },
         "questions":[
            35,
            29,
            167,
            180,
            101,
            16,
            71,
            23
         ]
      },
      {
         "chapter":{
            "$oid":"611478ac34dde61f28dbe4d8"
         },
         "questions":[
            162
         ]
      }
   ]
}

I want to "$addToSet" on "questions", such as

const someId = SOME_ID;
const chapterId = "611478ac34dde61f28dbe4d8";
const update = {
    $addToSet: {
        "chapters.$.questions": {
            $each: [5, 10, 32, 6],
        },
    },
};
await model.findOneAndUpdate(
    {
        _id: someId,
        "chapters.chapter": chapterId,
    },
    update,
    { upsert: true }
)
    .lean()
    .exec();

This works. However, if there is no document, the "upsert" doesn't create the document. How can I rewrite the operation so that it can update (addToSet) as well as ensure the document is created if it didn't exist?

2 Answers 2

1

I checked MongoDB native query use these

db.con.collection('example').updateOne(
    {"chapters": {$elemMatch:{"chapter.id":ObjectId("611478ac34dde61f28dbe4d8")}}},
    {$addToSet: {
        "chapters.$.questions": {
            $each: [5, 10, 32, 6],
        },
    }},
    {upsert: true})

you should find the element of array using elemMatch

{"chapters": {$elemMatch:{"chapter.id":"611478ac34dde61f28dbe4d8"}}}
Sign up to request clarification or add additional context in comments.

8 Comments

Unfortunately, it doesn't work when there is no document. It returns "BadValue" error.
is chapter object in chapters array , or chapters array is your collection ?
You can see the example document structure I have in the question. Chapter object is in chapters array.
ok my query is mongo query not mongoose did you translate my query to mongoose?
did you use {$elemMatch:{"chapter.id":ObjectId("611478ac34dde61f28dbe4d8")}} ?
|
0

I figured out, for some reason, I can't $addToSet if the parent object is not present. So I had to make one more operation.

Inspired from this Stackoverflow answer.

  1. I fetch the "chapters" which I need to add.
  2. From this list of fetched chapters, I check which ones exist and which ones don't.
  3. Using the knowledge from point 2, I am using $push to add the chapters which didn't exist entirely, and "adding to set ($addToSet)" questions on the chapters which do exist.

I am posting the code which works for me.

//Data to add (which chapter?: questionNumber[])
const docId = "SOMEID";
const questionsToAdd = {
    "611478ab34dde61f28dbe4d3": [1,5,6,10],
    "611478ab34dde61f28dbe4d8": [5,8,20,30],
};

//Find the chapters from questionsToAdd which exist
const existingDoc = await model.findOne({
    _id: docId,
    chapters: { $elemMatch: { chapter: { $in: Object.keys(questionsToAdd) } } },
})
    .select(["chapters.chapter"])
    .lean()
    .exec();

// Objectify the array of chapters
const existingChapters = (existingDoc?.chapters ?? []).map((x) => "" + x.chapter);

// Prepare what to insert, what to update
const updateObject = {
    $addToSet: {},
    arrayFilters: [],
    $push: [],
};
for (const [index, [chapterId, questionIndices]] of Object.entries(questionsToAdd).entries()) {
    if (existingChapters.includes(chapterId)) {
        updateObject.$addToSet["chapters.$[filter" + index + "].questions"] = { $each: questionIndices };
        updateObject.arrayFilters.push({
            ["filter" + index + ".chapter"]: Types.ObjectId(chapterId),
        });
    } else {
        updateObject.$push.push({
            chapter: chapterId,
            questions: questionIndices,
        });
    }
}

if (updateObject.arrayFilters.length) {
    // *Add to set the chapters which exist
    await model.findOneAndUpdate(
        { _id: userId },
        {
            $addToSet: updateObject.$addToSet,
        },
        {
            arrayFilters: updateObject.arrayFilters,
            upsert: true,
        }
    )
        .lean()
        .exec();
}

if (updateObject.$push.length) {
    // *Push the ones that does not exist
    await model.findOneAndUpdate(
        { _id: userId },
        {
            $push: { chapters: updateObject.$push },
        },
        {
            upsert: true,
        }
    )
        .lean()
        .exec();
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.