Spring Data Elasticsearch

1. Spring Data Elasticsearch

官网:https://docs.spring.io/spring-data/elasticsearch/docs/2.0.6.RELEASE/reference/html/

1.1 添加文档

1、在properties.xml中配置节点

# 节点
spring.data.elasticsearch.cluster-nodes=192.168.157.130:9300

2、实体类

  1. @Document:用在类上,代表这个类的实例是一个文档

    indexName :会自动创建索引index

    type :会自动创建类型type

  2. @Field(type = FieldType.Integer):用在成员变量上,type 的类型

  3. @Id:将es的id和实体类的id匹配上。

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@Document(indexName = "dangdang",type = "book")
public class Book {
    @Id
    private String id;
    @Field(type = FieldType.Text,analyzer = "ik_max_word")
    private String name;
    @Field(type = FieldType.Double)
    private Double price;
    @Field(type = FieldType.Date)
    private Date pubDate;
    @Field(type = FieldType.Text,analyzer = "ik_max_word")
    private String content;
    @Field(type = FieldType.Keyword)
    private String author;
}

3、接口:

  1. ElasticRepoitory接口:封装了增删改查方法

  2. ElasticsearchTemplate:用来处理复杂操作时使用

//泛型里面需要传入实体类和主键的类型
public interface BookRepository extends ElasticsearchRepository<Book,String> {
}

4、测试查询接口:

@SpringBootTest
@RunWith(SpringRunner.class)
public class ElasticSearchTest {
    @Autowired
    public BookRepository bookRepository;

    @Test
    public void save(){
        Book book = new Book("1", "面试", 23.23, new Date(), "继续加油吧,还有机会", "茶花女");
        bookRepository.save(book);
    }
}

创建的索引和类型:

在这里插入图片描述

添加的文档:

在这里插入图片描述

1.2 更新文档

    //save就是更新,id存在就是添加,不存在就是更新
    @Test
    public void update(){
        Book book = new Book("1", "面试", 23.23, new Date(), "继续加油吧,还有机会", "茶花女");
        bookRepository.save(book);
    }

1.3 查询所有文档

    //底层已经实现findAll()方法
    @Test
    public void findAll(){
        Iterable<Book> all = bookRepository.findAll();
        all.forEach(book -> System.out.println(book));
    }

1.4 删除文档

  //底层已经实现findAll()方法
    @Test
    public void delete(){
        bookRepository.deleteById(1);
        bookRepository.deleteAll();
    }

1.5 根据name和content查询

在这里插入图片描述

需要在接口中自定义方法声明:

public interface BookRepository extends ElasticsearchRepository<Book,Integer> {
    //方法名需要严格按照官网要求的格式书写
    List<Book> findByNameAndContent(String nameKeyword,String contentKeyword);
}
 	@Test
    public void testfindByNameandContent(){
        List<Book> books = bookRepository.findByNameAndContent("时光", "时间");
        books.forEach(book -> System.out.print(book));
    }

1.6 根据name或content查询

在这里插入图片描述

同样需要在接口中声明这个方法:

public interface BookRepository extends ElasticsearchRepository<Book,Integer> {
    //方法名需要严格按照官网要求的格式书写
    List<Book> findByNameOrContent(String nameKeyword,String contentKeyword);
}
    @Test
    public void testfindByNameOrContent(){
        List<Book> books = bookRepository.findByNameOrContent("时光", "时间");
        books.forEach(book -> System.out.print(book));
    }

同理,其他的用法也是如此:

