【问题标题】:MongoDB query to select documents with array with all of its elements matching some conditionsMongoDB查询以选择具有所有元素都匹配某些条件的数组的文档
【发布时间】:2022-01-17 18:45:09
【问题描述】:

我正在尝试在 MongoDB 中提出一个查询,它允许我根据几级深度数组中子文档的内容来选择集合中的文档。

示例(简化)中的集合表示情况。查询的目的是在某个时刻了解当前活动的情况。 conditionGroups 数组表示情况变得活跃的不同条件,每个条件都有一个条件数组,所有这些条件都必须为真。

换句话说,conditionGroups 数组作为 OR 条件运行,其子数组“conditions”作为 AND 运行。因此,给定任何根文档“情况”,如果至少有一个 conditionGroup 满足其所有条件,则该情况将处于活动状态。

[
  {
    "name": "Weekdays",
    "conditionGroups": [
      {
        "conditions": [
          {
            "type": "DayOfWeek",
            "values": [1, 2, 3, 4, 5]
          },
          {
            "type": "HourIni",
            "values": [8]
          },
          {
            "type": "HourEnd",
            "values": [19]
          }
        ]
      }
    ]
  },
  {
    "name": "Nights and weekends",
    "conditionGroups": [
      {
        "conditions": [
          {
            "type": "DayOfWeek",
            "values": [1, 2, 3, 4, 5]
          },
          {
            "type": "HourIni",
            "values": [20]
          },
          {
            "type": "HourEnd",
            "values": [23]
          }
        ]
      },
      {
        "conditions": [
          {
            "type": "DayOfWeek",
            "values": [6, 7]
          },
          {
            "type": "HourIni",
            "values": [8]
          },
          {
            "type": "HourEnd",
            "values": [19]
          }
        ]
      }
    ]
  },
  {
    "name": "Weekend night",
    "conditionGroups": [
      {
        "conditions": [
          {
            "type": "DayOfWeek",
            "values": [6, 7]
          },
          {
            "type": "HourIni",
            "values": [20]
          },
          {
            "type": "HourEnd",
            "values": [23]
          }
        ]
      }
    ]
  }
]

另外需要注意的是,还有其他类型的条件,例如 DayOfMonth、Month、Year 和其他可能出现的条件,因此查询应该查找与类型和值匹配或根本不存在的条件。

给定这个示例数据,并假设 12 月的星期一午餐时间(因此 DayOfWeek 为 1,当前时间为 12,DayOfMonth 为 13,Month 为 12,Year 为 2021)只应选择第一个文档,因为它有“conditionGroup”所有条件都匹配当前参数,即使没有指定像 DayOfMonth/Year/Month 这样的参数。重要的是必须满足所有条件。

现在,我尝试了以下方法,但没有成功:

db.situations.find({
  'conditionGroups': { $all: [
    {
      $elemMatch: { $nor: [
        { 'conditions.type': 'HourIni', 'conditions.values.0': { $gt: 12 } },
        { 'conditions.type': 'HourEnd', 'conditions.values.0': { $lte: 12 } },
        { 'conditions.type': 'DayOfWeek', 'conditions.values.0': { $nin: [1] } },
        { 'conditions.type': 'DayOfMonth', 'conditions.values.0': { $nin: [13] } },
        { 'conditions.type': 'Month', 'conditions.values.0': { $nin: [12] } },
        { 'conditions.type': 'Year', 'conditions.values.0': { $nin: [2021] } },
      ]}
    }
  ] }
})

此查询返回为空。

我尝试过的另一件事是首先使用聚合管道展开条件组,然后在条件上尝试 $elemMatch,但得到奇怪的结果。我的猜测是我不完全理解 $elemMatch 和其他数组运算符,我以某种方式混淆了它们......

这是一个相当棘手的问题......所以我已经简化了它,但一个非常值得赞赏的好处是考虑到除了“类型”和“值”之外的每个条件也可以有一个“逆”布尔属性就像一个“不”,所以这个条件必须被“逆转”。

我花了很多时间试图让它工作,但我现在有点迷失了。我知道这些信息可能还不够,所以如果有人能给我一个提示,我可以在需要时提供额外的信息......

任何提示将不胜感激,因为我很迷茫! ;)

【问题讨论】:

    标签: mongodb mongodb-query aggregation-framework


    【解决方案1】:

    您可以在聚合管道中执行以下操作:

    1. $unwindconditionGroups 用于未来的处理/过滤
    2. 使用$switchcondition 级别执行条件检查。如果条件匹配,则设置结果为true,否则设置结果为false。通过使用$map,您获得了condition 数组的映射布尔结果
    3. $allElementsTrue 检查第2步的结果数组是否全部为真;如果为真,则表示一个条件通过了所有匹配项
    4. 使用_id找回所有原始文档的_id
    db.collection.aggregate([
      {
        "$addFields": {
          "dateInput": ISODate("2021-12-13T12:00:00Z")
        }
      },
      {
        "$unwind": "$conditionGroups"
      },
      {
        "$addFields": {
          "matchedCondition": {
            "$map": {
              "input": "$conditionGroups.conditions",
              "as": "c",
              "in": {
                "$switch": {
                  "branches": [
                    {
                      "case": {
                        $and: [
                          {
                            $eq: [
                              "$$c.type",
                              "DayOfWeek"
                            ]
                          },
                          {
                            "$in": [
                              {
                                "$dayOfWeek": "$dateInput"
                              },
                              "$$c.values"
                            ]
                          }
                        ]
                      },
                      "then": true
                    },
                    {
                      "case": {
                        $and: [
                          {
                            $eq: [
                              "$$c.type",
                              "HourIni"
                            ]
                          },
                          {
                            "$gt": [
                              {
                                "$hour": "$dateInput"
                              },
                              {
                                "$arrayElemAt": [
                                  "$$c.values",
                                  0
                                ]
                              }
                            ]
                          }
                        ]
                      },
                      "then": true
                    },
                    {
                      "case": {
                        $and: [
                          {
                            $eq: [
                              "$$c.type",
                              "HourEnd"
                            ]
                          },
                          {
                            "$lte": [
                              {
                                "$hour": "$dateInput"
                              },
                              {
                                "$arrayElemAt": [
                                  "$$c.values",
                                  0
                                ]
                              }
                            ]
                          }
                        ]
                      },
                      "then": true
                    }
                  ],
                  default: false
                }
              }
            }
          }
        }
      },
      {
        "$match": {
          $expr: {
            $eq: [
              true,
              {
                "$allElementsTrue": "$matchedCondition"
              }
            ]
          }
        }
      },
      {
        "$group": {
          "_id": "$_id"
        }
      },
      {
        "$lookup": {
          "from": "collection",
          "localField": "_id",
          "foreignField": "_id",
          "as": "originalDocument"
        }
      },
      {
        "$unwind": "$originalDocument"
      },
      {
        "$replaceRoot": {
          "newRoot": "$originalDocument"
        }
      }
    ])
    

    这里是Mongo playground 供您参考。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-09-21
      • 1970-01-01
      • 2023-03-04
      • 2015-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多