玩转ElasticSearch - 指南

news/2025/9/19 11:26:59/文章来源:https://www.cnblogs.com/yfceshi/p/19100430

目录

一.初识ElasticSearch

1.认识和安装

2. Kibana的安装与界面

3.倒排索引

4.IK分词器

5.基础概念

二. 索引库操作

1. Mapping映射属性

2. 索引库操作

三. 文档操作

1. 文档CRUD

2.批量处理

四.RestAPI

1. 客户端初始化

2.索引库操作

3.文档操作 

五.DSL查询

1.快速入门

2.叶子查询

(1)全文检索

(2)精确查询

3.复合查询

4.排序和分页

5.高亮显示

六. RestClient查询

1. 快速入门

2.构建查询条件

3.数据聚合

(1)DSL聚合

(2)RestClient聚合


一.初识ElasticSearch

1.认识和安装

elasticsearch结合kibana、Logstash、Beats,是一整套技术栈,被叫做ELK。被广泛应用在日志数据分析、实时监控等领域。

Lucene是一个Java语言的搜索引擎类库,是Apache公司的顶级项目,由DougCutting于1999年研发 。官网地址:https://lucene.apache.org/

Lucene的优势:

  •    易扩展
  •    高性能(基于倒排索引)

Lucene的缺点:

  •    只限于Java语言开发
  •    学习曲线陡峭
  •    不支持水平扩展

2004年Shay Banon基于Lucene开发了Compass

2010年Shay Banon 重写了Compass,取名为Elasticsearch。

官网地址: https://www.elastic.co/cn/

相比与lucene,elasticsearch具备下列优势:

  • 支持分布式,可水平扩展
  • 提供Restful接口,可被任何语言调用

安装:

下载地址 : Download Elasticsearch | Elastic

下载后解压后点击 \bin\elasticsearch.bat

测试是否安装成功,浏览器输入 https://localhost:9200/

登录名是elastic,登录密码,第一次启动时控制台是会打印密码的

此时我的密码是:yb+QdTjfl*eUY39pcQUY

启动成功之后,浏览器显示:

2. Kibana的安装与界面

Kibana 是 ES的可视化工具

下载地址: Download Kibana Free | Get Started Now | Elastic

下载下来,解压,解压后找到bin目录下的 kibana.bat,点击执行(执行前一定要启动es)

出现地址表示启动成功

访问地址 :http://localhost:5601/, 提示需要密钥

到es的bin目录下执行执行指令:elasticsearch-create-enrollment-token.bat --scope kibana

此时我的密码是:

eyJ2ZXIiOiI4LjE0LjAiLCJhZHIiOlsiMTkyLjE2OC4xLjE4Nzo5MjAwIl0sImZnciI6IjllNGY1OTE5OTBjMzU2NTQ2Mzc2NDA4MWQwZTMzMjlhYTYyMDc2YTJiNTA1OWEyNDc1YzRhYjAzMTQ2YzUxOWQiLCJrZXkiOiI4V2FLSkprQnpPcHhVOEF4WW1mMTpMZ1VxQzFOazVDdkVTVFotOHJDVlBnIn0=

复制生成的密钥到浏览器, 点击配置密钥。之后跳出验证码确认

把指令 kibana.bat的控制台显示出来的复制到浏览器

接下来输入es的账户及密码:

成功进入:

3.倒排索引

倒排索引:记录词条在哪个文档出现几次及在文档中的位置

什么是文档和词条?

  •  每一条数据就是一个文档
  •  对文档中的内容分词,得到的词语就是词条

什么是正向索引?

  • 基于文档id创建索引。根据id查询快,但是查询词条时必须先找到文档,而后判断是否包含词条

什么是倒排索引? 

  • 对文档内容分词,对词条创建索引,并记录词条所在文档的id。 查询时先根据词条查询到文档id,而后根据文档id查询文档

4.IK分词器

分词器的作用是什么?

  •  创建倒排索引时,对文档分词
  •  用户搜索时,对输入的内容分词

默认分词器:标准分词器,对中文会一个个字切分,没有中文语义,形成不了有效的词条

例如:

语法说明:

     POST:请求方式

     /_analyze:请求路径,这里省略了http://192.168.12.168:9200,有kibana帮我们补充

     请求参数,json风格:  analyzer:分词器类型,这里是默认的standard分词器

                                          text:要分词的内容

