J'utilise Sequelize dans mon projet Nodejs et j'ai trouvé un problème que j'ai du mal à résoudre .. .. En gros, j'ai un cron qui récupère un tableau d'objets d'un serveur puis l'insère dans ma base de données en tant que objet (dans ce cas, les dessins animés). Mais si j'ai déjà l'un des objets, je dois le mettre à jour.
Fondamentalement, j'ai un tableau d'objets et un pourrait utiliser la méthode BulkCreate (). Mais comme le Cron redémarre, il ne résout pas le problème et il me fallait donc une sorte de mise à jour avec un drapeau plus grand. Et le problème principal: je dois avoir un rappel qui se déclenche une seule fois après que tout cela ait été créé ou mis à jour. Quelqu'un at-il une idée de comment puis-je faire cela? Itérer sur un tableau d'objets .. en le créant ou en le mettant à jour, puis en obtenant un seul rappel après?
Merci pour l'attention
A partir de docs , vous n'avez pas besoin d'interroger where
pour effectuer la mise à jour une fois que vous avez l'objet. De plus, l'utilisation de promise devrait simplifier les rappels:
La mise en oeuvre
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);
}
}
})
}
Utilisation
upsert({ first_name: 'Taku' }, { id: 1234 }).then(function(result){
res.status(200).send({success: true});
});
Remarque
ce qui signifie qu'il est conseillé de repenser l'approche et probablement simplement mettre à jour les valeurs dans un appel réseau et soit:
J'ai aimé l'idée d'Ataik, mais je l'ai raccourcie un peu:
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} }) ;
}
}
Usage:
updateOrCreate(models.NewsItem, {slug: 'sometitle1'}, {title: 'Hello World'})
.then(function(result) {
result.item; // the model
result.created; // bool, if a new item was created.
});
Facultatif: ajoutez le traitement des erreurs ici, mais je recommande fortement d'enchaîner toutes les promesses d'une demande et d'avoir un gestionnaire d'erreurs à la fin.
updateOrCreate(models.NewsItem, {slug: 'sometitle1'}, {title: 'Hello World'})
.then(..)
.catch(function(err){});
Vous pouvez utiliser upsert C'est beaucoup plus facile.
Détails d'implémentation:
MySQL - Implémenté comme une requête unique, INSERT des valeurs sur une clé dupliquée
Valeurs UPDATE PostgreSQL - Implémenté comme une fonction temporaire avec gestion des exceptions: INSERT EXCEPTION WHEN unique_constraint UPDATE
SQLite - implémenté sous la forme de deux requêtes INSERT; METTRE À JOUR. Cela signifie que la mise à jour est exécutée, que la ligne existe déjà ou non ou pas
C'est peut-être une vieille question, mais voici ce que j'ai fait:
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);
Vous aimez bien insérer vos appels Sequelize dans un async.each .
Cela peut être fait avec l'émetteur d'événements personnalisés.
En supposant que vos données se trouvent dans une variable appelée 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
Voici un exemple simple qui met à jour le mappage deviceID -> pushToken ou le crée:
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);
});
}
});
});
};
vous pouvez utiliser les méthodes findOrCreate
puis update
dans séquençage. voici un exemple avec 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
});
});