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回のネットワークコールで次のいずれかの値を更新することをお勧めします。
更新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){});
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
_を実行するため、行が挿入されたかどうかを知る方法がないためです。
これは古い質問かもしれませんが、これは私がやったことです:
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);
Sequelize呼び出しを async.each 内にラップしたいようなサウンドです。
これは、カスタムイベントエミッターで実行できます。
データが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
次に、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);
});
}
});
});
};
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
});
});
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);
});
});