【SpringCloud学习笔记】Elasticsearch

1. Elasticsearch

1.1 安装ES

  1. 启动Docker:service docker restart / systemctl restart docker
  2. 基于Docker创建网络docker network create hm-net
  3. 向云服务器上传elasticsearch以及kibana的tar包,并使用docker load -i xxx.tar进行加载
  4. 使用如下命令启动es:
docker run -d \--name es \-e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \-e "discovery.type=single-node" \-v es-data:/usr/share/elasticsearch/data \-v es-plugins:/usr/share/elasticsearch/plugins \--privileged \--network hm-net \-p 9200:9200 \-p 9300:9300 \elasticsearch:7.12.1
  1. 使用如下命令启动kibana:
docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTS=http://es:9200 \
--network=hm-net \
-p 5601:5601  \
kibana:7.12.1
  1. 使用docker logs -f es / kibana可查看运行日志:

image.png

  1. 验证在浏览器中访问:9200以及5601端口能观察到如下现象证明启动成功:

image.pngimage.png

1.2 初识Elasticsearch

1.2.1 简介

官方网站:https://www.elastic.co/cn/elasticsearch

**Elasticsearch:**是一个高性能的搜索引擎,它是ELK技术栈的一部分:

  • Logstash / Beats:用于数据收集
  • Elasticsearch:用于数据的存储、搜索、计算
  • Kibana:用于数据可视化(内置Devtools等高效工具)

**应用场景:**搜索引擎技术在日常生活中非常常见,例如:

  1. github等网站的高亮搜索关键词
  2. 百度/搜狗等搜索引擎
  3. 各大网站支持的模糊查询(关键字匹配)
  4. 打车软件地理位置搜索

1.2.2 倒排索引

ES的高性能得益于其底层实现的倒排索引
倒排索引中有两个重要概念:

  • 文档(Document):一条数据就是一个文档,例如商品记录、用户记录都是一个文档
  • 词条(Item):对于一个文档中的内容或者用户搜索句使用某种算法,基于语义划分得到的一组词就是词条,例如我喜欢学习Java,就可以分成:“我”、“喜欢”、“学习”、"Java"这些词条

倒排索引构建流程:
假设此时正向索引为:

idtitleprice
1小米手机4999
2华为手机3999
3华为手表2999
4小米汽车199999

此时每一条数据就是一个文档,我们可以对文档中的title内容构建倒排索引:

  1. 使用某种分词算法将title进行分词,例如"小米手机"就可以得到"小米"、"手机"两个词条
  2. 以词条作为键,文档id列表作为值
  3. 如果词条已经出现过,那么就在文档列表末尾追加当前文档,如果该词条没有出现过,则构建一个新的键值对,键为当前词条,值为当前文档id

当对上述四条数据分词结束后倒排索引为:

词条文档id列表
小米[1, 4]
手机[1, 2]
华为[2, 3]
手表[3]
汽车[4]

倒排索引工作流程:

  1. 此时当用户搜索关键词为"小米手表",使用相同的分词算法得出词条"小米"、“手表”
  2. 然后就会查询倒排索引,小米对应的文档id有1、4, 手表对应的文档有3
  3. 然后再根据正排索引查询id对应的具体文档内容

1.2.3 IK分词器

1.2.3.1 基本使用

我们已经了解到ES工作原理需要借助一定的分词算法进行分词匹配,那么就需要一个字典(dict)来保存一些常用的词语,分词算法才可以判断某一个序列是否可以作为一个词条。
而ES标准的分词器对于中文分词支持力度不大!例如我们尝试在devtools发起如下分词请求:

POST /_analyze
{"analyzer": "standard","text": "我喜欢学习Java"
}

image.png
可以观察到"standard"模式下的响应结果并非是我们想得到的!这个时候我们就需要使用到IK分词器了
安装步骤:

  1. 方式一:使用在线安装的方式,输入以下命令:
# 在线安装
docker exec -it es ./bin/elasticsearch-plugin  install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.12.1/elasticsearch-analysis-ik-7.12.1.zip
# 重启docker es服务
docker restart es
  1. 方式二:离线安装,将下载好的ik分词压缩包挂载在数据卷中
