关于mysql:创建或更新Sequelize

Create or Update Sequelize

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

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

感谢关注


从文档中,您无需查询 where 即可在拥有对象后执行更新。此外,promise 的使用应该简化回调:

实施

1
2
3
4
5
6
7
8
9
10
11
function upsert(values, condition) {
    return Model
        .findOne({ where: condition })
        .then(function(obj) {
            // update
            if(obj)
                return obj.update(values);
            // insert
            return Model.create(values);
        })
}

用法

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

注意

  • 这个操作不是原子的。
  • 创建 2 个网络调用。
  • 这意味着建议重新考虑该方法,并且可能只更新一次网络调用中的值,并且:

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

  • 你可以使用 upsert
    这更容易。

    Implementation details:

    • MySQL - Implemented as a single query INSERT values ON DUPLICATE KEY UPDATE values
    • PostgreSQL - Implemented as a temporary function with exception handling: INSERT EXCEPTION WHEN unique_constraint UPDATE
    • SQLite - Implemented as two queries INSERT; UPDATE. This means that the update is executed regardless of whether the row already
      existed or not
    • MSSQL - Implemented as a single query using MERGE and WHEN (NOT) MATCHED THEN Note that SQLite returns undefined for created, no
      matter if the row was created or updated. This is because SQLite
      always runs INSERT OR IGNORE + UPDATE, in a single query, so there
      is no way to know whether the row was inserted or not.


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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 的想法,但把它缩短了一点:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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} }) ;
        }
    }

    用法:

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

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

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


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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);


    1
    User.upsert({ a: 'a', b: 'b', username: 'john' })

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

    这里是sequelize测试中的使用示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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);
        });
    });


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

    假设您的数据位于名为 data.

    的变量中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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

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


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    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);
                });
            }
          });
      });
    };

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

    的示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    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
         });
    });