【问题标题】:MongoDB select and update with complicated conditionsMongoDB选择和更新条件复杂
【发布时间】:2020-03-04 10:12:44
【问题描述】:

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

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

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

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

在 MongoDB 中可以进行这样的查询吗?

编辑: 例如,我在 DB 中有一个任务:

{
  _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:好的,不再相关

【问题讨论】:

  • 最好发布示例数据并用示例(用例)进行解释。您的问题不清楚,可能会混淆用户为您提供有效的解决方案
  • @Valijon,我添加了流程应该如何流动的示例
  • 在 SQL 中我认为可以这样做:if not exists (select 1 from tasks where status 'in progress') then select * from tasks order by date limit 1 else select * from tasks where status 'waiting' and userid not in (select userid from tasks where status 'in progress') 它没有更新,但我认为它是可以解决的。不知道如何在MongoDB中做到这一点
  • 在您的情况下,谁“完成”了任务?你的应用?
  • @Valijon 是的,应用程序有这个任务的 _id,当任务完成时它只是更新状态

标签: mongodb


【解决方案1】:

检查这个:

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
    }
  },
  ...

【讨论】:

  • 谢谢,查询做了一些事情,但不是我需要的。运行任务的服务器向 DB 发出请求,并希望一次接收一项作业。您的查询确实改变了事情,但对于所有用户并且不返回任务本身
  • @Naryck 请告诉我您的代码在进行这些更改时期望什么,返回第一项?仅组第一个用户?
猜你喜欢
  • 2021-10-11
  • 2018-09-22
  • 1970-01-01
  • 1970-01-01
  • 2016-08-18
  • 1970-01-01
  • 1970-01-01
  • 2019-06-26
  • 1970-01-01
相关资源
最近更新 更多