中文分词器(ik分词器):

  •  ik_smart(最大粒度:按照最长的词条进行分词;更适合搜索的时候)
  •  ik_max_word:(最细粒度:按照最短的词条进行分词,分的词条会比较多;更适合在创建索引的时候使用)

官网下载:https://release.infinilabs.com/analysis-ik/stable/

注意:IK分词器插件的版本要和ElasticSearch的版本一致

把解压后的文件放在ES 的所在目录下的的plugins文件里然后重启ES

测试:

自定义词库:

找到ik 中的 config 文件夹的IKAnalyzer.cfg.xml文件并打开

在config文件创建ext.dic文件并添加词

重启ES并测试:

5.基础概念

二. 索引库操作

1. Mapping映射属性

2. 索引库操作

语法:

查看索引库语法: GET /索引库名
示例: GET /heima
删除索引库的语法: DELETE /索引库名
示例: DELETE /heima
索引库和mapping一旦创建无法修改,但是可以添加新的字段,语法如下:
PUT /索引库名/_mapping
{
"properties": {
"新字段名":{
"type": "integer"
}
}
}
示例:
PUT /heima/_mapping
{
"properties": {
"age":{
"type": "integer"
}
}
}

三. 文档操作

1. 文档CRUD

查看文档语法: GET /索引库名/_doc/文档id
示例:GET /heima/_doc/1
删除索引库的语法:DELETE /索引库名/_doc/文档id
示例:DELETE /heima/_doc/1

修改:

2.批量处理

四.RestAPI

ES官方提供了各种不同语言的客户端,用来操作ES。这些客户端的本质就是组装DSL语句,通过http请求发送给ES。

我们选择使用早期的JavaRestClient客户端来学习

官方文档地址:

https://www.elastic.co/guide/en/elasticsearch/client/index.html

1. 客户端初始化

添加依赖:

org.elasticsearch.client
elasticsearch-rest-high-level-client
7.17.29

初始化RestHighLevelClient:

public class IndexTest {
//es的操作客户端对象;执行任何与es有关的操作都需要
//说明接下来测试的每个方法都要先初始化该对象
private RestHighLevelClient client;
//执行每个方法之前先执行的方法
@BeforeEach
public void init() {
client = new RestHighLevelClient(
RestClient.builder(HttpHost.create("http://192.168.12.168:9200")));
}
//测试cLient是否有效
@Test
public void testGetConnection() {
System.out.println(client);
}
//每次执行完之后要关闭client客户端的连接
@AfterEach
public void close() throws Exception {
client.close();
}
}

运行测试类:

2.索引库操作

创建索引库:

//索引库名称
private static final String INDEX_NAME = "items";
//索引库映射结构
private  static final String MAPPING_TEMPLATE="{\n" +
"  \"mappings\": {\n" +
"    \"properties\": {\n" +
"      \"id\":{\n" +
"        \"type\": \"keyword\"\n" +
"      },\n" +
"      \"name\":{\n" +
"        \"type\": \"text\",\n" +
"        \"analyzer\": \"ik_max_word\"\n" +
"      },\n" +
"      \"price\":{ \n" +
"        \"type\": \"integer\"\n" +
"      },\n" +
"      \"stock\":{\n" +
"        \"type\": \"integer\"\n" +
"      },\n" +
"      \"image\":{\n" +
"        \"type\":\"keyword\",\n" +
"        \"index\": false\n" +
"      },\n" +
"      \"category\":{\n" +
"        \"type\": \"keyword\"\n" +
"      },\n" +
"      \"brand\":{\n" +
"        \"type\": \"keyword\"\n" +
"      },\n" +
"      \"sold\":{\n" +
"        \"type\": \"integer\"\n" +
"      },\n" +
"      \"commentCount\":{\n" +
"        \"type\": \"integer\"\n" +
"      },\n" +
"      \"isAD\":{\n" +
"        \"type\": \"boolean\"\n" +
"      },\n" +
"      \"updateTime\":{\n" +
"        \"type\": \"date\"\n" +
"      }\n" +
"    }\n" +
"  }\n" +
"}";
//创建索引库
@Test
public void createIndex() throws Exception {
//创建索引库对象;--->创建索引--->创建索引的请求
CreateIndexRequest request = new CreateIndexRequest(INDEX_NAME);
//设置请求参数
request.source(MAPPING_TEMPLATE, XContentType.JSON);
//发送请求
client.indices().create(request, RequestOptions.DEFAULT);
}

