Mongoose: pra, pasca middleware tidak dieksekusi di findByIdAndUpdate

Dibuat pada 15 Jun 2012  ·  102Komentar  ·  Sumber: Automattic/mongoose

Karena metode findAndUpdate disajikan untuk memotong kode berikut:

 Model.findById(_id, function (err, doc) {
      if (doc) {
          doc.field = 'value';
          doc.save(function (err) {
                 // do something;
          });
      }
 });

untuk ini:

   .findByIdAndUpdate(_id, {$set: {field: 'value'}}, function (err, doc) {
        // do something 
    });

Kita perlu menggunakan pre, post middleware yang persis sama. Saat ini pre, post middleware tidak dieksekusi ketika saya membuat findByIdAndUpdate.

Komentar yang paling membantu

Halo,

Saya tahu masalahnya sudah selesai, tetapi saya tidak sepenuhnya puas dengan jawabannya. Bukankah seharusnya ada middleware pasca-penyimpanan atau pasca-pembaruan untuk findOneAndUpdate dan operasi serupa lainnya? Tampaknya baik-baik saja sejak dokumen dikembalikan. Tidak layak untuk pra middlewares atau untuk Model.update, saya setuju.

Ini akan sangat meningkatkan kemampuan plugin seperti mongoosastic yang saat ini buta terhadap beberapa operasi yang seharusnya dapat didukungnya.

Jika tidak ada middleware, apakah seseorang memiliki ide tentang cara mengelola beberapa operasi pembaruan pos di sebuah plugin?

Terima kasih

Semua 102 komentar

dengan desain. ada dokumen yang terlibat untuk memanggil kait.

koreksi, tidak ada dokumen untuk memanggil kait.

Jadi? Jika saya ingin memanggil pre, post middleware, saya harus menggunakan pendekatan pertama?

ya itu benar. Model.update,findByIdAndUpdate,findOneAndUpdate,findOneAndRemove,findByIdAndRemove adalah semua perintah yang dieksekusi langsung di database.

Ini pasti harus diklarifikasi dalam panduan ini, terutama jika Anda berbicara tentang validasi di halaman yang sama dan menggambarkan findByIdAndUpdate sebagai "lebih baik"

jika Anda mengklik tautan "lebih baik", itu akan membawa Anda ke dokumentasi lengkap dari
metode di mana ia menjelaskan validasi dll.

jangan ragu untuk mengirim permintaan tarik untuk menambahkan sesuatu yang Anda rasa lebih baik. nya
cukup mudah untuk melakukannya:
https://github.com/LearnBoost/mongoose/blob/master/CONTRIBUTING.md

Pada hari Rabu, 31 Okt 2012 jam 18:03, Jesse [email protected] :

Ini pasti harus diklarifikasi dalam panduan http://mongoosejs.com/docs/documents.html ,
terutama jika Anda berbicara tentang validasi di halaman yang sama dan
menggambarkan findByIdAndUpdate sebagai "lebih baik"


Balas email ini secara langsung atau lihat di Gi tHubhttps://github.com/LearnBoost/mongoose/issues/964#issuecomment -9967865.

Harun
@aaronheckmann https://twitter.com/#!/aaronheckmann

Menambahkan catatan ke dokumen middleware

Tarik permintaan: https://github.com/LearnBoost/mongoose/pull/1750

Halo,

Saya tahu masalahnya sudah selesai, tetapi saya tidak sepenuhnya puas dengan jawabannya. Bukankah seharusnya ada middleware pasca-penyimpanan atau pasca-pembaruan untuk findOneAndUpdate dan operasi serupa lainnya? Tampaknya baik-baik saja sejak dokumen dikembalikan. Tidak layak untuk pra middlewares atau untuk Model.update, saya setuju.

Ini akan sangat meningkatkan kemampuan plugin seperti mongoosastic yang saat ini buta terhadap beberapa operasi yang seharusnya dapat didukungnya.

Jika tidak ada middleware, apakah seseorang memiliki ide tentang cara mengelola beberapa operasi pembaruan pos di sebuah plugin?

Terima kasih

@albanm metode tertentu melewati luwak sepenuhnya , jadi Anda tidak mendapatkan kait middleware. AFAIK, satu-satunya cara agar kait dapat dieksekusi adalah dengan menggunakan panggilan find() dan save() terpisah seperti yang disebutkan di atas.

Saya mengerti itu dan itu masuk akal. Pra middleware tidak diragukan lagi karena tidak ada pengambilan sebelum metode tertentu. Tapi tetap saja, luwak harus dapat membungkus operasi pembaruan yang juga mengembalikan dokumen yang diperbarui dan memicu beberapa kait pos.

Saya pikir @albanm benar, Orang mungkin menginginkan fungsi yang sama ketika mereka menggunakan fungsi yang sama. Bagaimana kalau membungkus metode 'perbarui langsung' itu dengan beberapa intersepsi untuk memeriksa apakah ada kait? Jika ada kait, gunakan, atau panggil metode pembaruan asli sebaliknya.

+1

+1

+1

+1

:+1:

+1

:-1:
Saya mendapatkan permintaan fitur, tetapi semakin lama saya bekerja dengan middleware, saya mendapati diri saya harus sering melewati middleware ketika memanggil skrip pembaruan db dan item lain yang tidak rutin ke operasi normal saya. Dengan menggunakan metode statis menjadi sangat mudah untuk membuat pembungkus khusus yang sudah mengimplementasikan permintaan fitur di atas. Karena luwak sekarang membuka ide untuk versi 4, kecil kemungkinan perubahan API sebesar ini akan terjadi di v3. Saya meminta ini dipindahkan ke diskusi v4.

