SpringData操作ElasticSearch实现高亮查询等

整体思路

在这里插入图片描述

1.引入依赖

<!--注意:低版本的springboot 不支持springboot data-->
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.0.6.RELEASE</version>
</parent>
<!--springboot web -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--通过spring data 操作Es-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!--springboot 继承test-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>
<!--引入lombook-->
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.16.20</version>
  <scope>provided</scope>
</dependency>

2.编写yml配置

spring:
  data:
    elasticsearch:
      cluster-nodes: 192.168.17.148:9300  #指定连接ip地址和端口号

3.创建实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "literature",type = "poem")
public class TPoem implements Serializable {
    
    
    private static final long serialVersionUID = -90864583830296063L;
    @Id
    private String id;
    @Field(type= FieldType.Keyword )
    private String name;
    @Field(type = FieldType.Text,analyzer = "ik_max_word")
    private String author;
    @Field(type = FieldType.Text,analyzer = "ik_max_word")
    private String type;
    @Field(type = FieldType.Text,analyzer = "ik_max_word")
    private String content;
    @Field(type = FieldType.Text,analyzer = "ik_max_word")
    private String href;
    @Field(type = FieldType.Text,analyzer = "ik_max_word")
    private String authordes;
    @Field(type = FieldType.Text,analyzer = "ik_max_word")
    private String origin;
    @Field(type = FieldType.Text,analyzer = "ik_max_word")
    private String imagepath;
    @Field(type = FieldType.Text,analyzer = "ik_max_word")
    private String categoryid;

@Document : 代表一个文档记录
	indexName : 用来指定索引名称
	type : 用来指定索引类型
@Id : 用来将对象中id和ES中_id映射
@Field : 用来指定ES中的字段对应Mapping
	type : 用来指定ES中存储类型
	analyzer : 用来指定使用哪种分词器

4-1.基本查询/添加/更新/删除

编写Repository接口类 实现 ElasticsearchRepository接口

                                        //ElasticsearchRepository接口泛型:<操作索引对应实体类的类型,id的类型>
public interface TPoemRepository extends ElasticsearchRepository<TPoem,String> {
    
    
    /*
    * ①接口中不写方法,使用默认提供的方法
    * ②接口中根据命名规则自定义方法
    * */
    List<TPoem> findAllByContent(String content);//根据内容查找
    List<TPoem> findAllByNameAndAndAuthor(String name,String author);//根据名字和作者查找
    List<TPoem> findAllByNameOrAuthor(String name,String author);//根据名字或作者查找
/*
    List<TPoem> findByXXXBetween(Double fromPrice,Double toPrice);//区间查询  xxx要是数值类型的字段
    List<TPoem> findByXXXAfter(Double price);//在指定数值之前  xxx要是数值类型的字段
    List<TPoem> findByXXXBefore(Double price);//在指定数值之后  xxx要是数值类型的字段
    等等...
    */
}

使用

@SpringBootTest(classes = Application.class)
@RunWith(SpringRunner.class)
public class poemTests {
    
    
    //注入TPoemRepository
    @Autowired
    private TPoemRepository poemRepository;

    // 一、使用默认提供的方法

    //添加索引和更新索引     id存在更新  不存在添加       id没赋值会随机生成id
    @Test
    public void test0(){
    
    
        TPoem tPoem = new TPoem("1", "雨", "某人", "唐诗", "春雨一直下", "...", "某人", "网络", ".", ".");
        poemRepository.save(tPoem);//添加单个文档
/*        //添加多个文档
        List<TPoem> list = new ArrayList<>();
        list.add(new TPoem("2", "雨", "某人", "唐诗", "春雨一直下", "...", "某人", "网络", ".", "."));
        list.add(new TPoem(null, "雪", "某人", "唐诗", "春雨一直下", "...", "某人", "网络", ".", "."));
        poemRepository.saveAll(list);*/
    }

    //删除
    @Test
    public void test1(){
    
    
        poemRepository.deleteById("2");//删除单个文档
        poemRepository.deleteAll();//删除所有文档
    }

