ฉันจะอัปเดต / รับเอกสารใน Mongoose ได้อย่างไร


367

บางทีมันอาจเป็นเวลาที่ฉันอาจจมอยู่กับเอกสารที่กระจัดกระจายและไม่สามารถคลุมหัวแนวคิดการอัปเดตใน 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
});

และตอนนี้เราถึงปัญหา:

  1. ถ้าฉันโทรcontact.save(function(err){...})ฉันจะได้รับข้อผิดพลาดหากมีผู้ติดต่อที่มีหมายเลขโทรศัพท์เดียวกันอยู่แล้ว (ตามที่คาดไว้ - ไม่เหมือนใคร)
  2. ฉันไม่สามารถโทรหาupdate()ผู้ติดต่อได้เนื่องจากวิธีการนั้นไม่มีอยู่ในเอกสาร
  3. ถ้าฉันเรียกการอัปเดตในโมเดล:
    Contact.update({phone:request.phone}, contact, {upsert: true}, function(err{...})
    ฉันเข้าสู่วงวนไม่สิ้นสุดบางประเภทเนื่องจากการใช้การอัพเดต Mongoose อย่างชัดเจนไม่ต้องการวัตถุเป็นพารามิเตอร์ตัวที่สอง
  4. ถ้าฉันทำแบบเดียวกัน แต่ในพารามิเตอร์ที่สองฉันผ่านอาเรย์แบบเชื่อมโยงของคุณสมบัติการร้องขอ{status: request.status, phone: request.phone ...}มันใช้งานได้ - แต่จากนั้นฉันไม่มีการอ้างอิงไปยังผู้ติดต่อเฉพาะและไม่สามารถค้นหาcreatedAtและupdatedAtคุณสมบัติได้

บรรทัดล่างดังนั้นหลังจากทั้งหมดที่ฉันลอง: ให้เอกสารcontactฉันจะอัพเดตได้อย่างไรถ้ามันมีอยู่หรือเพิ่มมันถ้ามันไม่มี

ขอบคุณที่สละเวลา.


สิ่งที่เกี่ยวกับตะขอในpreสำหรับsave?
Shamoon

คำตอบ:


427

ขณะนี้ Mongoose สนับสนุนการเริ่มต้นนี้ด้วยfindOneAndUpdate (เรียก MongoDB findAndModify )

ตัวเลือก upsert = true สร้างวัตถุหากไม่มีอยู่ ค่าเริ่มต้นเป็นเท็จ

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 รุ่นที่เก่ากว่าไม่รองรับ hooks เหล่านี้ด้วยวิธีนี้:

  • ค่าเริ่มต้น
  • setters
  • เครื่องมือตรวจสอบ
  • มิดเดิ้ล

17
นี่ควรเป็นคำตอบล่าสุด อื่น ๆ ส่วนใหญ่ใช้สองสายหรือ (ฉันเชื่อว่า) กลับไปใช้ไดรเวอร์ mongodb ดั้งเดิม
huggie

10
ปัญหาเกี่ยวกับ findOneAndUpdate คือการบันทึกล่วงหน้าจะไม่ถูกดำเนินการ
a77icu5

2
ฟังดูเหมือนเป็นข้อบกพร่องใน Mongoose หรือ MongoDB?
Pascalius

8
จากเอกสาร: "... เมื่อใช้ตัวช่วยเหลือ findAndModify จะไม่นำสิ่งต่อไปนี้มาใช้: ค่าเริ่มต้นตัวตั้งค่าตัวตรวจสอบความถูกต้องมิดเดิลแวร์" mongoosejs.com/docs/api.html#model_Model.findOneAndUpdate
kellen

2
@JamieHutber นี้ไม่ได้ตั้งค่าเริ่มต้นมันเป็นคุณสมบัติที่กำหนดเอง
Pascalius

194

ฉันเพิ่งเผาสามชั่วโมงพยายามแก้ปัญหาเดียวกัน โดยเฉพาะฉันต้องการ "แทนที่" ทั้งเอกสารหากมีอยู่หรือแทรกเป็นอย่างอื่น นี่คือทางออก:

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

ฉันสร้างปัญหาในหน้าโครงการพังพอนขอให้เพิ่มข้อมูลเกี่ยวกับสิ่งนี้ลงในเอกสาร


1
เอกสารดูเหมือนว่าจะไม่ดีในขณะนี้ มีบางอย่างในเอกสาร API (ค้นหา "อัปเดต" บนหน้าดูเหมือนว่า: MyModel.update({ age: { $gt: 18 } }, { oldEnough: true }, fn);และMyModel.update({ name: 'Tobi' }, { ferret: true }, { multi: true }, fn);
CpILL

สำหรับเอกสารเคสไม่พบ _id ใดที่ใช้? พังพอนสร้างมันหรือสิ่งที่ถูกสอบถาม?
ไฮเดอร์

91

คุณสนิทกับ

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

แต่พารามิเตอร์ตัวที่สองของคุณควรเป็นวัตถุที่มีตัวดำเนินการดัดแปลงเช่น

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

15
ฉันไม่คิดว่าคุณต้องการ{$set: ... }ส่วนนี้เนื่องจากการอ่านอัตโนมัติของฉัน
CpILL

5
ใช่พังพอนบอกว่ามันจะเปลี่ยนทุกอย่างเป็นชุด $
Grantwparks

1
สิ่งนี้ถูกต้องในขณะที่เขียนฉันไม่ได้ใช้ MongoDB อีกต่อไปดังนั้นฉันจึงไม่สามารถพูดคุยกับการเปลี่ยนแปลงในเดือนที่ผ่านมา: D
chrixian

5
การไม่ใช้ $ set อาจเป็นนิสัยที่ไม่ดีหากคุณจะต้องใช้ไดรเวอร์ดั้งเดิมเป็นครั้งคราว
UpTheCreek

คุณสามารถใช้ $ set และ $ setOnInsert เพื่อตั้งค่าเฉพาะบางฟิลด์ในกรณีของการแทรก
justin.m.chase

73

ฉันรอนานพอและไม่มีคำตอบ ในที่สุดก็เลิกใช้วิธีการอัพเดต / อัพเปอร์ทั้งหมดและไปกับ

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 เรียกแทนที่จะเป็นหนึ่ง
หวังว่าการดำเนินการพังพอนในอนาคตจะเกิดขึ้นกับModel.upsertฟังก์ชั่น


2
ตัวอย่างนี้ใช้อินเทอร์เฟซที่เพิ่มเข้ามาใน MongoDB 2.2 เพื่อระบุตัวเลือกแบบหลายและตัวเลือกในรูปแบบเอกสาร .. รวม :: /includes/fact-upsert-multi-options.rstเอกสารกำลังระบุสิ่งนี้ไม่ทราบว่าจะไปจากที่นี่
Donald Derek

1
แม้ว่าสิ่งนี้จะได้ผล แต่ตอนนี้คุณกำลังใช้งาน 2 การดำเนินการ (ค้นหาอัปเดต) เมื่อต้องการเพียง 1 (upsert) @chrixian แสดงวิธีการที่ถูกต้องในการทำเช่นนี้
respectTheCode

12
เป็นที่น่าสังเกตว่านี่เป็นคำตอบเดียวที่อนุญาตให้ตัวตรวจสอบความถูกต้องของ Mongoose ได้ตามเอกสารการตรวจสอบจะไม่เกิดขึ้นหากคุณเรียกใช้การอัปเดต
Tom Spencer

@fiznool ดูเหมือนว่าคุณสามารถส่งต่อตัวเลือกได้ด้วยตนเองrunValidators: trueระหว่างการอัปเดต: อัปเดตเอกสาร (แต่ตัวตรวจสอบการอัปเดตจะทำงาน$setและทำงานเท่านั้น$unset)
Danny

ดูคำตอบของฉันตามนี้หากคุณจำเป็นต้อง.upsert()มีในทุกรุ่น stackoverflow.com/a/50208331/1586406
spondbob

24

โซลูชันสุดหรูที่คุณสามารถทำได้โดยการใช้โซ่แห่งสัญญา:

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

ทำไมสิ่งนี้ถึงไม่ถูกโหวต ดูเหมือนจะเป็นทางออกที่ดีและสง่างามมาก
MadOgre

ทางออกที่ยอดเยี่ยมจริง ๆ ทำให้ฉันคิดใหม่ว่าฉันเข้าใกล้สัญญาอย่างไร
lux

4
สง่างามมากขึ้นแม้จะเขียน(model) => { return model.save(); }เป็นmodel => model.save()และยัง(err) => { res.send(err); }เป็นerr => res.send(err);)
เจเรมี Thille

1
ฉันจะหาข้อมูลเพิ่มเติมได้ที่ไหน
V0LT3RR4

17

ฉันเป็นคนดูแล Mongoose วิธีที่ทันสมัยมากขึ้นเพื่อ Upsert เอกสารคือการใช้ฟังก์ชั่นModel.updateOne()

await Contact.updateOne({
    phone: request.phone
}, { status: request.status }, { upsert: true });

หากคุณต้องการเอกสารที่ถูกตีพิมพ์คุณสามารถใช้ Model.findOneAndUpdate()

const doc = await Contact.findOneAndUpdate({
    phone: request.phone
}, { status: request.status }, { upsert: true });

ประเด็นสำคัญคือคุณต้องใส่คุณสมบัติเฉพาะในfilterพารามิเตอร์updateOne()หรือหรือfindOneAndUpdate()และคุณสมบัติอื่น ๆ ในupdateพารามิเตอร์

นี่คือการสอนเกี่ยวกับupserting เอกสารที่มีพังพอน


15

ฉันสร้างบัญชี StackOverflow เพียงเพื่อตอบคำถามนี้ หลังจากค้นหา interwebs อย่างไร้ผลฉันก็แค่เขียนบางอย่างด้วยตัวเอง นี่คือวิธีที่ฉันทำมันเพื่อให้สามารถนำไปใช้กับพังพอนรุ่นใดก็ได้ อาจนำเข้าฟังก์ชั่นนี้หรือเพิ่มลงในรหัสของคุณโดยตรงในขณะที่คุณกำลังทำการอัพเดท

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

จากนั้นเมื่อต้องการเพิ่มเอกสารพังพอนให้ทำดังต่อไปนี้

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

วิธีแก้ปัญหานี้อาจต้องใช้การเรียก 2 DB แต่คุณจะได้รับประโยชน์

  • การตรวจสอบสคีมากับโมเดลของคุณเนื่องจากคุณใช้. save ()
  • คุณสามารถเพิ่มความลึกของวัตถุที่ซ้อนกันได้โดยไม่ต้องทำการแจงนับด้วยตัวเองในการเรียกใช้การอัปเดตดังนั้นหากโมเดลของคุณเปลี่ยนแปลงคุณไม่ต้องกังวลกับการอัปเดตโค้ดของคุณ

เพียงจำไว้ว่าวัตถุปลายทางจะแทนที่แหล่งข้อมูลเสมอแม้ว่าแหล่งที่มาจะมีค่าอยู่

นอกจากนี้สำหรับอาร์เรย์ถ้าวัตถุที่มีอยู่มีอาร์เรย์ที่ยาวกว่าหนึ่งแทนที่มันแล้วค่าที่ท้ายอาร์เรย์เก่าจะยังคงอยู่ วิธีที่ง่ายที่สุดในการเพิ่มอาเรย์ทั้งหมดคือการตั้งค่าอาเรย์เก่าให้เป็นอาเรย์ที่ว่างเปล่าก่อนที่จะเพิ่มจำนวนหากเป็นสิ่งที่คุณตั้งใจจะทำ

อัปเดต - 01/16/2016 ฉันเพิ่มเงื่อนไขพิเศษสำหรับถ้ามีค่าดั้งเดิมจำนวนมาก Mongoose ไม่ทราบว่าอาร์เรย์จะได้รับการอัปเดตโดยไม่ใช้ฟังก์ชัน "set"


2
+1 สำหรับการสร้าง acc สำหรับสิ่งนี้: P หวังว่าฉันจะให้ +1 อีกตัวสำหรับการใช้. save (), เพราะ findOneAndUpate () ทำให้เราไม่สามารถใช้ตัวตรวจสอบความถูกต้องและก่อนโพสต์และอื่น ๆ ได้ ขอบคุณฉันจะตรวจสอบเรื่องนี้ด้วย
user1576978

ขออภัย แต่ใช้งานไม่ได้ที่นี่ :( ฉันได้รับขนาดสแต็กการโทรเกิน
user1576978

คุณกำลังใช้ Lodash รุ่นใดอยู่ ฉันใช้ lodash เวอร์ชั่น 2.4.1 ขอบคุณ!
Aaron Mast

นอกจากนี้คุณมีวัตถุที่ซับซ้อนเกินความสามารถอย่างไร หากมีขนาดใหญ่เกินไปกระบวนการของโหนดอาจไม่สามารถจัดการกับจำนวนการเรียกซ้ำที่ต้องการเพื่อรวมวัตถุ
Aaron Mast

ฉันใช้สิ่งนี้ แต่ต้องเพิ่มif(_.isObject(value) && _.keys(value).length !== 0) {เงื่อนไขการป้องกันเพื่อหยุดการโอเวอร์โฟลว์ของสแต็ก Lodash 4+ ที่นี่ดูเหมือนว่าจะแปลงค่าไม่ใช่วัตถุเป็นวัตถุในการkeysโทรดังนั้นยามเรียกซ้ำเป็นจริงเสมอ อาจจะมีวิธีที่ดีกว่า แต่ตอนนี้เกือบจะได้ผลสำหรับฉันแล้ว ...
Richard G

12

ฉันต้องการอัปเดต / เพิ่มเอกสารเป็นชุดเดียวสิ่งที่ฉันทำคือสร้างวัตถุใหม่ตามตัวอักษรดังนี้

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

ประกอบด้วยข้อมูลที่ฉันได้รับจากที่อื่นในฐานข้อมูลของฉันจากนั้นเรียกการอัพเดทในรุ่น

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

นี่คือ ouput ที่ฉันได้รับหลังจากรันสคริปต์เป็นครั้งแรก:

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

และนี่คือผลลัพธ์เมื่อฉันรันสคริปต์เป็นครั้งที่สอง:

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

ฉันใช้พังพอนรุ่น 3.6.16


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

        });
    });

