也许是时间的问题,也许是我淹没在稀疏的文档中,无法理解Mongoose中的更新概念:)

事情是这样的:

我有一个联系模式和模型(缩短属性):

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

我从客户端收到一个请求,包含我需要的字段,然后使用我的模型:

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

现在问题来了:

If I call contact.save(function(err){...}) I'll receive an error if the contact with the same phone number already exists (as expected - unique) I can't call update() on contact, since that method does not exist on a document If I call update on the model: Contact.update({phone:request.phone}, contact, {upsert: true}, function(err{...}) I get into an infinite loop of some sorts, since the Mongoose update implementation clearly doesn't want an object as the second parameter. If I do the same, but in the second parameter I pass an associative array of the request properties {status: request.status, phone: request.phone ...} it works - but then I have no reference to the specific contact and cannot find out its createdAt and updatedAt properties.

因此,我的底线是:给定一个文档联系人,如果它存在,我如何更新它,如果它不存在,我如何添加它?

谢谢你的时间。


当前回答

Mongoose现在支持本地findOneAndUpdate(调用MongoDB findAndModify)。

如果对象不存在,upsert = true选项将创建该对象。默认为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.');
});

在旧版本中,Mongoose不支持这些钩子:

违约 setter 验证器 中间件

其他回答

按照旅行技术人员的回答,这已经很棒了,我们可以创建一个插件,并在初始化后将其附加到mongoose上,这样.upsert()将在所有模型上可用。

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

然后您可以执行User之类的操作。upsert({_id: 1}, {foo: 'bar'})或YouModel。Upsert ({bar: 'foo'}, {value: 1})。

我等了很久,还是没有回音。最后放弃了整个更新/upsert方法,并使用:

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

这有用吗?是的。我满意吗?可能不会。2个DB调用而不是1个。 希望将来的Mongoose实现能提出一个模型。插入功能。

Mongoose现在支持本地findOneAndUpdate(调用MongoDB findAndModify)。

如果对象不存在,upsert = true选项将创建该对象。默认为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.');
});

在旧版本中,Mongoose不支持这些钩子:

违约 setter 验证器 中间件

没有其他的解决方案对我有效。我使用post请求和更新数据,如果发现其他插入它,_id也与需要删除的请求体一起发送。

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

});

以Martin Kuzdowicz上面的帖子为基础。我使用以下内容使用mongoose和json对象的深度合并进行更新。与mongoose中的model.save()函数一起,这允许mongoose进行完整的验证,即使是依赖于json中的其他值。它确实需要deepmerge包https://www.npmjs.com/package/deepmerge。但这是一个非常轻的包装。

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