删除索引库:

//测试删除索引库
@Test
public void testDeleteIndex() throws Exception {
//1、创建删除索引库请求对象
DeleteIndexRequest request = new DeleteIndexRequest(INDEX_NAME);
//2、发送请求
client.indices().delete(request, RequestOptions.DEFAULT);
}

判断索引库是否存在:

//测试查询索引库items是否存在
@Test
public void testExistsIndex() throws Exception {
//1、创建查询索引库请求对象
GetIndexRequest request = new GetIndexRequest(INDEX_NAME);
//2、发送请求
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
//3、打印结果
System.out.println(exists ? "索引库已经存在" : "索引库不存在");
}

索引库操作的基本步骤:

  • 初始化RestHighLevelClient
  • 创建XxxIndexRequest。XXX是Create、Get、Delete
  • 准备请求参数(Create时需要)
  • 发送请求。调用RestHighLevelClient#indices().xxx()方法 ,xxx是create、exists、delete

3.文档操作 

文档操作的基本步骤:

  • 初始化RestHighLevelClient
  • 创建XxxRequest。XXX是Index、Get、Update、Delete
  • 准备参数(Index和Update时需要)
  • 发送请求。调用RestHighLevelClient#.xxx()方法,xxx是 index、get、update、delete
  • 解析结果(Get时需要)

新增文档的JavaAPI如下:

新增商品:

public class DocumentTest {
//索引库名称
private static final String INDEX_NAME = "items";
//es的操作客户端对象;执行任何与es有关的操作都需要
//说明接下来测试的每个方法都要先初始化该对象
private RestHighLevelClient client;
//执行每个方法之前先执行的方法
@BeforeEach
public void init() {
client = new RestHighLevelClient(
RestClient.builder(HttpHost.create("http://192.168.12.168:9200")));
}
//每次执行完之后要关闭client客户端的连接
@AfterEach
public void close() throws Exception {
client.close();
}
//根据商品id查询mysql数据库中的商品,并将该商品保存es
@Test
public void testCreateIndex() throws IOException{
//1、根据商品id查询mysql数据库中的商品
Item item = itemService.getById(317578L);
//2、将Item对象转换为es可接受的对象ItemDoc
ItemDoc itemDoc = BeanUtils.copyBean(item, ItemDoc.class);
//3、创建文档的请求对象
IndexRequest request = new IndexRequest("items").id(itemDoc.getId().toString());
//4、设置请求参数
String jsonStr = JSONUtil.toJsonStr(itemDoc);
request.source(jsonStr, XContentType.JSON);
//3、发送请求
client.index(request, RequestOptions.DEFAULT);
}
}

查询:

查询文档包含查询和解析响应结果两部分。对应的JavaAPI如下:

修改:

删除:

4.批量操作

批处理代码流程与之前类似,只不过构建请求会用到一个名为BulkRequest的类

批处理的API示例:

由于商品数量达到数十万,因此不可能一次性全部导入。建议采用循环遍历方式,每次导入1000条左右的数据。

@Test
public void importDocuments() throws IOException {
//页号
int pageNo = 1;
//页大小
int pageSize = 1000;
while (true) {
//1、根据页号、页大小(1000);每次查询1000条数据
//分页查询状态为1的商品数据
Page page = itemService.lambdaQuery().eq(Item::getStatus, 1).page(new Page<>(pageNo, pageSize));
List itemList = page.getRecords();
if (CollUtils.isEmpty(itemList)) {
break;
}
//创建批量请求
BulkRequest bulkRequest = new BulkRequest();
for (Item item : itemList) {
//2.将每条数据转换为ItemDoc:并设置到IndexRequest
ItemDoc itemEsDTO = BeanUtils.copyBean(item, ItemDoc.class);
//将ItemDoc转换为json字符串
String jsonStr = JSONUtil.toJsonStr(itemEsDTO);
//创建request
IndexRequest request = new IndexRequest("items").id(itemEsDTO.getId().toString());
//设置请求参数
request.source(jsonStr, XContentType.JSON);
//添加到批量请求中
bulkRequest.add(request);
}
//3、发送批量请求,提交BuLkRequest
client.bulk(bulkRequest, RequestOptions.DEFAULT);
//4、继续分页查询;直到数据没有为止
pageNo++;
}
}

五.DSL查询

