在MongoDB中聚集双嵌套数组的文档

时间:2018-12-09 19:53:25

标签: node.js mongodb mongoose aggregate

我正在尝试计算具有不同条件的文档。这里我有这样简化的文本表(文档):

{ 
  "teamId": "1",
  "stage": "0",
  "answeredBy": [userId_1, userId_2],
  "skippedBy": [userId_3],
  "answers": []
},
{ 
  "teamId": "1",
  "stage": "0",
  "answeredBy": [userId_2],
  "skippedBy": [userId_1],
  "answers": []
},
{ 
  "teamId" : "1",
  "stage": "0",
  "answeredBy": [userId_3],
  "skippedBy": [userId_2],
  "answers": []
},
{ 
  "teamId" : "1",
  "stage": "1",
  "answeredBy": [userId_3],
  "skippedBy": [userId_1, userId_2],
  "answers": [
              { "readBy": [userId_1] },
              { "readBy": [userId_1, userId_2] },
              { "readBy": [userId_3, userId_1] },       
  ]
},
{ 
  "teamId" : "1",
  "stage": "1",
  "answeredBy": [userId_3],
  "skippedBy": [userId_1, userId_2],
  "answers": [
              { "readBy": [userId_1] },
              { "readBy": [userId_1, userId_2] },
              { "readBy": [userId_3] },       
  ]
};

我想对每个适当的用户ID,阶段和teamID进行一次查询(因此,第一个$ match必须是每个teamId和阶段:“ 0”或“ 1”:

  • 舞台上有多少个文档:“ 0”包含在AnswerBy或skipdBy数组中的用户ID(我将此文档称为“已回答”)
  • 舞台上有多少文档:“ 0”在answerBy和skippedBy数组中都不包含用户ID(我将此文档称为“未回答”)
  • 有多少个阶段为“ 1”的文档在答案数组中至少包含一个不包含用户的readBy数组(我称其为“未读”文档)

因此,我尝试了多种方法来实现它,但是最困难的部分是遍历数组 answers 的嵌套数组( readBy ),并找出哪个不不会包含适当的用户,并将此文档视为未读

可能的结果:

 {
   answered: 2,
   unanswered: 1,
   unread: 1,
 };

 [
   { _id: 'answered', count: 2 },
   { _id: 'unanswered', count: 1 },
   { _id: 'unread', count: 1 }
 ]

编写此查询后,我陷入了困境,不知道如何遍历readBy数组:

db.texts.aggregate([
      { $match: {teamId: 1, $or: [{currStage: 0}, {currStage: 1}]}},
      { $project: { 'stage': { $switch: { branches: [
      { case: 
             { $and: [ { $eq: [ '$currStage', 0 ] },
             { $not: [ { $or: [ { $in: [ userId_1, '$answeredBy' ] },
             { $in: [ userId_1, '$skippedBy' ] } ] } ] } ] },
        then: 'unanswered'},
      { case: 
             { $and: [ { $eq: [ '$currStage', 0 ] },
             { $or: [ { $in: [ userId_1, '$answeredBy' ] },
             { $in: [ userId_1, '$skippedBy' ] } ] } ] },
        then: 'answered'},
      { case:
             { $and: [ { $eq: [ '$currStage', 1 ] },
             { $not: [ { $in: [ userId_1, '$answers.readBy' ] } ] } ] },
        then: 'unread'},
                 ] } } } },
      { $group: { _id: '$stage', count: { $sum: 1 } } },
 ]); 

2 个答案:

答案 0 :(得分:1)

尝试一下,我假设userid = userId_1

db.getCollection('answers').aggregate([
      { $match: {teamId: '1', $or: [{stage: '0'}, {stage: '1'}]}},
      {$project:{
          counts :{$cond: [
              {$or:[{$in:["userId_1", "$answeredBy"]}, {$in:["userId_1", "$skippedBy"]}]}, 
              {$literal:{answered: 1, unaswered: 0}}, 
              {$literal:{answered: 0, unaswered: 1}}
          ]},
          unread : {$cond:[
                  {$gt:[{$reduce: {
                      input: "$answers", 
                      initialValue: 1, 
                      in: {$multiply:["$$value", 
                          {$cond:[
                              {$in:["userId_1", "$$this.readBy"]},
                              {$literal: 0},
                              {$literal: 1}
                           ]}
                       ]}}},
                       0
                     ]},
                     {$literal: 1},
                     {$literal: 0}
               ]}

      }},
      {$group: {_id: null, answered: {$sum: "$counts.answered"}, unanswered: {$sum: "$counts.unanswered"}, unread: {$sum: "$unread"}}}
])

答案 1 :(得分:0)

这是我的工作解决方案。谢谢所有尝试解决它并为我提供帮助的人。

db.test.aggregate([
  { $match: {teamId: "1", $or: [{stage: "0"}, {stage: "1", "answers": {$elemMatch: {"readBy": {$nin: ["userId_1"]}}}}]}},
  { $project: { 'stage': { $switch: { branches: [
  { case: 
         { $and: [ { $eq: [ '$stage', "0" ] },
         { $not: [ { $or: [ { $in: [ "userId_1", '$answeredBy' ] },
         { $in: [ "userId_1", '$skippedBy' ] } ] } ] } ] },
    then: 'unanswered'},
  { case: 
         { $and: [ { $eq: [ '$stage', "0" ] },
         { $or: [ { $in: [ "userId_1", '$answeredBy' ] },
         { $in: [ "userId_1", '$skippedBy' ] } ] } ] },
    then: 'answered'},
  { case:
         { $eq: [ '$stage', "1" ] } ,
    then: 'unread'},
             ] } } } },
  { $group: { _id: '$stage', count: { $sum: 1 } } },
])

也许我应该找到一个更好的解决方案,但是目前这是我所需要的。