KeywordSampleElasticsearch Query String
AndfindByNameAndPrice{"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}
OrfindByNameOrPrice{"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}
IsfindByName{"bool" : {"must" : {"field" : {"name" : "?"}}}}
NotfindByNameNot{"bool" : {"must_not" : {"field" : {"name" : "?"}}}}
BetweenfindByPriceBetween{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
LessThanEqualfindByPriceLessThan{"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
GreaterThanEqualfindByPriceGreaterThan{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}
BeforefindByPriceBefore{"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
AfterfindByPriceAfter{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}
LikefindByNameLike{"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}
StartingWithfindByNameStartingWith{"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}
EndingWithfindByNameEndingWith{"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}}
Contains/ContainingfindByNameContaining{"bool" : {"must" : {"field" : {"name" : {"query" : "**?**","analyze_wildcard" : true}}}}}
InfindByNameIn(Collectionnames){"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}}
NotInfindByNameNotIn(Collectionnames){"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}}
NearfindByStoreNearNot Supported Yet !
TruefindByAvailableTrue{"bool" : {"must" : {"field" : {"available" : true}}}}
FalsefindByAvailableFalse{"bool" : {"must" : {"field" : {"available" : false}}}}
OrderByfindByAvailableTrueOrderByNameDesc{"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"available" : true}}}}

1.7 分页查询

这个方法需要构造一个SearchQuery对象:

在这里插入图片描述

SearchQuery这个接口只有一个实现类: NativeSearchQuery

public class NativeSearchQuery extends AbstractQuery implements SearchQuery {
    private QueryBuilder query;
    private QueryBuilder filter;
    private List<SortBuilder> sorts;
    private final List<ScriptField> scriptFields = new ArrayList();
    private List<FacetRequest> facets;
    private List<AbstractAggregationBuilder> aggregations;
    private HighlightBuilder highlightBuilder;
    private Field[] highlightFields;
    private List<IndexBoost> indicesBoost;
    
     public NativeSearchQuery(QueryBuilder query) {
        this.query = query;
    }

    public NativeSearchQuery(QueryBuilder query, QueryBuilder filter) {
        this.query = query;
        this.filter = filter;
    }
    //......
}

而NativeSearchQuery(QueryBuilder query)中中需要传入QueryBuilder对象,可以通过QueryBuilders获取

   @Test
    public void testSearch(){
        //查询所有
        MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
        //设置分页
        SearchQuery searchQuery = new NativeSearchQuery(matchAllQueryBuilder)
                .setPageable(PageRequest.of(1,2));
		//执行查询
        Page<Book> page = bookRepository.search(searchQuery);
        for (Book book :page){
            System.out.println(book);
        }
    }

查询结果:

Book(id=2, name=时光的故事, price=23.23, pubDate=Fri Jul 24 17:04:37 CST 2020, content=时间真的过得很快, author=茶花女)
Book(id=1, name=小黑的故事, price=23.23, pubDate=Fri Jul 24 17:03:28 CST 2020, content=光阴似箭,时间飞快, author=茶花女)

1.8 排序加分页查询

如果是复杂查询,建议使用ElasticsearchTemplate:

在这里插入图片描述

    @Test
    public void testSearchPageAndSort(){
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withIndices("dangdang")
                .withTypes("book")
                .withQuery(QueryBuilders.matchAllQuery())//查询所有结果
                .withSort( new FieldSortBuilder("price").order(SortOrder.DESC))//排序条件
                .withPageable(PageRequest.of(0,3))//分页条件
                .build();

        //聚合结果
        AggregatedPage<Book> books = elasticsearchTemplate.queryForPage(searchQuery, Book.class);
        books.forEach(book -> System.out.println(book));
    }

查询结果:

Book(id=3, name=小明的故事, price=30.23, pubDate=Fri Jul 24 19:15:16 CST 2020, content=近期事情众多,时间不够用, author=茶花女)
Book(id=2, name=时光的故事, price=25.23, pubDate=Fri Jul 24 17:53:57 CST 2020, content=时间真的过得很快, author=茶花女)
Book(id=1, name=小黑的故事, price=23.23, pubDate=Fri Jul 24 17:03:28 CST 2020, content=光阴似箭,时间飞快, author=茶花女)

1.9 分页排序查询高亮显示

需要注意:spring boot整合ES的方式目前常见的有两种,一种是使用spring data elasticsearch,一种就是使用elasticsearchTemplate进行整合。如对搜索没有高亮需求,用前者即可,如有高亮需求,则必须使用后者。如果没有高亮的要求,那么7.8足以满足条件,但是因为7.8的结果中不含高亮的结果,因此需要单独处理。

如图:查询的结果汇总会先显示查询的结果,然后将高亮的内容在后面显示出来:

在这里插入图片描述

	@Test
    public void testSearchPageAndSortAndFilter(){
        //对content字段高亮
        HighlightBuilder.Field content = new HighlightBuilder.Field("content")
                //关闭多字段检索
                .requireFieldMatch(false)
                .preTags("<span style='color:red'>")
                .postTags("</span>");

        //对那么字段进行高亮
        HighlightBuilder.Field name = new HighlightBuilder.Field("name")
                .requireFieldMatch(false)                //关闭多字段检索
                .preTags("<font color='red'>")
                .postTags("</font>");

        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withIndices("dangdang")
                .withTypes("book")
                //设置查询条件:这次使用的查询方式是多字段分词检索方式
                .withQuery(QueryBuilders.queryStringQuery("小黑的故事").field("name").field("content").field("author"))
                //设置过滤条件:过滤出价格小于30.23的文档
                .withFilter(QueryBuilders.rangeQuery("price").lte(50))
                //设置排序条件
                .withSort(new FieldSortBuilder("price").order(SortOrder.DESC))
                //设置分页条件
                .withPageable(PageRequest.of(0,3))
                //设置高亮
                .withHighlightFields(content,name)
                .build();

        AggregatedPage<Book> books = elasticsearchTemplate.queryForPage(searchQuery, Book.class, new SearchResultMapper() {

            //查询的结果,返回到页面
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
                //存放查询的结果,以便返回给页面
                List<Book> list = new ArrayList<>();

                //获取检索结果hits
                SearchHit[] hits = searchResponse.getHits().getHits();

                for (SearchHit hit : hits) {
                    //每次遍历创建一个map对象封装查询的结果
                    Book book = new Book();

                    //获取查询结果的map
                    Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                    //获取高亮显示的map
                    Map<String, HighlightField> highlightFields = hit.getHighlightFields();

                    book.setId(sourceAsMap.get("id").toString());

                    book.setName(sourceAsMap.get("name").toString());
                    //判断高亮的map中是否含有这个字段
                    if (highlightFields.containsKey("name")) {
                        book.setName(highlightFields.get("name").getFragments()[0].toString());
                    }
                    
                    book.setPrice((Double) sourceAsMap.get("price"));

                    book.setContent(sourceAsMap.get("content").toString());
                    //判断高亮的map中是否含有这个字段
                    if (highlightFields.containsKey("content")) {
                        book.setContent(highlightFields.get("content").getFragments()[0].toString());
                    }


                    book.setPubDate(new Date(Long.valueOf(sourceAsMap.get("pubDate").toString())));
                    book.setAuthor(sourceAsMap.get("author").toString());

                    list.add(book);
                }
                return new AggregatedPageImpl<T>((List<T>) list);
            }
        });
        books.forEach(book -> System.out.println(book));
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我一直在流浪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值