Elasticsearch的查询可以分为两大类:

  • 叶子查询(Leaf query clauses):一般是在特定的字段里查询特定值,属于简单查询,很少单独使用。
  • 复合查询(Compound query clauses):以逻辑方式组合多个叶子查询或者更改叶子查询的行为方式。

在查询以后,还可以对查询的结果做处理,包括:

  • 排序:按照1个或多个字段值做排序
  • 分页:根据from和size做分页,类似MySQL
  • 高亮:对搜索结果中的关键字添加特殊样式,使其更加醒目
  • 聚合:对搜索结果做数据统计以形成报表

1.快速入门

基于DSL的查询语法如下:

GET /indexName/_search
{
"query": {
"查询类型": {
"查询条件": "条件值"
}
}
}
// 查询所有
GET /indexName/_search
{
"query": {
"match_all": {}
}
}

2.叶子查询

叶子查询还可以进一步细分,常见的有:

  • 全文检索(full text)查询:利用分词器对用户输入内容分词,然后去倒排索引库中匹配。例如:    match_querymulti_match_query
  • 精确查询:不对用户输入内容分词,直接精确匹配,一般是查找keyword、数值、日期、布尔等类型。例如: ids    range    term

(1)全文检索

match查询:全文检索查询的一种,会对用户输入内容分词,然后去倒排索引库检索,语法:

GET /indexName/_search
{
"query": {
"match": {
"FIELD": "TEXT"
}
}
}
#单个字符中分词查询
GET /items/_search
{
"query": {
"match": {
"name": "脱脂牛奶"
}
}
}

multi_match:与match查询类似,只不过允许同时查询多个字段,语法:

GET /indexName/_search
{
"query": {
"multi_match": {
"query": "TEXT",
"fields": ["FIELD1", " FIELD12"]
}
}
}
#多个字段中分词搜索
GET /indexName/_search
{
"query": {
"multi_match": {
"query": "手机",
"fields": ["name", "category"]
}
}
}

match和multi_match的区别是什么?

  • match:根据一个字段查询
  • multi_match:根据多个字段查询,参与查询字段越多,查询性能越差

(2)精确查询

精确查询,英文是Term-level query,顾名思义,词条级别的查询。也就是说不会对用户输入的搜索条件再分词,而是作为一个词条,与搜索的字段内容精确值匹配。

因此推荐查找keyword、数值、日期、boolean类型的字段。例如id、price、城市、地名、人名等作为一个整体才有含义的字段。

词条查询:

//term查询
GET /indexName/_search
{
"query": {
"term": {
"FIELD": {
"value": "VALUE"
}
}
}
}
例如:
GET /items/_search
{
"query": {
"term": {
"brand": {
"value": "锤子"
}
}
}
}

范围查询:

// range查询
GET /indexName/_search
{
"query": {
"range": {
"FIELD": {
"gte": 10,
"lte": 20
}
}
}
}
例如:
GET /items/_search
{
"query": {
"range": {
"price": {
"gte": 10000,
"lte": 20000
}
}
}
}

精确查询常见的有哪些?

  • term查询:根据词条精确匹配,一般搜索keyword类型、数 值类型、布尔类型、日期类型字段
  • range查询:根据数值范围查询,可以是数值、日期的范围

3.复合查询

复合查询大致可以分为两类:

  • 第一类:基于逻辑运算组合叶子查询,实现组合条件,例如  bool
  • 第二类:基于某种算法修改查询时的文档相关性算分,从而改变文档排名。例如:  function_score     dis_max

布尔查询是一个或多个查询子句的组合。子查询的组合方式有:

  • must:必须匹配每个子查询,类似“与”
  • should:选择性匹配子查询,类似“或”
  • must_not:必须不匹配,不参与算分,类似“非”
  • filter:必须匹配,不参与算分

 例如:我们要搜索"智能手机",但品牌必须是华为,价格必须是900~1599

GET /items/_search
{
"query": {
"bool": {
"must": [
{"match": {"name": "手机"}}
],
"filter": [
{"term": {"brand": { "value": "华为" }}},
{"range": {"price": {"gte": 90000, "lt": 159900}}}
]
}
}
}

4.排序和分页

排序:

elasticsearch支持对搜索结果排序 ,默认是根据相关度算分(_score)来排序,也可以指定字段排序。可以排序字段类 型有:keyword类型、数值类型、地理坐标类型、日期类型等。

语法:

GET /indexName/_search
{
"query": {
"match_all": {}
},
"sort": [
{"排序字段": {"order": "排序方式asc和desc"}}
]
}

例如:

搜索商品,按照销量排序,销量一样则按照价格升序

GET /indexName/_search
{
"query": {
"match_all": {}
},
"sort": [
{"sold":{"order": "desc"}},
{"price":{"order":"asc"}}
]
}

分页:

elasticsearch 默认情况下只返回top10的数据。而如果要查询更多数据就需要修改分页参数了。elasticsearch中通过修改from、size参数来控制要返回的分页结果:

  • from:从第几个文档开始
  • size:总共查询几个文档

语法:

GET /items/_search
{
"query": {
"match_all": {}
},
"from": 0, // 分页开始的位置,默认为0
"size": 10, // 期望获取的文档总数
"sort": [
{"price": "asc"}
]
}

例如:搜索商品,查询出销量排名前10的商品,销量一样时按照价格升序

GET /indexName/_search
{
"query": {
"match_all": {}
},
"from": 0, // 分页开始的位置,默认为0
"size": 10, // 期望获取的文档总数
"sort": [
{"sold":{"order": "desc"}},
{"price":{"order":"asc"}}
]
}

深度分页问题:

elasticsearch的数据一般会采用分片存储,也就是把一个索引中的数据分成N份,存储到不同节点上。这种存储方式比较有利于数据扩展,但给分页带来了一些麻烦。

比如一个索引库中有100000条数据,分别存储到4个分片,每个分片25000条数据。现在每页查询10条,查询第99页。那么分页查询的条件如下:

GET /items/_search
{
"from": 990, // 从第990条开始查询
"size": 10, // 每页查询10条
"sort": [
{
"price": "asc"
}
]
}

从语句来分析,要查询第990~1000名的数据。

从实现思路来分析,肯定是将所有数据排序,找出前1000名,截取其中的990~1000的部分。但问题来了,我们如何才能找到所有数据中的前1000名呢?

要知道每一片的数据都不一样,第1片上的第900~1000,在另1个节点上并不一定依然是900~1000名。所以我们只能在每一个分片上都找出排名前1000的数据,然后汇总到一起,重新排序,才能找出整个索引库中真正的前1000名,此时截取990~1000的数据即可。

如图:

试想一下,假如我们现在要查询的是第999页数据呢,是不是要找第9990~10000的数据,那岂不是需要把每个分片中的前10000名数据都查询出来,汇总在一起,在内存中排序?如果查询的分页深度更深呢,需要一次检索的数据岂不是更多?

由此可知,当查询分页深度较大时,汇总数据过多,对内存和CPU会产生非常大的压力。

因此elasticsearch会禁止from+ size超过10000的请求。

针对深度分页,elasticsearch提供了两种解决方案:

  • search after:分页时需要排序,原理是从上一次的排序值开始,查询下一页数据。(意思是要记录上一次查询的最后一条记录的排序值,然后携带到下一次查询。)官方推荐使用的方式。

  • scroll:原理将排序后的文档id形成快照,保存下来,基于快照做分页。官方已经不推荐使用

​​​​​详情见文档:

https://www.elastic.co/guide/en/elasticsearch/reference/7.12/paginate-search-results.html

总结:大多数情况下,我们采用普通分页就可以了。查看百度、京东等网站,会发现其分页都有限制。例如百度最多支持77页,每页不足20条。京东最多100页,每页最多60条。

因此,一般我们采用限制分页深度的方式即可,无需实现深度分页

5.高亮显示

高亮显示就是在搜索结果中把搜索关键字突出显示

语法:

GET /{索引库名}/_search
{
"query": {
"match": {
"搜索字段": "搜索关键字"
}
},
"highlight": {
"fields": {  //指定要高亮的字段
"高亮字段名称": {
"pre_tags": "",  //高亮的前置标签
"post_tags": "" //高亮的前置标签
}
}
}
}

注意

  • 搜索必须有查询条件,而且是全文检索类型的查询条件,例如match

  • 参与高亮的字段必须是text类型的字段

  • 默认情况下参与高亮的字段要与搜索字段一致,除非添加:required_field_match=false

例如:

GET /items/_search
{
"query": {
"match": {
"name": "手机"
}
},
"highlight": {
"fields": {  //指定要高亮的字段
"name": {
"pre_tags": "",  //高亮的前置标签
"post_tags": "" //高亮的前置标签
}
}
}
}

六. RestClient查询

