it-swarm.com.de

Wie aktualisiere / aktualisiere ich ein Dokument in Mongoose?

Vielleicht ist es an der Zeit, vielleicht ertrinke ich in spärlichen Unterlagen und kann mich nicht mit dem Konzept der Aktualisierung in Mongoose auseinandersetzen :)

Das ist der Deal:

Ich habe ein Kontaktschema und ein Modell (verkürzte Eigenschaften):

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

Ich erhalte eine Anfrage vom Kunden, die die Felder enthält, die ich benötige, und benutze mein Modell folgendermaßen:

mongoose.connect(connectionString);
var contact = new Contact({
    phone: request.phone,
    status: request.status
});

Und jetzt kommen wir zum Problem:

  1. Wenn ich contact.save(function(err){...}) anrufe, erhalte ich eine Fehlermeldung, wenn der Kontakt mit derselben Telefonnummer bereits existiert (wie erwartet - eindeutig).
  2. Ich kann update() bei Kontakt nicht aufrufen, da diese Methode in einem Dokument nicht vorhanden ist
  3. Wenn ich update am Modell anrufe:
    Contact.update({phone:request.phone}, contact, {upsert: true}, function(err{...})
    Ich gerate in eine Endlosschleife, da die Mongoose-Update-Implementierung offensichtlich kein Objekt als zweiten Parameter haben möchte.
  4. Wenn ich dasselbe mache, aber im zweiten Parameter ein assoziatives Array der Anforderungseigenschaften {status: request.status, phone: request.phone ...} Übergebe, funktioniert es - aber dann habe ich keinen Verweis auf den spezifischen Kontakt und kann dessen createdAt nicht herausfinden. und updatedAt Eigenschaften.

Also das Fazit, nachdem ich versucht habe: ein Dokument contact gegeben, wie aktualisiere ich es, wenn es existiert, oder füge es hinzu, wenn es nicht existiert?

Vielen Dank für Ihre Zeit.

315

Nun, ich habe lange genug gewartet und keine Antwort. Endlich gab das Update/Upsert-Konzept auf und ging mit:

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);
            }
        });
    }
});

Funktioniert es? Ja. Bin ich damit zufrieden? Wahrscheinlich nicht. 2 DB-Aufrufe statt einem.
Hoffentlich würde eine zukünftige Mongoose-Implementierung eine Model.upsert-Funktion haben.

66

Mongoose unterstützt dies nun nativ mit findOneAndUpdate (ruft MongoDB findAndModify auf). 

Die Option upsert = true erstellt das Objekt, wenn es nicht vorhanden ist. ist standardmäßig 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");
});

In älteren Versionen unterstützt Mongoose diese Hooks mit dieser Methode nicht:

  • standardeinstellungen
  • setter
  • validatoren
  • middleware
338
Pascalius

Ich habe nur 3 Stunden verbrannt und versucht, das gleiche Problem zu lösen. Insbesondere wollte ich das gesamte Dokument "ersetzen", wenn es existiert, oder es anderweitig einfügen. Hier ist die Lösung:

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

Ich habe ein Problem auf der Mongoose-Projektseite erstellt in dem ich bat, dass Informationen zu den Dokumenten hinzugefügt werden.

175
Clint Harris

Du warst in der Nähe

Contact.update({phone:request.phone}, contact, {upsert: true}, function(err){...})

ihr zweiter Parameter sollte jedoch beispielsweise ein Objekt mit einem Änderungsoperator sein

Contact.update({phone:request.phone}, {$set: { phone: request.phone }}, {upsert: true}, function(err){...})
87
chrixian

Eine sehr elegante Lösung, die Sie mit der Kette der Versprechen erreichen können:

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);
    });
});
22

Ich habe ein StackOverflow-Konto erstellt, um diese Frage zu beantworten. Nachdem ich die Interwebs erfolglos durchsucht hatte, schrieb ich einfach selbst etwas. So habe ich es gemacht, damit es auf jedes Mungo-Modell angewendet werden kann. Importieren Sie diese Funktion oder fügen Sie sie direkt in Ihren Code ein, wo Sie die Aktualisierung durchführen.

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;
}

Um ein Mungo-Dokument aufzurüsten, führen Sie folgende Schritte aus:

YourModel.upsert = function (id, newData, callBack) {
  this.findById(id, function (err, oldData) {
    if(err) {
      callBack(err);
    } else {
      upsertObject(newData, oldData).save(callBack);
    }
  });
};

Diese Lösung erfordert möglicherweise 2 DB-Aufrufe, jedoch profitieren Sie von

  • Schema-Validierung für Ihr Modell, weil Sie .save () verwenden
  • Sie können tief verschachtelte Objekte ohne manuelle Aufzählung in Ihrem Aktualisierungsaufruf aktualisieren. Wenn sich Ihr Modell ändert, müssen Sie sich also keine Gedanken um die Aktualisierung Ihres Codes machen

Denken Sie daran, dass das Zielobjekt die Quelle immer überschreibt, selbst wenn die Quelle einen vorhandenen Wert hat

