【问题标题】:Sequelize upsert续集更新
【发布时间】:2015-05-17 18:21:31
【问题描述】:

在sequelize 中使用.upsert() 时,我需要获取插入/更新记录的ID。

现在.upsert() 返回一个布尔值,指示该行是创建还是更新。

return db.VenueAddress.upsert({
            addressId:address.addressId,
            venueId: venue.venueId,
            street: address.street,
            zipCode: address.zipCode,
            venueAddressDeletedAt: null
        }).then(function(test){
            //test returned here as true or false how can i get the inserted id here so i can insert data in other tables using this new id?
        });

【问题讨论】:

  • 因为您需要一个唯一的 id 来执行 upsert,并且必须为该 id 提供一个值,所以您已经拥有它
  • 不,因为我插入了一条新记录,然后我想要该记录的自动增量 ID

标签: node.js sequelize.js


【解决方案1】:

我不认为当 OP 提出这个问题时返回插入的记录是可用的,但它已经用这个PR 实现了。从 Sequelize v4.32.1 开始,您可以将布尔值 returning 作为查询参数传递,以在返回包含记录的数组和布尔值之间进行选择,或者只是一个布尔值,指示是否有新记录被创建。

您仍然需要提供要更新插入的记录的 ID,否则将创建新记录。

例如:

const [record, created] = await Model.upsert(
  { id: 1, name: 'foo' }, // Record to upsert
  { returning: true }     // Return upserted record
);

【讨论】:

  • 自 2019 年 5 月起,MySQL 不支持此功能。
  • @max_i 现在有不同的方法吗?
  • upsert 仍然不支持 MySQL @cullanrocks 你可以根据提到的 Matthew Chung 实现自己的。
【解决方案2】:

我希望 upsert 返回创建或更新的对象。不是因为只有 PGSQL 直接支持它,显然。

所以我创建了一个幼稚的实现,它可能会以一种非性能的方式,并且可能有各种竞争条件,这样做:

Sequelize.Model.prototype.findCreateUpdate = function(findWhereMap, newValuesMap) {
  return this.findOrCreate({
    where: findWhereMap, 
    defaults: findWhereMap
  })
  .spread(function(newObj, created) {
    // set:
    for(var key in newValuesMap) {
      newObj[key] = newValuesMap[key];
    }

    return newObj.save();
  });
};

尝试在游戏中创建/更新动作时使用(人为的示例警报!):

models.Game
.findOne({where: {gameId: gameId}})
.then(function(game) {
  return db.Move.findCreateUpdate(
    {gameId: gameId, moveNum: game.moveNum+1}, 
    {startPos: 'kr4', endPos: 'Kp2'}
  );
});

【讨论】:

  • 这对我创建自己的 findAndUpdateOrCreate 函数非常有帮助 :-)
【解决方案3】:

这对我有用:

Model.upsert({
    title:your title,
    desc:your description,
    location:your locations
}).then(function (test) {
    if(test){
        res.status(200);
        res.send("Successfully stored");
    }else{
        res.status(200);
        res.send("Successfully inserted");
    }
})

它将根据您的主键检查 db 以查找。如果找到,它将更新数据,否则它将创建一个新行/插入新行。

【讨论】:

  • 我认为您误读了这个问题 - OP 想要获取通过运行 upsert 查询创建或更新的记录的 ID,此时不可能立即获得所有方言时间(Postgres 和 MSSQL 除外),因为在您的示例中 - 'test' 将是一个布尔值。 github.com/sequelize/sequelize/issues/…
【解决方案4】:

我知道这是一篇旧帖子,但万一这对任何人都有帮助

const upsert = async (model: any, values: any, condition: any): Promise<any> => {
  const obj = await model.findOne({ where: condition })
  if (obj) {
    // only do update is value is different from queried object from db
    for (var key in values) {
      const val = values[key]
      if (parseFloat(obj[key]) !== val) {
        obj.isUpdatedRecord = true
        return obj.update(values)
      }
    }
    obj.isUpdatedRecord = false
    return obj

  } else {
    // insert
    const merged = { ...values, ...condition }
    return model.create(merged)
  }
}