1. 快速入门

数据搜索的Java代码我们分为两部分: 构建请求参数和解析查询结果

@Test
public void testMatchAll() throws IOException {
//创建请求对象
SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
//设置请求参数
searchRequest.source().query(QueryBuilders.matchAllQuery());
//发送请求并获取结果
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
//解析响应结果
SearchHits searchHits = searchResponse.getHits();
//符合本次搜索的总文档 数(最多10000条)
long total = searchHits.getTotalHits().value;
System.out.println("本次搜索命中的总文档数:"+total);
SearchHit[] hits = searchHits.getHits();
if(hits!= null&&hits.length>0){
for (SearchHit hit:hits){
//获取原文的json字符串
String jsonStr = hit.getSourceAsString();
ItemDoc itemDoc = JSONUtil.toBean(jsonStr,ItemDoc.class);
System.out.println(itemDoc);
}
}
}

查询的基本步骤是:

1. 创建SearchRequest对象

2. 准备Request.source(),也就是DSL。

    ① QueryBuilders来构建查询条件

    ② 传入Request.source() 的query() 方法

3. 发送请求,得到结果

4. 解析结果(参考JSON结果,从外到内,逐层解析)

2.构建查询条件

全文检索的查询条件构造API如下:

// 单字段查询
QueryBuilders.matchQuery("name", "脱脂牛奶");
// 多字段查询
QueryBuilders.multiMatchQuery("脱脂牛奶", "name", "category");

精确查询的查询条件构造API如下:

// 词条查询
QueryBuilders.termQuery("category", "牛奶");
// 范围查询
QueryBuilders.rangeQuery("price").gte(100).lte(150);

布尔查询的查询条件构造API如下:

// 创建布尔查询
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 添加must条件
boolQuery.must(QueryBuilders.termQuery("brand", "华为"));
// 添加filter条件
boolQuery.filter(QueryBuilders.rangeQuery("price").lte(2500));

排序和分页:

ES中通过Restful请求操作索引库、文档。请求内容用DSL语句来表示。创建索引库和mapping的DSL语法如下:

// 查询
request.source().query(QueryBuilders.matchAllQuery());
// 分页
request.source().from(0).size(5);
// 价格排序
request.source().sort("price", SortOrder.ASC);

高亮显示:

request.source().highlighter(SearchSourceBuilder.highlight()
.field("name")
.preTags("")
.postTags(""));

代码如下:

//测试高亮
@Test
public void testHighlight() throws IOException {
//1、创建搜索请求
SearchRequest request = new SearchRequest(INDEX_NAME);
//2、设置查询参数
request.source().query(QueryBuilders.matchQuery("name", "华为"));
//设置高亮
request.source().highlighter(SearchSourceBuilder.highlight()
.field("name")
.preTags("")
.postTags("")
);
//3、发送请求获取响应结果
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//4、解析响应结果
handleResponse(response);
}
private static void handleResponse(SearchResponse response) {
System.out.println("共搜索到 " + response.getHits().getTotalHits().value + " 条数据");
//获取查询数组
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
//获取_source(原始json数据)
String jsonStr = hit.getSourceAsString();
ItemDoc itemDoc = JSONUtil.toBean(jsonStr, ItemDoc.class);
//解析高亮结果
Map highlightFields = hit.getHighlightFields();
if (CollUtils.isNotEmpty(highlightFields)) {
HighlightField highlightField = highlightFields.get("name");
if (highlightField != null) {
String highlightStr = highlightField.getFragments()[0].string();
itemDoc.setName(highlightStr);
}
}
System.out.println(itemDoc);
}
}

3.数据聚合