Wenn das vorhandene Objekt ein längeres Array als das ersetzende hat, bleiben die Werte am Ende des alten Arrays erhalten. Eine einfache Möglichkeit, das gesamte Array aufzurüsten, besteht darin, das alte Array vor dem Upert auf ein leeres Array zu setzen, wenn Sie dies beabsichtigen.

UPDATE - 16.01.2016 Ich fügte eine zusätzliche Bedingung hinzu, wenn ein Array von primitiven Werten vorhanden ist. Mongoose erkennt nicht, dass das Array ohne Verwendung der Funktion "set" aktualisiert wird.

14
Aaron Mast

Ich musste ein Dokument in einer Sammlung aktualisieren/aktualisieren, was ich tat, um ein neues Objektliteral wie folgt zu erstellen:

notificationObject = {
    user_id: user.user_id,
    feed: {
        feed_id: feed.feed_id,
        channel_id: feed.channel_id,
        feed_title: ''
    }
};

bestehend aus Daten, die ich von irgendwo anders in meiner Datenbank erhalten habe, und dann Update für das Modell aufrufen

Notification.update(notificationObject, notificationObject, {upsert: true}, function(err, num, n){
    if(err){
        throw err;
    }
    console.log(num, n);
});

dies ist die Ausgabe, die ich bekomme, nachdem ich das Skript zum ersten Mal ausgeführt habe:

1 { updatedExisting: false,
    upserted: 5289267a861b659b6a00c638,
    n: 1,
    connectionId: 11,
    err: null,
    ok: 1 }

Und dies ist die Ausgabe, wenn ich das Skript zum zweiten Mal ausführen:

1 { updatedExisting: true, n: 1, connectionId: 18, err: null, ok: 1 }

Ich verwende die Mungo-Version 3.6.16

12
andres_gcarmona
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!' });
            });

        });
    });

Hier ist ein besserer Ansatz zum Lösen der Aktualisierungsmethode in mongoose. Weitere Informationen finden Sie in Scotch.io . Das hat definitiv für mich funktioniert !!!

9
Eyo Okon Eyo

In 2.6 wurde ein Fehler eingeführt, der sich auch auf 2.7 auswirkt

Der Aufwärtstrend funktionierte früher korrekt auf 2.4

https://groups.google.com/forum/#!topic/mongodb-user/UcKvx4p4hnYhttps://jira.mongodb.org/browse/SERVER-13843

Schauen Sie mal rein, es enthält einige wichtige Informationen

AKTUALISIERTE:

Es bedeutet nicht, dass Upsert nicht funktioniert. Hier ist ein schönes Beispiel, wie man es benutzt:

User.findByIdAndUpdate(userId, {online: true, $setOnInsert: {username: username, friends: []}}, {upsert: true})
    .populate('friends')
    .exec(function (err, user) {
        if (err) throw err;
        console.log(user);

        // Emit load event

        socket.emit('load', user);
    });
8
helpse

Sie können den Datensatz einfach damit aktualisieren und die aktualisierten Daten als Antwort erhalten

router.patch('/:id', (req, res, next) => {
    const id = req.params.id;
    Product.findByIdAndUpdate(id, req.body, {
            new: true
        },
        function(err, model) {
            if (!err) {
                res.status(201).json({
                    data: model
                });
            } else {
                res.status(500).json({
                    message: "not found any relative data"
                })
            }
        });
});
5
Muhammad Awais

das hat für mich funktioniert. 

app.put('/student/:id', (req, res) => {
    Student.findByIdAndUpdate(req.params.id, req.body, (err, user) => {
        if (err) {
            return res
                .status(500)
                .send({error: "unsuccessful"})
        };
        res.send({success: "success"});
    });

});

4
Emmanuel Ndukwe

Hier ist die einfachste Methode zum Erstellen/Aktualisieren und Aufrufen der Middleware und der Validatoren.

Contact.findOne({ phone: request.phone }, (err, doc) => {
    const contact = (doc) ? doc.set(request) : new Contact(request);

    contact.save((saveErr, savedContact) => {
        if (saveErr) throw saveErr;
        console.log(savedContact);
    });
})
3
Min

Für jeden, der hier ankommt, der nach einer guten Lösung für das "Aufstoßen" mit Hakenunterstützung sucht, habe ich dies getestet und gearbeitet. Es erfordert immer noch 2 DB-Aufrufe, ist aber wesentlich stabiler als alles, was ich in einem einzigen Aufruf versucht habe.

// Create or update a Person by unique email.
// @param person - a new or existing Person
function savePerson(person, done) {
  var fieldsToUpdate = ['name', 'phone', 'address'];

  Person.findOne({
    email: person.email
  }, function(err, toUpdate) {
    if (err) {
      done(err);
    }

    if (toUpdate) {
      // Mongoose object have extra properties, we can either omit those props
      // or specify which ones we want to update.  I chose to update the ones I know exist
      // to avoid breaking things if Mongoose objects change in the future.
      _.merge(toUpdate, _.pick(person, fieldsToUpdate));
    } else {      
      toUpdate = person;
    }

    toUpdate.save(function(err, updated, numberAffected) {
      if (err) {
        done(err);
      }

      done(null, updated, numberAffected);
    });
  });
}
3
Terry

