水线ORM等效于重复密钥更新时的插入

时间:2013-11-08 17:12:04

标签: sql node.js orm sails.js waterline

我有一个表user_address,它有一些像

这样的字段
attributes: {
  user_id: 'integer',
  address: 'string' //etc.
}

目前我这样做是为了插入新记录,但如果该记录存​​在,请更新它:

UserAddress
  .query(
    'INSERT INTO user_address (user_id, address) VALUES (?, ?) ' +
      'ON DUPLICATE KEY UPDATE address=VALUES(address);',
    params,
    function(err) {
       //error handling logic if err exists
    }

有没有办法使用Waterline ORM而不是直接的SQL查询来实现同样的目的?我不想做两个查询,因为它效率低,难以维护。

3 个答案:

答案 0 :(得分:7)

上面的答案不太理想。它还将该方法作为模型属性的一部分,这是不正确的行为。

以下是理想的原生解决方案看起来像任何其他水线模型功能一样返回承诺:

module.exports = {
  attributes: {
    user_id: 'integer',
    address: 'string'
  },
  updateOrCreate: function (user_id, address) {
    return UserAddress.findOne().where({user_id: user_id}).then(function (ua) {
      if (ua) {
        return UserAddress.update({user_id: user_id}, {address: address});
      } else {
        // UserAddress does not exist. Create.
        return UserAddress.create({user_id: user_id, address: address});
      }
    });
  }
}

然后你可以像使用它一样:

UserAddress.updateOrCreate(id, address).then(function(ua) {
   // ... success logic here
}).catch(function(e) {
   // ... error handling here
});

答案 1 :(得分:4)

使用Waterline查询创建一个自定义模型方法,而不是原始SQL。您将使用Waterline语法进行两次查询。

下面的示例(如果您不知道延迟对象,那么只需使用回调语法,但逻辑是相同的):

var Q = require('q');

module.exports = {
  attributes: {
    user_id: 'integer',
    address: 'string',
    updateOrCreate: function (user_id, address) {
      var deferred = Q.defer();

      UserAddress.findOne().where({user_id: user_id}).then(function (ua) {
        if (ua) {
          // UserAddress exists. Update.
          ua.address = address;
          ua.save(function (err) {deferred.resolve();});
        } else {
          // UserAddress does not exist. Create.
          UserAddress.create({user_id: user_id, address: address}).done(function (e, ua) {deferred.resolve();});
        }
      }).fail(function (err) {deferred.reject()});

    return deferred.promise;
  }
};

答案 2 :(得分:4)

@ Eugene的答案很好,但它总是会运行2次操作:findOne + updatecreate。我相信我们可以进一步优化它,因为如果记录存在,我们只需要运行update。例如:

module.exports = {
  attributes: {
    user_id: 'integer',
    address: 'string'
  },
  updateOrCreate: function (user_id, address) {
    return UserAddress.update({user_id: user_id}, {address: address})
    .then(function(ua){
      if(ua.length === 0){
        // No records updated, UserAddress does not exist. Create.
        return UserAddress.create({user_id: user_id, address: address});
      }
    });
  }
}

顺便说一下,有一条在水线中实施.updateOrCreate的公开请求:#790