使用NEST进行ElasticSearch的聚合查询

ElasticSearch聚合

Elasticsearch中的聚合是一种以结构化的方式提取和展示数据的机制。可以把它视为SQL中的GROUP BY语句,但是它更加强大和灵活。

聚合类型

Elasticsearch支持很多类型的聚合,包括:

  1. 指标聚合(Metrics agregations):主要用于最大值、最小值、平均值、字段之和等指标的统计。这类聚合基于文档字段的数值进行计算并返回一个单一的数值结果。例如最大值(max)、最小值(min)、平均值(average)、总和(sum)、统计信息(stats,包含了上述几种操作),以及其他复杂的聚合如百分数(percentiles)、基数(cardinality)等。
  2. 分桶聚合(Bucket agregations):类比SQL中的group by的作用,主要用于统计不同类型数据的数量。这类聚合会创建一组buckets,每个bucket对应一个特定的条件或范围,然后文档会根据这些条件或范围被分类到相应的bucket中。常见的包括区间(range)、日期区间(date range)、直方图(histogram)、日期直方图(date histogram)、地理哈希网格(geohash grid)等。
  3. 管道聚合(Pipeline agregations):用于对聚合的结果进行二次聚合,如要统计绑定数量最多的标签bucket,就是要先按照标签进行分桶,再在分桶的结果上计算最大值。这类聚合可以基于其他聚合的结果进行二次计算。比如计算差异、比例、移动平均等。

Elasticsearch的聚合操作支持嵌套,即一个聚合内部可以包含别的子聚合,从而实现非常复杂的数据挖掘和统计需求。

聚合查询语法

{
  "size" : 0,
  "aggregations" : {
    "自已命名的聚合名称1" : {
      "filter" : {
        "bool" : {
          "filter" : [
            {
              "term" : {
                "查询字段1" : {
                  "value" : 查询值1,
                  "boost" : 1.0
                }
              }
            }
          ]
        }
      },
      "aggregations" : {
        "自已命名的聚合名称2" : {
          "sum" : {
            "field" : "查询值2"
          }
        }
      }
    }
  }
} 

编写聚合

Nest 提供了 3 种方式来让你使用聚合:

  • 通过 lambda 表达式的方式。
  • 通过内建的请求对象 AggregationDictionary。
  • 通过结合二元运算符来简化 AggregationDictionary 的使用。

假设有以下 Project 类:

public class Project
{
    public string Name { get; set; }
    public int Quantity { get; set; }
}

三种方式的请求命令见下方:

POST /project/_search?typed_keys=true
{
    "aggs": { //关键字 aggregations,可以用 aggs 简写
        "average_quantity": { //聚合的名字
            "avg": {  //聚合的类型,可以理解为相当于 sql server 中的聚合函数
                "field": "quantity"  //聚合体,对哪些字段进行聚合
            }
        },
        "max_quantity": {
            "max": {
                "field": "quantity"
            }
        },
        "min_quantity": {
            "min": {
                "field": "quantity"
            }
        }
    }
}

lambda 方式

通过 lambda 表达式来使用聚合是简洁的方式

var searchResponse = _client.Search<Project>(s => s
    .Aggregations(aggs => aggs
        .Average("average_quantity", avg => avg.Field(p => p.Quantity))
        .Max("max_quantity", avg => avg.Field(p => p.Quantity))
        .Min("min_quantity", avg => avg.Field(p => p.Quantity))
    )
);

一般进行聚合查询的时候,并不需要 _source 的东西,所以你在进行聚合查询是,可以在查询语句上指定 size=0,这样就只会返回 聚合 的结果,方式如下:

var searchResponse = _client.Search<Project>(s => s
    .Size(0)  //显式指定为 0
    .Aggregations(aggs => aggs
        .Average("average_quantity", avg => avg.Field(p => p.Quantity))
        .Max("max_quantity", avg => avg.Field(p => p.Quantity))
        .Min("min_quantity", avg => avg.Field(p => p.Quantity))
    )
);

通过内建对象 AggregationDictionary

以下代码的效果和通过 lambda 表达式的效果一样

