【问题标题】:Create or Update Sequelize创建或更新 Sequelize
【发布时间】:2013-08-20 16:49:38
【问题描述】:

我在我的 Nodejs 项目中使用 Sequelize,我发现了一个我很难解决的问题。 基本上我有一个 cron 从服务器获取对象数组,然后将其作为对象插入到我的数据库中(对于这种情况,卡通)。但如果我已经拥有其中一个对象,我必须对其进行更新。

基本上我有一个对象数组,并且可以使用 BulkCreate() 方法。但是当 Cron 再次启动时,它并没有解决它,所以我需要一些带有 upsert true 标志的更新。主要问题是:在所有这些创建或更新之后,我必须有一个只触发一次的回调。有谁知道我该怎么做?迭代一个对象数组..创建或更新它,然后得到一个回调?

感谢关注

【问题讨论】:

    标签: mysql node.js sequelize.js


    【解决方案1】:

    docs 获取对象后,您无需查询where 即可执行更新。此外,promise 的使用应该简化回调:

    实施

    function upsert(values, condition) {
        return Model
            .findOne({ where: condition })
            .then(function(obj) {
                // update
                if(obj)
                    return obj.update(values);
                // insert
                return Model.create(values);
            })
    }
    

    用法

    upsert({ first_name: 'Taku' }, { id: 1234 }).then(function(result){
        res.status(200).send({success: true});
    });
    

    注意

    1. 此操作不是原子操作。
    2. 创建 2 个网络调用。

    这意味着建议重新考虑该方法,并且可能只更新一次网络调用中的值,或者:

    1. 查看返回的值(即 rows_affected)并决定做什么。
    2. 如果更新操作成功则返回成功。这是因为资源是否存在不在此服务的职责范围内。

    【讨论】:

    • 返回 Model.create({...values, ...condition})
    • 处理高频数据时,非原子性会导致数据重复发生!否则,一个优雅的解决方案!
    【解决方案2】:

    您可以使用upsert 这更容易。

    实现细节:

    • MySQL - 作为单个查询实现INSERT values ON DUPLICATE KEY UPDATE values
    • PostgreSQL - 实现为带有异常处理的临时函数:INSERT EXCEPTION WHEN unique_constraint UPDATE
    • SQLite - 实现为两个查询INSERT; UPDATE。这意味着无论该行是否已经执行更新 存在与否
    • MSSQL - 使用MERGE and WHEN (NOT) MATCHED THEN 作为单个查询实现注意 SQLite 返回未定义的创建,没有 无论该行是创建还是更新。这是因为 SQLite 总是在一个查询中运行INSERT OR IGNORE + UPDATE,所以有 无法知道该行是否已插入。

    【讨论】:

    【解决方案3】:

    更新 07/2019 现在使用 async/await

    async function updateOrCreate (model, where, newItem) {
        // First try to find the record
       const foundItem = await model.findOne({where});
       if (!foundItem) {
            // Item not found, create a new one
            const item = await model.create(newItem)
            return  {item, created: true};
        }
        // Found an item, update it
        const item = await model.update(newItem, {where});
        return {item, created: false};
    }
    

    我喜欢 Ataik 的想法,但把它缩短了一点:

    function updateOrCreate (model, where, newItem) {
        // First try to find the record
        return model
        .findOne({where: where})
        .then(function (foundItem) {
            if (!foundItem) {
                // Item not found, create a new one
                return model
                    .create(newItem)
                    .then(function (item) { return  {item: item, created: true}; })
            }
             // Found an item, update it
            return model
                .update(newItem, {where: where})
                .then(function (item) { return {item: item, created: false} }) ;
        }
    }
    

    用法:

    updateOrCreate(models.NewsItem, {slug: 'sometitle1'}, {title: 'Hello World'})
        .then(function(result) {
            result.item;  // the model
            result.created; // bool, if a new item was created.
        });
    

    可选:在此处添加错误处理,但我强烈建议将一个请求的所有承诺链接起来,并在最后使用一个错误处理程序。

    updateOrCreate(models.NewsItem, {slug: 'sometitle1'}, {title: 'Hello World'})
        .then(..)
        .catch(function(err){});
    

    【讨论】:

    • 评论:我注意到 taht update() .. 不返回数据库对象。所以如果你需要它,你可以再拿一次。
    【解决方案4】:

    这可能是一个老问题,但这就是我所做的:

    var updateOrCreate = function (model, where, newItem, onCreate, onUpdate, onError) {
        // First try to find the record
        model.findOne({where: where}).then(function (foundItem) {
            if (!foundItem) {
                // Item not found, create a new one
                model.create(newItem)
                    .then(onCreate)
                    .catch(onError);
            } else {
                // Found an item, update it
                model.update(newItem, {where: where})
                    .then(onUpdate)
                    .catch(onError);
                ;
            }
        }).catch(onError);
    }
    updateOrCreate(
        models.NewsItem, {title: 'sometitle1'}, {title: 'sometitle'},
        function () {
            console.log('created');
        },
        function () {
            console.log('updated');
        },
        console.log);
    

    【讨论】:

    • 提示:使用承诺链进行错误处理并节省 50% 的代码 :)
    • 父子关系怎么用?如何更新子表?
    【解决方案5】:
    User.upsert({ a: 'a', b: 'b', username: 'john' })
    

    它将尝试通过第一个参数中的哈希查找记录以更新它,如果找不到 - 然后将创建新记录

    Here 是 sequelize 测试中的使用示例

    it('works with upsert on id', function() {
        return this.User.upsert({ id: 42, username: 'john' }).then(created => {
            if (dialect === 'sqlite') {
                expect(created).to.be.undefined;
            } else {
                expect(created).to.be.ok;
            }
    
            this.clock.tick(1000);
            return this.User.upsert({ id: 42, username: 'doe' });
        }).then(created => {
            if (dialect === 'sqlite') {
                expect(created).to.be.undefined;
            } else {
                expect(created).not.to.be.ok;
            }
    
            return this.User.findByPk(42);
        }).then(user => {
            expect(user.createdAt).to.be.ok;
            expect(user.username).to.equal('doe');
            expect(user.updatedAt).to.be.afterTime(user.createdAt);
        });
    });
    
    

    【讨论】:

    • 它将在唯一或主键列上找到匹配项
    • 我真的很喜欢回答问题的方式。荣誉
    【解决方案6】:

    听起来您想将 Sequelize 调用包装在 async.each 中。

    【讨论】:

    • 我希望在 Sequelize 的更新中提供一些令人不安的选项,但它似乎超出了路线图。我会试试这个。谢谢!
    【解决方案7】:

    这可以通过自定义事件发射器来完成。

    假设您的数据位于名为 data 的变量中。

    new Sequelize.Utils.CustomEventEmitter(function(emitter) {
        if(data.id){
            Model.update(data, {id: data.id })
            .success(function(){
                emitter.emit('success', data.id );
            }).error(function(error){
                emitter.emit('error', error );
            });
        } else {
            Model.build(data).save().success(function(d){
                emitter.emit('success', d.id );
            }).error(function(error){
                emitter.emit('error', error );
            });
        }
    }).success(function(data_id){
        // Your callback stuff here
    }).error(function(error){
       // error stuff here
    }).run();  // kick off the queries
    

    【讨论】:

      【解决方案8】:

      您可以在 sequelize 中使用 findOrCreateupdate 方法。这是一个带有 async.js 的示例

      async.auto({
         getInstance : function(cb) {
            Model.findOrCreate({
              attribute : value,
              ...
            }).complete(function(err, result) {
              if (err) {
                cb(null, false);
              } else {
                cb(null, result);
              }
            });
          },
          updateInstance : ['getInstance', function(cb, result) {
            if (!result || !result.getInstance) {
              cb(null, false);
            } else {
              result.getInstance.updateAttributes({
                 attribute : value,
                 ...
              }, ['attribute', ...]).complete(function(err, result) {
                if (err) {
                  cb(null, false);
                } else {
                  cb(null, result);
                }
              });
             }
            }]
           }, function(err, allResults) {
             if (err || !allResults || !allResults.updateInstance) {
               // job not done
             } else {
               // job done
           });
      });
      

      【讨论】:

        【解决方案9】:

        这是一个简单的例子,它要么更新 deviceID -> pushToken 映射,要么创建它:

        var Promise = require('promise');
        var PushToken = require("../models").PushToken;
        
        var createOrUpdatePushToken = function (deviceID, pushToken) {
          return new Promise(function (fulfill, reject) {
            PushToken
              .findOrCreate({
                where: {
                  deviceID: deviceID
                }, defaults: {
                  pushToken: pushToken
                }
              })
              .spread(function (foundOrCreatedPushToken, created) {
                if (created) {
                  fulfill(foundOrCreatedPushToken);
                } else {
                  foundOrCreatedPushToken
                    .update({
                      pushToken: pushToken
                    })
                    .then(function (updatedPushToken) {
                      fulfill(updatedPushToken);
                    })
                    .catch(function (err) {
                      reject(err);
                    });
                }
              });
          });
        };
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-12-05
          • 1970-01-01
          • 1970-01-01
          • 2016-03-15
          • 2020-10-31
          相关资源
          最近更新 更多