web-dev-qa-db-ja.com

Sequelizeの作成または更新

NodejsプロジェクトでSequelizeを使用していますが、解決するのに苦労している問題が見つかりました。基本的に、サーバーからオブジェクトの配列を取得するcronがあり、それをオブジェクトとしてデータベースに挿入します(この場合はcartoons)。しかし、オブジェクトのいずれかが既にある場合は、更新する必要があります。

基本的に、オブジェクトの配列があり、BulkCreate()メソッドを使用できます。しかし、Cronが再び起動すると、それは解決されないため、アップサートtrueフラグを使用した何らかの更新が必要でした。そして主な問題:これらすべての作成または更新後に一度だけ起動するコールバックが必要です。誰も私がそれをどのように行うことができるか考えていますか?オブジェクトの配列を反復処理します。作成または更新し、その後単一のコールバックを取得しますか?

注目してくれてありがとう

docs から、whereを照会してオブジェクトを取得したら更新を実行する必要はありません。また、promiseを使用すると、コールバックが簡単になります。

実装

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

使用法

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

  1. この操作はアトミックではありません。
  2. 2つのネットワークコールを作成します。

つまり、アプローチを再考し、おそらく1回のネットワークコールで次のいずれかの値を更新することをお勧めします。

  1. 返される値(rows_affected)を見て、何をすべきかを決定します。
  2. 更新操作が成功した場合、成功を返します。これは、リソースが存在するかどうかがこのサービスの責任の範囲内にないためです。
50
tsuz

更新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.
    });

オプション:ここにエラー処理を追加しますが、1つの要求のすべてのプロミスをチェーンし、最後に1つのエラーハンドラーを作成することを強くお勧めします。

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

psert を使用できます。これはずっと簡単です。

実装の詳細:

  • MySQL-単一のクエリとして実装_INSERT values ON DUPLICATE KEY UPDATE values_
  • PostgreSQL-例外処理付きの一時関数として実装:_INSERT EXCEPTION WHEN unique_constraint UPDATE_
  • SQLite-2つのクエリ_INSERT; UPDATE_として実装されます。これは、行がすでに存在していたかどうかに関係なく、更新が実行されることを意味します
  • MSSQL-MERGE and WHEN (NOT) MATCHED THENを使用して単一のクエリとして実装されます注 。これは、SQLiteが常に単一のクエリで_INSERT OR IGNORE + UPDATE_を実行するため、行が挿入されたかどうかを知る方法がないためです。
10
Alvaro Joao

これは古い質問かもしれませんが、これは私がやったことです:

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);
9
Ateik

Sequelize呼び出しを async.each 内にラップしたいようなサウンドです。

2
Dan Kohn

これは、カスタムイベントエミッターで実行できます。

データが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
1
Jeff Ryan

次に、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);
            });
        }
      });
  });
};
0
Zorayr

sequelizeでfindOrCreateメソッドを使用してから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
     });
});
0
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);
    });
});

0