    //查询
    @Test
    public void test2(){
    
    
        Optional<TPoem> optionalTPoem = poemRepository.findById("1");//根据id查询
        System.out.println(optionalTPoem.get());
        Iterable<TPoem> all = poemRepository.findAll();//查询所有
        Iterable<TPoem> all1 = poemRepository.findAll(Sort.by(Sort.Order.desc("排序的字段")));//查询所有并排序
        Page<TPoem> all2 = poemRepository.findAll(PageRequest.of(0, 2));//查询所有并分页
    }
    
    // 二、使用自定义方法

    //查询
    @Test
    public void test3(){
    
    
        List<TPoem> tPoems = poemRepository.findAllByContent("丞相");//根据内容查找
        List<TPoem> tPoems1= poemRepository.findAllByNameAndAndAuthor("蜀相","杜甫");//根据名字和作者查找
        List<TPoem> tPoems2= poemRepository.findAllByNameOrAuthor("蜀相","杜甫");//根据名字或作者查找
    }
}

4-2.复杂查询

自定义复杂方法接口(这里没写方法的参数,可以自己写索引,类型,查询字段和内容等等…)

public interface PoemDao {
    
    

    //删除索引
    public void deleteIndex();

    //条件普通查询
    List<TPoem> mathQuery();

    //boole查询
    List<TPoem> boolQuery();

    //分页和排序 查询所有
    List<TPoem> sortAndPageQuery();

    //多字段 高亮查询
    List<TPoem> highlightQuery();

}

实现类

@Repository
public class PoemDaoImpl implements PoemDao {
    
    

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    //删除索引
    public void deleteIndex() {
    
    
        elasticsearchTemplate.deleteIndex(TPoem.class);
    }

    //条件普通查询
    @Override
    public List<TPoem> mathQuery() {
    
    
        //构建查询对象
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withIndices("literature").withTypes("poem")//指定操作索引名和类型名
                .withQuery(QueryBuilders.matchQuery("name", "蜀相"))//指定查询方式
                .build();
        //执行查询
        List<TPoem> tPoems = elasticsearchTemplate.queryForList(searchQuery, TPoem.class);
        return tPoems;
    }

    //boole查询
    @Override
    public List<TPoem> boolQuery() {
    
    
        //构建查询对象
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withIndices("literature").withTypes("poem")//指定操作索引名和类型名
                .withQuery(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("content", "女")))//指定查询方式
                .build();
        //执行查询
        List<TPoem> tPoems = elasticsearchTemplate.queryForList(searchQuery, TPoem.class);
        return tPoems;
    }

    //分页和排序 查询所有
    @Override
    public List<TPoem> sortAndPageQuery() {
    
    
        //构建查询对象
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withIndices("literature").withTypes("poem")//指定操作索引名和类型名
                .withQuery(QueryBuilders.matchAllQuery())//指定查询方式
                .withPageable(PageRequest.of(0, 3))//分页
//                .withSort(SortBuilders.fieldSort("xxx").order(SortOrder.DESC))//根据数值类型的字段降序排列
                .build();
        //执行查询
        List<TPoem> tPoems = elasticsearchTemplate.queryForList(searchQuery, TPoem.class);
        return tPoems;
    }