Namun saya akan, :+1: jika saya dapat menonaktifkan middleware pada panggilan simpan. Saya sering menemukan diri saya dengan objek luwak yang diturunkan dari fungsi lain dan hanya ingin melakukan .save - dalam situasi ini, melakukan .save lebih baik daripada menulis kueri baru. Jika ini mungkin, tolong tunjukkan

Bagaimanapun, perpustakaan yang luar biasa. Kudos untuk pengelola yang luar biasa. Tidak yakin bagaimana saya akan beroperasi tanpanya.

+1

Cara sederhana untuk menambahkan kait untuk metode ini adalah menimpa fungsi embed:

_update = UserModel.update
UserModel.update = (conditions, update) ->
  update.updatedAt or= new Date
  _update.apply this, arguments

Kemudian setiap panggilan pembaruan luwak akan memperbaiki kunci data yang diperbarui.

Anda dapat mencoba model limbo ini. Ini adalah pembungkus sederhana model luwak, mendukung pengikatan statis/metode/timpa ke semua skema, dan memanggil metode rpc untuk menanyakan mongodb.

+1, perlu fitur ini...

Sementara saya agak memahami alasannya, kurangnya kait pada pembaruan atom IMHO membuat Mongoose agak tidak berguna dalam praktiknya. Ketika saya menggunakan pembaruan atom, validasi, default, dll. tidak dijalankan, sehingga seluruh tujuan menggunakan ODM dikalahkan. Menggunakan find/save akan berhasil, tetapi apakah ada jaminan ini selalu digunakan?

Selain itu, biasanya saya akan mencoba menghindari find/save karena ini bukan operasi atom. MongoDB mengkompensasi kurangnya dukungan transaksi dengan menyediakan fitur permintaan & pembaruan atom yang kuat. Jadi saya akan menggunakan operasi atom ini tetapi tanpa dukungan middleware Mongoose tidak akan memberikan banyak nilai dibandingkan MongoClient asli.

Bahkan contoh di http://aaronheckmann.tumblr.com/post/48943525537/mongoose-v3-part-1-versioning akan menggunakan update , karenanya melewati middleware. Saya dapat menggunakan versi dengan benar atau middleware tetapi tidak menggabungkan keduanya? Sungguh, di mana gunanya memilikinya?

Saya bahkan tidak sepenuhnya memahami alasan teknis: Jika update & co. membungkus operasi basis data, mengapa kami tidak dapat mencegat panggilan dan meneruskan objek kueri sehingga kami dapat melakukan beberapa validasi/penyesuaian sebelum kami benar-benar melakukan pembaruan?

@joerx +1 sudah cukup .. :) tetapi alasan Anda sempurna.

Cabang 3.9.x memiliki dukungan untuk pra dan pasca kait untuk find dan findOne - seharusnya mudah untuk menambahkan dukungan untuk findOneAndUpdate dan update .

Apakah fitur ini digabungkan?

Jadi kait pre('findOneAndUpdate') dan post('findOneAndUpdate') ada di master, belum ada kait pembaruan. Belum ada rilis yang berisi salah satu dari itu.

Jadi apakah itu memicu pra-simpan setelah .update() sekarang?

Tidak. Ada pengait update() terpisah untuk Query.update() . Simpan kait berbeda dari kait kueri.

@vkarpov15 Bisakah Anda menautkan ke dokumentasi pendukung untuk kait pembaruan? Ada kabar kapan pre('findOneAndUpdate') dan post('findOneAndUpdate') akan dirilis?

@karlstanton gunakan 4.0.0-rc2, npm install mongoose@unstable :)

'use strict';

var Promise  = require('bluebird');
var mongoose = Promise.promisifyAll(require('mongoose'));

var counterSchema = new mongoose.Schema({
    total: {
        type:    Number,
        default: 0
    }
});

counterSchema.post('findOneAndUpdate', function (doc) {
    console.log(doc.total);
});

var Counter = mongoose.model('Counter', counterSchema);

Promise.coroutine(function *() {
    yield mongoose.connectAsync(process.env.MONGODB_URI);
    console.log('Connected');
    let counter = yield Counter.createAsync({});
    console.log(`${counter.total}`);
    for (let i = 0; i < 10; i++) {
        yield Counter.findOneAndUpdateAsync({ _id: counter.id }, { $inc: { total: 1} });
    }
})();
0
0
1
2
3
4
5
6
7
8
9

Sepertinya itu selangkah di belakang. Apa yang saya lewatkan?

AH, perubahan default flag new melanggar. Meskipun ada sesuatu untuk dikatakan tentang konsistensi dengan driver yang mendasarinya, saya harus mengatakan itu benar-benar kontra-intuitif, terutama ketika mempertimbangkan kait baru ini.

@neverfox :)

Ya @neverfox tangkapan yang bagus. Perubahan ini didokumentasikan dalam catatan rilis dan Anda dapat melihat lebih banyak diskusi mengapa ini diubah di #2262.

Alasannya adalah new salah secara default di driver node mongodb, shell mongodb, server mongodb yang sebenarnya, dll. dan pengaturan lapisan pembungkus default non-standar membuat hidup menjadi sulit bagi pengembang. Contoh kanonik saya dari modul yang salah besar adalah gulp-uglify , yang menimpa banyak default uglify-js.