นี่เป็นวิธีที่ดีกว่าในการแก้ไขวิธีการอัปเดตในพังพอนคุณสามารถตรวจสอบScotch.ioสำหรับรายละเอียดเพิ่มเติม มันใช้งานได้ดีสำหรับฉันอย่างแน่นอน !!!


5
เป็นความผิดพลาดที่จะคิดว่านี่เป็นสิ่งเดียวกันกับการอัพเดทของ MongoDB มันไม่ใช่อะตอม
Valentin Waeselynck

1
ฉันต้องการสำรองข้อมูล @ValentinWaeselynck รหัสของสก๊อตนั้นสะอาด - แต่คุณกำลังดึงเอกสารแล้วอัปเดต ในช่วงกลางของกระบวนการเอกสารอาจมีการเปลี่ยนแปลง
Nick Pineda

8

มีข้อบกพร่องที่แนะนำใน 2.6 และมีผลต่อ 2.7 เช่นกัน

คนที่เคยทำงานอย่างถูกต้องใน 2.4

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

ลองดูมันมีข้อมูลสำคัญบางอย่าง

UPDATED:

มันไม่ได้หมายความว่า upsert ไม่ทำงาน นี่คือตัวอย่างที่ดีของวิธีใช้:

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

7

คุณสามารถอัปเดตระเบียนด้วยสิ่งนี้และรับข้อมูลที่อัปเดตตามการตอบสนอง

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

