mongodb:仅在未更新时更新文档

mongodb:仅在未更新时更新文档,mongodb,validation,mongoose,Mongodb,Validation,Mongoose,我有这个功能。仅允许接受服务不接受: 仅当可用参数为true时才采用 function takeService(req, res) { var serviceId = req.params.id; var driverId = req.body.driverId; Service.findById(serviceId, (err, service) =>{ if (!err) { if (!service) { res.status(404

我有这个功能。仅允许接受服务不接受: 仅当可用参数为true时才采用

function takeService(req, res) {
  var serviceId = req.params.id;
  var driverId = req.body.driverId;

  Service.findById(serviceId, (err, service) =>{
    if (!err) {
      if (!service) {
        res.status(404).send({message: 'Not found'});
      } else {
        if (service.available === false ) {
          res.status(409).send({message: 'The service is taken'});
        } else {
          Service.findByIdAndUpdate(serviceId, {
            driverId, 
            status: 1, 
            available: false
          }, (err, serviceUpdated) =>{
            if (!err && serviceUpdated) {
              res.status(200).send({message: "tomado"});
            }
          });
        }
      }
    }
  });
}
模式:

var ServiceSchema = Schema({
  clientId: {
    type: String,
    ref: 'Client'
  },
  available: Boolean,
  routeId: {
    type: String,
    ref: 'Route'
  },
  date: Date,
  radius: Number,
  driverId: {
    type: String,
    ref: 'Driver'
  },
  status: Number,
  time: String,
  createdTime: Number,
  rateId: {
    type: String,
    ref: 'Rate'
  }
});

var DriverSchema = Schema({
  name: String,
  surname: String,
  username: String,
  password: String,
  status: { type: Number, default: 0 },
  oneSignalId: String,
  plate: String,
  make: String,
  year: String,
  model: String,
  groupId: [{
    type: String,
    ref: 'DriverGroup'
  }],
  unit: String,
  telephone: String
});

问题是,当两个设备调用此函数时,在某些情况下,两个设备都会查找文档并检查是否可用,然后更新同一文档。我正在架构中查找自动检查此属性的一些验证。

如果我正确理解此问题,主要问题是两个设备可能认为服务仍然可用

最终原因是
findById
findbyidanddupdate
之间存在竞争条件:在这两个调用之间,存在一个时间窗口,在该时间窗口中,另一个请求可以更改数据库中的文档

要解决此问题,可以使用原子命令,Mongoose将其公开为(除其他外)
Model\findOneAndUpdate

您的代码将如下所示:

function takeService(req, res) {
  var serviceId = req.params.id;
  var driverId  = req.body.driverId;

  Service.findOneAndUpdate({
    _id       : serviceId,
    available : true
  }, {
    driverId  : driverId,
    status    : 1,
    available : false,
  }, (err, service) => {
    if (err) {
      return res.status(500);
    } else if (! service) {
      return res.status(409).send({message: 'The service is taken'});
    } else {
      return res.status(200).send({message: "tomado"});
    }
  });
}
您应该注意与原始代码的一些差异:

  • 您无法区分不存在的服务(无效/未知
    serviceId
    )和不再可用的服务;在这两种情况下,更新都不会产生任何结果,并返回409响应
  • findOneAndUpdate
    将返回更新前的旧文档。如果要接收更新的文档,请在查询中传递
    new
    选项:

    Service.findOneAndUpdate({ ... }, { ... }, { new : true }, (err, service) => { ... })
    
  • 我在其中添加了一个错误处理程序,它返回500(“内部服务器错误”)响应


您可以更改
findbyiandupdate
,或者创建一个新的
findAvailableByIdAndUpdate
,其中在mongo查询中包含
“可用”:true
。如果它不更新任何文档,那么其他东西就赢得了比赛。