2017-05-25 53 views
0

我有這個功能。不允許接受服務: 僅在可用參數爲真時才被採用。mongodb:只更新文件如果沒有更新

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

問題是當兩個設備調用這個函數,在某些情況下都找到該文件,並檢查是否可用,然後更新都在同一個文檔。我正在爲自動檢查此屬性的架構中尋找一些驗證。

+0

你可以改變'findByIdAndUpdate',或使一個新的'findAvailableByIdAndUpdate'包括' 「可用」:TRUE'在蒙戈查詢。如果它沒有更新任何文件,其他的東西就會贏得比賽。 – Joe

回答

0

如果我正確理解問題,主要問題是兩個設備可能認爲服務仍然可用。

最終的原因是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將在更新之前返回舊的文檔。如果您希望收到更新的文檔,通過new選項查詢:

    Service.findOneAndUpdate({ ... }, { ... }, { new : true }, (err, service) => { ... }) 
    
  • 我在裏面添加了一個錯誤處理程序,發送回一個500(「內部服務器錯誤」)響應。