Elasticseach RestClient Api

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 ? "索引库已经存在" : "索引不存在");

    }
文档
新增文档
  1. 实体类
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;
}
  1. api语法
POST /{索引库名}/_doc/1
{
    "name": "Jack",
    "age": 21
}    
  1. 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();
    }
}

查询文档
  1. api
GET /{索引库名}/_doc/{id}
  1. 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);

    }
删除文档
  1. api
DELETE /{索引库}/_doc/{id}
  1. 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删除,再新增
  • 局部修改:修改文档中的指定字段值
  1. api 局部修改
POST /{索引库名}/_update/{id}
{
  "doc": {
    "字段名": "字段值",
    "字段名": "字段值"
  }
}
  1. 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);

    }
批量导入
  1. 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);
    }
}
  • 12
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值