63

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

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

感谢关注

4

9 回答 9

94

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. 如果更新操作成功则返回成功。这是因为资源是否存在不在此服务的职责范围内。
于 2016-12-20T17:38:45.957 回答
27

您可以使用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在单个查询中运行,因此无法知道该行是否被插入。
于 2016-08-26T17:25:01.903 回答
21

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

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){});
于 2017-01-02T22:14:01.000 回答
12

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

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);
于 2016-01-02T05:31:06.553 回答
8
User.upsert({ a: 'a', b: 'b', username: 'john' })

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

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

于 2019-08-31T16:57:22.547 回答
2

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

于 2013-08-19T01:36:58.553 回答
2

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

假设您的数据位于名为 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
于 2014-08-13T00:01:28.653 回答
1

您可以在 sequelize中使用findOrCreatethen方法。update这是一个带有 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
     });
});
于 2014-12-17T09:01:59.633 回答
0

这是一个简单的示例,它要么更新 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);
            });
        }
      });
  });
};
于 2016-10-01T18:38:12.527 回答