在预验证中间件之后创建或更新字段?

时间:2017-01-18 10:58:41

标签: node.js mongodb mongoose

我试图创建一个" findOneAndUpdate"查询将触发预验证中间件。

需要这些中间件来验证给定的坐标并创建一个“id”。字段(是的,' _id' one)在请求的正文中没有提供。

正如您在下面看到的那样(查看代码中的注释),我已经关闭了,但不明白为什么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'
});

预验证中间件:

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

2 个答案:

答案 0 :(得分:1)

问题在于pre()&amp; post()的{​​{1}}中间件&amp; savevalidate等未执行update()docs中提到了findOneAnUpdate()以及GitHub issue

但是,有pre('findOneAndUpdate')post('findOneAndUpdate')个钩子可用(不确定update的挂钩是否有效)。

希望这会对你有所帮助。

答案 1 :(得分:1)

详细说明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();
  });
});