Elasticsearch RestclientApi基础用法
查询
索引库
初始化
添加依赖
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
创建链接
package com.hmall.item.es;
import org.apache.http.HttpHost;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.xcontent.XContentType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.*;
/**
* @ Author qwh
* @ Date 2024/6/19 8:51
*/
class itemApplicationTest {
private RestHighLevelClient client;
//创建链接
@BeforeEach
void setup(){
this.client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.70.145:9200")
));
}
//测试链接
@Test
void testConnect(){
System.out.println(client);
}
//关闭链接
@AfterEach
void destroy() throws IOException {
this.client.close();
}
}
创建索引库
PUT /索引库名称
{
"mappings": {
"properties": {
"字段名":{
"type": "text",
"analyzer": "ik_smart"
},
"字段名2":{
"type": "keyword",
"index": "false"
},
"字段名3":{
"properties": {
"子字段": {
"type": "keyword"
}
}
},
// ...略
}
}
}
@Test
void CreateIndex() throws IOException {
//1
CreateIndexRequest request = new CreateIndexRequest("items");
//2
request.source(MAPPING_TEMPLATE, XContentType.JSON);
//3
client.indices().create(request, RequestOptions.DEFAULT);
}
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" +
"}";
删除索引库
DELETE /索引库名
//删除索引库
@Test
void DeleteIndex() throws IOException {
//创建请求对象
DeleteIndexRequest items = new DeleteIndexRequest("items");
//发送请求
client.indices().delete(items,RequestOptions.DEFAULT);
}
判断索引库是否存在
GET /索引库名
@Test
void GetIndex() throws IOException {
//发送请求
GetIndexRequest items = new GetIndexRequest("items");
boolean exists = client.indices().exists(items, RequestOptions.DEFAULT);
System.out.println(exists ? "索引库已经存在" : "索引不存在");
}
文档
新增文档
- 实体类
package com.hmall.item.domain.po;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@ApiModel(description = "索引库实体")
public class ItemDoc{
@ApiModelProperty("商品id")
private String id;
@ApiModelProperty("商品名称")
private String name;
@ApiModelProperty("价格(分)")
private Integer price;
@ApiModelProperty("商品图片")
private String image;
@ApiModelProperty("类目名称")
private String category;
@ApiModelProperty("品牌名称")
private String brand;
@ApiModelProperty("销量")
private Integer sold;
@ApiModelProperty("评论数")
private Integer commentCount;
@ApiModelProperty("是否是推广广告,true/false")
private Boolean isAD;
@ApiModelProperty("更新时间")
private LocalDateTime updateTime;
}
- api语法
POST /{索引库名}/_doc/1
{
"name": "Jack",
"age": 21
}
- java
- 1)创建Request对象,这里是IndexRequest,因为添加文档就是创建倒排索引的过程
- 2)准备请求参数,本例中就是Json文档
- 3)发送请求
package com.hmall.item.es;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hmall.common.utils.CollUtils;
import com.hmall.item.domain.po.Item;
import com.hmall.item.domain.po.ItemDoc;
import com.hmall.item.service.IItemService;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
import java.util.List;
/**
* @ Author qwh
* @ Date 2024/6/19 9:29
*/
@Slf4j
@SpringBootTest(properties = "spring.profiles.active=local")
public class itemDocTest {
private RestHighLevelClient client;
@Autowired
private IItemService iItemService;
@BeforeEach
void setup(){
client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.70.145:9200")
));
}
@Test //添加文档
void addDocument() throws IOException {
//根据id查询item 添加到es
Item item = iItemService.getById(100002644680L);
//转换为文档类型
ItemDoc itemDoc = BeanUtil.copyProperties(item, ItemDoc.class);
//将文档类型转换为json
String doc = JSONUtil.toJsonStr(itemDoc);
System.out.println(doc.toString());
// 1.准备Request对象
IndexRequest request = new IndexRequest("items").id(itemDoc.getId());
// 2.准备Json文档
request.source(doc, XContentType.JSON);
// 3.发送请求
client.index(request, RequestOptions.DEFAULT);
}
@AfterEach
void destroy() throws IOException {
this.client.close();
}
}
查询文档
- api
GET /{索引库名}/_doc/{id}
- java
- 1)准备Request对象。这次是查询,所以是GetRequest
- 2)发送请求,得到结果。因为是查询,这里调用client.get()方法
- 3)解析结果,就是对JSON做反序列化
@Test
void GetDocument() throws IOException {
GetRequest request = new GetRequest("items").id("100002644680");
//发送请求
GetResponse response = client.get(request, RequestOptions.DEFAULT);
//获取相应结果
String json = response.getSourceAsString();
ItemDoc itemDoc = JSONUtil.toBean(json, ItemDoc.class);
System.out.println("itemDoc"+itemDoc);
}
删除文档
- api
DELETE /{索引库}/_doc/{id}
- java
- 1)准备Request对象,因为是删除,这次是DeleteRequest对象。要指定索引库名和id
- 2)
准备参数,无参,直接省略 - 3)发送请求。因为是删除,所以是client.delete()方法
@Test
void deleteDocument() throws IOException {
//准备request 参数 索引库名 文档id
DeleteRequest request = new DeleteRequest("items", "100002644680");
client.delete(request,RequestOptions.DEFAULT);
}
修改文档
- 全量修改:本质是先根据id删除,再新增
- 局部修改:修改文档中的指定字段值
- api 局部修改
POST /{索引库名}/_update/{id}
{
"doc": {
"字段名": "字段值",
"字段名": "字段值"
}
}
- java
- 1)准备Request对象。这次是修改,所以是UpdateRequest
- 2)准备参数。也就是JSON文档,里面包含要修改的字段
- 3)更新文档。这里调用client.update()方法
//修改文档
@Test
void updateDocument() throws IOException {
//request
UpdateRequest request = new UpdateRequest("items", "100002644680");
//修改内容
request.doc("price",99999,"commentCount",1);
//发送请求
client.update(request,RequestOptions.DEFAULT);
}
批量导入
- java
- 创建Request,但这次用的是BulkRequest
- 准备请求参数
- 发送请求,这次要用到client.bulk()方法
//批量导入
@Test
void loadItemDocs() throws IOException {
//分页查询
int curPage = 1;
int size = 1000;
while (true){
Page<Item> page = iItemService.lambdaQuery().eq(Item::getStatus, 1).page(new Page<>(curPage, size));
//f非空校验
List<Item> items = page.getRecords();
if (CollUtils.isEmpty(items)) {
return;
}
log.info("加载第{}页数据共{}条",curPage,items.size());
//创建request
BulkRequest request = new BulkRequest("items");
//准备参数
for (Item item : items) {
ItemDoc itemDoc = BeanUtil.copyProperties(item, ItemDoc.class);
request.add(new IndexRequest().id(itemDoc.getId()).source(JSONUtil.toJsonStr(itemDoc),XContentType.JSON));
}
//发送请求
client.bulk(request,RequestOptions.DEFAULT);
curPage++;
}
}
文档操作的基本步骤:
- 初始化RestHighLevelClient
- 创建XxxRequest。
- XXX是Index、Get、Update、Delete、Bulk
- 准备参数(Index、Update、Bulk时需要)
- 发送请求。
- 调用RestHighLevelClient#.xxx()方法,xxx是index、get、update、delete、bulk
- 解析结果(Get时需要)
模糊查询
叶子查询
- 基本语法
GET /{索引库名}/_search
{
"query": {
"查询类型": {
// .. 查询条件
}
}
}
- GET /{索引库名}/_search:其中的_search是固定路径,不能修改
GET /items/_search
{
"query": {
"match_all": {
}
}
}
package com.hmall.item.es;
import cn.hutool.json.JSONUtil;
import com.hmall.common.utils.CollUtils;
import com.hmall.item.domain.po.ItemDoc;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* @ Author qwh
* @ Date 2024/6/19 16:16
*/
public class JavaAPITest {
private RestHighLevelClient client;
@BeforeEach
void setup(){
this.client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.70.145:9200")
));
}@Test
void SearchMatchAll() throws IOException {
//创建request
SearchRequest request = new SearchRequest("items");
//请求参数
request.source().query(QueryBuilders.matchAllQuery());
//解析相应
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
handleResponse(response);
}
private void handleResponse(SearchResponse response) {
SearchHits searchHits = response.getHits();
// 1.获取总条数
long total = searchHits.getTotalHits().value;
System.out.println("共搜索到" + total + "条数据");
// 2.遍历结果数组
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
// 3.得到_source,也就是原始json文档
String source = hit.getSourceAsString();
// 4.反序列化并打印
ItemDoc item = JSONUtil.toBean(source, ItemDoc.class);
System.out.println(item);
}
}
@AfterEach
void destroy() throws IOException {
this.client.close();
}
}
你会发现虽然是match_all,但是响应结果中并不会包含索引库中的所有文档,而是仅有10条。这是因为处于安全考虑,elasticsearch设置了默认的查询页数。
全文检索
- 用分词器对用户输入搜索条件先分词,得到词条,然后再利用倒排索引搜索词条。例如:
- match:
- multi_match
- 语法match:
GET /{索引库名}/_search
{
"query": {
"match": {
"字段名": "搜索条件"
}
}
}
实例
查询名字里有荣耀的
GET /items/_search
{
"query": {
"match": {
"name": "华为荣耀"
}
}
}
结果
{
"_index" : "items",
"_type" : "_doc",
"_id" : "21233662915",
"_score" : 18.592987,
"_source" : {
"id" : "21233662915",
"name" : "华为(HUAWEI) 华为 荣耀8 手机10 荣耀8X 智能手机 荣耀8X MAX 手机 荣耀8C 极光蓝 6GB+128GB",
"price" : 39900,
"image" : "https://m.360buyimg.com/mobilecms/s720x720_jfs/t28714/124/965064480/118844/84e42061/5c036e18N0f680a90.jpg!q70.jpg.webp",
"category" : "手机",
"brand" : "华为",
"sold" : 0,
"commentCount" : 0,
"isAD" : false,
"updateTime" : 1556640000000
}
},
@Test //match
void matchQuery() throws IOException {
//创建request
SearchRequest request = new SearchRequest("items");
//请求参数
request.source().query(QueryBuilders.matchQuery("name","华为"));
//解析相应
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
handleResponse(response);
}
- multi_match
GET /{索引库名}/_search
{
"query": {
"multi_match": {
"query": "搜索条件",
"fields": ["字段1", "字段2"]
}
}
}
例如
查询品牌和名称都又华为的
GET /items/_search
{
"query": {
"multi_match": {
"query" : "华为",
"fields" : ["name","brand"]
}
}
}
"_source" : {
"id" : "28922228139",
"name" : "华为(HUAWEI)华为P20手机 极光色 全网通(6GB+64GB)(华为直供,送礼包)",
"price" : 5100,
"image" : "https://m.360buyimg.com/mobilecms/s720x720_jfs/t22243/131/709951731/332063/e5c95a29/5b15f92eN1f0677a5.jpg!q70.jpg.webp",
"category" : "手机",
"brand" : "华为",
"sold" : 0,
"commentCount" : 0,
"isAD" : false,
"updateTime" : 1556640000000
}
}
@Test //multi_match
void multi_matchQuery() throws IOException {
//创建request
SearchRequest request = new SearchRequest("items");
//请求参数
request.source().query(QueryBuilders.multiMatchQuery("name","华为","小米"));
//解析相应
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
handleResponse(response);
}
精确查询
- Term-level query不对用户输入搜索条件分词,根据字段内容精确值匹配。但只能查找keyword、数值、日期、boolean类型的字段。例如:ids term range 语法
- term
GET /{索引库名}/_search
{
"query": {
"term": {
"字段名": {
"value": "搜索条件"
}
}
}
}
GET /items/_search
{
"query": {
"term": {
"brand": {
"value":"华为"
}
}
}
}
{
"id" : "4093553",
"name" : "华为畅享7 Plus 3GB+32GB 普通版 香槟金 移动联通电信4G手机 双卡双待",
"price" : 26600,
"image" : "https://m.360buyimg.com/mobilecms/s720x720_jfs/t7804/212/1308671016/161589/6dd9c5fc/599c0da5N88410391.jpg!q70.jpg.webp",
"category" : "手机",
"brand" : "华为",
"sold" : 0,
"commentCount" : 0,
"isAD" : false,
"updateTime" : 1556640000000
}
}
@Test //term
void termQuery() throws IOException {
//创建request
SearchRequest request = new SearchRequest("items");
//请求参数 // 500-9999
request.source().query(QueryBuilders.termQuery("brand","华为"));
//解析相应
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
handleResponse(response);
}
- range
range是范围查询,对于范围筛选的关键字有:
- gte:大于等于
- gt:大于
- lte:小于等于
- lt:小于
GET /{索引库名}/_search
{
"query": {
"range": {
"字段名": {
"gte": {最小值},
"lte": {最大值}
}
}
}
}
GET /items/_search
{
"query": {
"range": {
"price": {
"gte": 100,
"lte": 2000
}
}
}
}
{
"id" : "1381868",
"name" : "ITO 拉杆箱20英寸旅行箱 商务时尚登机箱行李箱静音万向轮男女密码箱 CLASSIC 银色磨砂",
"price" : 900,
"image" : "https://m.360buyimg.com/mobilecms/s720x720_jfs/t4591/120/2388880972/76319/a3cda346/58ef603bN91f7daf4.jpg!q70.jpg.webp",
"category" : "拉杆箱",
"brand" : "ITO",
"sold" : 0,
"commentCount" : 0,
"isAD" : false,
"updateTime" : 1556640000000
}
}
@Test //range
void rangeQuery() throws IOException {
//创建request
SearchRequest request = new SearchRequest("items");
//请求参数 // 500-9999
request.source().query(QueryBuilders.rangeQuery("price").gte(500).lte(9999));
//解析相应
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
handleResponse(response);
}
- bool查询
- bool查询,即布尔查询。就是利用逻辑运算来组合一个或多个查询子句的组合。bool查询支持的逻辑运算有:
- must:必须匹配每个子查询,类似“与”
- should:选择性匹配子查询,类似“或”
- must_not:必须不匹配,不参与算分,类似“非”
- filter:必须匹配,不参与算分
- 其中输入框的搜索条件肯定要参与相关性算分,可以采用match。但是价格范围过滤、品牌过滤、分类过滤等尽量采用filter,不要参与相关性算分
GET /items/_search
{
"query": {
"bool": { //查询方式
"must": [ //查询条件1 与 名称必须为 小米
{"match": {"name": "手机"}}
],
"should": [ //查询条件2 或 品牌为 小米 或者 vivo
{"term": {"brand": { "value": "vivo" }}},
{"term": {"brand": { "value": "小米" }}}
], //查询条件2 非 价格不是小于等于 2500的
"must_not": [
{"range": {"price": {"gte": 2500}}}
],
"filter": [ //对于用户输入的关键字的搜索,都有匹配度相关问题,需要参与算分;对于过滤性的条件,一般不参与算分
{"range": {"price": {"lte": 1000}}}
]
}
}
}
// 搜索手机,但品牌必须是华为,价格必须是900~1599
GET /items/_search
{
"query": {
"bool": {
"must": [
{"match": {"name": "手机"}}
],
"filter": [
{"term": {"brand": { "value": "华为" }}},
{"range": {"price": {"gte": 90000, "lt": 159900}}}
]
}
}
}
@Test //复合查询
void compareQuery() throws IOException {
// 1.创建Request
SearchRequest request = new SearchRequest("items");
// 2.组织请求参数
// 2.1.准备bool查询
BoolQueryBuilder bool = QueryBuilders.boolQuery();
// 2.2.关键字搜索
bool.must(QueryBuilders.matchQuery("name", "脱脂牛奶"));
// 2.3.品牌过滤
bool.filter(QueryBuilders.termQuery("brand", "德亚"));
// 2.4.价格过滤
bool.filter(QueryBuilders.rangeQuery("price").lte(30000));
request.source().query(bool);
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
handleResponse(response);
}
- elasticsearch默认是根据相关度算分(_score)来排序,但是也支持自定义方式对搜索结果排序。不过分词字段无法排序,能参与排序字段类型有:keyword类型、数值类型、地理坐标类型、日期类型等。
GET /indexName/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"排序字段": {
"order": "排序方式asc和desc"
}
}
]
}
查询 所有按照价格排序
GET /items/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"price": {
"order": "desc"
}
}
]
}
- 分页
elasticsearch 默认情况下只返回top10的数据。而如果要查询更多数据就需要修改分页参
elasticsearch中通过修改from、size参数来控制要返回的分页结果:
- from:从第几个文档开始
- size:总共查询几个文档
类似于mysql中的limit ?, ?
GET /items/_search
{
"query": {
"match_all": {}
},
"from": 0, // 分页开始的位置,默认为0
"size": 10, // 每页文档数量,默认10
"sort": [
{
"price": {
"order": "desc"
}
}
]
}
@Test //排序和分页
void pageQuery() throws IOException {
int pageNo = 1, pageSize = 5;
// 1.创建Request
SearchRequest request = new SearchRequest("items");
// 2.组织请求参数
// 2.1.搜索条件参数
request.source().query(QueryBuilders.matchQuery("name", "脱脂牛奶"));
// 2.2.排序参数
request.source().sort("price", SortOrder.ASC);
// 2.3.分页参数
request.source().from((pageNo - 1) * pageSize).size(pageSize);
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
handleResponse(response);
}
- 深度分页
elasticsearch的数据一般会采用分片存储,也就是把一个索引中的数据分成N份,存储到不同节点上。这种存储方式比较有利于数据扩展,但给分页带来了一些麻烦。
比如一个索引库中有100000条数据,分别存储到4个分片,每个分片25000条数据。现在每页查询10条,查询第99页。那么分页查询的条件如下:
GET /items/_search
{
"from": 990, // 从第990条开始查询
"size": 10, // 每页查询10条
"sort": [
{
"price": "asc"
}
]
}
- 高亮
- 高亮词条都被加了标签 标签都添加了红色样式
GET /{索引库名}/_search
{
"query": {
"match": {
"搜索字段": "搜索关键字"
}
},
"highlight": {
"fields": {
"高亮字段名称": {
"pre_tags": "<em>",
"post_tags": "</em>"
}
}
}
}
GET /items/_search
{
"query": {
"match": {
"name": "华为"
}
},
"highlight": {
"fields": {
"name": {
"pre_tags": "<em>",
"post_tags": "</em>"
}
}
}
}
{
"id" : "28922228139",
"name" : "华为(HUAWEI)华为P20手机 极光色 全网通(6GB+64GB)(华为直供,送礼包)",
"price" : 5100,
"image" : "https://m.360buyimg.com/mobilecms/s720x720_jfs/t22243/131/709951731/332063/e5c95a29/5b15f92eN1f0677a5.jpg!q70.jpg.webp",
"category" : "手机",
"brand" : "华为",
"sold" : 0,
"commentCount" : 0,
"isAD" : false,
"updateTime" : 1556640000000
},
"highlight" : {
"name" : [
"<em>华</em><em>为</em>(HUAWEI)<em>华</em><em>为</em>P20手机 极光色 全网通(6GB+64GB)(<em>华</em><em>为</em>直供,送礼包)"
]
}
},
@Test
void HighlightQuery() throws IOException {
// 1.创建Request
SearchRequest request = new SearchRequest("items");
// 2.组织请求参数
// 2.1.query条件
request.source().query(QueryBuilders.matchQuery("name", "脱脂牛奶"));
// 2.2.高亮条件
request.source().highlighter(
SearchSourceBuilder.highlight()
.field("name")
.preTags("<em>")
.postTags("</em>")
);
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
handleResponse1(response);
}
private void handleResponse1(SearchResponse response) {
SearchHits searchHits = response.getHits();
// 1.获取总条数
long total = searchHits.getTotalHits().value;
System.out.println("共搜索到" + total + "条数据");
// 2.遍历结果数组
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
// 3.得到_source,也就是原始json文档
String source = hit.getSourceAsString();
// 4.反序列化
ItemDoc item = JSONUtil.toBean(source, ItemDoc.class);
// 5.获取高亮结果
Map<String, HighlightField> hfs = hit.getHighlightFields();
if (CollUtils.isNotEmpty(hfs)) {
// 5.1.有高亮结果,获取name的高亮结果
HighlightField hf = hfs.get("name");
if (hf != null) {
// 5.2.获取第一个高亮结果片段,就是商品名称的高亮值
String hfName = hf.getFragments()[0].string();
item.setName(hfName);
}
}
System.out.println(item);
}
}
地理坐标查询
- 用于搜索地理位置,搜索方式很多,例如:
- geo_bounding_box:按矩形搜索
- geo_distance:按点和半径搜索
- …略
聚合函数
@Test
void testAgg() throws IOException {
// 1.创建Request
SearchRequest request = new SearchRequest("items");
// 2.准备请求参数
BoolQueryBuilder bool = QueryBuilders.boolQuery()
.filter(QueryBuilders.termQuery("category", "手机"))
.filter(QueryBuilders.rangeQuery("price").gte(300000));
request.source().query(bool).size(0);
// 3.聚合参数
request.source().aggregation(
AggregationBuilders.terms("brand_agg").field("brand").size(5)
);
// 4.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 5.解析聚合结果
Aggregations aggregations = response.getAggregations();
// 5.1.获取品牌聚合
Terms brandTerms = aggregations.get("brand_agg");
// 5.2.获取聚合中的桶
List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
// 5.3.遍历桶内数据
for (Terms.Bucket bucket : buckets) {
// 5.4.获取桶内key
String brand = bucket.getKeyAsString();
System.out.print("brand = " + brand);
long count = bucket.getDocCount();
System.out.println("; count = " + count);
}
}