【问题标题】:Retrieve matched array element in Elastic Search query在 Elastic Search 查询中检索匹配的数组元素
【发布时间】:2014-08-13 20:10:18
【问题描述】:

在电影数据库中,我存储用户对每部电影的评分(0 到 5 星)。我在 Elastic Search(版本 1.2.2)中索引了以下文档结构

"_index": "my_index"
"_type": "film",
"_id": "6629",
"_source": {
  "id": "6629",
  "title": "Fight Club",
  "ratings" : [
    { "user_id" : 1234, "rating_value" : 3 },
    { "user_id" : 4567, "rating_value" : 2 },
    { "user_id" : 7890, "rating_value" : 1 }
    .....
  ]
}

"_index": "my_index"
"_type": "film",
"_id": "6630",
"_source": {
  "id": "6630",
  "title": "Pulp Fiction",
  "ratings" : [
    { "user_id" : 1234, "rating_value" : 1 },
    { "user_id" : 7654, "rating_value" : 2 },
    { "user_id" : 4321, "rating_value" : 5 }
    .....
  ]
}

等等……

我的目标是在一次搜索中获得用户(比如用户 1234)评分的所有电影,以及 rating_value

如果我进行以下搜索

GET my_index/film/_search
{
  "query": {
    "match": {
      "ratings.user_id": "1234"
    }
  }
}

对于所有匹配的电影,我得到整个文档,然后,我必须解析整个评级数组以找出数组中的哪个元素与我的查询匹配,以及与 user_id 1234 关联的 rating_value 是什么。

理想情况下,我希望这个查询的结果是

"hits": [ {
  "_index": "my_index"
  "_type": "film",
  "_id": "6629",
  "_source": {
    "id": "6629",
    "title": "Fight Club",
    "ratings" : [
      { "user_id" : 1234, "rating_value" : 3 }, // <= only the row that matches the query
    ]
  },
  "_index": "my_index"
  "_type": "film",
  "_id": "6630",
  "_source": {
    "id": "6630",
    "title": "Pulp Fiction",
    "ratings" : [
      { "user_id" : 1234, "rating_value" : 1 },  // <= only the row that matches the query
    ]
  }
} ]

提前致谢

【问题讨论】:

  • 您无法获得您想要的理想结果,因为 _source 将始终与您索引的 JSON 完全相同。但是,您可以使用聚合来获取所需的信息。

标签: elasticsearch


【解决方案1】:

如我之前的评论中所述,我设法使用 聚合 检索值。

以下是我是如何做到的。

首先,我使用的映射:

PUT test/movie/_mapping
{
  "properties": {
    "title":{
      "type": "string",
      "index": "not_analyzed"
    },
    "ratings": {
      "type": "nested"
    }
  }
}

我选择不对标题编制索引,但您可以使用 fields 属性并将其保留为“原始”字段。

然后,电影索引:

PUT test/movie/6629
{
  "title": "Fight Club",
  "ratings" : [
    { "user_id" : 1234, "rating_value" : 3 },
    { "user_id" : 4567, "rating_value" : 2 },
    { "user_id" : 7890, "rating_value" : 1 }
  ]
}


PUT test/movie/4456
{
  "title": "Jumanji",
  "ratings" : [
    { "user_id" : 1234, "rating_value" : 4 },
    { "user_id" : 4567, "rating_value" : 3 },
    { "user_id" : 4630, "rating_value" : 5 }
  ]
}

PUT test/movie/6547
{
  "title": "Hook",
  "ratings" : [
    { "user_id" : 1234, "rating_value" : 4 },
    { "user_id" : 7890, "rating_value" : 1 }
  ]
}

聚合查询是:

GET test/movie/_search
{
  "aggs": {
    "by_movie": {
      "terms": {
        "field": "title"
      },
      "aggs": {
        "ratings_by_user": {
          "nested": {
            "path": "ratings"
          },"aggs": {
            "for_user_1234": {
              "filter": {
                "term": {
                  "ratings.user_id": "1234"
                }
              },
              "aggs": {
                "rating_value": {
                  "terms": {
                    "field": "ratings.rating_value"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

最后,这是针对之前的文档执行此查询时产生的输出:

"aggregations": {
  "by_movie": {
     "buckets": [
        {
           "key": "Fight Club",
           "doc_count": 1,
           "ratings_by_user": {
              "doc_count": 3,
              "for_user_1234": {
                 "doc_count": 1,
                 "rating_value": {
                    "buckets": [
                       {
                          "key": 3,
                          "key_as_string": "3",
                          "doc_count": 1
                       }
                    ]
                 }
              }
           }
        },
        {
           "key": "Hook",
           "doc_count": 1,
           "ratings_by_user": {
              "doc_count": 2,
              "for_user_1234": {
                 "doc_count": 1,
                 "rating_value": {
                    "buckets": [
                       {
                          "key": 4,
                          "key_as_string": "4",
                          "doc_count": 1
                       }
                    ]
                 }
              }
           }
        },
        {
           "key": "Jumanji",
           "doc_count": 1,
           "ratings_by_user": {
              "doc_count": 3,
              "for_user_1234": {
                 "doc_count": 1,
                 "rating_value": {
                    "buckets": [
                       {
                          "key": 4,
                          "key_as_string": "4",
                          "doc_count": 1
                       }
                    ]
                 }
              }
           }
        }
     ]
  }

}

由于嵌套语法,这有点乏味,但您将能够检索所提供用户(此处为 1234)对每部电影的评分。

希望这会有所帮助!

【讨论】:

  • 非常感谢您的回复。它工作正常,但我仍然面临 2 个问题: 1/ 我不能按电影 ID 而不是电影标题聚合。 2/ 我无法为获得的汇总结果设置偏移量(ES 中的“来自”)。目前我正在尝试解决这两个问题 - 但感谢您指出这条路
  • 1) 这是因为 id 没有被索引。您可以通过在映射中添加“id”字段并将其索引为“not_analyzed”来解决此问题。然后,更新“by_movie”聚合以与“id”字段相关。
  • 2) 根据documentation,似乎 from/size 参数应用于结果,而不是聚合。
【解决方案2】:

将评分存储为嵌套文档(或子文档),然后您将能够单独查询它们。

可以在这里找到嵌套文档和子文档之间区别的一个很好的解释:http://www.spacevatican.org/2012/6/3/fun-with-elasticsearch-s-children-and-nested-documents/

【讨论】:

  • 感谢您的回复。实际上,它们已经存储为嵌套文档。但是查询仍然返回整个“父”(即电影)文档
  • (或者您是否暗示我应该使用“父子”结构而不是嵌套文档?)
  • 啊,那么您可以进行嵌套查询,这将为您提供各个元素。唯一的问题是获取父属性(即电影名称)。从那个 POV 来看,父子结构更容易使用。
猜你喜欢
  • 2014-12-03
  • 1970-01-01
  • 2015-07-13
  • 1970-01-01
  • 2021-02-20
  • 2023-01-13
  • 1970-01-01
  • 2021-02-17
  • 1970-01-01
相关资源
最近更新 更多