SpringBoot Data操作ES

14. SpringBoot Data操作ES

14.1 引入依赖


    org.springframework.boot
    spring-boot-starter-data-elasticsearch

14.2 编写yml配置

  • spring-data(2~3.x版本配置)
spring:
  data:
    elasticsearch:
      cluster-nodes: 172.16.251.142:9300
  • spring-data(新版本推荐配置) RestHighLevelClient rest客户端 ElasticSearchRespositoy接口
@Configuration
public class RestClientConfig extends AbstractElasticsearchConfiguration {

    @Override
    @Bean
    public RestHighLevelClient elasticsearchClient() {
        final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
                .connectedTo("192.168.202.200:9200")
                .build();
        return RestClients.create(clientConfiguration).rest();
    }

}

14.3 编写entity

@Document(indexName = "dangdang",type = "book")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Book {
    @Id
    private String id;

    @Field(type = FieldType.Text,analyzer ="ik_max_word")
    private String name;


    @Field(type = FieldType.Date)
    @JsonFormat(pattern="yyyy-MM-dd")
    private Date createDate;

    @Field(type = FieldType.Keyword)
    private String author;

    @Field(type = FieldType.Text,analyzer ="ik_max_word")
    private String content;
}

@Document: 代表一个文档记录

indexName: 用来指定索引名称

type: 用来指定索引类型

@Id: 用来将对象中id和ES中_id映射

@Field: 用来指定ES中的字段对应Mapping

type: 用来指定ES中存储类型

analyzer: 用来指定使用哪种分词器

14.4 编写BookRepository

public interface BookRepository extends ElasticsearchRepository {
}

14.5 索引or更新一条记录

NOTE:这种方式根据实体类中中配置自动在ES创建索引,类型以及映射

@SpringBootTest(classes = Application.class)
@RunWith(SpringRunner.class)
public class TestSpringBootDataEs {
    @Autowired
    private BookRepository bookRespistory;
    /**
     * 添加索引和更新索引 id 存在更新 不存在添加
     */
    @Test
    public void testSaveOrUpdate(){
        Book book = new Book();
        book.setId("21");
        book.setName("小陈");
        book.setCreateDate(new Date());
        book.setAuthor("李白");
        book.setContent("这是中国的好人,这真的是一个很好的人,李白很狂");
        bookRespistory.save(book);
    }
}

14.6 删除一条记录

    /**
     * 删除一条索引
     */
    @Test
    public void testDelete(){
        Book book = new Book();
        book.setId("21");
        bookRespistory.delete(book);
    }

14.7 查询

    /**
     * 查询所有
     */
    @Test
    public void testFindAll(){
        Iterable books = bookRespistory.findAll();
        for (Book book : books) {
            System.out.println(book);
        }
    }


    /**
     * 查询一个
     */
    @Test
    public void testFindOne(){
        Optional byId = bookRespistory.findById("21");
        System.out.println(byId.get());
    }

14.8 查询排序

    /**
     * 排序查询
     */
    @Test
    public void testFindAllOrder(){
        Iterable books = bookRespistory.findAll(Sort.by(Sort.Order.asc("createDate")));
        books.forEach(book -> System.out.println(book) );
    }

14.9 自定义基本查询

