javascript - Hvordan oppdaterer/oppretter jeg et dokument i Mongoose?
Kanskje det er tiden, kanskje det er meg som drukner i sparsom dokumentasjon og ikke klarer å få hodet rundt konseptet med oppdatering i Mongoose :)
Slik er avtalen:
Jeg har et kontaktskjema og en modell (forkortede egenskaper):
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var mongooseTypes = require("mongoose-types"),
useTimestamps = mongooseTypes.useTimestamps;
var ContactSchema = new Schema({
phone: {
type: String,
index: {
unique: true,
dropDups: true
}
},
status: {
type: String,
lowercase: true,
trim: true,
default: 'on'
}
});
ContactSchema.plugin(useTimestamps);
var Contact = mongoose.model('Contact', ContactSchema);
Jeg mottar en forespørsel fra klienten, som inneholder feltene jeg trenger, og bruker modellen min slik:
mongoose.connect(connectionString);
var contact = new Contact({
phone: request.phone,
status: request.status
});
Og nå når vi fram til problemet:
- Hvis jeg kaller
contact.save(function(err){...})vil jeg få en feil hvis kontakten med samme telefonnummer allerede finnes (som forventet - unikt) - Jeg kan ikke kalle
update()på kontakt, siden den metoden ikke finnes på et dokument - Hvis jeg kaller update på modellen:
Contact.update({phone:request.phone}, contact, {upsert: true}, function(err{...})
kommer jeg inn i en slags uendelig løkke, siden Mongoose sin update-implementasjon tydeligvis ikke vil ha et objekt som den andre parameteren. - Hvis jeg gjør det samme, men i den andre parameteren sender en assosiativ tabell med forespørselsegenskapene
{status: request.status, phone: request.phone ...}, fungerer det - men da har jeg ingen referanse til den spesifikke kontakten og kan ikke finne ut av denscreatedAtogupdatedAt-egenskaper.
Så bunnlinjen, etter alt jeg har prøvd: gitt et dokument contact, hvordan oppdaterer jeg det hvis det finnes, eller legger det til hvis det ikke gjør det?
Takk for tiden din.
Svar #1
Mongoose støtter nå dette nativity med findOneAndUpdate (kaller MongoDB findAndModify).
Alternativet upsert=true oppretter objektet hvis det ikke finnes. standard er false.
var query = {'username': req.user.username};
req.newData.username = req.user.username;
MyModel.findOneAndUpdate(query, req.newData, {upsert: true}, function(err, doc) {
if (err) return res.send(500, {error: err});
return res.send('Succesfully saved.');
});
I eldre versjoner støtter ikke Mongoose disse krokene med denne metoden:
- standardinnstillinger
- settere
- validatorer
- mellomvare
Svar #2
Jeg brukte nettopp tre hele timer på å prøve å løse det samme problemet. Spesielt ønsket jeg å «erstatte» hele dokumentet hvis det eksisterer, eller sette det inn ellers. Her er løsningen:
var contact = new Contact({
phone: request.phone,
status: request.status
});
// Convert the Model instance to a simple object using Model's 'toObject' function
// to prevent weirdness like infinite looping...
var upsertData = contact.toObject();
// Delete the _id property, otherwise Mongo will return a "Mod on _id not allowed" error
delete upsertData._id;
// Do the upsert, which works like this: If no Contact document exists with
// _id = contact.id, then create a new doc using upsertData.
// Otherwise, update the existing doc with upsertData
Contact.update({_id: contact.id}, upsertData, {upsert: true}, function(err{...});
Jeg opprettet en sak på Mongoose-prosjektsiden og ba om at informasjon om dette skulle legges til i dokumentasjonen.
Svar #3
Du var nær med
Contact.update({phone:request.phone}, contact, {upsert: true}, function(err){...})
men din andre parameter bør være et objekt med en modifikasjonsoperator for eksempel
Contact.update({phone:request.phone}, {$set: { phone: request.phone }}, {upsert: true}, function(err){...})
Svar #4
Jeg er vedlikeholderen av Mongoose. Den mer moderne måten å oppdatere eller sette inn et dokument på er å bruke Model.updateOne()-funksjonen.
await Contact.updateOne({
phone: request.phone
}, { status: request.status }, { upsert: true });
Hvis du trenger den innlagte eller oppdaterte dokumentet, kan du bruke Model.findOneAndUpdate()
const doc = await Contact.findOneAndUpdate({
phone: request.phone
}, { status: request.status }, { upsert: true, useFindAndModify: false });
Hovedpoenget er at du må legge de unike egenskapene i filter-parameteren til updateOne() eller findOneAndUpdate(), og de andre egenskapene i update-parameteren.
Her er en veiledning om oppdatering eller innsetting av dokumenter med Mongoose.
Svar #5
Vel, jeg ventet lenge nok og fikk inget svar. Til slutt ga jeg opp hele oppdaterings-/innsettingsmetoden og gikk for:
ContactSchema.findOne({phone: request.phone}, function(err, contact) {
if(!err) {
if(!contact) {
contact = new ContactSchema();
contact.phone = request.phone;
}
contact.status = request.status;
contact.save(function(err) {
if(!err) {
console.log("contact " + contact.phone + " created at " + contact.createdAt + " updated at " + contact.updatedAt);
}
else {
console.log("Error: could not save contact " + contact.phone);
}
});
}
});
Fungerer det? Jepp. Er jeg fornøyd med dette? Sannsynligvis ikke. 2 DB-kall i stedet for ett.
Forhåpentligvis vil en fremtidig Mongoose-implementering komme opp med en Model.upsert-funksjon.
Svar #6
Veldig elegant løsning du kan oppnå ved å bruke en kjede av Promises:
app.put('url', (req, res) => {
const modelId = req.body.model_id;
const newName = req.body.name;
MyModel.findById(modelId).then((model) => {
return Object.assign(model, {name: newName});
}).then((model) => {
return model.save();
}).then((updatedModel) => {
res.json({
msg: 'model updated',
updatedModel
});
}).catch((err) => {
res.send(err);
});
});
Svar #7
Jeg opprettet en StackOverflow-konto BARE for å svare på dette spørsmålet. Etter å ha søkt fruktløst på internett skrev jeg rett og slett noe selv. Slik gjorde jeg det, så det kan brukes på hvilken som helst Mongoose-modell. Enten importer denne funksjonen eller legg den direkte inn i koden din der du gjør oppdateringen.
function upsertObject (src, dest) {
function recursiveFunc (src, dest) {
_.forOwn(src, function (value, key) {
if(_.isObject(value) && _.keys(value).length !== 0) {
dest[key] = dest[key] || {};
recursiveFunc(src[key], dest[key])
} else if (_.isArray(src) && !_.isObject(src[key])) {
dest.set(key, value);
} else {
dest[key] = value;
}
});
}
recursiveFunc(src, dest);
return dest;
}
Deretter, for å sette inn eller oppdatere et mongoose-dokument, gjør følgende,
YourModel.upsert = function (id, newData, callBack) {
this.findById(id, function (err, oldData) {
if(err) {
callBack(err);
} else {
upsertObject(newData, oldData).save(callBack);
}
});
};
Denne løsningen kan kreve 2 databasekall, men du får fordelen av,
- Skjemavalidering mot modellen din fordi du bruker .save()
- Du kan oppdatere eller sette inn dypt nestede objekter uten manuell opplisting i oppdateringskallet ditt, så hvis modellen din endres, trenger du ikke å bekymre deg for å oppdatere koden din
Husk bare at destinasjonsobjektet alltid vil overstyre kilden selv om kilden har en eksisterende verdi
Også, for arrays, hvis det eksisterende objektet har en lengre array enn den som erstatter det, vil verdiene på slutten av den gamle arrayen forbli. En enkel måte å oppdatere hele arrayen på er å sette den gamle arrayen til å være en tom array før oppdateringen hvis det er det du har tenkt å gjøre.
OPPDATERING - 16.01.2016 Jeg la til en ekstra betingelse for hvis det er et array av primitive verdier, Mongoose skjønner ikke at arrayet blir oppdatert uten å bruke "set"-funksjonen.
Svar #8
Jeg trengte å oppdatere/sett inn et dokument i en samling, det jeg gjorde var å lage et nytt objekt litteral på denne måten:
notificationObject = {
user_id: user.user_id,
feed: {
feed_id: feed.feed_id,
channel_id: feed.channel_id,
feed_title: ''
}
};
sammensatt fra data som jeg får fra et annet sted i databasen min og deretter kaller oppdatering på Modellen
Notification.update(notificationObject, notificationObject, {upsert: true}, function(err, num, n){
if(err){
throw err;
}
console.log(num, n);
});
dette er utskriften jeg får etter å ha kjørt skriptet for første gang:
1 { updatedExisting: false,
upserted: 5289267a861b659b6a00c638,
n: 1,
connectionId: 11,
err: null,
ok: 1 }
Og dette er resultatet når jeg kjører skriptet for andre gang:
1 { updatedExisting: true, n: 1, connectionId: 18, err: null, ok: 1 }
Jeg bruker mongoose versjon 3.6.16
Svar #9
app.put('url', function(req, res) {
// use our bear model to find the bear we want
Bear.findById(req.params.bear_id, function(err, bear) {
if (err)
res.send(err);
bear.name = req.body.name; // update the bears info
// save the bear
bear.save(function(err) {
if (err)
res.send(err);
res.json({ message: 'Bear updated!' });
});
});
});
Her er en bedre tilnærming for å løse oppdateringsmetoden i mongoose, du kan sjekke Scotch.io for flere detaljer. Dette fungerte definitivt for meg!!!