var searchRequest = new SearchRequest<Project>
{
    Size = 0,
    Aggregations = new AggregationDictionary
    {
        {"average_quantity", new AverageAggregation("average_quantity", "quantity")},
        {"max_quantity", new MaxAggregation("max_quantity", "quantity")},
        {"min_quantity", new MinAggregation("min_quantity", "quantity")},
    }
};
var searchResponse = _client.Search<Project>(searchRequest);

通过结合二元运算符来简化 AggregationDictionary 的使用

通过二元运算符,可以让代码的可读性更高,以下代码等效于上方:

var searchRequest = new SearchRequest<Project>
{
    Size = 0,
    Aggregations = new AverageAggregation("average_quantity", "quantity")
    &&new MaxAggregation("max_quantity", "quantity")
    &&new MinAggregation("min_quantity", "quantity")
};
var searchResponse = _client.Search<Project>(searchRequest);

查询聚合值实践

多个聚合条件查询

在C#中使用NEST进行聚合查询时,可以使用AggregationContainer类来添加多个聚合条件。

首先,你需要创建一个AggregationContainer对象,并添加你需要的聚合条件。例如,假设你要查询一个索引中商品的总销售量和平均价格,你可以这样编写代码:

var searchResponse = client.Search<Product>(s => s
    .Aggregations(a => a
        .Sum("total_sales", st => st
            .Field(f => f.Sales)
        )
        .Average("average_price", ap => ap
            .Field(f => f.Price)
        )
    )
);

在上面的代码中,我们创建了一个AggregationContainer对象,并使用SumAverage方法来添加两个聚合条件。Sum方法用来计算销售总量,Average方法用来计算价格平均值。同时,我们还指定了对应的字段,SalesPrice

最后,我们使用client.Search方法来执行查询,查询结果会包含聚合结果。

请注意,上面的示例中使用了一个名为Product的类来表示商品,你需要根据自己的数据结构来替换。同时,你需要根据实际的字段名来替换示例中的SalesPrice字段。

多个过滤条件的聚合查询

要使用NEST来编写C#代码生成上述的Elasticsearch聚合查询语句,你可以使用AggregationContainer类的Filters方法来添加多个过滤条件。

