How to use upsert | Exception while simulating the effect of invoking '' TypeError: Cannot read properties of undefined (reading '_id')

Hi, I’m having trouble with upsert . I could update questions finely with the below codes but I won’t be able to insert new data.

file in client side

const onSave = () =>{
       // there is  more codes but omitted
     questions.forEach(question => {
     Meteor.call('modifyQuestion', question);
 })
}

file in server side (collection file)

  modifyQuestion(question) {
    check(question, Object);

    const questionId = Measures.findOne({questionId: question._id});
    Measures.upsert(
      {_id: questionId._id},
      {
        $set: {
          title: question.text,
        },
      },
    );
  },

Got error saying…
Exception while simulating the effect of invoking 'modifyQuestion' TypeError: Cannot read properties of undefined (reading '_id')

I thought when {_id: questionId._id} got undefined, that’s the time upsert understand that there is no matching data found and insert as new data into the database.
Is this wrong?

it’s not the upsert error. The problem is the questionId is undefined.
you can change it from

{_id: questionId._id},

to

{_id: questionId?._id},

But it would lead you to other problem. The find condition will be empty object {}, which means it will match any record, then it will update the first one in your collection.
You should use 2 different cases for insert and update or modify the upsert condition like:

import { Random } from 'meteor/random'
...
{_id: questionId?._id || Random.id()},

Hi @miki11, Welcome to the forum :slight_smile:

it seems to me that this code is wrong… you don’t need to perform a findOne to do an upsert, you can simply just use it like this: {_id: question._id},

Thank you for your answer! @minhna and @hschmaiske

As you two suggested when I remove fiindOne and change operator, the error was gone!
I changed the code like below

  modifyQuestion(question) {
    check(question, Object);
    Measures.upsert(
      {questionId: questionId._id},  // changed _id to questionId
      {
        $set: {
          title: question.text,
        },
      },
    );
  },

Now my problem is I can update documents but not insert.

Do you have any ideas how to insert documents if they don’t exist?

Do I need to use not only $set but also $setOnInsert ?

I just saw a article that is using $setOnInsert when they have upsert

I’m sure that code doesn’t work. questionId is undefined.
When you’re not sure how to use upsert function, you can use insert and update instead.
For example:

if (document_exist) {
 // call update function
} else {
 // call insert function
}

Again Thank you for your answer!

Actually that’s exactly I’m trying to do now

modifyQuestion(question) {
  const checkExistingQuestion = Measures.findOne({questionId: question._id});

  if (checkExistingQuestion) {
    Measures.update(
      {questionId: question._id},
      {
        $set: {
          title: question.text,
        },
      },
    );
  } else {
    Measures.insert({
      $set: {
        title: question.text,
      },
    });
  }
},

And I’m seeing the error saying

while simulating the effect of invoking 'modifyQuestion' Error: When the validation object contains mongo operators, you must set the modifier option to true

I also tried removing findOne({}) and using if(question){ } else{ } but still got the same error…

Do you know what is meaning of this error? I’m googling but couldn’t figure out a answer

Check the format of insert parameters

1 Like