Создать или обновить Sequelize
Я использую Sequelize в своем проекте Nodejs, и я нашел проблему, с которой мне трудно решить.
В основном у меня есть cron, который получает массив объектов с сервера, чем вставляет его в мою базу данных как объект (для этого случая - мультфильмы). Но если у меня уже есть один из объектов, я должен его обновить.
В основном у меня есть массив объектов, и он может использовать метод BulkCreate(). Но по мере того, как Cron запускается снова, он не решает проблему, поэтому мне нужно какое-то обновление с флагом upsert true. И главная проблема: у меня должен быть обратный вызов, который срабатывает только один раз после того, как все это создает или обновляет. Кто-нибудь имеет представление о том, как я могу это сделать? Итерации по массиву объектов.. создание или обновление его, а затем получение одного обратного вызова после?
Спасибо за внимание
Ответы
Ответ 1
Из документов вам не нужно запрашивать where
, чтобы выполнить обновление, как только у вас появится объект. Кроме того, использование обещания должно упростить обратные вызовы:
Реализация
function upsert(values, condition) {
return Model
.findOne({ where: condition })
.then(function(obj) {
// update
if(obj)
return obj.update(values);
// insert
return Model.create(values);
})
}
Usage
Usage
upsert({ first_name: 'Taku' }, { id: 1234 }).then(function(result){
res.status(200).send({success: true});
});
Примечание
- Эта операция не является атомарной.
- Создает 2 сетевых вызова.
Это означает, что рекомендуется переосмыслить подход и, возможно, просто обновить значения в одном сетевом вызове, а также:
- Посмотрите на возвращаемое значение (т.е. row_affered) и решите, что делать.
- Вернуть успех, если операция обновления прошла успешно. Это связано с тем, что наличие ресурса не входит в эту обязанность службы.
Ответ 2
Вы можете использовать upsert It намного проще.
Детали реализации:
- MySQL - реализован в виде одного запроса
INSERT values ON DUPLICATE KEY UPDATE values
- PostgreSQL - реализован как временная функция с обработкой исключений:
INSERT EXCEPTION WHEN unique_constraint UPDATE
- SQLite - реализован как два запроса
INSERT; UPDATE
INSERT; UPDATE
. Это означает, что обновление выполняется независимо от того, существовала строка или нет - MSSQL - реализован как один запрос с использованием
MERGE and WHEN (NOT) MATCHED THEN
Обратите внимание, что SQLite возвращает undefined для созданного, независимо от того, была ли строка создана или обновлена. Это связано с тем, что SQLite всегда выполняет INSERT OR IGNORE + UPDATE
в одном запросе, поэтому невозможно узнать, была ли вставлена строка или нет.
Ответ 3
Обновление 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.
});
Необязательно: добавьте обработку ошибок здесь, но я настоятельно рекомендую объединить все обещания одного запроса и иметь один обработчик ошибок в конце.
updateOrCreate(models.NewsItem, {slug: 'sometitle1'}, {title: 'Hello World'})
.then(..)
.catch(function(err){});
Ответ 4
Это может быть старый вопрос, но это то, что я сделал:
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);
Ответ 5
Звук нравится, что вы хотите обернуть ваши вызовы Sequelize внутри async.each.
Ответ 6
Это можно сделать с помощью специального события-эмиттера.
Предполагая, что ваши данные находятся в переменной, называемой данными.
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
Ответ 7
вы можете использовать findOrCreate
, а затем update
методы в sequelize. вот пример с 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
});
});
Ответ 8
Вот простой пример, который либо обновляет 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);
});
}
});
});
};
Ответ 9
User.upsert({ a: 'a', b: 'b', username: 'john' })
Он попытается найти запись по хешу в 1-м параметре, чтобы обновить ее, если он не найдет ее, то будет создана новая запись
Здесь - это пример использования в тестах на продолжение
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);
});
});