【问题标题】:Avoid empty array elements in mongo db避免MongoDB中的空数组元素
【发布时间】:2019-08-27 19:26:37
【问题描述】:

如何在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 个输入文档,一个与所需输出匹配,一个不匹配。并去掉 Page1extensions 等。这些与您寻找的过滤器无关,它使“复制和测试”材料变得更加困难。
  • 我已经更正了 josn。我遇到嵌套数组,我的场景涉及不为空过滤以仅获取历史属性
  • 我认为数据形状仍然有点......关闭。我不明白 Score 对象数组的设计原理。您可以通过将 Score 作为一个简单对象来简化您的设计,例如Score: { "maths":100, "science":40, "lab1":25 } 而且,这些分数应该是数字(整数)而不是字符串......
  • 你能检查我的答案吗?它应该有助于满足您的要求并简化您的结果!

标签: arrays mongodb mongodb-query


【解决方案1】:

我使用了类似下面的东西;

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 表示任何给定data 集合的"extensions.Roll No" 值。

你怎么看?

check with a bigger input on mongoplayground

【讨论】:

  • 不确定这历史测试...?可以肯定的是,这个逻辑至少可以测试一个历史,而不仅仅是历史。但我不是 100% 确定 OP 的要求。
  • @BuzzMoschetti OP 表示他想要 only history 时的最终输出,因为输出有两个 Score 数组,一个带有预期的history 结果和另一个空对象,另一个带有两个空对象。检查this
【解决方案2】:

好的,所以我仍然认为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.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-28
    • 2018-03-26
    • 2019-04-08
    • 1970-01-01
    • 1970-01-01
    • 2010-09-30
    • 2021-10-19
    相关资源
    最近更新 更多