【讨论】:

  • 这很有帮助。我对检查是否需要使用 JSON.stringify() ` const val = JSON.stringify(values[key]); 进行一些更改常量 updateVal = JSON.stringify(obj[key]); if (updateVal !== val) { await obj.update(values).then((data) => { toCheckAfterSql = data; data.isUpdatedRecord = true; }).catch(err => { throw new Error(err) ; }); } `
【解决方案5】:

它没有使用 upsert,但 .bulkCreate 有一个 updateOnDuplicate 参数,它允许您在主键已经存在的情况下更新某些字段(而不是创建新行)。

MyModel.bulkCreate(
  newRows,
  {
    updateOnDuplicate: ["venueId", ...]
  }
)

我相信这会返回结果对象,因此我认为这可能会启用您正在寻找的功能?

【讨论】:

    【解决方案6】:

    janmeier 说:

    这仅受 postgres 支持,因此要保持 API 跨方言一致是不可能的。

    请看:https://github.com/sequelize/sequelize/issues/3354

    【讨论】:

    • 注意:这正在实施。见PR #6325
    • @MaxTruxa 知道什么时候?我个人希望在 Sequelize 中包含该功能,即使内部代码是:“插入或更新并返回对象”。 (我假设 findCreateFind 没有做我需要的事情,因为我假设默认值 only 在创建时设置,并且不会进行更新)。
    • @Ian Grainger 我不知道。我没有参与这个项目,只是找到了PR。您可以询问 PR 的维护者/创建者之一。
    【解决方案7】:

    我相信我的解决方案是最新的,代码最少。

    const SequelizeModel = require('sequelize/lib/model')
    SequelizeModel.upsert = function() {
      return this.findOne({
        where: arguments[0].where
      }).then(obj => {
        if(obj) {
          obj.update(arguments[0].defaults)
          return
        }
        return this.create(arguments[0].defaults)
      })
    }
    

    【讨论】:

      【解决方案8】:

      我知道这是一篇旧帖子,但如果这对任何人都有帮助...您可以根据 OP 数据以这种方式获取返回的 id 或任何其他值。

         var data = {
          addressId:address.addressId,
          venueId: venue.venueId,
          street: address.street,
          zipCode: address.zipCode,
          venueAddressDeletedAt: null
         }
      
         const result = await db.VenueAddress.upsert(data, { returning: true });
         console.log('resulttttttttttttttttt =>', result)
         
        res.status(200).json({ message: 'Your success message', data: result[0].id});
      

      注意到我如何通过{ returning: true } 并从结果数据中获取值。

      【讨论】:

      • Sequelize code cmets on 'returning' 属性说它“仅适用于 postgres”。
      【解决方案9】:

      我自己解决如下:

      return db.VenueAddress.upsert({
              addressId:address.addressId,
              venueId: venue.venueId,
              street: address.street,
              zipCode: address.zipCode,
              venueAddressDeletedAt: null
          },{individualHooks: true}).then(function(test){ 
              // note individualHooks
          });
      

      【讨论】:

      • 如果您想为每个单独的记录发出钩子,以及批量钩子,您可以将 individualHooks: true 传递给调用。 Model.destroy({ where: {accessLevel: 0}, individualHooks: true}) // 将选择所有即将被删除的记录,并在每个实例上销毁前-+后-销毁 Model.update({username: 'Toni '}, { where: {accessLevel: 0}, individualHooks: true}) // 将选择所有即将更新的记录,并在每个实例上发出 before- + after- Update
      • afterupsert 挂钩返回“创建”变量(真/假),而不是对象实例。所以id不是从这里获取的
      猜你喜欢
      • 2016-07-27
      • 1970-01-01
      • 2022-08-16
      • 2018-06-15
      • 1970-01-01
      • 1970-01-01
      • 2022-01-23
      • 1970-01-01
      相关资源
      最近更新 更多