【问题标题】:Find one or create with Mongoose使用 Mongoose 查找或创建
【发布时间】:2017-02-27 09:08:25
【问题描述】:

我有

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

我的问题是,如果没有给出页面 id,它应该只取第一个可用的页面给定一些条件,这是由

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

但是如果没有找到页面,它应该创建一个新页面并使用它,这是完成的

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

但是如何将所有这些组合到尽可能少的行中呢?

我有很多逻辑在里面发生

page => { ... }

所以我很想聪明地做这件事,所以我可以避免这样做

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

我在想我也许可以用类似的东西为架构分配一个静态

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

【问题讨论】:

  • 您是否要在此处插入一个空文档:Page.create({}).then(page)
  • 是的,这是有意的 :-)

标签: javascript node.js mongodb mongoose mongoose-schema


【解决方案1】:

根据猫鼬docs

根据previous SO answer

Model.findByIdAndUpdate()

“找到一个匹配的文档,根据更新参数更新它,传递任何选项,并将找到的文档(如果有的话)返回给回调。”

在选项中设置 upsert 为 true:

upsert: bool - 如果对象不存在则创建它。默认为 false。

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

【讨论】:

【解决方案2】:

Yosvel Quintero 的回答有关,但对我不起作用:

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

然后像这样使用它:

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

【讨论】:

  • 我猜,对于 NodsJS 和 Mongoose 用户来说,好消息是 'mongoose-find-one-or-create' npm 插件,它的工作方式非常有魅力,并且像插件一样易于修补您的数据库架构。希望对大家有所帮助。
  • 答案不是原子的。
  • 你是对的。 要求:NodeJs 和 Mongoose
【解决方案3】:

承诺async/await 版本。

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

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

用法

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

或者

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

【讨论】:

  • 这不是原子的。
  • 如果多个客户端同时写入集合,有可能在.findOne()返回false之后,另一个客户端创建一个文档,然后这段代码创建另一个。见atomicity
  • 什么时候成为事实?我相信,如果你有一个好的系统设计,你的开发人员就不需要处理这个问题。如果findOne 在很多情况下实际上并不好,我相信 mongoose/mongodb 会将其标记为已弃用。为简单起见,只需将上述代码视为find or create 的快捷方式。或者使用upsert 作为提到的另一个答案。
  • 没有“好的系统设计”可以阻止我所描述的情况。当然,这不太可能发生在小型开发中,很少有写入。一个很好的问题是,在这个答案中使用两步方法与the findOneAndUpdate answer 中的单一原子步骤相比,真正有什么好处。
【解决方案4】:

每个模式都可以为其模型定义实例和static methods。静态与方法几乎相同,但允许定义直接存在于模型中的函数

静态方法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);
      });
  });
};

现在,当您拥有Page 的实例时,您可以调用findOneOrCreate

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

【讨论】:

  • 用承诺而不是回调会是什么样子?
  • 这是PagSchema.static 不是PageSchema.statics
  • 这不是原子的。
【解决方案5】:

async/await 的单行解决方案:

const page = Page.findOne({}).then(p => p || p.create({})

【讨论】:

  • 不是原子的。原子解决方案必须使用具有upsert 选项的.updateOne 或.findOneAndUpdate。
【解决方案6】:

如果你不想在模型中添加静态方法,你可以尝试移动一些东西,至少不要让所有这些回调嵌套级别:

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

【讨论】:

    【解决方案7】:

    试试这个..

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

    【讨论】:

      猜你喜欢
      • 2013-12-17
      • 2013-05-26
      • 2014-09-26
      • 2020-04-12
      • 2017-03-07
      • 2018-02-09
      • 1970-01-01
      • 2018-02-16
      • 2021-10-25
      相关资源
      最近更新 更多