Find one or create with Mongoose

Jamgreen picture Jamgreen · Oct 18, 2016 · Viewed 29.1k times · Source

I have

Page.findById(pageId).then(page => {
  const pageId = page.id;
   ..
});

My problem is that if no page id is given, it should just take the first available page given some conditions, which is done by

Page.findOne({}).then(page => {
  const pageId = page.id;
  ..
});

but if no page is found, it should create a new page and use this, which is done with

Page.create({}).then(page => {
  const pageId = page.id;
  ..
});

But how do I combine all this to as few lines as possible?

I have a lot of logic going on inside

page => { ... }

so I would very much like to do this smart, so I can avoid doing it like this

if (pageId) {
  Page.findById(pageId).then(page => {
    const pageId = page.id;
     ..
  });
} else {
  Page.findOne({}).then(page => {
    if (page) {
      const pageId = page.id;
      ..
    } else {
      Page.create({}).then(page => {
        const pageId = page.id;
        ..
      });
    }
  });
}

I am thinking I maybe could assign a static to the schema with something like

pageSchema.statics.findOneOrCreate = function (condition, doc, callback) {
  const self = this;
  self.findOne(condition).then(callback).catch((err, result) => {
    self.create(doc).then(callback);
  });
};

Answer

Julian Boyce picture Julian Boyce · Jun 1, 2018

As per the Mongoose docs:

As per previous SO answer

Model.findByIdAndUpdate()

"Finds a matching document, updates it according to the update arg, passing any options, and returns the found document (if any) to the callback."

In the options set upsert to true:

upsert: bool - creates the object if it doesn't exist. defaults to false.

Model.findByIdAndUpdate(id, { $set: { name: 'SOME_VALUE' }}, { upsert: true  }, callback)