预validation中间件之后创build或更新字段?

我试图创build一个“findOneAndUpdate”查询,将启动预validation中间件。

这些中间件是需要validation给定的坐标,并创build一个“id”字段(是的, 而不是 “_id”之一),当它没有提供请求的身体。

正如你可以看到下面( 见代码中的注释 ),我很接近,但不明白为什么mongo抛出的错误是重复的关键变种。

也许承诺不是要走到这里的方式,虽然他们让我成为比链接pre中间件更成功的方式。

这是我的快车路线:

 /* POST /geolocs */ router.post('/', function(req, res, next) { Geoloc.create(req.body, function(err, post) { if (err) return next(err); res.status(201).json(post); }); }); 

这是我的模式:

 var GeolocSchema = new mongoose.Schema({ id: { type: String, required: true, unique: true }, location: { type: { type: String, default: 'Point', required: true }, coordinates: { type: [Number], required: true } }, count: Number }); GeolocSchema.index({ location: '2dsphere' }); 

预validation中间件:

 // Before validation, check given coordinates for errors. GeolocSchema.pre('validate', function(next) { coord = this.location.coordinates if (this.location.type && coord) { if (Array.isArray(coord) && coord.length === 2) { lat = coord[1]; lon = coord[0]; if ((-90 <= lat && lat <= 90) && (-180 <= lat && lat <= 180)) next(); } } var err = new Error('...'); // Long error text, irrelevant here err.status = 400; next(err); }); // Then, if no 'id' is given, create it. GeolocSchema.pre('validate', function(next) { if (!this.id) { strLat = this.location.coordinates[1].toFixed(3).replace('.', '_'); strLon = this.location.coordinates[0].toFixed(3).replace('.', '_'); this.id = strLat + '-' + strLon; } next(); }); 

我希望能够做的是在上面添加以下内容:

 // Here, using the validate or save hook doesn't change anything. GeolocSchema.pre('validate', function(next) { var prom = Geoloc.findOne({ 'id': { $eq: this.id } }).exec(); prom.then((err, geoloc) => { // Arrow function here to access 'this' if (err) next(err); // If no geoloc was found, go ahead and save. if (!geoloc) next(); // Else, update to increment the count (THIS WORKS). return Geoloc.update({'id': this.id}, {$inc: {count: 1}}).exec(); }).then((toto) => { // This doesn't work, the error thrown by mongo is a duplicate key error (E11000). if (toto) next(new Error('204')); else next(new Error("Something that shouldn't happen, happened...")); }); }); 

问题是savevalidate pre()post()中间件不是由update()findOneAnUpdate()等执行的。它在文档中提到,还有一个GitHub的问题

但是,有pre('findOneAndUpdate')post('findOneAndUpdate')钩子可用(不知道如果钩子update工作)。

希望这可以帮助你。

为了详细说明Santanu Biswas接受的答案,这里是基于它的工作代码:

请注意,它仍然有一些怪癖,特别是在响应中返回给客户端的是什么,但MongoDB操作是按预期发生的。

快线

 router.post('/', function(req, res, next) { Geoloc.findOneAndUpdate({ id: req.body.id }, req.body, { runValidators: true, // MANDATORY OPTION upsert: true, context: 'query' // MANDATORY OPTION }, function(err, post) { if (err) return next(err); res.status(201).json(post); }); }); 

预先 findOneAndUpdate 中间件

 GeolocSchema.pre('findOneAndUpdate', function(next) { doc = this.getUpdate(); coord = doc.location.coordinates; if (doc.location.type && coord) { if (Array.isArray(coord) && coord.length === 2) { lat = coord[1]; lon = coord[0]; if ((-90 <= lat && lat <= 90) && (-180 <= lat && lat <= 180)) { return next(); } } } var err = new Error('...'); // Long error text, irrelevant here err.status = 400; next(err); }); 

和:

 GeolocSchema.pre('findOneAndUpdate', function(next) { doc = this.getUpdate(); query = this.getQuery(); if (!query.id) { strLat = doc.location.coordinates[1].toFixed(3).replace('.', '_'); strLon = doc.location.coordinates[0].toFixed(3).replace('.', '_'); query.id = strLat + '-' + strLon; doc.id = query.id; } var prom = Geoloc.findOne({ 'id':query.id }).exec(); prom.then((geoloc, err) => { if (err) return next(err); if (!geoloc) return next(); doc.count += geoloc.count; next(); }); });