初始化

查看官方网站文档
image.png

ES支持很多中语言的客户端,使用java REST Client
image.png

选择对应的版本。选择7.6。
提供了两个级别的客户端,选择高级的。
image.png
项目开始:
image.png
image.png

添加maven依赖之前,首先要是spring项目,我们直接使用springboot。
我们使用springboot,只需要添加对应的场景启动器,springboot会自动添加对应的依赖。
但是要注意的是,springboot仲裁了依赖版本,可能和我们需要的版本是不一致的,我们要自己调整版本。

  1. <!-- 添加ES的场景启动器的依赖 -->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
  5. </dependency>

如果依赖的版本和我们使用的ES的版本一致,不需要做任何调整,如果不一致,需要调整。
image.png

官方给出的初始化代码:
image.png

创建一个工程:

image.png

pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.5.RELEASE</version>
    </parent>

    <groupId>com.st</groupId>
    <artifactId>ES-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <!--覆盖mysql驱动版本-->
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!-- 单元测试场景启动 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!--添加ES的场景启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
    </dependencies>

</project>


ESClient的使用**

准备一个索引:

PUT product
{
  "mappings": {
    "properties": {
      "title":{
        "type":"text",
        "analyzer": "ik_max_word"
      },
      "sellpoint":{
        "type":"text",
        "analyzer": "ik_max_word"
      },
      "price":{
        "type":"integer"
      },
      "created":{
        "type": "date"
      }
    }
  }
}


[1]添加索引**

/**
 * @author:刘倩云
 * @createTime:2021-04-13
 */
@SpringBootTest
@RunWith(SpringRunner.class)
public class ESApplicationTest {
    //注入客户端
    @Autowired
    private RestHighLevelClient client;

    //添加索引
    @Test
    public void testAddIndex() throws IOException {
        //创建索引请求对象 POST /索引名字/类型/文档id
        IndexRequest request = new IndexRequest("product");
        // 设置文档id,如果不设置id会随机生成一个id
        request.id("1");
        //设置类型 在7.x之后,就建议使用默认的类型,而不需要设置类型,默认类型是"_doc" \
        // request.type("emp");
        //创建json数据
        Map data = new HashMap<>();
        data.put("title","Populele2二代智能尤克里里23英寸复合碳纤维材质小吉他学生乌克丽丽初学者ukulele 黑色");
        data.put("sellpoint","【智能尤克里里】【7天价保】直降立省50元,到手价仅549!联系客服另赠惊喜好礼!八仓发货,极速送达!【立即抢购】");
        data.put("price",500);
        data.put("created",new Date());
        //将map转换成json数据
        String json = new ObjectMapper().writeValueAsString(data);
        //添加数据 (json字符串,source类型)
        request.source(json, XContentType.JSON);
        //提交索引
        IndexResponse response = client.index(request, RequestOptions.DEFAULT);
        //这里的IndexResponse提供了大量的API方便我们获取添加之后响应的json数据
        System.out.println(response);
    }
}

image.png
上一个案例中,将map转换为json添加,实际上可以直接将map添加到索引库:

 //添加索引 Map
    @Test
    public void testAddIndexMap() throws IOException {
        //创建索引请求对象 POST /索引名字/类型/文档id
        IndexRequest request = new IndexRequest("product");
        // 设置文档id,如果不设置id会随机生成一个id
        request.id("2");
        // 创建数据
        Map data = new HashMap();
        data.put("title","KAKA卡卡 KUC-25D 尤克里里乌克丽丽ukulele单板桃花心木迷你小吉他23英寸款");
        data.put("sellpoint","4.11大牌闪购~暖春特惠】【下单送首购礼金】【进阶款单板升级款相思木】【加购送教学陪练和精品豪礼】【京库配送】【蕞快当日达】~点击抢购");
        data.put("price",498);
        data.put("created",new Date());
        //将数据添加到source
        request.source(data);
        //提交索引
        IndexResponse response = client.index(request,RequestOptions.DEFAULT);
        //这里的IndexResponse提供了大量的API方便我们获取添加之后响应的json数据
        System.out.println(response);
    }

RestHighLevelClient的index方法会返回一个IndexResponse对象,这个对象就是一个添加之后响应的json字符串。
提供了api可以让我们获取这个json中的所有的数据
image.png
直接key-value形式添加文档