# 查看所有的数据卷位置
docker volume ls 
# 查看docker es-plugins数据卷位置
docker volume inspect es-plugins
# cd xxx
# 上传ik压缩包
# 重启docker es服务
docker restart es

此时再次发起请求,结果如下:
image.png

1.2.3.2 设置扩展词以及停用词

**扩展词:**我们需要自定义一些单词放在字典当中,例如"白嫖"、“米饭论坛”
**停用词:**在中文中一些语气词比如啊、哦、了是不需要进行分词的,可以剔除
前面我们提到IK分词器需要依赖字典,该配置文件就可以在ik文件夹目录中的/config/IKAnalyzer.cfg.xml进行配置扩展词以及暂停词的文件image.png
我们进行上述配置:然后重启es: docker restart es此后es就会读取同级目录下的ext.dic获取扩展字典当中的内容,读取stopwords.dic获取停用词列表
image.png
证明拓展词以及停用词配置成功!

1.2.4 索引库操作

  1. 创建索引库和映射:

请求方式:PUT
请求路径:/索引库名(自己定义)
请求参数:mapping映射

PUT /索引库名
{"mappings": {"properties": {"字段名": {"type": "text","index": true,"analyzer": "ik_smart"},"字段名": {"type": object,"properties": {"字段名": {"type": boolean}}}}}
}
  1. 查询索引库:

请求方式:GET
请求路径:/索引库名
请求参数:无

GET /索引库名
  1. 删除索引库:

请求方式:DELETE
请求路径:/索引库名
请求参数:无

DELETE /索引库名
  1. 修改索引库

修改索引结构在es中是不被允许的,因此如果修改了字段结构会导致倒排索引重建,开销巨大,但是我们可以新增字段
请求方式:PUT
请求路径:/索引库名/_mapping

PUT /索引库名/_mapping
{"properties": {"新字段名": {}}
}

索引库操作总结:

  1. 新增:PUT /索引库名
  2. 查询:GET /索引库名
  3. 修改 PUT /索引库名/_mapping
  4. 删除:DELETE /索引库名

1.2.5 文档操作

1.3 Java客户端

1.3.1 Java客户端初始化

上述我们实践了借助Kibana发送请求,但是我们还是需要学习使用Java客户端编程的方式实现:

  1. 在pom文件中引入RestHignLevelClient依赖
<!-- 引入es依赖 -->
<dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.12.1</version>
</dependency>
  1. 初始化RestHignLevelClient对象
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
class EsDemoApplicationTests {private RestHighLevelClient client;@BeforeEachpublic void init() {client = new RestHighLevelClient(RestClient.builder(HttpHost.create("http://129.211.6.176:9200")));}@Testpublic void testClient() {System.out.println(client);}
}

1.3.2 创建映射以及索引库

原先我们在Kibana中使用如下HTTP请求配置映射信息以及索引库:

PUT /user
{"mappings": {"properties": {"desc": {"type": "text","index": "true","analyzer": "ik_smart"},"name": {"type": "text","index": "false"},"age": {"type": "integer","index": "false"}}}
}

而在Java代码中我们需要通过以下的方式来创建:

/*** 测试创建索引库*/
@Test
public void testCreateIndex() throws IOException {// 1. 创建Request对象CreateIndexRequest request = new CreateIndexRequest("user");// 2. 配置携带参数request.source(MAPPING_TEMPLATE, XContentType.JSON);// 3. 发起请求client.indices().create(request, RequestOptions.DEFAULT);
}

1.3.3 查看索引库

原先我们在Kibana中使用如下HTTP请求查看索引库:

GET /user
/*** 测试获取索引库信息*/
@Test
public void testGetIndex() throws IOException {// 1. 创建request对象GetIndexRequest request = new GetIndexRequest("user");// 2. 发起请求boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);System.out.println("exists: " + exists);
}

1.3.4 删除索引库

原先我们在Kibana中使用如下HTTP请求删除索引库:

DELETE /user
/*** 测试删除索引库*/
@Test
public void testDeleteIndex() throws IOException {// 1. 创建request对象DeleteIndexRequest request = new DeleteIndexRequest("user");// 2. 发起请求client.indices().delete(request, RequestOptions.DEFAULT);
}

