首页 > 解决方案 > MongoDB选择和更新条件复杂

问题描述

一台服务器正在运行多个进程,进程从数据库中读取任务。任务包含状态和_userid,这是用户收集的外部键。看起来像:

{
  _userid: ObjectId(),
  status: 'waiting',
  date: Date()
}

状态可以是:'等待'、'进行中'、'完成'。对于每个用户,我需要按日期排序的任务运行,并且一次只运行一个。如何构建选择一项任务的查询:

  1. 如果没有“进行中”,则首先选择状态“等待”并将状态更新为“进行中”;
  2. 如果某事“进行中”,请选择状态为“等待”且不属于任何状态为“进行中”的任务的_userid 的任务(提醒:每个用户一次只有一个)。并且还将状态更新为“进行中”。

这样的查询在 MongoDB 中是否可行?

编辑:例如,我在数据库中有一个任务:

{
  _userid: ObjectId(1),
  status: 'waiting',
  date: Date(1)
},
{
  _userid: ObjectId(1),
  status: 'waiting',
  date: Date(2)
},
{
  _userid: ObjectId(2),
  status: 'waiting',
  date: Date(3)
},
{
  _userid: ObjectId(2),
  status: 'waiting',
  date: Date(4)
}

在这种情况下,应该从任务中选择第一个任务,因为一切都在“等待”。选择第一个任务后,它变为“进行中”:

{
  _userid: ObjectId(1),
  status: 'in progress',
  date: Date(1)
},
{
  _userid: ObjectId(1),
  status: 'waiting',
  date: Date(2)
},
{
  _userid: ObjectId(2),
  status: 'waiting',
  date: Date(3)
},
{
  _userid: ObjectId(2),
  status: 'waiting',
  date: Date(4)
}

现在,应该选择状态为“等待”而不是 _userid:ObjectId(1) 的下一个任务。它将是 ObjectId(2) 和 Date(3)。之后我们有 2 个 _userid 'in progress':ObjectId(1) 和 ObjectId(2),所以下一个请求应该什么也没有收到。

{
  _userid: ObjectId(1),
  status: 'in progress',
  date: Date(1)
},
{
  _userid: ObjectId(1),
  status: 'waiting',
  date: Date(2)
},
{
  _userid: ObjectId(2),
  status: 'in progress',
  date: Date(3)
},
{
  _userid: ObjectId(2),
  status: 'waiting',
  date: Date(4)
}

当第一个任务完成时,它的状态将是“完成”,现在查询必须接收 ObjectId(1) 的第二个任务,因为它的状态是“等待”,并且没有其他任何东西为此 ObjectId(1) 运行。

{
  _userid: ObjectId(1),
  status: 'finished',
  date: Date(1)
},
{
  _userid: ObjectId(1),
  status: 'in progress',
  date: Date(2)
},
{
  _userid: ObjectId(2),
  status: 'in progress',
  date: Date(3)
},
{
  _userid: ObjectId(2),
  status: 'waiting',
  date: Date(4)
}

编辑2:好的,不再相关

标签: mongodb

解决方案


检查这个:

db.task.aggregate([
  {
    $sort: {
      _userid: 1,
      date: 1
    }
  },
  {
    $group: {
      _id: "$_userid",
      task: {
        $push: "$$ROOT"
      }
    }
  }
]).forEach(function(doc){

    for(var i=0; i<doc.task.length; i++){

        //If we want to check if there is any "in progress" for this user
        //doc.task.filter(x => x.status == "in progress").length > 0
        if(doc.task[i].status == "in progress") {
            //Nothing to do
            break;
        } else if(doc.task[i].status == "waiting") {
            //We set i element to in progress
            doc.task[i].status = "in progress";
            db.task.save(doc.task[i]);
            break;
       } else if(doc.task[i].status == "finished") {
           //We skip this task
           continue;
       }
   }
});

如果您保证“已完成”任务按日期排序,则可以对其进行过滤。

db.task.aggregate([
  {
    $match: {
      status: {
        $ne: "finished"
      }
    }
  },
  {
    $sort: {
      _userid: 1,
      date: 1
    }
  },
  ...

推荐阅读