Ich kam gerade nach einer Weile zu dieser Ausgabe zurück und beschloss, ein Plugin basierend auf der Antwort von Aaron Mast zu veröffentlichen. 

https://www.npmjs.com/package/mongoose-recursive-upsert

Verwenden Sie es als Mungo-Plugin. Es richtet eine statische Methode ein, die das übergebene Objekt rekursiv zusammenführt. 

Model.upsert({unique: 'value'}, updateObject});
2
Richard G

Nach der Antwort von Traveling Tech Guy , die bereits großartig ist, können wir ein Plugin erstellen und es an mongoose anhängen, sobald wir es initialisieren, so dass .upsert() für alle Modelle verfügbar ist.

plugins.js

export default (schema, options) => {
  schema.statics.upsert = async function(query, data) {
    let record = await this.findOne(query)
    if (!record) {
      record = new this(data)
    } else {
      Object.keys(data).forEach(k => {
        record[k] = data[k]
      })
    }
    return await record.save()
  }
}

db.js

import mongoose from 'mongoose'

import Plugins from './plugins'

mongoose.connect({ ... })
mongoose.plugin(Plugins)

export default mongoose

Dann können Sie etwas wie User.upsert({ _id: 1 }, { foo: 'bar' }) oder YouModel.upsert({ bar: 'foo' }, { value: 1 }) machen, wann immer Sie möchten.

2
spondbob
//Here is my code to it... work like ninj

router.param('contractor', function(req, res, next, id) {
  var query = Contractors.findById(id);

  query.exec(function (err, contractor){
    if (err) { return next(err); }
    if (!contractor) { return next(new Error("can't find contractor")); }

    req.contractor = contractor;
    return next();
  });
});

router.get('/contractors/:contractor/save', function(req, res, next) {

    contractor = req.contractor ;
    contractor.update({'_id':contractor._id},{upsert: true},function(err,contractor){
       if(err){ 
            res.json(err);
            return next(); 
            }
    return res.json(contractor); 
  });
});


--
2
Ron Belson
ContactSchema.connection.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);
            }
        });
    }
});
2
Lun

Wenn Generatoren verfügbar sind, wird es noch einfacher:

var query = {'username':this.req.user.username};
this.req.newData.username = this.req.user.username;
this.body = yield MyModel.findOneAndUpdate(query, this.req.newData).exec();
2
Blacksonic

Keine andere Lösung hat für mich funktioniert. Ich verwende eine Post-Anfrage und aktualisiere die Daten, wenn sie gefunden wurde. Andernfalls fügen Sie sie ein. Außerdem wird _id mit dem Anfragetext gesendet, der entfernt werden muss.

router.post('/user/createOrUpdate', function(req,res){
    var request_data = req.body;
    var userModel = new User(request_data);
    var upsertData = userModel.toObject();
    delete upsertData._id;

    var currentUserId;
    if (request_data._id || request_data._id !== '') {
        currentUserId = new mongoose.mongo.ObjectId(request_data._id);
    } else {
        currentUserId = new mongoose.mongo.ObjectId();
    }

    User.update({_id: currentUserId}, upsertData, {upsert: true},
        function (err) {
            if (err) throw err;
        }
    );
    res.redirect('/home');

});
1
User.findByIdAndUpdate(req.param('userId'), req.body, (err, user) => {
    if(err) return res.json(err);

    res.json({ success: true });
});
1
Zeeshan Ahmad

Dieses Coffeescript funktioniert mit Node für mich. Der Trick besteht darin, dass die _id von ihrem ObjectID-Wrapper befreit wird, wenn sie vom Client gesendet und zurückgegeben wird. Dies muss für Aktualisierungen ersetzt werden ein).

app.post '/new', (req, res) ->
    # post data becomes .query
    data = req.query
    coll = db.collection 'restos'
    data._id = ObjectID(data._id) if data._id

    coll.save data, {safe:true}, (err, result) ->
        console.log("error: "+err) if err
        return res.send 500, err if err

        console.log(result)
        return res.send 200, JSON.stringify result
0
Simon H

um auf das aufzubauen, was Martin Kuzdowicz oben geschrieben hat. Ich verwende Folgendes, um ein Update mit Mungo und ein tiefes Zusammenführen von Json-Objekten durchzuführen. Zusammen mit der Funktion model.save () in mongoose kann mongoose auch dann eine vollständige Validierung durchführen, die andere Werte im json verwendet. Es erfordert das Deepmerge-Paket https://www.npmjs.com/package/deepmerge . Das ist jedoch ein sehr leichtes Paket. 

var merge = require('deepmerge');

app.put('url', (req, res) => {

    const modelId = req.body.model_id;

    MyModel.findById(modelId).then((model) => {
        return Object.assign(model, merge(model.toObject(), req.body));
    }).then((model) => {
        return model.save();
    }).then((updatedModel) => {
        res.json({
            msg: 'model updated',
            updatedModel
        });
    }).catch((err) => {
        res.send(err);
    });
});
0
Chris Deleo