6

สิ่งนี้ใช้ได้สำหรับฉัน

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

});


ขอบคุณ นี่คือสิ่งที่ฉันได้ผลในที่สุด!
Luis Febro

4

ต่อไปนี้เป็นวิธีที่ง่ายที่สุดในการสร้าง / อัปเดตในขณะที่เรียกมิดเดิลแวร์และเครื่องมือตรวจสอบความถูกต้อง

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

สำหรับทุกคนที่มาถึงที่นี่ยังคงมองหาทางออกที่ดีสำหรับ "upserting" พร้อมการรองรับ hooks นี่คือสิ่งที่ฉันได้ทดสอบและทำงาน มันยังต้องการการโทร 2 DB แต่มีความเสถียรมากกว่าทุกอย่างที่ฉันได้ลองในการโทรครั้งเดียว

// 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

หากมีเครื่องกำเนิดไฟฟ้ามันจะยิ่งง่ายขึ้น:

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

3

ไม่มีวิธีอื่นที่เหมาะกับฉัน ฉันกำลังใช้คำขอโพสต์และอัปเดตข้อมูลหากพบว่ามีผู้อื่นแทรกไว้ __ จะถูกส่งไปพร้อมกับเนื้อหาคำขอที่จะต้องถูกลบออก

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

});