    //多字段 高亮查询
    @Override
    public List<TPoem> highlightQuery() {
    
    
        //构建高亮查询对象
        HighlightBuilder.Field nameField = new HighlightBuilder
                .Field("*")//设置高亮的字段,*代表匹配到的所有字段都是高亮显示
                .preTags("<span style='color:red'>")
                .postTags("</span>").requireFieldMatch(false);
        //构建查询对象
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withIndices("literature").withTypes("poem")//指定操作索引名和类型名
                .withQuery(QueryBuilders.multiMatchQuery("王", "name", "author", "content"))//可同时在name和content和author查询 对应实体类中的属性名
                .withHighlightFields(nameField)
                .build();
        //执行查询
        AggregatedPage<TPoem> aggregatedPage = elasticsearchTemplate.queryForPage(searchQuery, TPoem.class,
                new SearchResultMapper() {
    
    
                    @Override
                    public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
    
    
                        // 获取查询结果中的所有文档
                        SearchHit[] hits = searchResponse.getHits().getHits();
                        ArrayList<TPoem> poemList = new ArrayList<>();
                        for (SearchHit hit : hits) {
    
    
                            // 获取原生的结果
                            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                            //赋值给sourceAsMap中的id→与hit中的id一致
                            sourceAsMap.put("id", hit.getId());

                            //获取高亮字段的结果
                            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                            Set<Map.Entry<String, Object>> entries = sourceAsMap.entrySet();
                            for (Map.Entry<String, Object> entry : entries) {
    
    
                                //通过key获取高亮字段
                                HighlightField highlightField = highlightFields.get(entry.getKey());
                                //将找到的高亮字段的内容  替换到原生结果集的map中
                                if (highlightField != null) {
    
    
                                    sourceAsMap.put(entry.getKey(), highlightField.fragments()[0].toString());
                                }
                            }

                            /*
                             * 自定义结果集的封装  将sourceAsMap中的结果封装成list集合返回
                             * */
                            TPoem tPoem = new TPoem();
                            tPoem.setId((String) sourceAsMap.get("id"));
                            tPoem.setName((String) sourceAsMap.get("name"));
                            tPoem.setAuthor((String) sourceAsMap.get("author"));
                            tPoem.setType((String) sourceAsMap.get("type"));
                            tPoem.setContent((String) sourceAsMap.get("content"));
                            tPoem.setHref((String) sourceAsMap.get("href"));
                            tPoem.setAuthordes((String) sourceAsMap.get("authordes"));
                            tPoem.setOrigin((String) sourceAsMap.get("origin"));
                            tPoem.setImagepath((String) sourceAsMap.get("imagepath"));
                            tPoem.setCategoryid((String) sourceAsMap.get("categoryid"));
                            poemList.add(tPoem);
                        }
                        //将封装好的结果集 传给 一个数据传输的载体对象
                        return new AggregatedPageImpl(poemList);
                    }
                });
        //获取自定义封装的集合
        return aggregatedPage.getContent();
    }



    /*         //分页和排序查询所有sortAndPageQuery()的   自定义查询结果集的封装
        AggregatedPage<TPoem> aggregatedPage = elasticsearchTemplate.queryForPage(searchQuery, TPoem.class,
                new SearchResultMapper() {
                    @Override
                    public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {

                        SearchHit[] hits = searchResponse.getHits().getHits();
                        ArrayList<TPoem> poemList = new ArrayList<>();
                        for (SearchHit hit : hits) {
                            // 获取原生的结果
                            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                            //自定义结果集的封装
                            TPoem tPoem = new TPoem();
                            tPoem.setId((String) sourceAsMap.get("id"));
                            tPoem.setName((String) sourceAsMap.get("name"));
                            tPoem.setAuthor((String) sourceAsMap.get("author"));
                            tPoem.setType((String) sourceAsMap.get("type"));
                            tPoem.setContent((String)sourceAsMap.get("content"));
                            tPoem.setHref((String) sourceAsMap.get("href"));
                            tPoem.setAuthordes((String) sourceAsMap.get("authordes"));
                            tPoem.setOrigin((String) sourceAsMap.get("origin"));
                            tPoem.setImagepath((String) sourceAsMap.get("imagepath"));
                            tPoem.setCategoryid((String) sourceAsMap.get("categoryid"));
                            poemList.add(tPoem);
                        }
                        //将封装好的结果集 传给 一个数据传输的载体对象
                        return new AggregatedPageImpl(poemList);
                    }
                }
        );
        return aggregatedPage.getContent();*/
}

测试

@SpringBootTest(classes = Application.class)
@RunWith(SpringRunner.class)
public class testElasticsearchPoem {
    
    

    @Autowired
    private PoemDaoImpl poemDao;

    @Test
    public void test0() throws Exception{
    
    
       poemDao.deleteIndex();
    }

    @Test
    public void test1() throws Exception{
    
    
        List<TPoem> tPoems = poemDao.mathQuery();
        for (TPoem tPoem : tPoems) {
    
     System.out.println(tPoem); }
    }

    @Test
    public void test2() throws Exception{
    
    
        List<TPoem> tPoems = poemDao.boolQuery();
        for (TPoem tPoem : tPoems) {
    
     System.out.println(tPoem); }
    }

    @Test
    public void test3() throws Exception{
    
    
        List<TPoem> tPoems = poemDao.sortAndPageQuery();
        for (TPoem tPoem : tPoems) {
    
     System.out.println(tPoem); }
    }