1.3.5 新增文档

原先我们在Kibana中使用如下HTTP请求增加文档:

# 插入文档
POST /user/_doc/1
{"desc": "测试描述","name": "ricejson","age": 20
}
/*** 新增文档*/
@Test
public void testCreateDocument() throws IOException {// 1. 创建requestIndexRequest request = new IndexRequest("user").id("1");// 创建用户对象User user1 = new User("测试描述", "ricejson", 20);Gson gson = new Gson();String userStr = gson.toJson(user1);// 2. 填写内容request.source(userStr, XContentType.JSON);// 3. 发起请求client.index(request, RequestOptions.DEFAULT);
}

1.3.6 查看文档

原先我们在Kibana中使用如下HTTP请求查看文档:

GET /user/_doc/1
/*** 获取文档*/
@Test
public void testGetDocument() throws IOException {// 1. 创建requestGetRequest request = new GetRequest("user", "1");// 2. 发起请求GetResponse resp = client.get(request, RequestOptions.DEFAULT);// 3. 获取返回内容String source = resp.getSourceAsString();System.out.println(source);
}

1.3.7 删除文档

原先我们在Kibana中使用如下HTTP请求删除文档:

DELETE /user/_doc/1
/*** 删除文档*/
@Test
public void deleteDocument() throws IOException {// 1. 创建request对象DeleteRequest request = new DeleteRequest("user", "1");// 2. 发起删除文档请求client.delete(request, RequestOptions.DEFAULT);
}

1.3.8 修改文档

全量修改:与新增文档一致
局部修改:

POST /user/_update/1
{"doc": {"name": "米饭好好吃"}
}
/*** 局部更新文档*/
@Test
public void updateDocument() throws IOException {// 1. 创建request对象UpdateRequest request = new UpdateRequest("user", "1");// 2. 设置更新内容request.doc("name", "米饭好好吃");// 3. 发起请求client.update(request, RequestOptions.DEFAULT);
}

1.3.9 批处理

/*** 测试批处理操作*/
@Test
public void testBatch() throws IOException {// 1. 创建request对象BulkRequest bulkRequest = new BulkRequest();// 2. 添加多个request操作// 创建用户对象User user = new User("测试描述", "ricejson", 20);Gson gson = new Gson();String userStr = gson.toJson(user);bulkRequest.add(new IndexRequest("user").id("1").source(userStr, XContentType.JSON));bulkRequest.add(new IndexRequest("user").id("2").source(userStr, XContentType.JSON));// 3. 发起批处理请求client.bulk(bulkRequest, RequestOptions.DEFAULT);
}

1.4 DSL语句

1.4.1 快速入门

现在我们需要设计一个商品表,其中MySQL存储如下字段内容:
id: int 编号
name: varchar(20) 名称
brand:varchar(20) 品牌
price: int 价格
desc: varchar(100) 描述内容

# 定义以及新增商品索引库
PUT /goods
{"mappings": {"properties": {"id": {"type": "integer"},"name": {"type": "text","analyzer": "ik_smart"},"brand": {"type": "keyword"},"price": {"type": "integer"},"desc": {"type": "text","analyzer": "ik_smart"}}}
}

现在插入几条模拟数据:

POST /goods/_doc/1
{"id": 1,"name": "超级智能手表","brand": "FutureTech","price": 1299,"desc": "这款超级智能手表拥有最新的健康监测功能,能够实时追踪您的心率和睡眠质量。"
}POST /goods/_doc/2
{"id": 2,"name": "无线蓝牙耳机","brand": "AudioMaster","price": 499,"desc": "享受无线自由,这款无线蓝牙耳机提供卓越的音质和长达24小时的续航能力。"
}POST /goods/_doc/3
{"id": 3,"name": "便携式咖啡机","brand": "CoffeeCraft","price": 699,"desc": "随时随地享受新鲜咖啡,这款便携式咖啡机设计精巧,操作简便,是咖啡爱好者的理想选择。"
}POST /goods/_doc/4
{"id": 4,"name": "智能扫地机器人","brand": "CleanRobotics","price": 2999,"desc": "智能扫地机器人,自动导航,高效清洁,为您节省宝贵的时间。"
}POST /goods/_doc/5
{"id": 5,"name": "多功能运动相机","brand": "ActionCam","price": 1499,"desc": "这款多功能运动相机防水防尘,适合各种极限运动场景,记录您的每一个精彩瞬间。"
}

