ElasticSearch返回信息过滤API的灵活运用
ElasticSearch 返回信息过滤 API 的基础认知
在 ElasticSearch 中,返回信息过滤 API 是一项极为关键的功能,它允许用户精准地控制从 ElasticSearch 集群检索到的信息。通过灵活运用这些 API,开发者能够极大地提升数据获取的效率,减少不必要的数据传输,进而优化应用程序的性能。
源字段过滤(Source Filtering)
源字段过滤是 ElasticSearch 返回信息过滤中最为基础的部分。默认情况下,ElasticSearch 在返回搜索结果时会包含文档的完整 _source
字段内容。然而,在许多实际场景中,我们可能只需要其中的部分字段。例如,假设我们有一个存储博客文章的索引,文档结构如下:
{
"title": "这是一篇精彩的博客文章",
"author": "张三",
"content": "这里是文章的详细内容,非常长……",
"publish_date": "2023 - 01 - 01",
"tags": ["技术", "生活"]
}
如果我们只是想获取文章的标题和作者,而不需要冗长的文章内容,就可以使用源字段过滤。在 ElasticSearch 的查询 DSL(Domain - Specific Language)中,通过 _source
参数来指定需要返回的字段。例如,使用 HTTP 请求的方式:
GET /blog_posts/_search
{
"_source": ["title", "author"],
"query": {
"match_all": {}
}
}
上述代码中,_source
数组内指定了我们希望返回的字段。如果只想排除某些字段,也可以使用否定语法:
GET /blog_posts/_search
{
"_source": {
"excludes": ["content"]
},
"query": {
"match_all": {}
}
}
在这个例子中,content
字段将不会出现在返回结果中。
脚本字段(Script Fields)
脚本字段允许我们基于文档中的现有字段,通过自定义脚本生成新的字段,并在返回结果中包含这些新字段。这在需要对数据进行一些计算或转换时非常有用。例如,我们有一个索引存储商品信息,其中包含商品价格 price
和折扣率 discount
字段,我们想在搜索结果中直接获取折扣后的价格。首先,确保 ElasticSearch 开启了脚本功能(在较新版本中默认开启,但某些安全配置下可能需要额外设置)。然后,使用如下查询:
GET /products/_search
{
"script_fields": {
"discounted_price": {
"script": {
"lang": "painless",
"source": "doc['price'].value * (1 - doc['discount'].value)"
}
}
},
"query": {
"match_all": {}
}
}
上述代码中,script_fields
定义了一个新的字段 discounted_price
。使用 painless
脚本语言,通过源文档中的 price
和 discount
字段计算出折扣后的价格。脚本字段生成的结果会出现在返回结果的 fields
部分,而不会影响 _source
字段内容。
深度过滤:聚合结果的过滤
在 ElasticSearch 中,聚合(Aggregations)是一项强大的数据分析功能,它允许我们对搜索结果进行分组、统计等操作。同时,我们也可以对聚合结果进行过滤,以获取更有针对性的数据。
桶选择器(Bucket Selector)
桶选择器是一种在聚合桶层面进行过滤的工具。假设我们对博客文章按标签进行聚合,并且只想获取包含文章数量大于 10 的标签桶。我们可以这样实现:
GET /blog_posts/_search
{
"size": 0,
"aggs": {
"by_tags": {
"terms": {
"field": "tags"
},
"aggs": {
"filter_buckets": {
"bucket_selector": {
"buckets_path": {
"count": "_count"
},
"script": "params.count > 10"
}
}
}
}
}
}
在这个例子中,首先通过 terms
聚合按 tags
字段对文档进行分组。然后,使用 bucket_selector
对生成的桶进行过滤。buckets_path
定义了要引用的桶指标,这里 _count
表示每个标签桶中的文档数量。script
则定义了过滤条件,只有满足 count > 10
的桶才会出现在最终的聚合结果中。
管道聚合过滤(Pipeline Aggregation Filtering)
管道聚合允许我们基于其他聚合的结果进行进一步的聚合操作。同样也可以对管道聚合的结果进行过滤。例如,我们先按月份对销售数据进行聚合,然后计算每个月的销售总额占全年销售总额的比例,并且只保留占比大于 5% 的月份。
GET /sales/_search
{
"size": 0,
"aggs": {
"sales_by_month": {
"date_histogram": {
"field": "sale_date",
"calendar_interval": "month"
},
"aggs": {
"total_sales_per_month": {
"sum": {
"field": "amount"
}
}
}
},
"total_sales": {
"sum": {
"field": "amount"
}
},
"percentage_of_total": {
"bucket_script": {
"buckets_path": {
"monthly_total": "sales_by_month>total_sales_per_month"
},
"script": {
"source": "params.monthly_total / params._agg.total_sales.value * 100",
"lang": "painless"
}
},
"aggs": {
"filter_percentage": {
"bucket_selector": {
"buckets_path": {
"percentage": "_value"
},
"script": "params.percentage > 5"
}
}
}
}
}
}
在这段代码中,首先通过 date_histogram
按月份对销售数据进行聚合,并计算每个月的销售总额。然后,通过 sum
聚合计算全年销售总额。接着,使用 bucket_script
计算每个月销售总额占全年销售总额的百分比。最后,使用 bucket_selector
对百分比结果进行过滤,只保留占比大于 5% 的月份。
多层嵌套文档的返回信息过滤
当文档结构较为复杂,存在多层嵌套关系时,返回信息过滤需要更多的技巧和特定的语法。
嵌套字段过滤(Nested Field Filtering)
假设我们有一个索引存储电商订单信息,每个订单可能包含多个商品,商品信息以嵌套文档的形式存储。文档结构如下:
{
"order_id": "12345",
"customer": "李四",
"order_date": "2023 - 02 - 15",
"products": [
{
"product_name": "手机",
"price": 5000,
"quantity": 1
},
{
"product_name": "耳机",
"price": 500,
"quantity": 2
}
]
}
如果我们只想获取订单中包含价格大于 1000 的商品信息,并且在返回结果中体现。可以使用如下查询:
GET /orders/_search
{
"query": {
"nested": {
"path": "products",
"query": {
"range": {
"products.price": {
"gt": 1000
}
}
}
}
},
"nested": [
{
"path": "products",
"inner_hits": {
"name": "matching_products",
"_source": ["product_name", "price"]
}
}
]
}
在这个例子中,nested
查询用于匹配包含价格大于 1000 的商品的订单。inner_hits
则用于在返回结果中展示匹配的嵌套文档(商品信息),并且通过 _source
过滤只返回 product_name
和 price
字段。
父子文档过滤(Parent - Child Document Filtering)
父子文档关系在 ElasticSearch 中也是常见的结构。例如,我们有一个博客文章索引,文章和评论是父子关系。假设文章文档如下:
{
"title": "技术分享",
"author": "王五",
"content": "……",
"_id": "article1"
}
评论文档如下:
{
"comment": "写得真好",
"author": "赵六",
"_parent": "article1"
}
如果我们想获取某篇文章及其点赞数大于 10 的评论。可以这样查询:
GET /blog/_search
{
"query": {
"has_child": {
"type": "comment",
"query": {
"range": {
"likes": {
"gt": 10
}
}
}
}
},
"inner_hits": {
"name": "matching_comments",
"type": "comment",
"_source": ["comment", "author", "likes"]
}
}
这里 has_child
查询用于匹配包含点赞数大于 10 的评论的文章。inner_hits
用于在返回结果中展示匹配的评论,并通过 _source
过滤评论的相关字段。
基于地理空间数据的返回信息过滤
随着地理空间应用的增多,ElasticSearch 对地理空间数据的支持也越来越完善。在处理地理空间数据时,也可以对返回信息进行过滤。
地理距离过滤(Geo - Distance Filtering)
假设我们有一个索引存储店铺信息,每个店铺包含地理位置信息(经纬度)。我们想获取距离某个坐标点(例如北京天安门)10 公里以内的店铺,并且只返回店铺名称和地址。可以使用如下查询:
GET /shops/_search
{
"query": {
"geo_distance": {
"distance": "10km",
"location": {
"lat": 39.9042,
"lon": 116.4074
}
}
},
"_source": ["shop_name", "address"]
}
在这个例子中,geo_distance
查询用于筛选距离指定坐标点 10 公里以内的店铺。_source
则用于过滤返回的字段,只保留店铺名称和地址。
地理边界框过滤(Geo - Bounding Box Filtering)
如果我们想获取位于某个矩形区域内的店铺,可以使用地理边界框过滤。例如,获取位于某个城市特定区域内的店铺:
GET /shops/_search
{
"query": {
"geo_bounding_box": {
"location": {
"top_left": {
"lat": 39.95,
"lon": 116.35
},
"bottom_right": {
"lat": 39.85,
"lon": 116.45
}
}
}
},
"_source": ["shop_name", "rating"]
}
这里 geo_bounding_box
查询通过指定矩形区域的左上角和右下角坐标,筛选出位于该区域内的店铺。_source
过滤只返回店铺名称和评分。
高级过滤技巧:结合多种过滤方式
在实际应用中,往往需要结合多种过滤方式来满足复杂的业务需求。
源字段过滤与聚合过滤结合
假设我们有一个新闻文章索引,文章包含标题、内容、发布时间、分类等字段。我们想获取科技类文章中,按年份聚合后发布数量大于 100 的年份,并且在返回结果中只显示年份和文章标题。可以这样实现:
GET /news/_search
{
"size": 0,
"_source": ["title"],
"query": {
"match": {
"category": "科技"
}
},
"aggs": {
"by_year": {
"date_histogram": {
"field": "publish_date",
"calendar_interval": "year"
},
"aggs": {
"filter_buckets": {
"bucket_selector": {
"buckets_path": {
"count": "_count"
},
"script": "params.count > 100"
}
}
}
}
}
}
在这个例子中,首先通过 match
查询筛选出科技类文章,并通过 _source
过滤只保留标题字段。然后,对筛选后的文章按年份进行聚合,并使用 bucket_selector
对聚合结果进行过滤,只保留文章数量大于 100 的年份。
嵌套文档过滤与地理空间过滤结合
假设我们有一个索引存储旅游景点信息,每个景点可能包含多个推荐路线,路线信息以嵌套文档形式存储,并且景点包含地理位置信息。我们想获取位于某个区域内,且推荐路线中距离小于 5 公里的景点及其路线信息。可以使用如下查询:
GET /tourist_spots/_search
{
"query": {
"bool": {
"must": [
{
"geo_bounding_box": {
"location": {
"top_left": {
"lat": 30.5,
"lon": 104.0
},
"bottom_right": {
"lat": 30.4,
"lon": 104.1
}
}
}
},
{
"nested": {
"path": "routes",
"query": {
"range": {
"routes.distance": {
"lt": 5
}
}
}
}
}
]
}
},
"nested": [
{
"path": "routes",
"inner_hits": {
"name": "matching_routes",
"_source": ["route_name", "distance"]
}
}
]
}
在这个例子中,通过 bool
查询结合地理边界框过滤和嵌套文档过滤。先通过地理边界框筛选出位于指定区域内的景点,再通过嵌套文档过滤筛选出推荐路线中距离小于 5 公里的景点。inner_hits
用于在返回结果中展示匹配的路线信息,并通过 _source
过滤只返回路线名称和距离。
通过灵活运用上述各种 ElasticSearch 返回信息过滤 API,开发者能够根据具体的业务场景,精准地获取所需的数据,提升应用程序的性能和用户体验。无论是简单的源字段过滤,还是复杂的多层嵌套、地理空间等多种过滤方式的结合,都为数据的高效利用提供了强大的支持。在实际开发中,需要根据数据结构和业务需求,不断探索和优化过滤策略,以达到最佳的效果。同时,也要注意在使用脚本等功能时的安全性和性能问题,合理配置 ElasticSearch 集群,确保系统的稳定运行。例如,在使用脚本时,要避免复杂度过高的脚本,防止性能瓶颈;在处理大量数据时,要合理设置分页和批量处理参数,避免内存溢出等问题。只有综合考虑各方面因素,才能充分发挥 ElasticSearch 返回信息过滤 API 的优势。