Saya melihat bahwa masalahnya sudah selesai, tetapi apakah fungsionalitasnya sudah ada di versi 4.0.2 apakah hanya masih dalam versi yang tidak stabil? Tampaknya tidak dijalankan pada findOneAndUpdate dengan Schema.pre('update') atau Schema.pre('findOneAndUpdate') dengan versi 4.0.2. Apakah saya melewatkan sesuatu yang harus saya berikan ke fungsi?

Bagaimana Anda mendeklarasikan pra kait @CaptainStaplerz ?

Apakah hook middleware findOneAndUpdate tersedia di 4.0.2? Saya memutakhirkan dari 3.8 ke luwak 4.0.2 terbaru untuk menggunakan ini dan middlware Document.schema.post('findOneAndUpdate', function (doc) tidak terpicu seperti save() atau remove()

@honitus tunjukkan kodemu

@vkarpov15 - Terima kasih atas tanggapan cepatnya, ini dia

blog.controller,js

// Updates an existing Blog in the DB, adds comment
exports.update = function(req, res) {

Blog.findOneAndUpdate(
     {"_id": req.body._id,},
      {"$push": {"comments": buildComment}},
     {safe: true, upsert: true}, function (err, workspace) {
      if (err) {
         return handleError(res, err);
       }
       return res.send(200);
      }
   );

}

blog.socket.js

/**
 * Broadcast updates to client when the model changes
 */

'use strict';

var Blog= require('./blog.model');

exports.register = function(socket) {
//SAVE WORKS
  Blog.schema.post('save', function (doc) {
    onSave(socket, doc);
  });

// IS NOT TRIGGERED :(
 Blog.schema.post('findOneAndUpdate', function (doc) {
    onComment(socket, doc);
  });

  Blog.schema.post('remove', function (doc) {
    onRemove(socket, doc);
  });
}

//SAVE WORKS when a new blog is created
function onSave(socket, doc, cb) {
  socket.emit('blog:save', doc);
}

// IS NOT TRIGGERED :(
function onComment(socket, doc, cb) {
  socket.emit('blog:findOneAndUpdate', doc);
}

function onRemove(socket, doc, cb) {
  socket.emit('blog:remove', doc);
}

@vkarpov15 Terima kasih telah membuka kembali. Apakah ini berarti kait untuk findOneAndUpdate tidak ada di 4.0.2 dan Anda berencana untuk menyertakan di 4.0.3.

@vkarpov15 Inilah kode tempat saya mendeklarasikan kait:

...
var TodoSchema = new mongoose.Schema({
  name: {type: String, required: true},
  note: String,
  completed: {type: Boolean, default: false},
  updatedAt: {type: Date, default: Date.now},
  user: {
    type: mongoose.Schema.ObjectId,
    ref: 'Users'
  }
});
...
// Not executed
TodoSchema.pre('update', function() {
  console.log('------------->>>>>> update updatedAt')
  this.updatedAt = Date.now();
});
// Not executed
TodoSchema.pre('findOneAndUpdate', function() {
  console.log('------------->>>>>> update updatedAt')
  this.updatedAt = Date.now();
});

Dan di sinilah saya menyebut pembaruan:

...
router.route('/:id')
.put(function(req, res, next) {
  TodoModel.findOneAndUpdate({_id: req.params.id, user: req.user.id}, req.body, {new: true}, function(err, post) {
    if(err) return next(err);
    if(post) {
      res.status(200).json(post);
    }
    else {
      next(newSystemError(errorCodes.TODO_NOT_FOUND, undefined, req.params.id));
    }
  });
});

Sejauh yang saya tahu kait findOneAndUpdate() harus ada di sana, jika tidak berfungsi itu bug

@CaptainStaplerz coba:

TodoSchema.pre('update', function(next) {
  console.log('------------->>>>>> update updatedAt')
  this.updatedAt = Date.now();
  next();
});

Juga, apakah pernyataan console.log dieksekusi, atau hanya bagian Date.now() yang memberikan hasil yang tidak diharapkan?

@vkarpov15
Saya mengubah kode sumber saya, menambahkan perubahan yang Anda buat di Tambahkan implementasi #964 https://github.com/Automattic/mongoose/commit/e98ef98e857965c4b2ae3339fdd7eefd2a5a9913

Ini bekerja seperti pesona sekarang. Jadi saya pikir perbaikannya tidak dicentang ke main

@honitus apakah Anda yakin menggunakan luwak 4.0.2? Perubahan itu sebenarnya berkomitmen untuk 4.0.0 dan lebih tinggi.

honitus$ npm lihat versi luwak
4.0.2

@honitus apa yang Anda lakukan salah adalah Anda menambahkan kait ke skema setelah mengkompilasi model Anda. Model.schema.pre('remove'); diharapkan tidak berfungsi, lihat "Mengkompilasi model pertama Anda" pada dokumen model . Lampirkan kait ke skema terlebih dahulu dan kemudian semuanya akan berfungsi - itulah satu-satunya perbedaan yang saya lihat antara kode Anda dan pengujian kami.

@CaptainStaplerz satu-satunya cara saya dapat menemukan untuk merepro kode Anda adalah dengan pembaruan kosong. Kode di bawah ini berfungsi

var mongoose = require('mongoose');
mongoose.set('debug', true);
var util = require('util');

mongoose.connect('mongodb://localhost:27017/gh964');

var TodoSchema = new mongoose.Schema({
  name: {type: String, required: true},
  note: String,
  completed: {type: Boolean, default: false},
  updatedAt: {type: Date, default: Date.now},
  user: {
    type: mongoose.Schema.ObjectId,
    ref: 'Users'
  }
});
TodoSchema.pre('update', function() {
  console.log('------------->>>>>> update updatedAt')
  this.updatedAt = Date.now();
});
TodoSchema.pre('findOneAndUpdate', function() {
  console.log('------------->>>>>> update updatedAt')
  this.updatedAt = Date.now();
});

var Todo = mongoose.model('Todo', TodoSchema);

Todo.update({}, { note: "1" }, function(err) {
  if (err) {
    console.log(err);
  }
  console.log('Done');
  process.exit(0);
});

Tetapi begitu Anda membuat pembaruan kosong:

var mongoose = require('mongoose');
mongoose.set('debug', true);
var util = require('util');

mongoose.connect('mongodb://localhost:27017/gh964');

var TodoSchema = new mongoose.Schema({
  name: {type: String, required: true},
  note: String,
  completed: {type: Boolean, default: false},
  updatedAt: {type: Date, default: Date.now},
  user: {
    type: mongoose.Schema.ObjectId,
    ref: 'Users'
  }
});
TodoSchema.pre('update', function() {
  console.log('------------->>>>>> update updatedAt')
  this.updatedAt = Date.now();
});
TodoSchema.pre('findOneAndUpdate', function() {
  console.log('------------->>>>>> update updatedAt')
  this.updatedAt = Date.now();
});

var Todo = mongoose.model('Todo', TodoSchema);

Todo.update({}, { }, function(err) {
  if (err) {
    console.log(err);
  }
  console.log('Done');
  process.exit(0);
});

Kait pra pembaruan tidak lagi dijalankan. Apakah ini konsisten dengan apa yang Anda lihat?

@vkarpov15
Model.schema.pre('hapus'); adalah salah satu rintisan yang secara otomatis dibuat oleh tumpukan penuh sudut
Saya menambahkan implementasi #964 e98ef98
Yang saya lakukan hanyalah menambahkan satu baris di bawah ini
this._schema.s_.hooks.wrap('findOneAndUpdate', Query.base.findOneAndUpdate,
Lihat Baris 1526 di 4.0.2 - this.model.hooks.wrap('findOneAndUpdate', Query.base.findOneAndUpdate,

Ganti Baris 1526 dengan- this.schema.s.hooks.wrap('findOneAndUpdate', Query.base.findOneAndUpdate,
dan itu berhasil.

Itu dengan desain. Luwak seharusnya tidak mengizinkan modifikasi kait skema setelah model dikompilasi (yaitu, panggilan mongoose.model() ), itulah sebabnya itu tidak lagi berfungsi.

@ vkarpov15 Saya berhasil membuat pengait bekerja sekarang dengan yang berikut ini, itu pasti kesalahan ketik yang bodoh atau yang lainnya:

TodoSchema.pre('findOneAndUpdate', function(next) {
  console.log('------------->>>>>> update updatedAt: ', this.updatedAt);
  this.updatedAt = Date.now();
  next();
});

Namun 'ini' sepertinya tidak merujuk ke model yang diperbarui (tetapi itu ada di kait 'simpan'?), jadi this.updatedAt merujuk ke undefined.

Bagaimana cara memperbarui 'updatedAt' -bidang di 'findOneAndUpdate' -hook?

Saya harus menambahkan lebih banyak dokumen yang mengklarifikasi ini - dokumen yang sedang diperbarui mungkin tidak ada di memori ketika Anda memanggil findOneAndUpdate , jadi objek this merujuk ke kueri daripada dokumen di middleware kueri . Coba this.update({ $set: { updatedAt: Date.now() } });

@vkarpov15 Saya tidak bisa membuat this.update({ $set: { updatedAt: Date.now() } }); berfungsi, tetapi saya berhasil memperbarui dokumen di kait findOneAndUpdate dengan ini: this._update['$setOnInsert'].updatedAt=Date.now(); .

Saya sangat merekomendasikan untuk tidak bergantung pada tweaker keadaan internal seperti itu. Saya cukup terkejut bahwa Anda tidak dapat membuat this.update() berfungsi - dapatkah Anda menunjukkan kepada saya seperti apa bentuk pengait Anda?

Tentu! (Perhatikan bahwa saya menggunakan 4.0.2)

tagSchema.pre('findOneAndUpdate',function(next){
  var self = this;

  //NOTE THAT 'this' in the findOneAndUpdate hook refers to the query, not the document
  //https://github.com/Automattic/mongoose/issues/964

  geoData.country.findOne({'_id':self._update['$setOnInsert'].countryCode}).select('_id name cca2 cca3 ccn3').lean().exec(function(err,country){
    if (err){throw err;}
    if (!country){throw 'no coutnry';}
    self._update['$setOnInsert'].country=country;
    next();
  });
});

Saya jelas bisa menangani ini ketika saya menginisialisasi dokumen di tempat lain di aplikasi saya, tetapi senang memiliki semuanya di sana di Mongoose. Selamat datang pemikiran apa pun!

Ya saya bingung dan mengira Anda menggunakan update . Script di bawah ini

var mongoose = require('mongoose');
mongoose.set('debug', true);
var util = require('util');

mongoose.connect('mongodb://localhost:27017/gh964');

var TodoSchema = new mongoose.Schema({
  name: {type: String, required: true},
  note: String,
  completed: {type: Boolean, default: false},
  updatedAt: {type: Date, default: Date.now},
  user: {
    type: mongoose.Schema.ObjectId,
    ref: 'Users'
  }
});
TodoSchema.pre('findOneAndUpdate', function() {
  this.findOneAndUpdate({}, { updatedAt: Date.now() });
});

var Todo = mongoose.model('Todo', TodoSchema);

Todo.findOneAndUpdate({}, { note: "1" }, function(err) {
  if (err) {
    console.log(err);
  }
  console.log('Done');
  process.exit(0);
});

Bekerja dengan benar dan menjalankan kueri yang diinginkan:

Mongoose: todos.findAndModify({}) [] { '$set': { note: '1', updatedAt: new Date("Thu, 07 May 2015 20:36:39 GMT") } } { new: false, upsert: false }
Done

Jadi tolong gunakan

TodoSchema.pre('findOneAndUpdate', function() {
  this.findOneAndUpdate({}, { updatedAt: Date.now() });
});

Alih-alih secara manual memanipulasi keadaan internal mquery - itu biasanya ide yang buruk kecuali Anda benar-benar tahu apa yang Anda lakukan.

Apakah ini bekerja atau tidak? Satu-satunya kait yang berfungsi untuk saya adalah 'simpan', sisanya diabaikan sepenuhnya. Saya menjalankan versi 4.0.6. Terima kasih

@agjs berikan contoh kode.

UserController.prototype.updateAvatar = function (req, res) {
    return new Promise(function (resolve, reject) {
        CompanyDetails.update({
            _author: req.user._id
        }, {
            avatarPath: req.files.file
        }, function (error, updated) {
            if (error) {
                reject(error);
            } else {
                resolve(updated);
            }
        });
    }).then(function (resolved) {
        res.sendStatus(204).send(updated);
    }).catch(function (error) {
        next(error);
    })

};

CompanyAvatarSchema.pre('update', function (next) {
    console.log('pre save');
    let VirtualModel = this,
        parent = this.ownerDocument(),
        PATH = path.normalize('./public/images/uploads/avatars/' + parent._id);

    mkdirp(PATH, function (error) {
        if (error) {
            next(error);
        } else {
            fs.rename(VirtualModel.path, path.join(PATH, VirtualModel.name), function (error2) {
                if (error2) {
                    next(error2);
                } else {
                    next();
                }
            });
        }
    });

});

Saya memiliki pre-hook lain pada model lain dengan model.create dan pre-save dan berfungsi normal.

Dengan pembaruan apa pun, Itu tidak melihat kaitan, bahkan tidak console.log itu. Saya mencoba findOneAndUpdate juga dll, tidak benar-benar berfungsi ... Lucunya saya telah membaca seluruh utas ini dan seperti biasanya, memeriksa dokumentasi resmi dan kalian mengklaim bahkan di sana bahwa itu berfungsi.

Apa hubungan antara CompanyAvatarSchema dan CompanyDetails dalam kode di atas?

Detail perusahaan memiliki CompanyAvatar.schema sebagai subdokumen

avatarPath: {
        type: [CompanyAvatar.schema],
        required: true
    }

Juga, ini bukan hanya pra-kait tetapi validasi juga diabaikan sepenuhnya. Subdokumen ini terisi tetapi mengabaikan keduanya, validasi dan pra-kait. Saya sudah mencari di Google semuanya, juga mencoba INI tetapi tidak ada yang berhasil. Ketika saya hanya untuk menguji mengubah kueri saya untuk membuat dan memanggil model dengan new alias var parent = new Parent(), itu berhasil.

Anda menelepon CompanyDetails.update() tetapi pre hook didefinisikan pada skema terpisah. Middleware kueri tidak mengaktifkan atm pre('update') skema bersarang.

Juga, berikan contoh kode yang lebih menyeluruh untuk kasus 'validasi akan diabaikan sepenuhnya'.

Berikut adalah skema avatar perusahaan saya yang dibuat untuk validasi dan pra-kait bagi pengguna yang memperbarui profil mereka (foto avatar):

'use strict';

let mongoose = require('mongoose'),
    mkdirp = require('mkdirp'),
    fs = require('fs'),
    path = require('path'),
    Schema = mongoose.Schema;

let CompanyAvatarSchema = new Schema({
    name: String,
    width: Number,
    height: Number,
    size: Number,
    type: String
});


CompanyAvatarSchema.path('type').validate(function (type) {
    return /^image\//.test(type);
}, 'Image type not allowed!');

CompanyAvatarSchema.path('size').validate(function (size) {
    return size < 5;
}, 'Image too big!');



CompanyAvatarSchema.virtual('path').set(function (path) {
    return this._path = path;
}).get(function () {
    return this._path;
});


CompanyAvatarSchema.virtual('public_path').get(function () {
    var parent = this.ownerDocument();
    var PATH = path.normalize('images/uploads/avatars/' + parent._id);
    if (this.name) {
        return path.join(PATH, this.name);
    }
});

CompanyAvatarSchema.set('toJSON', {
    getters: true
});

CompanyAvatarSchema.pre('findOneAndUpdate', function (next) {
    console.log('pre save');
    let VirtualModel = this,
        parent = this.ownerDocument(),
        PATH = path.normalize('./public/images/uploads/avatars/' + parent._id);

    mkdirp(PATH, function (error) {
        if (error) {
            next(error);
        } else {
            fs.rename(VirtualModel.path, path.join(PATH, VirtualModel.name), function (error2) {
                if (error2) {
                    next(error2);
                } else {
                    next();
                }
            });
        }
    });

});


let runValidatorsPlugin = function (schema, options) {
    schema.pre('findOneAndUpdate', function (next) {
        this.options.runValidators = true;
        next();
    });
};

CompanyAvatarSchema.plugin(runValidatorsPlugin);

let CompanyAvatar = mongoose.model('CompanyAvatar', CompanyAvatarSchema);
module.exports = CompanyAvatar;

Berikut adalah skema company_details di mana company_avatar adalah subdocument:

let CompanyDetailsSchema = new mongoose.Schema({
    _author: [{
        type: Schema.Types.ObjectId,
        ref: 'CompanyAccount'
    }],
    company_name: {
        type: String,
        es_indexed: true,
        es_boost: 2.0
    },
    contact_email: {
        type: String,
        es_indexed: true
    },
    website: {
        type: String,
        es_indexed: true
    },
    country: {
        type: String,
        es_indexed: true
    },
    industry: {
        type: String,
        es_indexed: true
    },
    address: {
        type: String,
        es_indexed: true
    },
    about: {
        type: String,
        es_indexed: true
    },
    avatarPath: {
        type: [CompanyAvatar.schema],

    }
});

Dan di sini adalah pengontrol profil pembaruan dan avatarPath yang harus divalidasi/dikaitkan sebelum pembaruan ini dilakukan:

UserController.prototype.updateAvatar = function (req, res, next) {
    let updates = {
        $set: {
            avatarPath: req.files.file
        }
    };
    return new Promise(function (resolve, reject) {
        CompanyDetails.findOneAndUpdate({
            _author: req.user._id
        }, updates, function (error) {
            if (error) {
                reject(error);
            } else {
                resolve('done');
            }
        });
    }).then(function () {
        res.sendStatus(204);
    }).catch(function (error) {
        next(error);
    });

};

Pada dasarnya, mongodb saya diisi dengan bidang dari req.files.file tetapi selain itu, validasi diabaikan dan tidak ada kait yang berfungsi.

Masalahnya adalah bahwa middleware pra('findOneAndUpdate') didefinisikan pada skema bersarang. Saat ini, luwak hanya menjalankan middleware kueri untuk skema tingkat atas, jadi middleware yang ditentukan pada CompanyDetailsSchema akan dipecat re: #3125

+1

@vkarpov15 saya mengerti

TodoSchema.pre('findOneAndUpdate', function() {
  this.findOneAndUpdate({}, { updatedAt: Date.now() });
});

berfungsi untuk mengatur properti ke nilai yang sulit, tetapi bagaimana saya _membaca dan mengubah_ properti, misalnya hash kata sandi?

TodoSchema.pre('findOneAndUpdate', function() {
  this.findOneAndUpdate({}, { password: hashPassword(.....?....) });
});

Ada pikiran? Ini adalah usecase yang cukup umum, bukan? Atau apakah orang biasanya menemukan() dan kemudian memisahkan save() ?

TodoSchema.pre('findOneAndUpdate', function() {
  this.findOneAndUpdate({}, { password: hashPassword(this.getUpdate().$set.password) });
});

Itu harus bekerja @willemmulder.

@vkarpov15 sempurna, terima kasih! Aku akan mencobanya malam ini. Itu juga harus bekerja untuk pre('update') kan?

@vkarpov15 Jadi saya hanya mencobanya menggunakan

schema.pre('update', function(next) {
this.update({}, { $set : { password: bcrypt.hashSync(this.getUpdate().$set.password) } });
next();
});

dan jika saya console.log this saya mendapatkan

_update: { '$set': { password: '$2a$10$CjLYwXFtx0I94Ij0SImk0O32cyQwsShKnWh1248BpYsJLIHh7jb66', postalAddress: [Object], permissions: [Object], firstName: 'Willem', lastName: 'Mulder', email: '...@...', _id: 55ed4e8b6de4ff183c1f98e8 } },

yang tampaknya baik-baik saja (dan saya bahkan mencoba mengatur properti itu secara langsung sebelumnya) tetapi pada akhirnya, itu tidak benar-benar menulis nilai hash ke database, tetapi hanya nilai 'mentah'. Apakah ada yang bisa saya coba?

Itu aneh. Anda dapat mencoba mengaktifkan mode debug luwak dengan require('mongoose').set('debug', true); dan melihat kueri apa yang dikirim ke db, yang dapat menjelaskan.

Terima kasih untuk sarannya. Baru saja melakukan itu:

Saya menjalankan ini:

schema.pre('update', function(next) {
    this.update({}, { password: bcrypt.hashSync(this.getUpdate().$set.password) } );
    console.log(this.getUpdate());
    next();
});

yang mengembalikan ini untuk console.log

{ '$set':
   { password: '$2a$10$I1oXet30Cl5RUcVMxm3GEOeTFOLFmPWaQvXbr6Z5368zbfpA8nFEK',
     postalAddress: { street: '', houseNumber: '', zipCode: '', city: '', country: '' },
     permissions: [ '' ],
     __v: 0,
     lastName: '',
     firstName: '',
     email: '[email protected]',
     _id: 563b0410bd07ce2030eda26d } }

dan kemudian ini untuk debug Mongoose

Mongoose: users.update({ _id: ObjectId("563b0410bd07ce2030eda26d") }) { '$set': { password: 'test', postalAddress: { street: '', houseNumber: '', zipCode: '', city: '', country: '' }, permissions: [ '\u001b[32m\'\'\u001b[39m' ], __v: 0, lastName: '', firstName: '', email: '[email protected]', _id: ObjectId("563b0410bd07ce2030eda26d") } } { overwrite: false, strict: true, multi: false, upsert: false, safe: true }
Mongoose: users.findOne({ _id: ObjectId("563b0410bd07ce2030eda26d") }) { fields: { password: 0 } }

Ada petunjuk?

Tidak yakin, membuka masalah baru untuk dilacak.

@vkarpov15 Terima kasih, akan melacak masalah lainnya.

@ vkarpov15 Saya pikir cara yang tepat untuk mengatur opsi untuk kueri yang sedang berlangsung di pra kait adalah seperti:

  finishSchema.pre('findOneAndUpdate', function (next) {
    this.setOptions({
      new: true,
      runValidators: true
    });
    this.update({}, {
      lastEdited: Date.now()
    });
    next();
  });

tetapi dokumentasinya, http://mongoosejs.com/docs/api.html#query_Query -setOptions tidak menyebutkan salah satu opsi itu. Jika ini dianggap sebagai solusi peretasan, apa yang lebih tepat?

Itu masalah dengan dokumen, kode yang Anda gambarkan sepertinya harus berfungsi pada pandangan pertama

Bisakah Anda membuka masalah terpisah untuk itu?

@vkarpov15 Ya, itu berhasil. Saya pikir saya tidak cukup jelas.

setOptions berlaku new dan runValidators dengan benar, saya hanya bertanya apakah menyetel opsi tersebut melalui setOptions harus lebih disukai daripada this.options .

setOptions() lebih disukai IMO, tetapi keduanya harus berfungsi. Atau Anda bisa melakukannya

this.update({}, { lastEdited: Date.now() }, { new: true, runValidators: true });
schema.pre('update', function(next) {
this.update({}, { $set : { password: bcrypt.hashSync(this.getUpdate().$set.password) } });
next();
});

Ini akan memperbarui kata sandi pada setiap panggilan update(). Jadi jika saya hanya mengubah nilai properti lain, yaitu nama atau umur, maka kata sandi akan diperbarui juga yang tidak benar!?

@nlonguit saya kira itu akan. Tetapi Anda dapat mengakses melalui this bidang yang akan diperbarui dan Anda dapat melakukan sesuatu seperti:

if (this._fields.password) { // <- I'm sure about this one, check in debugger the properties of this 
    this.update({}, { $set : { password: bcrypt.hashSync(this.getUpdate().$set.password) } });
}

if (this._update.$set.password) { this.update({}, { $set: { password: bcrypt.hashSync(this.getUpdate().$set.password)} }); }

Kode ini bekerja dengan baik untuk saya. Terima kasih @akoskm

Saya ingin tahu apakah mungkin menambahkan pra kait untuk findByIdAndUpdate juga. Akan lebih baik jika kedua kait tersedia.

Saya melakukannya dengan cara ini dan berhasil: Temukan sajaById lalu simpan tanpa memperbarui bidang apa pun lalu gunakan metode findByIdAndUpdate:

dbModel.findById(barId, function (err, bar) {
        if (bar) {

            bar.save(function (err) {
                if (err) throw err;
            });
        }
    });
    dbModel.findByIdAndUpdate(barId, {$set:req.body}, function (err, bar) {
        if (err) throw err;
        res.send('Updated');
    });`

Saya mencoba mengatur properti untuk memiliki panjang array.

schema.post('findOneAndUpdate', function(result) {
    console.log(result.comments.length);
    this.findOneAndUpdate({}, { totalNumberOfComments: result.comments.length });
});

Panjang yang benar dicatat, meskipun kueri tidak pernah menetapkan totalNumberOfComments, dan bidang tetap pada 0 (karena referensi skema default: 0).

Ketika saya console.log(this) di akhir hook, saya dapat melihat bahwa query saya berisi yang berikut:

_update: { '$push': { comments: [Object] }, totalNumberOfComments: 27 }

Meskipun ketika saya mengaktifkan mode debug, kueri tidak pernah dicatat oleh Mongoose.

Apakah ada sesuatu yang saya lakukan salah, atau ini bug?

@zilions this.findOneAndUpdate({}, { totalNumberOfComments: result.comments.length }).exec(); harus benar-benar mengeksekusi kueri :) Berhati-hatilah, Anda akan mendapatkan rekursi tak terbatas di sana karena kait penyimpanan kiriman Anda akan memicu kait penyimpanan kiriman lainnya

@vkarpov15 Ahhhh benar! Maka saya bisa menggunakan this.update({} {....}).exec() saja :)
Namun pertanyaan, saat menggunakan ini, ia menetapkan bidang totalNumberOfComments dengan sempurna, meskipun melakukan pembaruan asli dari findOneAndUpdate juga.

Sebagai contoh:

Post.findOneAndUpdate({_id: fj394hri3hfj}, {$push: {comments: myNewComment}})

Akan memicu kait berikut:

schema.post('findOneAndUpdate', function(result) {
    this.update({}, {
        totalNumberOfComments: result.comments.length
    }).exec();
}));

Meskipun, hook akan $push ke comments myNewComment lagi, oleh karena itu membuat entri duplikat.

Karena Anda secara teknis menjalankan kueri yang sama -

schema.post('findOneAndUpdate', function(result) {
    this.update({}, {
        totalNumberOfComments: result.comments.length
    }).exec();
}));

pada dasarnya sama dengan

var query = Post.findOneAndUpdate({_id: fj394hri3hfj}, {$push: {comments: myNewComment}});
query.update({}, {
        totalNumberOfComments: result.comments.length
    }).exec();
query.findOneAndUpdate().exec();

Jika Anda ingin membuat kueri baru dari awal, lakukan saja

schema.post('findOneAndUpdate', function(result) {
    this.model.update({}, { // <--- `this.model` gives you access to the `Post` model
        totalNumberOfComments: result.comments.length
    }).exec();
}));

jangan sentuh kait pra simpan Anda,

router.put('/:id', jsonParser, function(req, res, next) {

  currentCollection.findByIdAndUpdate(req.params.id, req.body, function (err, item) {
    if (err) {
        res.status(404);
        return res.json({'error': 'Server Error', 'trace': err});
    }
    item.save(); // <=== this is were you save your data again which triggers the pre hook :)
    res.status(200); 
    return res.json({'message': 'Saved successfully'});
  });
});

Saya menemukan bahwa urutan itu penting di mana Anda mendefinisikan model dan mendefinisikan kait pre . Izinkan saya untuk menunjukkan:

Tidak bekerja:

// Create Model
let model = Database.Connection.model(`UserModel`, this._schema, `users`);

// Attach Pre Hook
this._schema.pre(`findOneAndUpdate`, function(next) {
    console.log('pre update');
    return next();
});

Apakah bekerja:

// Attach Pre Hook
this._schema.pre(`findOneAndUpdate`, function(next) {
    console.log('pre update');
    return next();
});

// Create Model
let model = Database.Connection.model(`UserModel`, this._schema, `users`);

Semoga ini bisa membantu siapa pun!

Saya baru tahu hal yang sama dengan @nicky-lenaers.

Ini berfungsi dengan baik dengan 'safe' . 'delete' . dll. jika Anda menentukan kait setelah model ditentukan.

Apakah ada solusi yang mendefinisikan kait 'findOneAndUpdate' setelah model didefinisikan?

@albert-92 tidak tidak untuk saat ini

Bagi siapa pun yang mencoba mendapatkan sesuatu yang dulu seperti

SCHEMA.pre('validate', function(done) {
    // and here use something like 
    this.yourNestedElement 
    // to change a value or maybe create a hashed character    
    done();
});

Ini harus bekerja

SCHEMA.pre('findOneAndUpdate', function(done){
    this._update.yourNestedElement
    done();
});

Saya tidak bisa mendapatkan kait posting untuk memperbarui dokumen dalam koleksi.

`module.exports = fungsi (luwak) {
var mySchema = luwak.Schema({
id: { jenis: Nomor, indeks: { unik: benar } },
field1: { ketik: String },
field2: { ketik: String}
}, {
koleksi: "mySchema",
versionKey: salah
});

mySchema.post('findOneAndUpdate', function (result) {
    this.model.update({}, {
        field2: 'New Value'
    }).exec();
});
return mySchema;

}`

mySchema.findOneAndUpdate({id: 1}, {field1: 'test'}, {new: true});

Setel bidang dalam koleksi ke { id:1, field1: 'test' ) tetapi harus {id: 1, field1: 'test', field2:'New Value'}
Tidak yakin apa yang saya lakukan salah

Saya dapat mengubah mengubah hasil findOneAndUpdate dengan melakukan ini
mySchema.post('findOneAndUpdate', function (result) { result.field2 = 'something' });

Saya pikir mungkin Anda mencoba memperbarui model dengan elemen yang sudah ada pada model. Atau mungkin Anda salah memilihnya. Coba cetak "ini" di mySchema.post Anda. Anda juga tampaknya tidak memiliki done() atau next() di posting Anda. Saya tidak terlalu tahu tentang masalah ini tetapi saya tahu mencetak ini setidaknya akan memberi Anda gambaran tentang apa yang Anda hadapi.

Bukankah tujuan pembaruan untuk mengubah dokumen yang ada di model Anda?

ini adalah objek kueri

Anda tidak perlu selesai atau selanjutnya di kait posting sejauh yang saya mengerti.

Nah Anda memiliki this.model.update yang merupakan skema bukan model objek. Saya pikir .. Yang berarti Anda harus menggunakan

mySchema.post('findOneAndUpdate', function (result) {
    this.model.update({}, {
        $set: { field2: 'New Value'}
    }).exec();
});
return mySchema;

Ini tampaknya sedikit mundur untuk memanggil fungsi model di dalam model. Karena Anda bisa menggunakan bagian dari objek "ini" yang diberikan kepada Anda. Anda mungkin lebih baik hanya menggunakan findOneAndUpdate daripada memanggilnya dan kemudian memanggil fungsi model lain di atasnya.
dalam seorang manajer

var data = yourNewData;
self.findOneAndUpdate({id: something._id}, data, {safe: false, new: true})
    .exec()
    .then(resolve)
    .catch(reject);

Dalam contoh saya di atas, saya menggunakan this._update karena itu adalah objek pembaruan yang perlu digunakan dari ini.

Saya telah mencoba menggunakan $set. Itu masih tidak mengubah dokumen saya di koleksi.

Di mana saya dapat menemukan semua pra dan pasca kait yang tersedia?

+1

Apakah halaman ini membantu?
0 / 5 - 0 peringkat