MongoDB查询以选择具有所有元素都匹配某些条件的数组的文档
Posted
技术标签:
【中文标题】MongoDB查询以选择具有所有元素都匹配某些条件的数组的文档【英文标题】:MongoDB query to select documents with array with all of its elements matching some conditions 【发布时间】: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 和其他数组运算符,我以某种方式混淆了它们......
这是一个相当棘手的问题......所以我已经简化了它,但一个非常值得赞赏的好处是考虑到除了“类型”和“值”之外的每个条件也可以有一个“逆”布尔属性就像一个“不”,所以这个条件必须被“逆转”。
我花了很多时间试图让它工作,但我现在有点迷失了。我知道这些信息可能还不够,所以如果有人能给我一个提示,我可以在需要时提供额外的信息......
任何提示将不胜感激,因为我很迷茫! ;)
【问题讨论】:
【参考方案1】:您可以在聚合管道中执行以下操作:
$unwind
conditionGroups
用于未来的处理/过滤
使用$switch
对condition
级别执行条件检查。如果条件匹配,则设置结果为true
,否则设置结果为false
。通过使用$map
,您获得了condition
数组的映射布尔结果
$allElementsTrue
检查第2步的结果数组是否全部为真;如果为真,则表示一个条件通过了所有匹配项
使用_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 供您参考。
【讨论】:
以上是关于MongoDB查询以选择具有所有元素都匹配某些条件的数组的文档的主要内容,如果未能解决你的问题,请参考以下文章
MongoDB查询以获取与具有多个值的键匹配的所有文档[重复]