{"aggregations": {
		"group": {
			"filters": {
				"filters": {
					"pendingCount": {
						"match": {
							"dataBody.status": 0
						}
					},
					"storedingCount": {
						"match": {
							"dataBody.status": 1
						}
					},
					"finishedCount": {
						"match": {
							"dataBody.status": 2
						}
					},
					"closedCount": {
						"match": {
							"dataBody.status": 3
						}
					},
					"thisMonthCount": {
						"range": {
							"dataBody.creationTime": {
								"gte": "2023-11-03"
							}
						}
					}
				}
			}
		}
	}

下面是一个示例代码,展示如何使用NEST来生成上述的聚合查询语句:

var searchResponse = client.Search<Document>(s => s
    .Aggregations(a => a
        .Filters("group", f => f
            .NamedFilters(flt => flt
                .Filter("pendingCount", q => q
                    .Match(m => m
                        .Field("dataBody.status")
                        .Query("0")
                    )
                )
                .Filter("storedingCount", q => q
                    .Match(m => m
                        .Field("dataBody.status")
                        .Query("1")
                    )
                )
                .Filter("finishedCount", q => q
                    .Match(m => m
                        .Field("dataBody.status")
                        .Query("2")
                    )
                )
                .Filter("closedCount", q => q
                    .Match(m => m
                        .Field("dataBody.status")
                        .Query("3")
                    )
                )
                .Filter("thisMonthCount", q => q
                    .DateRange(dr => dr
                        .Field("dataBody.creationTime")
                        .GreaterThanOrEquals("2023-11-03")
                    )
                )
            )
        )
    )
);

在上面的代码中,我们使用Filters方法来创建一个AggregationContainer对象,并添加了多个过滤条件。每个过滤条件都是通过Filter方法添加的,使用MatchDateRange方法来指定字段和查询条件。

请注意,上面的示例中使用了一个名为Document的类来表示文档,你需要根据自己的数据结构来替换。同时,你需要根据实际的字段名来替换示例中的dataBody.statusdataBody.creationTime字段。

获取聚合结果中的值

要获取使用以上代码查询出来的聚合结果中的值,你可以使用AggregationContainer类的Aggregations属性来访问聚合结果。然后,可以使用聚合名称来获取具体的聚合结果。

以下是一个示例代码,展示如何获取聚合结果中的值并根据字段名称进行访问:

var groupAggregation = searchResponse.Aggregations.Filters("group");

var pendingCount = groupAggregation.Buckets["pendingCount"].DocCount;
var storingCount = groupAggregation.Buckets["storingCount"].DocCount;
var finishedCount = groupAggregation.Buckets["finishedCount"].DocCount;
var closedCount = groupAggregation.Buckets["closedCount"].DocCount;
var thisMonthCount = groupAggregation.Buckets["thisMonthCount"].DocCount;

// 或如下方式
var pendingCount = ((SingleBucketAggregate)groupAggregation["pendingCount"]).DocCount;
var storingCount = ((SingleBucketAggregate)groupAggregation["storedingCount"]).DocCount;
var finishedCount = ((SingleBucketAggregate)groupAggregation["finishedCount"]).DocCount;
var closedCount = ((SingleBucketAggregate)groupAggregation["closedCount"]).DocCount;
var thisMonthCount = ((SingleBucketAggregate)groupAggregation["thisMonthCount"]).DocCount;

在上面的代码中,我们首先使用Aggregations属性来获取聚合结果,然后使用Filters方法来获取名为"group"的聚合。接下来,我们可以通过访问聚合结果的Buckets属性来获取每个过滤条件的结果。使用聚合名称作为键,可以直接访问聚合结果的DocCount属性来获取匹配的文档数量。

注意,聚合结果的类型可能是不同的,具体取决于你在查询语句中使用的聚合类型。上面的示例假设聚合结果是一个FiltersAggregation类型,你可能需要根据实际的聚合类型来进行适当的类型转换。

您的问题是 "nest elastic search",我理解为您想了解 Nest.js 中如何使用 ElasticSearchNest.js 是一个基于 Node.js 的 Web 框架,它提供了一些便捷的工具和模块来帮助我们构建高效、可扩展的应用程序。而 ElasticSearch 是一个基于 Lucene 的搜索引擎,它提供了强大的全文搜索和分析功能。 在 Nest.js 中使用 ElasticSearch,可以使用官方提供的 Elasticsearch.js 客户端库,也可以使用第三方库 nest-elasticsearch使用 Elasticsearch.js 客户端库,可以直接在 Nest.js 中调用 ElasticSearch 的 REST API,进行数据的增删改查等操作。而使用 nest-elasticsearch,则可以更加方便地集成 ElasticSearchNest.js 应用中,提供更加友好的 API 接口。 以下是使用 nest-elasticsearch 的示例代码: 首先安装依赖: ``` npm install --save @nestjs/elasticsearch elasticsearch ``` 然后在模块中引入 ElasticsearchModule,并配置连接信息: ```typescript import { Module } from '@nestjs/common'; import { ElasticsearchModule } from '@nestjs/elasticsearch'; @Module({ imports: [ ElasticsearchModule.register({ node: 'http://localhost:9200', }), ], }) export class AppModule {} ``` 接着在服务中注入 ElasticsearchService,并使用进行数据操作: ```typescript import { Injectable } from '@nestjs/common'; import { ElasticsearchService } from '@nestjs/elasticsearch'; @Injectable() export class SearchService { constructor(private readonly elasticsearchService: ElasticsearchService) {} async search(query: string) { const { body } = await this.elasticsearchService.search({ index: 'my_index', body: { query: { match: { title: query, }, }, }, }); return body.hits.hits; } } ``` 以上代码中,我们定义了一个 SearchService,它注入了 ElasticsearchService,并提供了一个 search 方法,用于搜索数据。在 search 方法中,我们使用 ElasticsearchService 的 search 方法,传入查询条件,即可进行搜索操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值