//添加索引 key-value
    @Test
    public void testAddIndexKeyAndValue() throws IOException {
        //创建索引请求对象 POST /索引名字/类型/文档id
        IndexRequest request = new IndexRequest("product");
        // 设置文档id,如果不设置id会随机生成一个id
        request.id("3");
        // 添加数据
        request.source(
                "title","雅马哈(YAMAHA)雅马哈吉他FG800VN美国型号单板民谣吉他木吉它复古木色亮光41英寸",
                "sellpoint","【闪购狂欢】北美爆款FG800VN,闪购优惠260元~手价仅1939元,活动时间(4月11号0点-4月13号23点)抓紧加购!!点击",
                "price",1999,
                "created",new Date()
        );
        //提交索引
        IndexResponse response = client.index(request,RequestOptions.DEFAULT);
        //这里的IndexResponse提供了大量的API方便我们获取添加之后响应的json数据
        System.out.println(response);
    }

其他设置
设置超时时间:

//设置超时时间 为10s request.timeout(“10s”);

springboot对ESClient的自动配置

上面config的配置类手动创建的,实际的时候可以使用springboot自动配置。
在springboot的自动配置类:ElasticsearchRestClientAutoConfiguration
image.png
image.png
配置方式的查看:
image.png

我们可以在springboot的配置文件中添加配置:

spring:
  #ES客户端配置#
  elasticsearch:
    rest:
      #ES的服务器地址列表,如果是集群,就需要配置多个#
      uris:
        - http://192.168.37.190:9200
      read-timeout: 30s
      connection-timeout: 5s

将我们自己配置的ESClient注释。再测试。

批量的添加索引

    //批量添加索引
    @Test
    public void testBulk() throws IOException{
        //准备两套数据
        Map data1 = new HashMap<>();
        data1.put("title","Gibson吉普森Firebird火鸟Gallery美术馆3号 蜂鸟Doves鸽子民谣吉他 木吉他 SJ200 Silver Gallery");
        data1.put("sellpoint","4.11大牌闪购~暖春特惠】【下单送首购礼金】【进阶款单板升级款相思木】【加购送教学陪练和精品豪礼】【京库配送】【蕞快当日达】~点击抢购");
        data1.put("price",15000);
        data1.put("created",new Date());

        Map data2 = new HashMap<>();
        data2.put("title","戴尔dell成就3681办公商用台式机电脑主机(英特尔酷睿i3-10100 8G 1T 三年上门售后)+21.5英寸电脑显示器");
        data2.put("sellpoint","全新升级版十代酷睿爆款小机箱上市,更强动力,成就高效,提前预定更享首发好价,点击抢先体验");
        data2.put("price",5000);
        data2.put("created",new Date());

        List<Map> list = new ArrayList<>();
        list.add(data1);
        list.add(data2);
        //准备一个批处理的对象
        BulkRequest bulkRequest = new BulkRequest();
        for(Map map : list){
            //使用IndexRequest循环添加  这个不是批处理,就是循环一个一个的添加
//            IndexRequest request = new IndexRequest("product");
//            request.source(map);
//            client.index(request,RequestOptions.DEFAULT);
            //--批处理操作方式--
            //创建IndexRequest
            IndexRequest request = new IndexRequest("product");
            request.source(map);
            //将request加入批处理对象
            bulkRequest.add(request);
        }
        //批处理添加
        BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
        System.out.println(response);
    }

修改和删除索引

修改

只要索引的id存在就是修改。 使用UpdateRequest

//修改索引
@Test
public void testUpdateIndex() throws IOException {
    UpdateRequest request = new UpdateRequest("product","3");
    Map map = new HashMap();
    map.put("title","雅马哈(YAMAHA)雅马哈吉他FG800VN美国型号单板民谣吉他木吉它复古木色亮光42英寸");
    //将map加入request
    request.doc(map);
    UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
    System.out.println(response);
}

删除

//删除索引
@Test
public void testDeletIndex() throws IOException {
    DeleteRequest request = new DeleteRequest("product","J2JCyXgBaEE5g_LwIR-w");
    DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
    System.out.println(response);
}

通过ID查询

GET API 查询之后得到是一个GetResponse,其中提供了大量API获取查询的结果

//根据ID查询
@Test
public void testQueryById(){
    GetRequest request = new GetRequest("product","2");
    GetResponse response = null;
    try {
        response = client.get(request, RequestOptions.DEFAULT);
        System.out.println(response);
    } catch (IOException e) {
        System.out.println(e);
    }
}

查询之后得到的response对象的toString结构:

{
  "_index": "product",
  "_type": "_doc",
  "_id": "2",
  "_version": 2,
  "_seq_no": 7,
  "_primary_term": 1,
  "found": true,
  "_source": {
    "sellpoint": "4.11大牌闪购~暖春特惠】【下单送首购礼金】【进阶款单板升级款相思木】【加购送教学陪练和精品豪礼】【京库配送】【蕞快当日达】~点击抢购",
    "price": 498,
    "created": "2021-04-13T03:23:35.823Z",
    "title": "KAKA卡卡 KUC-25D 尤克里里乌克丽丽ukulele单板桃花心木迷你小吉他23英寸款"
  }
}

