MongoDB select and update with complicated conditions

心已入冬 提交于 2021-01-29 14:43:23

问题


A server is running multiple processes, process reads task from DB. Tasks contain status and _userid, which is external key for users collection. Look like like:

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

Status can be: 'waiting', 'in progress', 'finished'. For every user I need tasks to run ordered by date and only one at a time. How can I build a query that selects one task:

  1. If nothing 'in progress', select first with status 'waiting' and update status to 'in progress';
  2. If something is 'in progress', select task that has status 'waiting' and does not belong to _userid of any task that has status 'in progress' (reminder: only one at a time for every user). And also update status to 'in progress'.

Is the query like this possible in MongoDB?

EDIT: For example, I've got a tasks in 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)
}

In this case, first task should be chosen from tasks, because everything is 'waiting'. After first task has been chosen it becomes 'in progress':

{
  _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)
}

Now, next task should be chosen with status 'waiting' and not with _userid: ObjectId(1). It will be ObjectId(2) with Date(3). After that we have 2 _userid 'in progress': ObjectId(1) and ObjectId(2), so next request should receive nothing.

{
  _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)
}

When first task is finished it's status will be 'finished' and now query must receive second task for ObjectId(1), because it's status 'waiting' and nothing else runs for this 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)
}

EDIT 2: OK, irrelevant any more


回答1:


Check this one:

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

If you guarantee that the "finished" tasks are sorted by date, you can filter them.

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


来源:https://stackoverflow.com/questions/60523655/mongodb-select-and-update-with-complicated-conditions

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!