Elasticseach RestClient Api

news2024/11/16 7:55:49
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);
    }
}

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

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

相关文章

今日分享丨点亮这四个技能,你也可以成为可视化专家

引言 以大数据、人工智能等为代表的新质生产力时代已悄然而至&#xff0c;央企、国企逐步意识到数据资源展示对于经营管理的重要性和紧迫性。数据可视化成为连接用户与数据的桥梁&#xff0c;藉由设计师的巧手&#xff0c;把复杂抽象的数据以基于管理需求&#xff0c;转化为直…

PyTorch -- RNN 快速实践

RNN Layer torch.nn.RNN(input_size,hidden_size,num_layers,batch_first) input_size: 输入的编码维度hidden_size: 隐含层的维数num_layers: 隐含层的层数batch_first: True 指定输入的参数顺序为&#xff1a; x&#xff1a;[batch, seq_len, input_size]h0&#xff1a;[batc…

探究C语言函数栈帧的创建和销毁

引言 在C语言程序中&#xff0c;每当一个函数被调用时&#xff0c;系统都会在栈上为该函数分配一块内存空间&#xff0c;这块内存空间就被称为栈帧。 栈帧中包含了函数执行所需的所有信息&#xff0c;如局部变量、参数、返回地址等。栈帧的创建和销毁是函数调用的核心部分&am…

【华为HCIA数通网络工程师真题-数据通信与网络基础】

文章目录 选择题判断题 选择题 1、在 VRP 平台上&#xff0c;可以通过下面哪种方式访向上条历史命令&#xff1f; 上光标 &#xff08;ctrlU 为自定义快捷键&#xff0c;ctrlP 为显示历史缓存区的前一条命令&#xff0c;左光标为移动光标&#xff09; 2、主机 A &#xff08;1…

TensorRT-常见问题

1、ModelImporter.cpp:779: ERROR: builtin_op_importers.cpp:3608 In function importResize:[8] Assertion failed: scales.is_weights() && "Resize scales must be an initializer!"解决方法&#xff1a;将TensorRT版本升到可以匹配cuda版本的最高版本&a…

多态性(Java)

本篇学习面向对象语言的第三个特性——多态。 目录 1、多态的概念 2、继承多态实现条件 3、重写 4、重新与重载的区别&#xff1a; 5、向上转移和向下转型 5、1向上转型&#xff1a; 5、2 向下转型 1、多态的概念 多态的概念&#xff1a;通俗来说&#xff0c;就是多种形态…

Servlet实践操作

Servlet运行原理 Tomcat 的代码内置了 main 方法&#xff0c;当我们启动 Tomcat 的时候&#xff0c;就是从 Tomcat 的 main 方法开始执行的 被 WebServlet 注解修饰的类会在 Tomcat 启动的时候就被获取并集中管理 Tomcat 通过反射这样的语法机制来创建被 WebServlet 注解修饰…

Day 27:2596. 检查骑士巡视方案

Leetcode 2596. 检查骑士巡视方案 骑士在一张 n x n 的棋盘上巡视。在 **有效 **的巡视方案中&#xff0c;骑士会从棋盘的 左上角 出发&#xff0c;并且访问棋盘上的每个格子 恰好一次 。 给你一个 n x n 的整数矩阵 grid &#xff0c;由范围 [0, n * n - 1] 内的不同整数组成&…

超神级!Markdown最详细教程,程序员的福音

超神级&#xff01;Markdown最详细教程&#xff0c;程序员的福音Markdown最详细教程&#xff0c;关于Markdown的语法和使用就先讲到这里&#xff0c;如果喜欢&#xff0c;请关注“IT技术馆”。馆长会更新​最实用的技术&#xff01;https://mp.weixin.qq.com/s/fNzhLFyYRd3skG-…

linux环境编程基础学习

Shell编程&#xff1a; 相对的chmod -x xx.sh可以移除权限 想获取变量的值要掏点dollar&#xff08;&#xff04;&#xff09; 多位的话要加个花括号 运算&#xff1a;expr 运算时左右两边必须要加空格 *号多个含义必须加转义符 双引号可以加反单&#xff0c;但是发过来就不行 …

