【问题标题】:mongodb: only update document if is not updatedmongodb:仅在未更新时更新文档
【发布时间】:2017-05-26 02:27:18
【问题描述】:

我有这个功能。只允许取服务不取: 仅当可用参数为真时才采用。

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

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

【问题讨论】:

  • 您可以更改 findByIdAndUpdate,或在 mongo 查询中创建一个包含 "available":true 的新 findAvailableByIdAndUpdate。如果它没有更新任何文档,那么其他东西就赢得了比赛。

标签: mongodb validation mongoose


【解决方案1】:

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

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

要解决此问题,您可以使用原子 findAndModify 命令,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 将返回 old 文档,在它更新之前。如果您想接收更新的文档,请在查询中传递new 选项:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-04
    • 1970-01-01
    • 2015-02-21
    • 2017-08-18
    • 1970-01-01
    • 2022-09-25
    相关资源
    最近更新 更多