Keyword Sample Elasticsearch Query String
And findByNameAndPrice {"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}
Or findByNameOrPrice {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}
Is findByName {"bool" : {"must" : {"field" : {"name" : "?"}}}}
Not findByNameNot {"bool" : {"must_not" : {"field" : {"name" : "?"}}}}
Between findByPriceBetween {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
LessThanEqual findByPriceLessThan {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
GreaterThanEqual findByPriceGreaterThan {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}
Before findByPriceBefore {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
After findByPriceAfter {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}
Like findByNameLike {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}
StartingWith findByNameStartingWith {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}
EndingWith findByNameEndingWith {"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}}
Contains/Containing findByNameContaining {"bool" : {"must" : {"field" : {"name" : {"query" : "**?**","analyze_wildcard" : true}}}}}
In findByNameIn
(Collectionnames)
{"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}}
NotIn findByNameNotIn
(Collectionnames)
{"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}}
Near findByStoreNear Not Supported Yet !
True findByAvailableTrue {"bool" : {"must" : {"field" : {"available" : true}}}}
False findByAvailableFalse {"bool" : {"must" : {"field" : {"available" : false}}}}
OrderBy findByAvailable
TrueOrderByNameDesc
{"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"available" : true}}}}
public interface BookRepository extends ElasticsearchRepository {

    //根据作者查询
    List findByAuthor(String keyword);

    //根据内容查询
    List findByContent(String keyword);

    //根据内容和名字查
    List findByNameAndContent(String name,String content);

    //根据内容或名称查询
    List findByNameOrContent(String name,String content);

    //范围查询
    List findByPriceBetween(Double start,Double end);

    //查询名字以xx开始的
    List  findByNameStartingWith(String name);

    //查询某个字段值是否为false
    List  findByNameFalse();
    
    //.......
}

14.10 实现复杂查询

分页查询并排序
@Test
public void testSearchPage() throws IOException {
  SearchRequest searchRequest = new SearchRequest();
  SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
  sourceBuilder.from(0).size(2).sort("age", SortOrder.DESC).query(QueryBuilders.matchAllQuery());
  searchRequest.indices("ems").types("emp").source(sourceBuilder);
  SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
  SearchHit[] hits = search.getHits().getHits();
  for (SearchHit hit : hits) {
    System.out.println(hit.getSourceAsString());
  }
}
高亮查询
@Test
    public void testSearchHig() throws IOException {
        SearchRequest searchRequest = new SearchRequest();
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        HighlightBuilder highlightBuilder =  new HighlightBuilder();
        highlightBuilder.field("content").requireFieldMatch(false).preTags("").postTags("");
        sourceBuilder.from(0).size(2).sort("age", SortOrder.DESC).highlighter(highlightBuilder).query(QueryBuilders.termQuery("content","框架"));
        searchRequest.indices("ems").types("emp").source(sourceBuilder);
        SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHit[] hits = search.getHits().getHits();
        for (SearchHit hit : hits) {
            System.out.println(hit.getSourceAsString());
            Map highlightFields = hit.getHighlightFields();
            highlightFields.forEach((k,v)-> System.out.println("key: "+k + " value: "+v.fragments()[0]));
        }
    }

下面是编写的代码:

1、自动创建springboot项目
2、maven引入springboot-data的jar包


            org.springframework.boot
            spring-boot-starter-data-elasticsearch
        

3、编写entity:

package com.nono.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import java.util.Date;

@Data
/**
 * 用在类上 作用:将Emp的对象映射成ES中一条json格式文档
 *      indexName: 用来指定这个对象的转为json文档存入那个索引中 要求:ES服务器中之前不能存在此索引名
 *      type     : 用来指定在当前这个索引下创建的类型名称
 */
@Document(indexName = "ems",type = "emp")
public class Emp {

    @Id //用来将对象中id属性与文档中_id 一一对应
    private String id;
    // 用在属性上 代表mapping中一个属性 一个字段 type:属性 用来指定字段类型 analyzer:指定分词器
    @Field(type = FieldType.Text,analyzer = "ik_max_word")
    private String name;
    @Field(type = FieldType.Integer)
    private Integer age;
    @Field(type = FieldType.Date)
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date bir;
    @Field(type = FieldType.Text,analyzer = "ik_max_word")
    private String content;
    @Field(type = FieldType.Text,analyzer = "ik_max_word")
    private String address;

}

springboot操作es有两种情况 第一种是用RestHighLevelClient,第二种是用ElasticSearchRepository

4、编写ElasticSearchRestClientConfig继承AbstractElasticsearchConfiguration

package com.nono.config;

import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.RestClients;
import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;

@Configuration
public class ElasticSearchRestClientConfig extends AbstractElasticsearchConfiguration {

    //这个client 用来替换 transportClient(9300)对象
    @Override
    @Bean
    public RestHighLevelClient elasticsearchClient() {
        //定义客户端配置对象 RestClient  9200
        ClientConfiguration clientConfiguration = ClientConfiguration.builder()
                .connectedTo("192.168.159.5:9200")
                .build();
       //通过RestClients对象创建
        return RestClients.create(clientConfiguration).rest();
    }
}

5、编写TestRestClient注入RestHighLevelClient的方式进行操作es。
(注意我们用自动构建的springboot项目在测试的时候只要用@SpringBootTest就可以了。不过记住@Test要引入的是import org.junit.jupiter.api.Test;)

package com.nono;

import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;

import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.IOException;


/**
 * client 操作
 *
 *  RestHighLevelClient         强大 更灵活  不能友好的对象操作
 *
 *  ElasticSearchRepository     对象操作友好
 *
 */
@SpringBootTest
public class TestRestClient {

    @Autowired
    private RestHighLevelClient restHighLevelClient;//复杂查询使用  高亮查询  指定条件查询  如同transportClient


    //添加文档
    @Test
    public void testAddIndex() throws IOException {
        IndexRequest indexRequest = new IndexRequest("ems","emp","14");
        indexRequest.source("{\"name\":\"小黑\",\"age\":23}", XContentType.JSON);
        IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
        System.out.println(indexResponse.status());
    }

    //删除文档
    @Test
    public void testDeleteIndex() throws IOException {
        DeleteRequest deleteRequest = new DeleteRequest("ems","emp","14");
        DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
        System.out.println(deleteResponse.status());
    }


    //更新文档
    @Test
    public void testUpdateIndex() throws IOException {
        UpdateRequest updateRequest = new UpdateRequest("ems","emp","12");
        updateRequest.doc("{\"name\":\"小黑\",\"age\":30}",XContentType.JSON);
        UpdateResponse updateResponse = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
        System.out.println(updateResponse.status());
    }

    //批量更新
    @Test
    public void testBulk() throws IOException {
        BulkRequest bulkRequest = new BulkRequest();

        //添加
        IndexRequest request = new IndexRequest("ems","emp","13");
        request.source("{\"name\":\"李四\",\"age\":23}",XContentType.JSON);
        //删除

        //修改


        bulkRequest.add(request);
        BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        BulkItemResponse[] items = bulkResponse.getItems();
        for (BulkItemResponse item : items) {
            System.out.println(item.status());
        }
    }

    //查询所有
    @Test
    public void testQueryAll() throws IOException {
        SearchRequest searchRequest = new SearchRequest("ems");
        //创建搜索条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //查询所有
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());

        searchRequest.types("emp").source(searchSourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHit[] hits = searchResponse.getHits().getHits();
        for (SearchHit hit : hits) {
            System.out.println(hit.getSourceAsString());
        }


    }


    @Test
    public void testSearch() throws IOException {
        //创建搜索对象
        SearchRequest searchRequest = new SearchRequest("ems");
        //搜索构建对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        searchSourceBuilder.query(QueryBuilders.matchAllQuery())//执行查询条件
                .from(0)//起始条数
                .size(20)//每页展示记录
                .postFilter(QueryBuilders.matchAllQuery()) //过滤条件
                .sort("age", SortOrder.DESC)//排序
                .highlighter(new HighlightBuilder().field("*").requireFieldMatch(false));//高亮

        //创建搜索请求
        searchRequest.types("emp").source(searchSourceBuilder);

        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        System.out.println("符合条件的文档总数: "+searchResponse.getHits().getTotalHits());
        System.out.println("符合条件的文档最大得分: "+searchResponse.getHits().getMaxScore());
        SearchHit[] hits = searchResponse.getHits().getHits();
        for (SearchHit hit : hits) {
            System.out.println(hit.getSourceAsMap());
        }
    }








}

6、编写EmpRepository接口 实现ElasticsearchRepository

package com.nono.elasticsearch.dao;

import com.nono.entity.Emp;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

import java.util.List;

//自定义EmpRepository
public interface EmpRepository extends ElasticsearchRepository {

    //根据姓名查询
    List findByName(String  name);

    //根据年龄查询
    List findByAge(Integer age);

    //根据姓名和地址
    List findByNameAndAddressAndAge(String name,String address,Integer age);

    //根据姓名或年龄查询
    List findByNameOrAge(String name,Integer age);

    //查询年龄大于等于 8
    List findByAgeGreaterThanEqual(Integer value);





}

7、编写TestEmpRespository测试

package com.nono;

import com.nono.elasticsearch.dao.EmpRepository;
import com.nono.entity.Emp;
import lombok.ToString;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;

import java.io.IOException;
import java.sql.SQLOutput;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

@SpringBootTest
public class TestEmpRespository {


    @Autowired
    private EmpRepository empRepository;  //1

    @Autowired
    private RestHighLevelClient restHighLevelClient;//2


    //保存|更新一条文档 id存在更新  id不存在 添加
    @Test
    public void testSave(){
        Emp s = new Emp();
        //s.setId("a1389021-2ecf-40cb-95e7-4c3be73604d9");
        s.setAge(2);
        s.setBir(new Date());
        s.setName("张君宝一代武侠");
        s.setAddress("武当山武侠学院毕业");
        s.setContent("武侠大师!");
        empRepository.save(s);

    }

    //根据id删除一条文档
    @Test
    public void testDelete(){
        empRepository.deleteById("a1389021-2ecf-40cb-95e7-4c3be73604d9");
    }


    //删除所有
    @Test
    public void testDeleteAll(){
        empRepository.deleteAll();
    }

    //检索一条记录
    @Test
    public void testFindOne(){
        Optional optional = empRepository.findById("a1389021-2ecf-40cb-95e7-4c3be73604d9");
        System.out.println(optional.get());
    }

    //查询所有 排序
    @Test
    public void testFindAll(){
        Iterable all = empRepository.findAll(Sort.by(Sort.Order.asc("age")));
        all.forEach(emp-> System.out.println(emp));
    }

    //分页
    @Test
    public void testFindPage(){
        //PageRequest.of 参数1: 当前页-1
        Page search = empRepository.search(QueryBuilders.matchAllQuery(), PageRequest.of(1, 1));
        search.forEach(emp-> System.out.println(emp));
    }

    //基础查询 根据姓名  姓名和年龄  地址 等
    @Test
    public void testFindByName(){
        List emps = empRepository.findByName("张君宝");
        List emps1 = empRepository.findByAge(23);
        List emps2 = empRepository.findByNameAndAddressAndAge("张君宝", "武当山",23);
        List emps3 = empRepository.findByNameOrAge("张君宝", 2);
        List emps4 = empRepository.findByAgeGreaterThanEqual(23);
        emps4.forEach(emp-> System.out.println(emp));
    }



    //复杂查询RestHighLevelClient  高亮
    @Test
    public void testSearchQuery() throws IOException, ParseException {
        List emps = new ArrayList();
        //创建搜索请求
        SearchRequest searchRequest = new SearchRequest("ems");

        //创建搜索对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.termQuery("content","武侠"))//设置条件
                .sort("age", SortOrder.DESC)//排序
                .from(0) //起始条数(当前页-1)*size的值
                .size(20)//每页展示条数
                .highlighter(new HighlightBuilder().field("*").requireFieldMatch(false).preTags("").postTags(""));//设置高亮

        searchRequest.types("emp").source(searchSourceBuilder);

        //执行搜索
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        SearchHit[] hits = searchResponse.getHits().getHits();
        for (SearchHit hit : hits) {
            //原始文档
            Map sourceAsMap = hit.getSourceAsMap();
            Emp emp =  new Emp();
            emp.setId(hit.getId());
            emp.setAge(Integer.parseInt(sourceAsMap.get("age").toString()));
            emp.setBir(new SimpleDateFormat("yyyy-MM-dd").parse(sourceAsMap.get("bir").toString()));
            emp.setContent(sourceAsMap.get("content").toString());
            emp.setName(sourceAsMap.get("name").toString());
            emp.setAddress(sourceAsMap.get("address").toString());

            //高亮字段
            Map highlightFields = hit.getHighlightFields();
            if(highlightFields.containsKey("content")){
                emp.setContent(highlightFields.get("content").fragments()[0].toString());
            }
            if(highlightFields.containsKey("name")){
                emp.setName(highlightFields.get("name").fragments()[0].toString());
            }
            if(highlightFields.containsKey("address")){
                emp.setAddress(highlightFields.get("address").fragments()[0].toString());
            }

            //放入集合
            emps.add(emp);
        }


        emps.forEach(emp-> System.out.println(emp));
    }





}

你可能感兴趣的:(SpringBoot Data操作ES)