这个结果就是我们在kibana中使用 GET /product/_doc/2查询的结果:
其中_source中存储的是查询的结果数据。
GetResponse提供了专门的API可以轻松获取其中的数据:
image.png
可以这样处理:

//根据ID查询
@Test
public void testQueryById(){
    GetRequest request = new GetRequest("product","2");
    GetResponse response = null;
    try {
        response = client.get(request, RequestOptions.DEFAULT);
        if(response.isExists()) {
            //直接得到map
            Map<String, Object> source = response.getSource();
            System.out.println(source);
        }else{
            System.out.println("没有查询到任何数据");
        }
    } catch (IOException e) {
        System.out.println(e);
    }
}


Search查询一个列表

简单的查询

不设置任何查询条件,查询所有数据

//使用search查询所有
@Test
public void testSearchAll() throws IOException {
    SearchRequest request = new SearchRequest("product");
    //不设置条件
    SearchResponse respons = client.search(request, RequestOptions.DEFAULT);
    System.out.println(respons);
}

response结果

{
  "took": 2,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 6,
      "relation": "eq"
    },
    "max_score": 1.0,
    "hits": [
      {
        "_index": "product",
        "_type": "_doc",
        "_id": "2",
        "_score": 1.0,
        "_source": {
          "sellpoint": "4.11大牌闪购~暖春特惠】【下单送首购礼金】【进阶款单板升级款相思木】【加购送教学陪练和精品豪礼】【京库配送】【蕞快当日达】~点击抢购",
          "price": 498,
          "created": "2021-04-13T03:23:35.823Z",
          "title": "KAKA卡卡 KUC-25D 尤克里里乌克丽丽ukulele单板桃花心木迷你小吉他23英寸款"
        }
      },
      .......
    ]
  }
}

查询的结果是存储在hits里面的hits中:
返回的结果对象SearchResponse中提供了大量的API,用来获取返回的结果。
查询的数据是放在SearchResponse对象的属性hits中的,源码中的类型是:
image.png
按照迭代器的方式迭代结果集:

//使用search查询所有
@Test
public void testSearchAll() throws IOException {
    SearchRequest request = new SearchRequest("product");
    //不设置条件
    SearchResponse respons = client.search(request, RequestOptions.DEFAULT);
    System.out.println(respons);
    //得到hits
    SearchHits hits = respons.getHits();
    System.out.println("查询的总条数:"+hits.getHits().length);
    //按照iterator的方式迭代
    for(SearchHit hit : hits){
        System.out.println("分数:"+hit.getScore());
        System.out.println("结果:"+hit.getSourceAsMap());
    }
}


带条件查询

可以组合各种查询条件,然后将条件装入:SearchSourceBuilder。 SearchSourceBuilder放入request进行查询。

    //使用search进行条件查询
    @Test
    public void testQuery() throws IOException {
        //创建SearchRequest (传入索引库的名字)
        SearchRequest request = new SearchRequest("product");

        //使用SearchSourceBuilder设置查询条件条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //这里需要传入QueryBuilder
        searchSourceBuilder.query(new MatchQueryBuilder("title","电脑"));

        //设置查询体
        request.source(searchSourceBuilder);
        //查询
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        System.out.println(response);
        //得到hits
        SearchHits hits = response.getHits();
        System.out.println("查询的总条数:"+hits.getHits().length);
        //按照iterator的方式迭代
//        for(SearchHit hit : hits.getHits()){
        for(SearchHit hit : hits){
            System.out.println("分数:"+hit.getScore());
            System.out.println("结果:"+hit.getSourceAsMap());
        }
    }

QueryBuilder说明: 所有条件对象的统一接口。
我们可以创建各种不同的条件,也可以按照需求进行组合。
比如:

BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.must(new MatchQueryBuilder("title","吉他"));
boolQueryBuilder.must(new MatchQueryBuilder("title","尤克里里"));
boolQueryBuilder.should(new MatchQueryBuilder("title","泰勒"));
searchSourceBuilder.query(boolQueryBuilder);

在kibana中的写法:

GET product/_search
{
  "query": {
    "bool": {
      "must": [
        {"match": {
          "title": "吉他"
           }
        }
        ,
         { "match": {
            "title": "尤克里里"
            }
         }
      ],"should": [
        {"match": {
          "title": "泰勒"
        }}
      ]
    }
  }
}

使用工具类QueryBuilders完成条件的设置

image.png
实际上工具类的内部就是自己new了一个条件对象:
image.png

范围查询