containerd手动配置容器网络

containerd手动配置容器网络 机器详情nerdctl启动一个不带网络的容器获取容器ID、PID与network namespace路径准备bridge插件的执行配置文件通过下面的命令调用bridge插件准备tuning插件文件执行下面的命令调用tuning插件准备portmap插件文件执行下面的命令调用portmap插件删除…

算法竞赛数论杂题

menji 和 gcd 题目&#xff1a; 一开始以为是只有l不确定&#xff0c;r是确定的&#xff0c;这样的话我们可以枚举r的所有约数&#xff0c;然后对其每个约数x进行判断&#xff0c;判断是否满足题意&#xff0c;具体做法是先让l % x如果 0则该约数可行&#xff0c;如果不可行…

文件扫描工具都有哪些?职场大佬都在用的文本提取工具大盘点~

回想起刚毕业初入职场那阵子&#xff0c;领导让帮忙把纸质文件扫描提取为文本时&#xff0c;还只会傻乎乎地一点点操作&#xff0c;属实是费劲得很&#xff01; 好在后面受朋友安利&#xff0c;找到了4个能够快速实现文件扫描文字提取的方法&#xff0c;这才让我的办公效率蹭蹭…

[SCAU 课程设计参考] 活动管理程序

(仅供参考!!!!!!) 废话不多说&#xff0c;直接上代码&#xff01;(但是量有点多&#xff0c;放前面影响观感&#xff0c;所以还是先不放了&#xff0c;文章末尾有链接) 题目的要求: 提要:我的设计只是一个参考&#xff0c;当时还是大一的时候写的&#xff0c;代码比较青涩&a…

[学习笔记]-MyBatis-Plus简介

简介 Mybatis-Plus&#xff08;简称 MP&#xff09;是一个 MyBatis (opens new window)的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。 简言之就是对单表的增删改查有了很好的封装。基本不用再单独写sql语句了。目前此类…

微博舆情分析系统可以继续完善的基于python 前端vue

微博舆情分析系统可以继续完善的&#xff0c;前后端分离&#xff0c;前端基于vue 后端基于python的flask可以说是非常的简洁&#xff0c;支持实时更新数据。界面如图 主要工作点体现在后端实时更新数据跟数据的处理方面上&#xff0c;后续有空会用hadoop来处理海量数据真…

数据库 | 试卷四

1.数据库系统的特点是 数据共享、减少数据冗余、数据独立、避免了数据不一致和加强了数据保护 2.关系模型的数据结构是二维表结构 3.聚簇索引 cluster index 4. 这里B&#xff0c;C都是主属性&#xff0c;所以B->C不是非主属性对码的部分函数依赖 候选键&#xff08;AC&a…

Jlink下载固件到RAM区

Jlink下载固件到RAM区 准备批处理搜索exe批处理调用jlink批处理准备jlink脚本 调用执行 环境&#xff1a;J-Flash V7.96g 平台&#xff1a;arm cortex-m3 准备批处理 搜索exe批处理 find_file.bat echo off:: 自动识别脚本名和路径 set "SCRIPT_DIR%~dp0" set &qu…

开发者黑板报#65

第65期 AI 谷歌Gemini 终于&#xff0c;GPT-4独霸时代终结了&#xff01; 过去一个月里&#xff0c;四款大模型横空出世&#xff0c;在各项关键基准测试中与GPT-4相匹敌&#xff0c;甚至更胜一筹。 谷歌Gemini 1.5突破100万个tokens&#xff0c;是GPT-4的近8倍&#xff0c…

【Docker】——安装镜像和创建容器,详解镜像和Dockerfile

前言 在此记录一下docker的镜像和容器的相关注意事项 前提条件&#xff1a;已安装Docker、显卡驱动等基础配置 1. 安装镜像 网上有太多的教程&#xff0c;但是都没说如何下载官方的镜像&#xff0c;在这里记录一下&#xff0c;使用docker安装官方的镜像 Docker Hub的官方链…