web-dev-qa-db-fra.com

Trouver ou créer avec Mongoose

J'ai 

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

Mon problème est que si aucun identifiant de page n’est donné, il faut simplement prendre la première page disponible en fonction de certaines conditions, ce qui est fait par

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

mais si aucune page n'est trouvée, il faut créer une nouvelle page et l'utiliser, ce qui est fait avec

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

Mais comment combiner tout cela avec le moins de lignes possible?

J'ai beaucoup de logique à l'intérieur

page => { ... }

donc je voudrais bien faire cela intelligemment pour pouvoir éviter de le faire comme ça

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;
        ..
      });
    }
  });
}

Je pense que je pourrais peut-être assigner un statique au schéma avec quelque chose comme

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

Relatif à Yosvel Quintero's réponse qui n'a pas fonctionné pour moi:

pageSchema.statics.findOneOrCreate = function findOneOrCreate(condition, callback) {
    const self = this
    self.findOne(condition, (err, result) => {
        return result ? callback(err, result) : self.create(condition, (err, result) => { return callback(err, result) })
    })
}

Et puis l'utiliser comme:

Page.findOneOrCreate({ key: 'value' }, (err, page) => {
    // ... code
    console.log(page)
})
6
David Joos

Selon le Mongoose docs :

Selon réponse précédente SO

Model.findByIdAndUpdate ()

"Trouve un document correspondant, le met à jour conformément à l'argument de mise à jour, en passant les options et renvoie le document trouvé (le cas échéant) au rappel."

Dans les options, définissez upsert sur true:

upsert: bool - crée l'objet s'il n'existe pas. par défaut à false.

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

Chaque schéma peut définir une instance et des méthodes statiques pour son modèle. La statique est à peu près la même chose que les méthodes mais permet de définir des fonctions qui existent directement sur votre modèle.

Méthode statique findOneOrCreate:

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

Maintenant, quand vous avez une instance de Page, vous pouvez appeler findOneOrCreate:

Page.findOneOrCreate({id: 'somePageId'}, (err, page) => {
  console.log(page);
});
8
Yosvel Quintero

Promise async/await version.

Page.static('findOneOrCreate', async function findOneOrCreate(condition, doc) {
  const one = await this.findOne(condition);

  return one || this.create(doc);
});

Usage

Page.findOneOrCreate({ id: page.id }, page).then(...).catch(...)

Ou

async () => {
  const yourPage = await Page.findOneOrCreate({  id: page.id }, page);
}
5
Ninh Pham

Utiliser des promesses:

pageSchema.statics.findOneOrCreate = function(id, cb){
  return (id?this.findById(id, cb):this.findOne({}, cb))
  .then(page=>page? page : this.create({}, cb))
}

alors vous pouvez l'utiliser comme ceci: 

Page.findOneOrCreate(pageId, (err, page)=>{
  if(err){
    //if theres an error, do something
  }
  //  or do something with the page
})

ou avec des promesses:

Page.findOneOrCreate(id)
.then(page=> /* do something with page*/ )
.catch(err=> /* do something if there's an error*/ )
0
flakolefluk

Si vous ne souhaitez pas ajouter de méthode statique au modèle, vous pouvez essayer de déplacer certaines choses et au moins ne pas avoir tous ces niveaux imbriqués de rappel:

function getPageById (callback) {
  Page.findById(pageId).then(page => {
    return callback(null, page);
  });
}

function getFirstPage(callback) {
  Page.findOne({}).then(page => {
    if (page) {
      return callback(null, page);
    }

    return callback();
  });
}

let retrievePage = getFirstPage;
if (pageId) {
  retrievePage = getPageById;
}

retrievePage(function (err, page) {
  if (err) {
    // @todo: handle the error
  }

  if (page && page.id) {
    pageId = page.id;
  } else {
    Page.create({}).then(page => {
      pageId = page.id;
    });
  }
});
0
stavros.zavrakas

essaye ça.. 

 var myfunc = function (pageId) {
  // check for pageId passed or not
 var newId = (typeof pageId == 'undefined') ? {} : {_id:pageId};

 Page.findOne(pageId).then(page => {
 if (page)
 const pageId = page.id;
 else {  // if record not found, create new

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

 }
0
Umakant Mane