2
//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
User.findByIdAndUpdate(req.param('userId'), req.body, (err, user) => {
    if(err) return res.json(err);

    res.json({ success: true });
});

แม้ว่าข้อมูลโค้ดนี้อาจช่วยแก้ปัญหาได้ แต่ก็ไม่ได้อธิบายว่าทำไมหรือตอบคำถามอย่างไร โปรดระบุคำอธิบายสำหรับรหัสของคุณเนื่องจากช่วยปรับปรุงคุณภาพการโพสต์ของคุณได้อย่างแท้จริง จำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตและคนเหล่านั้นอาจไม่ทราบสาเหตุของการแนะนำรหัสของคุณ ผู้ตั้งค่าสถานะ / ผู้ตรวจทาน: สำหรับคำตอบที่ใช้รหัสอย่างเช่น downvote อย่าลบ!
Patrick

2

ต่อไปนี้การเดินทางเทคผู้ชายคำตอบ 's ที่แล้วน่ากลัวเราสามารถสร้างปลั๊กอินและแนบไปกับพังพอนเมื่อเรา initialise เพื่อที่ .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 })เมื่อใดก็ตามที่คุณต้องการ


2

ฉันเพิ่งกลับมาที่ปัญหานี้หลังจากผ่านไปครู่หนึ่งและตัดสินใจที่จะเผยแพร่ปลั๊กอินตามคำตอบของ Aaron Mast

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