//使用search进行条件查询
@Test
public void testQueryRange() throws IOException {
    //创建SearchRequest (传入索引库的名字)
    SearchRequest request = new SearchRequest("product");
    //使用SearchSourceBuilder设置查询条件条件
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    //使用QueryBuilders创建条件  范围查询
    searchSourceBuilder.query(QueryBuilders.rangeQuery("price").from(500,true).to(5000,false));
    //设置查询体
    request.source(searchSourceBuilder);
    //查询
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    System.out.println(response);
    //得到hits
    SearchHits hits = response.getHits();
    System.out.println("查询的总条数:"+hits.getHits().length);
}

模糊查询

模糊查询表明在查询的关键字拼写错误时,也可以查询。
错误长度是有限是的, 默认是 0~1,也就是最多错1个,可以匹配和识别。
image.png
java程序的实现

//使用search进行条件查询 模糊查询
@Test
public void testQueryFuzzy() throws IOException {
    //创建SearchRequest (传入索引库的名字)
    SearchRequest request = new SearchRequest("product");

    //使用SearchSourceBuilder设置查询条件条件
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    //使用QueryBuilders创建条件  范围查询
    searchSourceBuilder.query(QueryBuilders.fuzzyQuery("title","Custoa").maxExpansions(1));

    //设置查询体
    request.source(searchSourceBuilder);
    //查询
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    System.out.println(response);
    //得到hits
    SearchHits hits = response.getHits();
    System.out.println("查询的总条数:"+hits.getHits().length);
}

容错的范围:只能是0或者1。

查询结果排序

像sql查询一样给查询结果排序

GET product/_search
{
  "sort": [
    {
      "price": {
        "order": "desc"
      },
      "created": {
        "order": "asc"
      }
    }
  ]
}

java程序的写法

    //使用search查询所有 排序
    @Test
    public void testSearchAllByOrder() throws IOException {
        SearchRequest request = new SearchRequest("product");
        SearchSourceBuilder builder= new SearchSourceBuilder();
        //没有条件
//        builder.sort("price");//默认降序
//        builder.sort("price", SortOrder.DESC);
        builder.sort(new FieldSortBuilder("price").order(SortOrder.DESC));
        builder.sort(new FieldSortBuilder("created").order(SortOrder.ASC));
        request.source(builder);
        //不设置条件
        SearchResponse respons = client.search(request, RequestOptions.DEFAULT);
        //得到hits
        SearchHits hits = respons.getHits();
        //按照iterator的方式迭代
//        for(SearchHit hit : hits.getHits()){
        for(SearchHit hit : hits){
            System.out.println("分数:"+hit.getScore());
            System.out.println("结果:"+hit.getSourceAsMap());
        }
    }

分页查询

GET product/_search
{
  "from": 3,
  "size": 2
}

只要设置了SearchSourceBuilder的from和size就行

    //使用search查询所有 分页
    @Test
    public void testSearchAllByOrderPage() throws IOException {
        SearchRequest request = new SearchRequest("product");
        SearchSourceBuilder builder= new SearchSourceBuilder();

        //没有条件 - 按照价格排序
        builder.sort("price", SortOrder.DESC);
        //设置分页信息
        builder.from(2);
        builder.size(2);

        request.source(builder);
        //不设置条件
        SearchResponse respons = client.search(request, RequestOptions.DEFAULT);
        //得到hits
        SearchHits hits = respons.getHits();
        //按照iterator的方式迭代
//        for(SearchHit hit : hits.getHits()){
        for(SearchHit hit : hits){
            System.out.println("分数:"+hit.getScore());
            System.out.println("结果:"+hit.getSourceAsMap());
        }
    }

高亮查询

image.png
java实现

    //高亮显示查询
    @Test
    public void testSearchHightlight() throws IOException {
        SearchRequest request = new SearchRequest("product");
        //设置条件
        SearchSourceBuilder builder = new SearchSourceBuilder();
        builder.query(QueryBuilders.matchQuery("title","吉他"));

        //设置高亮显示
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<font style='color:red'>");//前缀
        highlightBuilder.postTags("</font>");//后缀
        highlightBuilder.field("title");
        builder.highlighter(highlightBuilder);

        request.source(builder);
        //不设置条件
        SearchResponse respons = client.search(request, RequestOptions.DEFAULT);
        //得到hits
        SearchHits hits = respons.getHits();
        //按照iterator的方式迭代
//        for(SearchHit hit : hits.getHits()){
        for(SearchHit hit : hits){
            System.out.println("分数:"+hit.getScore());
            //取出原始数据
            Map<String, Object> sourceMap = hit.getSourceAsMap();
            //处理高亮显示的结果
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            HighlightField title = highlightFields.get("title");
            if(title!=null) {
                Text[] fragments = title.getFragments();
                if (fragments != null && fragments.length > 0) {
                    //修改sourceMap中title为高亮显示的结果
                    sourceMap.put("title", fragments[0]);
                }
            }
            System.out.println("结果:"+sourceMap);
        }
    }