搜索索引库中全部的文档内容语法如下:

GET /goods/_search
{"query": {"match_all": {}}
}

1.4.2 叶子查询

常见的叶子查询主要有如下两类:

  • 全量查询:要求查询条件必须是可以分词的
  • 精确查询
1.4.2.1 全量查询

其中全量查询语法格式如下:

# 全量查询
GET /goods/_search
{"query": {"match": {"name": "智能"}}
}

除此以外我们还可以指定查询多个字段:

# 全量查询(查询多个字段)
GET /goods/_search
{"query": {"multi_match": {"query": "您","fields": ["name", "desc"]}}
}
1.4.2.2 精确查询

精确查询有如下两种常见方式:

  1. term:词条完全匹配
  2. range:范围查询,比如price >= 1000 以及 price <= 1999
# 精确查询(词条查询)
GET /goods/_search
{"query": {"term": {"brand": "ActionCam"}}
}
# 精确查询(范围查询)
GET /goods/_search
{"query": {"range": {"price": {"gte": 1499,"lte": 2999}}}
}

1.4.3 复合查询

bool查询:
将多个叶子查询经过mustshouldmust_notfilter等与或非操作合并成一个复杂查询:

# 复合查询(查询name中包含机器人并且价格在1499-2999之间)
GET /goods/_search
{"query": {"bool": {"must": [{"match": {"name": "机器人"}}],"filter": [{"range": {"price": {"gte": 1499,"lte": 2999}}}]}} 
}

1.4.4 排序+分页

排序:
我们还可以在query的同级目录下加入order: {"排序字段": "asc|desc"}

  • ASC:表示正序
  • DESC:表示倒序
# 排序
GET /goods/_search
{"query": {"match_all": {}},"sort": {"price": "asc"}
}

分页:
我们还可以在query的同级目录下加入"from": x, "size": x

  • from:表示从哪一条数据开始
  • size:表示单页的数量
# 分页
GET /goods/_search
{"query": {"match_all": {}},"from": 0,"size": 3
}

1.4.5 高亮显示

想必大家都遇到过以下场景:
image.png
即百度搜索返回的结果中,与关键字匹配的结果都会高亮(红色)显示,打开开发者工具能够发现被高亮的部分由标签进行包括并使用CSS语法标红醒目。
那么问题就出现了,这个标签到底是由前端分析生成的还是由后端返回的?

  • 前端:如果让前端进行处理,都需要对查询内容进行分词,然后在内容中进行匹配,但是这样一来性能开销就非常大,前端一般只做数据展示,数据处理一般让后端处理
  • 后端:事实上ES在进行倒排索引的构建过程中,会保存查询分词在文档中的位置,然后在查询词前后加上等标签返回给前端。✔

DSL添加高亮显示:

# 高亮显示
GET /goods/_search
{"query": {"match": {"name": "智能"}},"highlight": {"fields": {"name": {"pre_tags": "<em>","post_tags": "</em>"}}}
}

1.5 Java客户端操作DSL

1.5.1 快速入门

我们现在想要查询出索引库为goods下全部的文档内容,与之匹配的DSL语句如下:

GET /goods/_search
{"query": {"match_all": {}}
}

相对应的java代码如下:

/*** 测试查询全部文档*/
@Test
public void testMatchAll() throws IOException {// 1. 创建requestSearchRequest request = new SearchRequest("goods");// 2. 添加query查询条件request.source().query(QueryBuilders.matchAllQuery());// 3. 发起请求获取响应SearchResponse resp = client.search(request, RequestOptions.DEFAULT);// 4. 解析响应// 4.1 获取到hitsSearchHits searchHits = resp.getHits();// 4.2 获取到数据条数TotalHits hitsCount = searchHits.getTotalHits();// 4.3 获取到数据数组SearchHit[] hitsHits = searchHits.getHits();// 4.3 逐个解析for (SearchHit searchHit : hitsHits) {// 4.4 获取source数据String source = searchHit.getSourceAsString();System.out.println(source);}
}