    @Test
    public void test4() throws Exception{
    
    
        List<TPoem> tPoems = poemDao.highlightQuery();
        for (TPoem tPoem : tPoems) {
    
     System.out.println(tPoem); }
    }
}

基于反射实现通用查询

public class SearchForElasticsearch {
    
    

    public static<T> List<T> queryInfo(Class<T> clazz,
                                       ElasticsearchTemplate elasticsearchTemplate,
                                       String indices,
                                       String type,
                                       String searchField,
                                       String searchFieldValue,
                                       String descField) {
    
    
        HighlightBuilder.Field field = new HighlightBuilder.Field("*")
                .preTags("<span style='color:red'>")
                .postTags("</span>").requireFieldMatch(false);;
        // 构建查询对象
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withIndices(indices).withTypes(type)
                .withQuery(QueryBuilders.matchQuery(searchField, searchFieldValue))
                .withPageable(PageRequest.of(0, 3))
                .withSort(SortBuilders.fieldSort(descField).order(SortOrder.DESC))
                .withHighlightFields(field)
                .build();
        // 执行查询
        AggregatedPage<T> aggregatedPage = elasticsearchTemplate.queryForPage(searchQuery, clazz,
                new SearchResultMapper() {
    
    
                    @Override
                    public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
    
    
                        // 获取查询结果中的所有文档
                        SearchHit[] hits = response.getHits().getHits();
                        List<T> list = new ArrayList<>();
                        for (SearchHit hit : hits) {
    
    
                            // 获取id
                            String id = hit.getId();
                            // 获取原生的结果
                            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                            sourceAsMap.put("id", id);

                            // 获取高亮字段
                            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                            Set<Map.Entry<String, Object>> entries = sourceAsMap.entrySet();
                            for (Map.Entry<String, Object> entry : entries) {
    
    
                                // 获取高亮字段
                                HighlightField highlightField = highlightFields.get(entry.getKey());
                                if (highlightField != null) {
    
    
                                    sourceAsMap.put(entry.getKey(), highlightField.fragments()[0].toString());
                                }
                            }
                            System.out.println("带高亮字段的结果:" + sourceAsMap);
                            /**
                             * 封装结果集
                             */
                            try {
    
    
                                // 自定义结果集的封装
                                // 反射创建类的对象
                                T t = clazz.newInstance();
                                // 反射获取所有的类中的字段
                                Field[] fields = clazz.getDeclaredFields();
                                for (Field field : fields) {
    
    
                                    // 设置运行反射操作属性
                                    field.setAccessible(true);
                                    // 获取属性名
                                    String name = field.getName();
                                    // 获取属性类型
                                    Class<?> type = field.getType();
//                                    System.out.println(name+"的类型是:"+type);
                                    // 根据当前这个字段的名字,到map中获取值,而得到的值就是要封装到当前字段的值
                                    Object value = sourceAsMap.get(name);
                                    // 给对象的属性反射赋值的方法 : 参数一 需要指定操作的对象   参数二 是给属性的赋值
                                    if (type.equals(Double.class)) {
    
    
                                        String strValue = (String) value;
                                        Double doubleValue = Double.valueOf(strValue);
                                        field.set(t,doubleValue);
                                    } else if (type.equals(Integer.class)) {
    
    
                                        String strValue = (String) value;
                                        Integer integerValue = Integer.valueOf(strValue);
                                        field.set(t,integerValue);
                                    } else if (type.equals(Date.class)) {
    
    
                                        String strValue = (String) value;
                                        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
                                        Date date = dateFormat.parse(strValue);
                                        field.set(t, date);
                                    } else {
    
    
                                        field.set(t,value);
                                    }
                                }
                                list.add(t);
                            } catch (Exception e) {
    
    
                                e.printStackTrace();
                            }
                        }
                        return new AggregatedPageImpl(list);
                    }
                });
        return aggregatedPage.getContent();
    }
}

命名规则示例

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
(Collection<String>names)
{"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}}
NotIn findByNameNotIn
(Collection<String>names)
{"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}}}}

猜你喜欢

转载自blog.csdn.net/qq_45928727/article/details/106943762