使用 Docker 安装
可视化检索数据,非必需
$ docker pull kibana:7.4.2
- 使用 docker-compose 创建容器
```shell
version: "2"
services:
DOClever:
image: elasticsearch:7.4.2
restart: always
container_name: "elasticsearch"
ports:
- 9200:9200
- 9300:9300
environment:
- ES_JAVA_OPTS=-Xms64m -Xmx512m # 注意设置占用内存大小,否则可能机器内存不足启动不了
- "discovery.type=single-node"
kibana:
image: kibana:7.4.2
restart: always
container_name: "kibana"
ports:
- 5601:5601
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
- 启动容器
docker-compose up -d
基本概念
Elasticsearch 是面向文档型数据库,一条数据在这里就是一个文档。为了方便理解,我们将 Elasticsearch 里存储文档数据和关系型数据库 MySQL 存储数据的概念进行一个类比:
ES 里的 Index 可以看做一个库,而 Types 相当于表,Documents 则相当于表的行。这里 Types 的概念已经被逐渐弱化,Elasticsearch 6.X 中,一个 index 下已经只能包含一个 type,Elasticsearch 7.X 中, Type 的概念已经被删除了。基本操作
查看服务状态与信息
```shell查看所有节点
GET _cat/nodes
查看健康状态
GET _cat/health
查看主节点
GET _cat/master
<a name="j8i6u"></a>
### 索引的增删改查
```shell
# 创建一个名为 product 的索引
PUT product
# 查看所有索引,?v 的作用是让返回的数据格式更方便查看
GET _cat/indices?v
# 查看名为 product 的索引
GET _cat/indices/product?v
或
GET product
# 删除名为 product 的索引
DELETE product
增删改文档
# 不指定 id 的创建方式,自动生成一个随机 id
POST product/_doc
{
"title": "小米手机",
"category": "小米",
"images": "http://www.gulixueyuan.com/xm.jpg",
"price": 3999
}
# 指定 id,如果 id 存在则会修改这个数据,版本号加 1
POST product/_doc/1
{
"title": "小米手机",
"category": "小米",
"images": "http://www.gulixueyuan.com/xm.jpg",
"price": 3999
}
# 只修改指定的字段
POST product/_update/1
{
"doc": {
"price": 2000
}
}
# PUT 请求也可以新增或修改数据,但 PUT 必须指定 id,所以一般使用 PUT 修改数据
PUT product/_doc/1
{
"title": "小米手机 new",
"category": "小米",
"images": "http://www.gulixueyuan.com/xm.jpg",
"price": 3999
}
# 根据 id 删除文档
DELETE product/_doc/1
# 根据条件删除文档(删除 price 为 2000 的文档)
POST product/_delete_by_query
{
"query": {
"match": {
"price": 2000
}
}
}
根据 id 查看文档数据
# 根据主键获取文档数据
GET product/_doc/1
检索信息 Query DSL 语法
Elasticsearch 提供了一个可以执行查询的 Json 风格的 DSL(domain-specific language 领域特 定语言)。这个被称为 Query DSL。该查询语言非常全面,并且刚开始的时候感觉有点复杂, 真正学好它的方法是从一些基础的示例开始的。
推荐阅读官方文档:https://www.elastic.co/guide/cn/elasticsearch/guide/current/search-in-depth.html
导入测试数据:
https://github.com/elastic/elasticsearch/blob/master/docs/src/test/resources/accounts.json
query查询基本语法
# 检索数据
# query 定义如何查询
# match_all 查询类型(代表查询所有的所有),es 中可以在 query 中组合非常多的查询类型完成复杂查询
# 除了 query 参数之外,我们也可以传递其它的参数以改变查询结果。如 sort、size,可以用 from + size 实现分页
# sort 排序,多字段排序,会在前序字段相等时后续字段内部排序,否则以前序为准
GET bank/_search
{
"query": {
"match_all": {}
}
}
返回部分字段
# _source 定义返回部分字段
GET bank/_search
{
"query": {
"match_all": {}
},
"_source": [
"balance",
"firstname"
]
}
match 匹配查询
## match 匹配查询
GET bank/_search
{
"query": {
"match": {
"account_number": 20
}
},
"_source": [
"balance",
"firstname",
"account_number"
]
}
# match 字符串,全文检索,查询出 address 包含 mill 的记录
GET bank/_search
{
"query": {
"match": {
"address": "mill"
}
},
"_source": [
"address",
"firstname",
"account_number"
]
}
# 字符串,多个单词(分词+全文检索),查询出 address 包含 mill 和 road 的记录,并给出相关性得分
GET bank/_search
{
"query": {
"match": {
"address": "mill road"
}
},
"_source": [
"address",
"firstname",
"account_number"
]
}
match_phrase 短语匹配,不分词
# 短语匹配,不分词
GET bank/_search
{
"query": {
"match_phrase": {
"address": "mill road"
}
},
"_source": [
"address",
"firstname",
"account_number"
]
}
multi_match 多字段匹配
# multi_match 多字段匹配,查询 state 或者 address 包含 mill 的记录
GET bank/_search
{
"query": {
"multi_match": {
"query": "mill",
"fields": [
"state",
"address"
]
}
},
"_source": [
"address",
"firstname",
"state"
]
}
bool 复合查询
# bool 复合查询
# 复合语句可以合并任何其它查询语句,包括复合语句,了解这一点是很重要的
# 这就意味着复合语句之间可以互相嵌套,可以表达非常复杂的逻辑
# must:必须达到must列举的所有条件
# should:应该达到should列举的条件,如果达到会增加相关文档的评分,并不会改变 查询的结果。
# 如果 query 中只有 should 且只有一种匹配规则,那么 should 的条件就会被作为默认匹配条而改变查询结果
GET bank/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"address": "mill"
}
},
{
"match": {
"gender": "M"
}
}
],
"should": [
{
"match": {
"address": "lane"
}
}
],
"must_not": [
{
"match": {
"email": "baluba.com"
}
}
]
}
}
}
filter 结果过滤
# 并不是所有的查询都需要产生分数,特别是那些仅用于 “filtering”(过滤)的文档
# 为了不计算分数 Elasticsearch 会自动检查场景并且优化查询的执行
GET bank/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"address": "mill"
}
}
],
"filter": {
"range": {
"balance": {
"gte": 10000,
"lte": 20000
}
}
}
}
},
"_source": [
"balance",
"address"
]
}
term
### term 和 match 一样。匹配某个属性的值。全文检索字段用 match,其他非 text 字段匹配用 term,这是一个规范
GET bank/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"age": {
"value": "28"
}
}
}
]
}
}
}
aggregations 聚合
### 搜索 address 中包含 mill 的所有人的年龄分布以及平均年龄,但不显示这些人的详情
GET bank/_search
{
"query": {
"match": {
"address": "mill"
}
},
"aggs": {
"group_by_state": {
"terms": {
"field": "age"
}
},
"avg_age": {
"avg": {
"field": "age"
}
}
},
"size": 0
}
# 按照年龄聚合,并且请求这些年龄段的这些人的平均薪资
GET bank/_search
{
"query": {
"match_all": {}
},
"aggs": {
"ageAgg": {
"terms": {
"field": "age",
"size": 1000
},
"aggs": {
"balanceAvg": {
"avg": {
"field": "balance"
}
}
}
}
}
}
# 查出所有年龄分布,并且这些年龄段中 M 的平均薪资和 F 的平均薪资以及这个年龄段的总体平均薪资
GET bank/_search
{
"query": {
"match_all": {}
},
"aggs": {
"ageAgg": {
"terms": {
"field": "age"
},
"aggs": {
"genderAgg": {
"terms": {
"field": "gender.keyword",
"size": 5
},
"aggs": {
"balanveAvg": {
"avg": {
"field": "balance"
}
}
}
},
"ageBalanceAvg": {
"avg": {
"field": "balance"
}
}
}
}
}
}
Mapping 字段类型
映射
Mapping 是用来定义一个文档(document),以及它所包含的属性(field)是如何存储和 索引的。比如,使用 mapping 来定义:
- 哪些字符串属性应该被看做全文本属性(full text fields)
- 哪些属性包含数字,日期或者地理位置
- 文档中的所有属性是否都能被索引(_all 配置)
- 日期的格式
-
新版本改变
ES7 及以上移除了 type 的概念。
关系型数据库中两个数据表示是独立的,即使他们里面有相同名称的列也不影响使用,但 ES 中不是这样的。elasticsearch 是基于 Lucene 开发的搜索引擎,而 ES 中不同 type 下名称相同的 filed 最终在 Lucene 中的处理方式是一样的。
两个不同 type 下的两个 user_name,在 ES 同一个索引下其实被认为是同一个 filed,你必须在两个不同的 type 中定义相同的 filed 映射。否则,不同 type 中的相同字段名称就会在处理中出现冲突的情况,导致 Lucene 处理效率下降。
去掉 type 就是为了提高 ES 处理数据的效率。 Elasticsearch 7.x
URL中的type参数为可选。比如,索引一个文档不再要求提供文档类型。
- Elasticsearch 8.x
不再支持URL中的type参数。
- 解决方法:
将索引从多类型迁移到单类型,每种类型文档一个独立索引,将已存在的索引下的类型数据,全部迁移到指定位置即可,详见数据迁移。
Mapping 类型
- type:类型
- String:分为两种,text:可分词;keyword:不可分词,数据会作为完整字段进行匹配
- Numerical:数值类型,分两类,基本数据类型:long、integer、short、byte、double、float、 half_float;浮点数的高精度类型:scaled_float
- Date:日期类型
- Array:数组类型
- Object:对象
- index:是否索引,默认为 true,也就是说你不进行任何配置,所有字段都会被索引。true:字段会被索引,则可以用来进行搜索,false:字段不会被索引,不能用来搜索。
- store:是否将数据进行独立存储,默认为 false,原始的文本会存储在_source 里面,默认情况下其他提取出来的字段都不是独立存储的,是从_source 里面提取出来的。当然也可以独立的存储某个字段,只要设置”store”: true 即可,获取独立存储的字段要比从_source 中解析快得多,但是也会占用更多的空间,所以要根据实际业务需求来设置。
-
Mapping 基本操作
查看索引的 mapping 信息
# 查看 product 索引的 mapping 信息
GET product/_mapping
创建 mapping
PUT product
{
"mappings": {
"properties": {
"age": {
"type": "integer"
},
"email": {
"type": "keyword"
},
"name": {
"type": "text"
}
}
}
}
添加新的字段 mapping
# 添加新的字段 mapping
PUT product2/_mapping
{
"properties": {
"price": {
"type": "integer"
}
}
}
更新 mapping
对于已经存在的 mapping,不能对它进行更新,只能创建新的索引再进行数据迁移。
数据迁移
创建新索引,命名为 product2
# 创建新索引,修改字段类型
PUT product2
{
"mappings": {
"properties": {
"category": {
"type": "text"
},
"images": {
"type": "text"
},
"price": {
"type": "half_float"
},
"title": {
"type": "text"
}
}
}
}
进行数据迁移,将 product 的数据迁移到 product2
POST _reindex
{
"source": {
"index":"product"
},
"dest": {
"index": "product2"
}
}
分词
安装分词器
下载分词器,版本要和 elasticsearch 一样:https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.4.2
将下载的文件解压到 plugins,重启容器
$ pwd
/usr/share/elasticsearch/plugins
$ ls
elasticsearch-analysis-ik-7.4.2
进行 bin 目录,查看插件是否安装成功 ```shell $ pwd /usr/share/elasticsearch/bin
$ ./elasticsearch-plugin list elasticsearch-analysis-ik-7.4.2
<a name="c6849b04"></a>
### 测试分词器
```shell
GET _analyze
{
"analyzer": "ik_smart",
"text": ["尚硅谷电商项目", "苹果手机"]
}
GET _analyze
{
"analyzer": "ik_max_word",
"text": ["尚硅谷电商项目", "苹果手机"]
}
自定义词库
- 增加本地词库
/usr/share/elasticsearch/plugins/elasticsearch-analysis-ik-7.4.2/config/ext_dict.dic
尚硅谷
电商
- 修改配置文件,加入在 key 为 ext_dict 的 entry 节点内容填上 ext_dict.dic
/usr/share/elasticsearch/plugins/elasticsearch-analysis-ik-7.4.2/config/IKAnalyzer.cfg.xml
<properties>
<comment>IK Analyzer 扩展配置</comment>
<!--用户可以在这里配置自己的扩展字典 -->
<entry key="ext_dict">ext_dict.dic</entry>
<!--用户可以在这里配置自己的扩展停止词字典-->
<entry key="ext_stopwords"></entry>
<!--用户可以在这里配置远程扩展字典 -->
<!-- <entry key="remote_ext_dict">https://xxx.com/keyword/word.txt</entry>-->
<!--用户可以在这里配置远程扩展停止词字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
- 重启容器即可,测试分词: ```shell GET _analyze
{ “analyzer”: “ik_max_word”, “text”: “尚硅谷电商项目” }
响应:
```shell
{
"tokens": [
{
"token": "尚硅谷",
"start_offset": 0,
"end_offset": 3,
"type": "CN_WORD",
"position": 0
},
{
"token": "电商",
"start_offset": 3,
"end_offset": 5,
"type": "CN_WORD",
"position": 1
},
{
"token": "项目",
"start_offset": 5,
"end_offset": 7,
"type": "CN_WORD",
"position": 2
}
]
}
Elasticsearch-Rest-Client
- 9300端口
TCP 协议,适用于 spring data,spring-data-elasticsearch:transport-api.jar,spring boot 版本不同则 transport-api.jar 不同,不能适配 es 版本,
7.x已经不建议使用,8以后就要废弃
- 9200端口
HTTP 协议,可使用多种 http 工具直接发送 http 请求
JestClient:非官方,更新慢
RestTemplate:模拟发HTTP请求,ES 很多操作需要自己封装,比较麻烦
HttpClient:同上
Elasticsearch-Rest-Client:官方 RestClient,封装了 ES 操作,API 层次分明,上手简单
最终选择 Elasticsearch-Rest-Client
文档地址:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high.html
spring boot 整合
- pom.xml 加入依赖,注意版本要与 es 一致
```xml
…
… 7.4.2
…
- 配置客户端 bean
> com.atguigu.gulimall.search.config.GulimallElasticSearchConfig
```java
@Configuration
public class GulimallElasticSearchConfig {
@Bean
public RestHighLevelClient esRestClient() {
return new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
}
}
官方文档:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-search.html
索引的增删查操作
创建索引
public static void testCreateIndex() throws IOException {
RestHighLevelClient esClient = createClient();
CreateIndexRequest createIndexRequest = new CreateIndexRequest("user");
CreateIndexResponse createIndexResponse = esClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);
boolean acknowledged = createIndexResponse.isAcknowledged();
esClient.close();
System.out.println("create index response: " + acknowledged);
}
获取索引
public static void testGetIndex() throws IOException {
RestHighLevelClient esClient = createClient();
GetIndexRequest getIndexRequest = new GetIndexRequest("product");
GetIndexResponse getIndexResponse = esClient.indices().get(getIndexRequest, RequestOptions.DEFAULT);
esClient.close();
System.out.println(getIndexResponse.getAliases());
System.out.println(getIndexResponse.getMappings());
}
删除索引
public static void testDeleteIndex() throws IOException {
RestHighLevelClient esClient = createClient();
DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("user");
AcknowledgedResponse acknowledgedResponse = esClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
esClient.close();
System.out.println("delete index response: " + acknowledgedResponse.isAcknowledged());
}
文档的增删改查操作
创建文档 ```java public static void testCreateDoc() throws IOException { RestHighLevelClient esClient = createClient();
IndexRequest indexRequest = new IndexRequest(); indexRequest.index(“user”).id(“1002”);
HashMap
map = new HashMap<>(); map.put(“name”, “jone”); map.put(“password”, “123”); indexRequest.source(JSON.toJSONString(map), XContentType.JSON); IndexResponse response = esClient.index(indexRequest, RequestOptions.DEFAULT); esClient.close();
System.out.println(“create doc result: “ + response.getResult()); }
// 批量插入文档 public static void teatBatchCreateDoc() throws IOException { RestHighLevelClient esClient = createClient();
BulkRequest bulkRequest = new BulkRequest();
for (int i = 0; i < 10; i++) {
IndexRequest indexRequest = new IndexRequest()
.index("user")
.id("100" + i)
.source(XContentType.JSON, "name", "name:" + i, "password", "password:" + i);
bulkRequest.add(indexRequest);
}
BulkResponse response = esClient.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println(response.getTook());
System.out.println(Arrays.toString(response.getItems()));
}
- 更新文档
```java
public static void testUpdateDoc() throws IOException {
RestHighLevelClient esClient = createClient();
UpdateRequest updateRequest = new UpdateRequest();
updateRequest.index("user").id("1001");
updateRequest.doc(XContentType.JSON, "name", "jone nea");
UpdateResponse response = esClient.update(updateRequest, RequestOptions.DEFAULT);
esClient.close();
System.out.println("update doc result: " + response.getGetResult());
}
删除文档 ```java public static void testDeleteDoc() throws IOException { RestHighLevelClient esClient = createClient();
DeleteRequest deleteRequest = new DeleteRequest(); deleteRequest.index(“user”).id(“1001”);
DeleteResponse response = esClient.delete(deleteRequest, RequestOptions.DEFAULT); esClient.close();
System.out.println(“delete doc result: “ + response.getResult()); }
// 批量删除文档 public static void testBatchDeleteDoc() throws IOException { RestHighLevelClient esClient = createClient();
BulkRequest bulkRequest = new BulkRequest();
for (int i = 0; i < 5; i++) {
DeleteRequest deleteRequest = new DeleteRequest();
deleteRequest.index("user").id("100" + i);
bulkRequest.add(deleteRequest);
}
BulkResponse response = esClient.bulk(bulkRequest, RequestOptions.DEFAULT);
esClient.close();
System.out.println(response.getTook());
System.out.println(Arrays.toString(response.getItems()));
}
- 根据id获取文档
```java
public static void testGetDoc() throws IOException {
RestHighLevelClient esClient = createClient();
GetRequest getRequest = new GetRequest();
getRequest.index("user").id("1006");
GetResponse response = esClient.get(getRequest, RequestOptions.DEFAULT);
esClient.close();
System.out.println(response.getSource().toString());
}
查询索引中的全部数据
public static void testQueryAll() throws IOException {
RestHighLevelClient esClient = createClient();
SearchRequest request = new SearchRequest();
request.indices("user");
request.source(new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()));
SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
SearchHits hits = response.getHits();
esClient.close();
System.out.println(hits.getTotalHits());
System.out.println(response.getTook());
for (SearchHit hit : hits) {
System.out.println(hit.getSourceAsString());
}
}
条件查询 : termQuery
public static void testQueryTerm() throws IOException {
RestHighLevelClient esClient = createClient();
SearchRequest request = new SearchRequest();
request.indices("user");
TermQueryBuilder termQuery = QueryBuilders.termQuery("age", "20");
SearchSourceBuilder query = new SearchSourceBuilder().query(termQuery);
request.source(query);
SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
esClient.close();
SearchHits hits = response.getHits();
System.out.println(hits.getTotalHits());
System.out.println(response.getTook());
for ( SearchHit hit : hits ) {
System.out.println(hit.getSourceAsString());
}
}
分页查询
public static void testQueryPage() throws IOException {
RestHighLevelClient esClient = createClient();
SearchRequest request = new SearchRequest();
request.indices("user");
SearchSourceBuilder builder = new SearchSourceBuilder().query(QueryBuilders.matchAllQuery());
// (当前页码 - 1) * 每页显示数据条数
builder.from(0);
// 每页显示数量
builder.size(4);
request.source(builder);
SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
esClient.close();
SearchHits hits = response.getHits();
System.out.println(hits.getTotalHits());
System.out.println(response.getTook());
for ( SearchHit hit : hits ) {
System.out.println(hit.getSourceAsString());
}
}
排序
public static void testQueryOrderBy() throws IOException {
RestHighLevelClient esClient = createClient();
SearchRequest request = new SearchRequest();
request.indices("user");
SearchSourceBuilder builder = new SearchSourceBuilder().query(QueryBuilders.matchAllQuery());
// 根据 age 反向排序
builder.sort("age", SortOrder.DESC);
request.source(builder);
SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
esClient.close();
SearchHits hits = response.getHits();
System.out.println(hits.getTotalHits());
System.out.println(response.getTook());
for ( SearchHit hit : hits ) {
System.out.println(hit.getSourceAsString());
}
}
只查询部分字段
public static void testQuerySource() throws IOException {
RestHighLevelClient esClient = createClient();
SearchRequest request = new SearchRequest();
request.indices("user");
SearchSourceBuilder builder = new SearchSourceBuilder().query(QueryBuilders.matchAllQuery());
// 声明不需要查询的字段
String[] excludes = {"age"};
// 声明需要查询的字段
String[] includes = {};
builder.fetchSource(includes, excludes);
request.source(builder);
SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
esClient.close();
SearchHits hits = response.getHits();
System.out.println(hits.getTotalHits());
System.out.println(response.getTook());
for ( SearchHit hit : hits ) {
System.out.println(hit.getSourceAsString());
}
}
范围查询
public static void testQueryScope() throws IOException {
RestHighLevelClient esClient = createClient();
SearchRequest request = new SearchRequest();
request.indices("user");
SearchSourceBuilder builder = new SearchSourceBuilder();
RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("age");
rangeQuery.gte(18);
rangeQuery.lt(28);
builder.query(rangeQuery);
request.source(builder);
SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
SearchHits hits = response.getHits();
System.out.println(hits.getTotalHits());
System.out.println(response.getTook());
for ( SearchHit hit : hits ) {
System.out.println(hit.getSourceAsString());
}
}
模糊查询
public static void testQueryFuzzy() throws IOException {
RestHighLevelClient esClient = createClient();
SearchRequest request = new SearchRequest();
request.indices("user");
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.fuzzyQuery("name", "name:6")
.fuzziness(Fuzziness.ZERO)); // Fuzziness.ZERO ONE TWO 代表相差n个字符
request.source(builder);
SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
esClient.close();
SearchHits hits = response.getHits();
System.out.println(hits.getTotalHits());
System.out.println(response.getTook());
for ( SearchHit hit : hits ) {
System.out.println(hit.getSourceAsString());
}
}
高亮查询
public static void testHighlightQuery() throws IOException {
RestHighLevelClient esClient = createClient();
SearchRequest request = new SearchRequest();
request.indices("user");
SearchSourceBuilder builder = new SearchSourceBuilder();
TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery("name", "name");
builder.query(termsQueryBuilder);
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.preTags("<font color='red'>");
highlightBuilder.postTags("</font>");
highlightBuilder.field("name");
builder.highlighter(highlightBuilder);
request.source(builder);
SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
esClient.close();
SearchHits hits = response.getHits();
System.out.println(hits.getTotalHits());
System.out.println(response.getTook());
for ( SearchHit hit : hits ) {
System.out.println(hit.getSourceAsString());
// 高亮标签拼接在这里
System.out.println(hit.getHighlightFields());
}
}
聚合查询
public static void testQueryAggregation() throws IOException {
RestHighLevelClient esClient = createClient();
SearchRequest request = new SearchRequest();
request.indices("user");
SearchSourceBuilder builder = new SearchSourceBuilder();
// 查询age最大值
AggregationBuilder aggregationBuilder = AggregationBuilders.max("maxAge").field("age");
// 按照age分组,并返回每组的数量
// AggregationBuilder aggregationBuilder = AggregationBuilders.terms("maxGroup").field("age");
builder.aggregation(aggregationBuilder);
request.source(builder);
SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
esClient.close();
SearchHits hits = response.getHits();
System.out.println(hits.getTotalHits());
System.out.println(response.getTook());
for ( SearchHit hit : hits ) {
System.out.println(hit.getSourceAsString());
}
}
复杂的查询
/**
* 测试查询数据
*/
@Test
public void searchData() throws IOException {
// 创建查询对象
SearchRequest searchRequest = new SearchRequest();
// 指定索引
searchRequest.indices("bank");
// 封装检索条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 构造检索条件
searchSourceBuilder.query(QueryBuilders.matchQuery("address", "mill"));
TermsAggregationBuilder ageAgg = AggregationBuilders.terms("ageAgg").field("age").size(10);
searchSourceBuilder.aggregation(ageAgg);
AvgAggregationBuilder balanceAvg = AggregationBuilders.avg("balanceAvg").field("balance");
searchSourceBuilder.aggregation(balanceAvg);
// 将查询条件传给查询请求对象
searchRequest.source(searchSourceBuilder);
// 发起查询请求,返回结果
SearchResponse searchResponse = client.search(searchRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);
// 获取聚集结果
Aggregations aggregations = searchResponse.getAggregations();
Terms ageAggRes = aggregations.get("ageAgg");
for (Terms.Bucket bucket : ageAggRes.getBuckets()) {
System.out.println(bucket.getKey() + " : " + bucket.getDocCount());
}
Avg balanceAvgRes = aggregations.get("balanceAvg");
System.out.println("balanceAvg: " + balanceAvgRes.getValue());
// 获取 _source 结果
SearchHit[] hits = searchResponse.getHits().getHits();
for (SearchHit hit : hits) {
System.out.println("docId: " + hit.docId());
System.out.println("id: " + hit.getId());
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
System.out.println("firstname: " + sourceAsMap.get("firstname"));
System.out.println("lastname: " + sourceAsMap.get("lastname"));
System.out.println("---");
}
}