1.5.2 叶子查询

前面我们已经介绍过叶子查询主要有如下几类:

  1. 全量查询
    1. match:单字段匹配
    2. match_all:多字段匹配
  2. 精确查询
    1. term:根据词条内容匹配
    2. range:根据范围匹配

需求1:现在我们需要找出desc中包含"这款"的文档
对应的DSL语句如下:

GET /goods/_search
{"query": {"match": {"desc": "这款"}}
}

对应的Java代码如下:

    /*** 查询desc中包含"这款"的内容*/@Testpublic void testMatch() throws IOException {// 1. 创建request对象SearchRequest request = new SearchRequest("goods");// 2. 设置查询条件request.source().query(QueryBuilders.matchQuery("desc", "这款"));// 3. 发起请求SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4.1 获取到hitsSearchHits hits = response.getHits();// 4.2 获取到数据总条数TotalHits totalHits = hits.getTotalHits();// 4.3 获取到数据集合SearchHit[] searchHits = hits.getHits();// 4.4 逐条解析for (SearchHit searchHit : searchHits) {String source = searchHit.getSourceAsString();System.out.println(source);}}

需求2:现在我们需要查询desc中包含"相机"并且name中包含"相机"的文档
对应的DSL语句如下:

GET /goods/_search
{"query": {"multi_match": {"query": "相机","fields": ["name", "desc"]}}
}

对应的Java代码如下:

/*** 查询desc并且name中都包含"相机"的文档*/
@Test
public void testMultiMatch() throws IOException {// 1. 创建requestSearchRequest request = new SearchRequest("goods");// 2. 添加查询条件request.source().query(QueryBuilders.multiMatchQuery("相机", "name", "desc"));// 3. 发起请求获取响应SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4.1 获取到hitsSearchHits hits = response.getHits();// 4.2 获取到数据总数TotalHits totalHits = hits.getTotalHits();// 4.3 获取全部查询文档SearchHit[] searchHits = hits.getHits();// 4.4 解析每条数据for (SearchHit searchHit : searchHits) {// 4.5 获取source数据String source = searchHit.getSourceAsString();System.out.println(source);}

需求3:现在我们需要精确查找手机品牌为"ActionCam"的文档
对应的DSL语句如下:

GET /goods/_search
{"query": {"term": {"brand": "ActionCam"}}
}

对应的Java代码如下:

/*** 查询手机品牌为"ActionCam"的文档*/
@Test
public void testTerm() throws IOException {// 1. 创建request对象SearchRequest request = new SearchRequest("goods");// 2. 设置查询条件request.source().query(QueryBuilders.termQuery("brand", "ActionCam"));// 3. 发起请求获取响应SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4.1 获取hitsSearchHits hits = response.getHits();// 4.2 获取数据总数TotalHits totalHits = hits.getTotalHits();// 4.3 获取全部匹配文档SearchHit[] searchHits = hits.getHits();// 4.4 解析出每一条数据for (SearchHit searchHit : searchHits) {// 4.5 获取sourceString source = searchHit.getSourceAsString();System.out.println(source);}

需求4:现在我们需要查询价格高于1999的文档
对应的DSL语句如下:

GET /goods/_search
{"query": {"range": {"price": {"gt": 1999}}}
}

对应的Java代码如下:

/*** 查询价格高于1499的文档*/
@Test
public void testRange() throws IOException {// 1. 创建requestSearchRequest request = new SearchRequest("goods");// 2. 设置查询条件request.source().query(QueryBuilders.rangeQuery("price").gt(1499));// 3. 发起请求,获取响应SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4.1 获取hitsSearchHits hits = response.getHits();// 4.2 获取数据总数TotalHits totalHits = hits.getTotalHits();// 4.3 获取数据全集SearchHit[] searchHits = hits.getHits();// 4.4 解析每条数据for (SearchHit searchHit : searchHits) {// 4.5 获取sourceString source = searchHit.getSourceAsString();System.out.println(source);}
}

1.5.3 复合查询

需求:查询价格低于2999并且name中包含"耳机"并且品牌为"AudioMaster"的文档

GET /goods/_search
{"query": {"bool": {"must": [{"match": {"name": "耳机"}}],"filter": [{"term": {"brand": "AudioMaster"}},{"range": {"price": {"lt": 2999}}}]}}
}

对应的Java代码如下:

/*** 查询价格低于2999并且name中包含"耳机"并且品牌为"AudioMaster"的文档*/
@Test
public void testBool() throws IOException {// 1. 创建requestSearchRequest request = new SearchRequest("goods");// 2. 设置查询条件request.source().query(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("name", "耳机")).filter(QueryBuilders.termQuery("brand", "AudioMaster")).filter(QueryBuilders.rangeQuery("price").lt(2999)));// 3. 发起请求,获取响应SearchResponse response = client.search(request, RequestOptions.DEFAULT);process(response);
}

1.5.4 分页+排序查询

需求:查询全部文档,按照价格从低到高,分页为第一页,三条数据

GET /goods/_search
{"query": {"match_all": {}},"sort": {"price": "ASC"},"from": 1,"size": 3
}

对应的Java代码如下:

/*** 查询全部文档,按照价格从低到高,分页为第一页,三条数据*/
@Test
public void testPageAndSort() throws IOException {// 1. 创建requestSearchRequest request = new SearchRequest("goods");// 2. 设置查询条件request.source().query(QueryBuilders.matchAllQuery()).sort("price", SortOrder.ASC).from(0).size(3);// 3. 发起请求,获取响应SearchResponse response = client.search(request, RequestOptions.DEFAULT);process(response);
}

1.5.5 高亮显示

DSL高亮语法:

GET /goods/_search
{"query": {"match": {"name": "相机"}},"highlight": {"fields": {"name": {"pre_tags": "<em>","post_tags": "</em>"}}}
}

Java客户端实现:

/*** 测试高亮*/
@Test
public void testHighlight() throws IOException {// 1. 创建requestSearchRequest request = new SearchRequest("goods");// 2. 设置查询条件request.source().query(QueryBuilders.matchQuery("name", "相机"));// 3. 设置高亮条件request.source().highlighter(new HighlightBuilder().field("name").preTags("<em>").postTags("</em>"));// 4. 进行查询SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 5. 进行解析SearchHits hits = response.getHits();SearchHit[] searchHits = hits.getHits();for (SearchHit searchHit : searchHits) {Map<String, HighlightField> hlfs = searchHit.getHighlightFields();HighlightField hlf = hlfs.get("name");String value = hlf.getFragments()[0].string();System.out.println(value);}
}

1.5.6 聚合

1.5.6.1 聚合分类

在DSL中,聚合可以分为如下几类:

  1. 桶(Buckets)聚合
    1. Terms Aggregation: 词条聚合
    2. Date 日期时间聚合
  2. 度量(Metric)聚合
    1. min:最小值
    2. max:最大值
    3. avg:平均值
    4. stats:统计最小值、最大值、平均值
  3. 管道(Pipeline)聚合

基于别的聚合再次聚合

1.5.6.2 聚合DSL语句

聚合三要素:

  1. 聚合名称
  2. 聚合类型
  3. 聚合字段

聚合DSL语法:

GET /goods/_search
{"size": 0,"aggs": {"brand_agg": {"terms": {"field": "brand","size": 5}}}
}
1.5.6.3 Java客户端操作聚合DSL语句
/*** 测试聚合DSL*/
@Test
public void testAggregation() throws IOException {// 1. 创建request对象SearchRequest request = new SearchRequest("goods");// 2. 编写查询条件request.source().query(QueryBuilders.matchAllQuery());// 2.1 设置分页request.source().size(0);// 2.2 设置聚合三要素request.source().aggregation(AggregationBuilders.terms("brand_agg").field("brand").size(5));// 3. 查询结果SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4. 解析结果Aggregations aggregations = response.getAggregations();Terms terms = aggregations.get("brand_agg");List<? extends Terms.Bucket> buckets = terms.getBuckets();for (Terms.Bucket bucket : buckets) {System.out.println("key: " + bucket.getKeyAsString());System.out.println("count: " + bucket.getDocCount());}
}

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

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

相关文章

APD系列特高频局放监测装置

安科瑞电气股份有限公司 祁洁 15000363176 一、产品概述 现阶段&#xff0c;电力系统对于电能的质量提出越来越高的要求&#xff0c;不仅要确保供电稳定可靠&#xff0c;而且供电的安全性也是重要要求。电力系统中&#xff0c;金属封闭开关设备得到广泛应用&#xff0c;因…

程序猿大战Python——流程控制——其他控制语句

for循环 目标&#xff1a;掌握for循环的使用。 与while循环功能类似&#xff0c;for语句也能完成反复多次的执行。 for语法&#xff1a; for 临时变量 in 序列:满足条件时&#xff0c;执行的代码1满足条件时&#xff0c;执行的代码2…… [else:当for循环正常执行结束后&#…

简单了解java中的异常

异常 1、异常的概述 1.1、概述 异常就是程序出现了不正常的情况&#xff0c;程序在执行过程中&#xff0c;数据导致程序不正常&#xff0c;最终导致JVM的非正常停止。语句错误不算在异常体系中。 1.2、异常的存在形式 异常有类型之分&#xff0c;比如我们比较熟悉的数组越…

TikTok Shop账号需要防关联吗?

在TikTokShop作为新兴的电商销售渠道中&#xff0c;保护账号的安全和隐私&#xff0c;防止账号关联成为了重要的任务。为了更好地理解为何需要防关联以及如何进行防范&#xff0c;让我们深入探讨一下这个问题。 为什么要防关联&#xff1f; 1. 账号异常风险&#xff1a;防关联…

苹果AI来了,ios18史诗级发布

今天凌晨1点&#xff0c;苹果举行了WWDC开发者大会&#xff0c;正式发布了 全新iOS 18、iPadOS 18、watchOS 11、tvOS 18、macOS 等以及Apple Intelligence的个人化智能系统 苏音给大家汇总下&#xff0c;ios18的更新内容以及苹果的AI。 本次更新&#xff0c;官方带来的title…

HBuilder X运行项目到微信开发者工具调试和发布Uniapp小程序

1.下载和安装 HBuilderX hbuilder首页&#xff1a;https://www.dcloud.io/hbuilderx.html 下载hbuilder编辑器,选择对应的系统,Windows和mac正式版即可,下载后免安装直接点击即可使用。 打开HBuilder之后&#xff0c;它会要求你注册一个用户&#xff0c;然后才可以使用。 …

如何预览XtraGrid控件

如何预览XtraGrid。 private void ShowGridPreview(DevExpress.XtraGrid.GridControl grid) {// Check whether the Grid Control can be previewed.if(!grid.IsPrintingAvailable) {MessageBox.Show("The DevExpress.XtraPrinting Library is not found", "Er…

使用 Scapy 库编写 TCP 窗口大小探测攻击脚本

一、介绍 1.1 概述 TCP窗口大小探测攻击是一种信息收集攻击&#xff0c;攻击者通过向目标服务器发送特制的TCP数据包&#xff0c;探测目标服务器的TCP接收窗口大小&#xff08;TCP Window Size&#xff09;。了解目标服务器的TCP接收窗口大小&#xff0c;可以帮助攻击者优化后…

【漏洞复现】CraftCMS ConditionsController.php 代码执行漏洞(CVE-2023-41892)

0x01 产品简介 Crat CMS是一个开源的内容管理系统&#xff0c;它专注于用户友好的内容创建过程&#xff0c;逻辑清晰明了&#xff0c;是一个高度自由&#xff0c;高度自定义设计的平台&#xff0c;可以用来创建个人或企业网站也可以搭建企业级电子商务系统。 0x02 漏洞概述 …

万兴优转 v15 解锁版安装教程(全能音视频格式转换器)

前言 Wondershare UniConverter&#xff08;万兴优转&#xff09;国产全能音视频格式转换器。万兴格式转换器具有音视频格式转换、合并视频、视频压缩、视频编辑、视频录制、下载视频、元数据修复、VR视频转换、字幕编辑器、GIF制作、DVD刻录等一站式视频工具箱功能。万兴转换…

数新网络签单国泰君安:利用数据服务平台提升金融业务用数能力

近日&#xff0c;数新网络与国泰君安证券股份有限公司&#xff08;以下简称“国泰君安”&#xff09;达成了数据服务平台升级项目的签约。这一项目的推进将更好地服务于国泰君安内部业务部门的数据需求&#xff0c;帮助数据平台更加有效地实现提升业务响应效率的目标&#xff0…

Nvidia/算能 +FPGA+AI大算力边缘计算盒子:AI智能监控 用于沙滩救援

以色列的一个团队在人工智能领域取得的成果引起了轰动。 今天他们取得的成果源于多年前的一个想法。Netanel Eliav 和 Adam Bismut 是校园时代的旧伙伴&#xff0c;当时他们想要解决一个可以改变世界的问题&#xff0c;由此引出这样一个想法&#xff1a;溺水的 Bismut 漂流到死…

【数据分析基础】实验二 Python程序流程控制、函数设计与使用

实验目的 熟悉选择结构嵌套时代码的缩进与对齐。理解带 else 子句的循环结构执行流程和条件表达式 value1 if condition else value2 的用法。熟悉使用循环和异常处理机构对用户输入进行约束的用法。掌握Python程序中进行选择、循环流程控制的语句、语法&#xff0c;熟练运用选…

SpringAI--使用免费API-Key和API进行配置

目录 &#x1f4da; 前言 &#x1f4d1;Spring AI简介 &#x1f4ac; 项目配置 &#x1f4da; 系统功能的具体实现 &#x1f4ac; 编写Controller控件 &#x1f4da; 前言 &#x1f4d1;博客主页&#xff1a;丘比特惩罚陆 &#x1f496;欢迎关注&#xff1a;点赞收藏⭐留言✒…

【Vue】getters

除了state之外&#xff0c;有时我们还需要从state中筛选出符合条件的一些数据&#xff0c;这些数据是依赖state的&#xff0c;此时会用到getters getters就类似于属性中的计算属性 这个getter只有获取&#xff0c;如果需要设置修改&#xff0c;还是需要经过mutations getters里…

C++网络编程基础

文章目录 协议局域网通信IP 地址网络通信的本质tcp 和 udp 协议网络字节序网络主机数据转化接口 协议 协议&#xff1a;收到数据后&#xff0c;多出来的那一部分&#xff0c;也叫一种 “约定”&#xff0c;一整套的自硬件到软件&#xff0c;都有协议&#xff0c;需要有人定制&a…

如何使用Python在word文档中创建表格

如何使用Python在word文档中创建表格 介绍效果代码 介绍 本文将介绍如何使用Python库python-docx在Word文档中创建表格。 效果 插入表格前的word文档&#xff1a; 插入表格后的word文档&#xff1a; 代码 from docx import Document# 加载现有的Word文档 doc Document(…

【护网简历模版】改了10个在校大学生的简历的感想

护网简历模版 吉祥学安全知识星球&#x1f517;除了包含技术干货&#xff1a;Java代码审计、web安全、应急响应等&#xff0c;还包含了安全中常见的售前护网案例、售前方案、ppt等&#xff0c;同时也有面向学生的网络安全面试、护网面试等。 在上篇文章中&#xff1a;如何面试…

前端JS必用工具【js-tool-big-box】学习,获取当前浏览器向上滚动还是向下滚动,获取当前距离顶部和底部的距离

这一小节&#xff0c;我们说一下 js-tool-big-box 添加的最新工具方法&#xff0c;在日常前端开发工作中&#xff0c;如果网页很长&#xff0c;我们就需要获取当前浏览器是在向上滚动&#xff0c;还是向下滚动。如果向上滚动&#xff0c;滚动到0的时候呢&#xff0c;需要做一些…

建筑二建考试试题及答案,分享几个实用搜题和学习工具 #微信#经验分享

大学生必备的搜题工具&#xff0c;专业课本习题、电子版教材、考研资料、英语四六级等考试题目也能一并搜索&#xff0c;每道题目都有详细的讲解&#xff0c;每个都堪称大学神器。 1.掌上识别王 一个可以快速纸质书籍上内容扫描成电子档的工具&#xff0c;为了方便大家快速搜…