3

In MongoDB, this is the simplified structure of my account document:

{
    "_id" : ObjectId("5a70a60ca7fbc476caea5e59"),
    "templates" : [ 
        {
            "name" : "Password Reset",
            "content" : "AAAAAAAA"
        },
        {
            "name" : "Welcome Message",
            "content" : "BBBBBB"
        }
     ]
}

There's a similar default_templates collection

let accnt = await Account.findOne({ _id: req.account._id }, { templates: 1 });
let defaults = await DefaultTemplate.find({}).lean();

My goal is to find the missing templates under account and grab them from defaults. (a) I need to upsert templates if it doesn't exist in an account and (b) I don't want to update a template if it already exists in an account.

I've tried the following:

if (!accnt.templates || accnt.templates.length < defaults.length) {

  const accountTemplates = _.filter(accnt.templates, 'name');
  const templateNames = _.map(accountTemplates, 'name');

  Account.update({ _id: req.account._id, 'templates.name' : { $nin: templateNames } },
      { '$push': { 'templates': { '$each' : defaults } } }, { 'upsert' : true },
      function(err, result) {
        Logger.error('error %o', err);
        Logger.debug('result %o', result);
      }
  );
}

This succeeds at the upsert but it will enter all default templates even if there's a matching name in templateNames. I've verified that templateNames array is correct and I've also tried using $addToSet instead of $push, so I must not understand Mongo subdoc queries.

Any ideas on what I'm doing wrong?

Edit: I've gotten this to work by simply removing elements from the defaults array before updating, but I'd still like to know how this could be accomplished with Mongoose.

4
  • what does templateNames and defaults contain? Commented Jul 27, 2018 at 2:40
  • @AnthonyWinzlet defaults contains doc/schema identical to the one shown above for account templateNames contains an array of those name properties on that same doc (e.g., [ "Password Reset", "Welcome Message" ]) Commented Jul 27, 2018 at 12:37
  • upsert works on document level, not sub-documents. addToSet works (like doesn't push) only when both name and content are identical. Your approach is how it supposed to work. Commented Jul 27, 2018 at 13:41
  • The only thing is $nin is not doing what you expect. It matches only documents where no single template with name from templateNames, which is somehow different to "find the missing templates". Commented Jul 27, 2018 at 13:47

1 Answer 1

2

You can try with bulkWrite operation in mongodb

Account.bulkWrite(
  req.body.accountTemplates.map((data) => 
    ({
      updateOne: {
        filter: { _id: req.account._id, 'templates.name' : { $ne: data.name } },
        update: { $push: { templates: { $each : data } } },
        upsert : true
      }
    })
  )
})
Sign up to request clarification or add additional context in comments.

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.