聚合(aggregations可以实现对文档数据的统计、分析、运算。聚合常见的有三类:

桶(Bucket)聚合:用来对文档做分组

       TermAggregation:按照文档字段值分组

       Date Histogram:按照日期阶梯分组,例如一周为一组,或者一月为一组

度量(Metric)聚合:用以计算一些值,比如:最大值、最小值、平均值等

       Avg:求平均值

Max:求最大值

Min:求最小值

       Stats:同时求maxminavgsum

管道(pipeline)聚合:其它聚合的结果为基础做聚合

注意:参与聚合的字段必须是Keyword、数值、日期、布尔的类型的字段

(1)DSL聚合

我们要统计所有商品中共有哪些商品分类,其实就是以分类(category)字段对数据分组。category值一样的放在同一组,属于Bucket聚合中的Term聚合。

GET /items/_search
{
"size": 0,  // 设置size为0,结果中不包含文档,只包含聚合结果
"aggs": { // 定义聚合
"cateAgg": { //给聚合起个名字
“terms”: { // 聚合的类型,按照品牌值聚合,所以选择term
"field": “category", // 参与聚合的字段
"size": 20 // 希望获取的聚合结果数量
}
}
}
}

默认情况下,Bucket聚合是对索引库的所有文档做聚合,我们可以限定要聚合的文档范围,只要添加query条件即可

GET /items/_search
{
"query": {
"bool": {
"filter": [
{ "term": {"category": "手机"}},
{ "range": { "price": { "gte": 300000}}}
]
}
},
"size": 0,
"aggs": {
"brand_agg": {
"terms": {
"field": "brand",
"size": 20
}
}
}
}

除了对数据分组(Bucket)以外,我们还可以对每个Bucket内的数据进一步做数据计算和统计。例如:我想知道手机有哪些品牌,每个品牌的价格最小值、最大值、平均值。

GET /items/_search
{
"query": {
"term": {"category": "手机"}
},
"size": 0,
"aggs": {
"brand_agg": {
"terms": {
"field": "brand"
},
"aggs": {
"stats_metric": {
"stats": {
"field": "price"
}
}
}
}
}
}

aggs代表聚合,与query同级,此时query的作用是?

     限定聚合的的文档范围

聚合必须的三要素:

     聚合名称
     聚合类型
     聚合字段

聚合可配置属性有:

     size:指定聚合结果数量
     field:指定聚合字段

(2)RestClient聚合

//聚合统计
@Test
public void testAggs() throws IOException {
SearchRequest searchRequest=new SearchRequest(INDEX_NAME);
//不返回文件
searchRequest.source().size(0);
//设置品牌的聚合统计
searchRequest.source().aggregation(
AggregationBuilders.terms("brand_agg").field("brand").size(20)
.subAggregation(
AggregationBuilders.stats(name:"stat_metric").field("price")
)
);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
//解析聚合统计结果
Aggregations aggregations = searchResponse.getAggregations();
Terms brandAgg = aggregations.get("brand_agg");
if(brandAgg!=null){
List buckets = brandAgg.getBuckets();
for (Terms.Bucket bucket : buckets){
System.out.println(bucket.getKeyAsString()+"."+bucket.getDocCount());
Aggregations statAgg=bucket.getAggregations();
Stats statMetric=statAgg.get("stat_metric");
System.out.println("平均值:"+statMetric.getAvg());
System.out.println("最大值:"+statMetric.getMax());
System.out.println("最小值:"+statMetric.getMin());
System.out.println("总和:"+statMetric.getSum());
}
}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/907724.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

详细介绍:终端里跑图形应用「GitHub 热点速览」

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

飞算JavaAI炫技赛:一天完成学生成绩综合统计分析系统研发(含源码)

飞算JavaAI炫技赛:一天完成学生成绩综合统计分析系统研发(含源码)2025-09-19 11:19 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !…

AI 赋能 APP 界面设计公司:从美学到交互的智能升级

AI 赋能 APP 界面设计公司:从美学到交互的智能升级在移动应用竞争日益激烈的今天,APP 界面设计公司不再只比拼视觉美感,而是必须在 效率、交互、个性化与智能化 上全面提升。随着人工智能(AI)技术的成熟,界面设计…

开源项目进度管理系统 PJMan:让技术项目进度可视化、数据化的利器

在软件项目管理过程中,进度不透明、任务卡点难定位、人员效率难量化是许多技术团队面临的痛点。今天为大家介绍一款开源项目进度管理系统 ——PJMan,其「项目概览」页面通过分层可视化与数据驱动的设计,将项目的 “…

【光照】[漫反射]UnityURP兰伯特能量守恒吗?

【从UnityURP开始探索游戏渲染】专栏-直达兰伯特漫反射的能量守恒性 ‌能量守恒基本原理‌ 在物理正确的渲染中,能量守恒要求:表面反射的光能总量 ≤ 入射光能 漫反射+高光反射 ≤ 1.0 没有能量凭空产生或消失‌经典…

Microsoft AI Genius 限时挑战赛:实战开启,等你应战!

通过 Microsoft AI Genius 系列 2.0 的实战专题课程,相信各位开发者对智能 GitHub Copilot 副驾驶 Agent Mode、Azure AI Foundry Agent Service(国际版)及 Copilot Studio 的理解与掌握达到了新高度。现在,是时候…

DevSecOps革命:测试工具如何重塑企业数字化转型的质量防线

DevSecOps革命:测试工具如何重塑企业数字化转型的质量防线 在数字化转型浪潮席卷全球的当下,软件质量保障体系正经历着前所未有的范式转变。DevSecOps作为这场变革的核心方法论,正在重新定义测试工具在企业技术栈中…

3.sysaux tablesace 清理

select min(snap_id),max(snap_id) from dba_hist_snapshot; 查完后,记录min和max的值 select dbid from v$database; 18701与18953分别为min与max的snap_id的值,387090299为dbid的值,将这些值代入下面的语句执行 b…

2.LOCK session

select * from v$session_blockers; 或者 select * from gv$session_blockers; (在rac情况下) 通过 select count(1) from v$locked_object; 可以查出内容 首先询问客户能否提供lock table的session sid和serial#,如…

php本地搭建知识库实现rag遇到的一些问题解决方式

1、向量化的问题,中文的话,使用尽量使用国内的嵌入模型,国外的虽然支持中文,但是还是比不上国内专门针对中文的优化 本地使用ollama 搭建的话 ,我使用的是 quentinz/bge-large-zh-v1.5:latest2、不规则的pdf文件…

2025 ~ 2026 游击 - gfoi

2025/09/19 试了试 CSP-S 的历年题目,2019 年有 84.5 分。

【初赛】第二类斯特林数意义 - Slayer

第二类斯特林数(斯特林子集数) \(\begin{Bmatrix}n\\ k\end{Bmatrix}\),也可记做 \(S(n,k)\),表示将 \(n\) 个两两不同的元素,划分为 \(k\) 个互不区分的非空子集的方案数。 通项公式 \(\begin{Bmatrix}n\\m\end{…

在AI技术快速实现功能的时代,挖掘新需求成为核心竞争力——某知名Android面试题库需求洞察

该篇文章无摘要a.内容描述 该项目是一个专注于Android开发领域的技术面试题库,核心功能定位为提供全面的Android面试问题与答案集合,帮助开发者准备技术面试。关键应用场景包括Android开发者求职准备、技术知识查漏补…

php本地搭建知识库实现rag遇到的各种问题解决方式

1、向量化的问题,中文的话,使用尽量使用国内的嵌入模型,国外的虽然支持中文,但是还是比不上国内专门针对中文的优化 本地使用ollama 搭建的话 ,我使用的是 quentinz/bge-large-zh-v1.5:latest2、不规则的pdf文件…

docker操作包括使用docker制作为接口

Docker 化 Flask OCR 应用指南 1. 创建必要的文件 在你的 Flask 应用项目根目录下,你需要创建以下文件: 1.1 Dockerfile 这是一个文本文件,包含了构建 Docker 镜像所需的所有指令1 # 使用官方 Python 3.10 (Python…

BuildingSystemPlugin使用指南

使用自定义碰撞 1.启用Use Custom Overlay:2.修改Overlapping Box的BoxExtent来设置大小(不能设置Scale来设置大小):

openEuler 24.03 (LTS-SP2)安装mysql5.7.42

环境:OS:openEuler 24.03 (LTS-SP2)(安装时候没有图形界面的选择项可选)mysql:5.7.42 glib.2.17 操作系统下载https://www.openeuler.org/en/download/#openEuler%2024.03%20LTS%20SP2 查看系统glibc版本[root@localho…

Trae AI IDE与Gitee MCP深度整合:开启智能协作开发新时代

Trae AI IDE与Gitee MCP深度整合:开启智能协作开发新时代 在AI技术快速渗透软件开发领域的当下,字节跳动推出的Trae AI IDE凭借其创新的智能编码能力,正在重塑开发者的工作流程。这款国产AI编程工具通过深度整合Git…

【字节跳动】LLM大模型算法面试题:大模型 LLM的架构介绍? - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

LangChain 入门:从 0 到 1 搞懂 LLM 应用开发框架​

LangChain 入门:从 0 到 1 搞懂 LLM 应用开发框架​如果你常逛技术社区,大概率听过 “大语言模型(LLM)能做很多事”—— 写文案、答问题、编代码,但真要把它放进实际业务里,比如给公司做个智能客服、给团队搭个文…