Mongodb 使用$elemMatch和$or实现回退逻辑(在投影中)
在给定的Mongodb 使用$elemMatch和$or实现回退逻辑(在投影中),mongodb,mongodb-query,aggregation-framework,Mongodb,Mongodb Query,Aggregation Framework,在给定的项目中我需要一个只包含一个响应的投影,因此$elemMatch。理想情况下,寻找精确匹配: db.projects.findOne({"_id": "5CmYdmu2Aanva3ZAy"}, { "responses": { "$elemMatch": { "match.nlu": { "$elemMatch": { "intent": "intent1", "$and": [ {
项目中
我需要一个只包含一个响应的投影,因此$elemMatch
。理想情况下,寻找精确匹配:
db.projects.findOne({"_id": "5CmYdmu2Aanva3ZAy"},
{
"responses": {
"$elemMatch": {
"match.nlu": {
"$elemMatch": {
"intent": "intent1",
"$and": [
{
"$or": [
{
"entities.entity": "entity1",
"entities.value": "value1"
},
{
"entities.entity": "entity1",
"entities.value": {
"$exists": false
}
}
]
}
],
"entities.1": {
"$exists": false
}
}
}
}
}
})
但如果这样的匹配不存在,请查找实体.value
不存在的记录
上面的查询不起作用,因为如果它找到一个带有entities.value
未设置的项,它将返回该项。如何在Mongo查询中获取此回退逻辑
下面是一个文档的示例
{
"entities.entity": "entity1",
"entities.value": "value1"
}
要回答这个问题,首先要做的是,做你想做的事情并不像投影那么简单,需要聚合框架的特殊投影逻辑。这里的第二个主要原则是“嵌套数组是一个非常糟糕的主意”,这就是为什么:
{
"_id": "5CmYdmu2Aanva3ZAy",
"responses": [
{
"match": {
"nlu": [
{
"entities": [],
"intent": "intent1"
}
]
},
"key": "utter_intent1_p3vE6O_XsT"
},
{
"match": {
"nlu": [
{
"entities": [{
"entity": "entity1",
"value": "value1"
}],
"intent": "intent1"
}
]
},
"key": "utter_intent1_p3vE6O_XsT"
},
{
"match": {
"nlu": [
{
"intent": "intent2",
"entities": []
},
{
"intent": "intent1",
"entities": [
{
"entity": "entity1"
}
]
}
]
},
"key": "utter_intent2_Laag5aDZv2"
}
]
}
将返回:
db.collection.aggregate([
{ "$match": { "_id": "5CmYdmu2Aanva3ZAy" } },
{ "$addFields": {
"responses": {
"$filter": {
"input": {
"$map": {
"input": "$responses",
"in": {
"match": {
"nlu": {
"$filter": {
"input": {
"$map": {
"input": "$$this.match.nlu",
"in": {
"entities": {
"$let": {
"vars": {
"entities": {
"$filter": {
"input": "$$this.entities",
"cond": {
"$and": [
{ "$eq": [ "$$this.entity", "entity1" ] },
{ "$or": [
{ "$eq": [ "$$this.value", "value1" ] },
{ "$ifNull": [ "$$this.value", false ] }
]}
]
}
}
}
},
"in": {
"$cond": {
"if": { "$gt": [{ "$size": "$$entities" }, 1] },
"then": {
"$slice": [
{ "$filter": {
"input": "$$entities",
"cond": { "$eq": [ "$$this.value", "value1" ] }
}},
0
]
},
"else": "$$entities"
}
}
}
},
"intent": "$$this.intent"
}
}
},
"cond": { "$ne": [ "$$this.entities", [] ] }
}
}
},
"key": "$$this.key"
}
}
},
"cond": { "$ne": [ "$$this.match.nlu", [] ] }
}
}
}}
])
这就是从实体
的嵌套内部数组中提取第一个匹配元素,其中实体
和值
的条件都满足或值
属性不存在的
请注意另外一个回退,即如果这两个条件都意味着返回多个数组元素,那么只有值存在且匹配的第一个匹配才会返回结果
查询深度嵌套的数组需要链接使用和,以便遍历这些数组内容并仅返回符合条件的项。您无法在投影中指定这些条件,在MongoDB的最新版本之前,甚至都不可能在不覆盖文档的重要部分或引入更新并发性问题的情况下以原子方式更新此类结构
关于这一点,更详细的解释是在我现有的问题答案中
请注意,这两个响应都显示了作为“查询”运算符的用法,这实际上只是关于“文档选择”(因此不适用于\u id
匹配条件),不能与前一个“投影”变体或投影运算符配合使用
然后建议您“不要嵌套数组”,而是选择“更平坦”的数据结构,因为这些答案已经详细讨论过了。显示实际文档的外观以及您希望收到的响应。如果您有不同的案例,则显示不同的文档和不同的预期响应。@NeilLunn绝对正确。问题更新提供的答案中是否有您认为无法解决您问题的内容?如果是,请对答案进行评论,以澄清哪些问题需要解决,哪些问题尚未解决。如果它确实回答了您提出的问题,那么请注意您提出的问题,谢谢您的回答。我请了几天假,因此我认为我必须使用聚合框架。我意识到,我可以通过假设具有更多实体值集的响应是首选的来简化它。使用reduce和group步骤,按计数排序并取第一个值可以使用ou-unwind responses and responses.match.nlu-匹配实体和实体值(这将返回所有匹配实体,带值或不带值)-项目->reduce:包含所有实体值的数组的新字段-排序和分组(带第一个)选择最佳响应。它最终是可读的。但是我想知道在开始时解卷对性能的影响。非常感谢您详细的回答和建议recommendations@znat$unwind
的性能通常很差,在分片集群上基本上会变得更差。这里的基本概念不是您实际要求的需要在数组内容的“内部”或跨“多个文档”进行“分组”的内容,而是一个“每个文档”的操作。在这种情况下,即使这个“看起来”比你想象的要复杂,但事实并非如此。无论如何,这里真正的教训“应该”是不要嵌套数组。我知道你“认为”这就是你处理关系结构的方式,但事实并非如此。我犯了同样的错误。我意识到嵌套数组的局限性。但在我们重构数据模型之前,我必须和他们相处一段时间
{
"_id" : "5CmYdmu2Aanva3ZAy",
"responses" : [
{
"match" : {
"nlu" : [
{
"entities" : [
{
"entity" : "entity1",
"value" : "value1"
}
],
"intent" : "intent1"
}
]
},
"key" : "utter_intent1_p3vE6O_XsT"
}
]
}