避免在mongo db中使用空数组元素

时间:2019-08-27 19:26:37

标签: arrays mongodb mongodb-query

在MongoDb中查询集合时如何在过滤结果时避免空数组

[
  {
    "_id": ObjectId("5d429786bd7b5f4ae4a64790"),
    "extensions": {
      "outcome": "success",
      "docType": "ABC",
      "Roll No": "1"
    },
    "data": [
      {
        "Page1": [
          {
            "heading": "LIST",
            "content": [
              {
                "text": "<b>12345</b>"
              },

            ],

          }
        ],
        "highlights": [
          {
            "name": "ABCD",
            "text": "EFGH",

          }
        ],
        "marks": [
          {
            "revision": "revision 1",
            "Score": [
              {
                "maths": "100",
                "science": "40",
                "history": "90"
              },
              {
                "lab1": "25",
                "lab2": "25"
              }
            ],
            "Result": "Pass"
          },
          {
            "revision": "revision 1",
            "Score": [
              {
                "maths": "100",
                "science": "40"
              },
              {
                "lab1": "25",
                "lab2": "25"
              }
            ],
            "Result": "Pass"
          }
        ]
      }
    ]
  }
]

我正在寻找分数数组中只有“历史”标记的结果。

我尝试了以下查询(在mongo 3.6.10中),但是它返回了空得分数组以及具有历史记录的数组

db.getCollection('student_scores').find({
  "data.marks.score.history": {
    $not: {
      $type: 10
    },
    $exists: true
  }
},
{
  "extensions.rollNo": 1,
  "data.marks.score.history": 1
})

所需的输出是

{
  "extensions": {
    "rollNo": "1"
  },
  "data": [
    {
      "marks": [
        {
          "Score": [
            {
              "history": "90"
            }
          ]
        }
      ]
    }
  ]
}

2 个答案:

答案 0 :(得分:2)

我使用了类似下面的内容;

db.getCollection('student_scores').aggregate([
  {
    $unwind: "$data"
  },
  {
    $unwind: "$data.marks"
  },
  {
    $unwind: "$data.marks.Score"
  },
  {
    $match: {
      "data.marks.Score.history": {
        $exists: true,
        $not: {
          $type: 10
        }
      }
    }
  },
  {
    $project: {
      "extensions.Roll No": 1,
      "data.marks.Score.history": 1
    }
  },
  {
    $group: {
      _id: "$extensions.Roll No",
      history_grades: {
        $push: "$data.marks.Score.history"
      }
    }
  }
])

在您输入的内容中,我得到以下结果(我认为比预期的输出更具可读性);

[
  {
    "_id": "1",
    "history_grades": [
      "90"
    ]
  }
]

其中_id代表任何给定"extensions.Roll No"集的data值。

你怎么看?

check with a bigger input on mongoplayground

答案 1 :(得分:0)

好吧,所以我仍然认为此处使用Score数组的数据设计有点过时,但这是确保Score数组仅包含 1的解决方案条目,该条目用于history的密钥。我们使用点路径数组潜水作为获得历史价值的技巧。

c = db.foo.aggregate([
{$unwind: "$data"}
,{$unwind: "$data.marks"}

,{$project: {
result: {$cond: [
    {$and: [ // if
        {$eq: [1, {$size: "$data.marks.Score"}]}, // Only 1 item...

        //  A little trick!  $data.marks.Score.history will resolve to an *array*
        //  of the values associated with each object in $data.marks.Score (the parent
        //  array) having a key of history. BUT: As it resolves, if there is no 
        // field for that key, nothing is added to resolution vector -- not even a null.
        //  This means the resolved array could
        //  be **shorter** than the input.  FOr example:
        //    > db.foo.insert({"x":[ {b:2}, {a:3,b:4}, {b:7}, {a:99} ]});
        //    WriteResult({ "nInserted" : 1 })
        //    > db.foo.aggregate([ {$project: {z: "$x.b", n: {$size: "$x.b"}} } ]);
        //    { "z" : [ 2, 4, 7 ], "n" : 3 }
        //    > db.foo.aggregate([ {$project: {z: "$x.a", n: {$size: "$x.a"}} } ]);
        //    { "z" : [ 3, 99 ], "n" : 2 }
        //
        //  You must be careful about this.
        //  But we also know this resolved vector is of size 1 (see above) so we can go ahead and grab
        //  the 0th item and that becomes our output. 
        //  Note that if we did not have the requirement of ONLY history, then we would not
        //  need the fancy $cond thing. 
        {$arrayElemAt: ["$data.marks.Score.history",0]} 
             ]},
    {$arrayElemAt: ["$data.marks.Score.history",0]},  // then (use value of history)
    null ] }   // else set null

,extensions: "$extensions"  // just carry over extensions
    }}

,{$match: {"result": {$ne: null} }} // only take good ones.