ใช้เป็นปลั๊กอินพังพอน มันตั้งค่าวิธีการคงที่ซึ่งจะรวมซ้ำวัตถุที่ผ่านเข้ามา

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

0

coffeescript นี้ทำงานกับฉันด้วย Node - กลอุบายคือ _id get ถูกปล้นของ wrapper ObjectID ของมันเมื่อส่งและส่งคืนจากไคลเอนต์ดังนั้นจึงจำเป็นต้องแทนที่สำหรับการปรับปรุง (เมื่อไม่มี _id ให้บันทึกจะเปลี่ยนกลับไปแทรกและเพิ่ม หนึ่ง).

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

เพื่อสร้างสิ่งที่ Martin Kuzdowicz โพสต์ไว้ด้านบน ฉันใช้สิ่งต่อไปนี้เพื่อทำการอัปเดตโดยใช้พังพอนและการผสานวัตถุ json อย่างลึกซึ้ง พร้อมกับ model.save () ฟังก์ชั่นในพังพอนนี้ช่วยให้พังพอนทำการตรวจสอบเต็มรูปแบบแม้หนึ่งที่อาศัยค่าอื่น ๆ ใน 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);
    });
});

1
ฉันขอเตือนไม่ให้ใช้req.bodyตามเดิมก่อนทดสอบการฉีด NoSQL (ดูowasp.org/index.php/Testing_for_NoSQL_inject )
Guy Tech กำลังเดินทาง

1
@TravelingTechGuy ขอบคุณสำหรับความระมัดระวังฉันยังใหม่กับโหนดและพังพอน โมเดลพังพอนของฉันที่มีเครื่องมือตรวจสอบจะไม่เพียงพอที่จะลองฉีดได้หรือไม่ ในระหว่าง model.save ()
Chris Deleo

-5

หลังจากอ่านโพสต์ด้านบนฉันตัดสินใจใช้รหัสนี้:

    itemModel.findOne({'pid':obj.pid},function(e,r){
        if(r!=null)
        {
             itemModel.update({'pid':obj.pid},obj,{upsert:true},cb);
        }
        else
        {
            var item=new itemModel(obj);
            item.save(cb);
        }
    });

ถ้า r เป็นโมฆะเราจะสร้างรายการใหม่ มิฉะนั้นให้ใช้ upsert ในการอัปเดตเนื่องจากการอัปเดตไม่ได้สร้างรายการใหม่


ถ้าเป็นสองสายไปยัง Mongo มันไม่ได้โกรธมากนักใช่ไหม?
huggie
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.