使用$ elemMatch和$ or实现后备逻辑(投影中)

时间:2019-03-07 22:38:27

标签: mongodb mongodb-query aggregation-framework

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
          }
        }
      }
    }
  }
})

在给定的project中,我需要一个仅包含一个响应的投影,因此$elemMatch。理想情况下,寻找完全匹配的内容:

{
    "entities.entity": "entity1",
    "entities.value": "value1"
}

但是如果不存在这样的匹配项,请寻找entities.value不存在的记录

上面的查询不起作用,因为如果找到未设置entities.value的项目,它将返回它。如何在Mongo查询中获得此后备逻辑

这是文档的示例

{
    "_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"
      }
    ]
}

1 个答案:

答案 0 :(得分:1)

要回答这个问题,首先要做的是做您想做的事并不像$elemMatch投影那样简单,并且需要聚合框架的特殊投影逻辑。这里的第二个主要原则是“嵌套数组是一个真正的坏主意,这正是原因:

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", [] ] }
      }
    }
  }}
])

会返回:

{
  "_id" : "5CmYdmu2Aanva3ZAy",
  "responses" : [
    {
      "match" : {
        "nlu" : [
          {
            "entities" : [
              {
                      "entity" : "entity1",
                      "value" : "value1"
              }
            ],
            "intent" : "intent1"
          }
        ]
      },
      "key" : "utter_intent1_p3vE6O_XsT"
    }
  ]
}

这是从{嵌套1的嵌套内部数组提取 first 匹配元素(以尽我所能确定的规格)。 }},同时满足entitiesentity的条件 OR ,其中value属性不存在。

请注意其他回退,如果 both 条件都意味着返回多个数组元素,则只有出现value且匹配的第一个匹配项结果返回。

查询深度嵌套的数组需要$map$filter的链式使用,以便遍历那些数组内容并仅返回符合条件的项目。您无法在$elemMatch投影中指定这些条件,甚至在最近的MongoDB版本中甚至无法以原子方式更新这样的结构,而不会覆盖文档的重要部分或引入更新问题的情况下,并发。

对此的更详细解释是在我对Updating a Nested Array with MongoDB的现有答案中以及在查询方面的Find in Double Nested Array MongoDB上。

请注意,两个响应均显示$elemMatch作为“查询”运算符的用法,这实际上仅与“文档选择” 有关(因此不适用于{{1} }匹配条件),不能与以前的“ projection”变体或positional $投影运算符一起使用。

然后建议您“不嵌套数组” ,而选择“ flatter” 数据结构,因为这些答案已经详细讨论了。