引入依赖
spring-boot-starter-data-elasticsearch
  配置Elasticsearch
-cluster-name、cluster-nodes
  Spring Data Elasticsearch
ElasticsearchTemplate
ElasticsearchRepository
其中ElasticsearchRepository是比较方便的接口,底层依赖于ElasticsearchTemplate;当ElasticsearchRepository不适用时,才去编写调用ElasticsearchTemplate。
  去mvn库搜索spring-boot-starter-data-elasticsearch粘贴到pom.xml。在application.properties里添加配置

# ElasticsearchProperties
spring.data.elasticsearch.cluster-name=nowcoder
# 注意:elasticsearch有两个默认端口,其中9200是http形式访问,9300是tcp形式访问,java程序中一般用9300这个端口
spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300
# 还要注意:elasticsearch底层依赖Netty,而redis底层也依赖Netty,它俩在底层有个小冲突,需要手动处理一下
# 解决办法:在d:\javing\workspace\community/src/main/java/com/nowcoder/community/CommunityApplication提前里加一句

  在CommunityApplication里添加

@PostConstruct
	public void init(){
		//解决netty启动冲突的问题
		//see Netty4Utils.setAvailableProcessors(),解决调用NettyRuntime这个类的时候由于之前redis调用了而产生的冲突
		System.setProperty("es.set.netty.runtime.available.processors", "false");
	}

  在entity包下的DiscussPost类里,添加相应的注解

//底层自动把实体和elasticsearch的索引映射起来
@Document(indexName = "discusspost",type = "_doc",shards = 6,replicas = 3) //type(类型)以后会被废弃(这里固定写为_doc就好),shards代表分片数,replicas代表副本数(往往是根据服务器的容量能力来配的,这里先不写太多,随便写一下)
public class DiscussPost {

    @Id //和elasticsearch的字段对应起来
    private int id;

    @Field(type = FieldType.Integer)
    private int userId;

    // 比如“互联网校招”(尽可能地把这句话拆分成多个词条,与之匹配,增加搜索的范围,而搜索时用ik_smart分词器,缩小范围较精确地满足需求)
    @Field(type = FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_smart") //analyzer是存储时的解析器,searchAnalyzer是搜索时的解析器
    private String title;
    @Field(type = FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_smart")
    private String content;

    @Field(type = FieldType.Integer)
    private int type;
    @Field(type = FieldType.Integer)
    private int status;
    @Field(type = FieldType.Date)
    private Date createTime;
    @Field(type = FieldType.Integer)
    private int commentCount;
    @Field(type = FieldType.Double)
    private double score;

  在dao包下新建一个子包elasticsearch,新建一个接口DiscussPostRepository,

@Repository
public interface DiscussPostRepository extends ElasticsearchRepository<DiscussPost,Integer> {

}

  先用postman查一下es里面有多少个索引,输入

localhost:9200/_cat/indices?v

然后新建测试类ElasticSearchTests,

@Autowired
    private DiscussPostMapper discussMapper;

    @Autowired
    private DiscussPostRepository discussRepository;

    @Autowired
    private ElasticsearchTemplate elasticTemplate;

    @Test
    public void testInsert(){
        discussRepository.save(discussMapper.selectDiscussPostById(241));
        discussRepository.save(discussMapper.selectDiscussPostById(242));
        discussRepository.save(discussMapper.selectDiscussPostById(243));
    }

执行完后再去postman输入

localhost:9200/discusspost/_search

能查到刚才插入的数据。
  往ElasticSearchTests里添加

@Test
    public void testInsertList(){
        discussRepository.saveAll(discussMapper.selectDiscussPosts(101,0,100,0));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(102,0,100,0));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(103,0,100,0));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(111,0,100,0));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(112,0,100,0));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(131,0,100,0));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(132,0,100,0));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(133,0,100,0));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(134,0,100,0));
    }

执行完后再去postman输入

localhost:9200/discusspost/_search

能查到刚才添加的多条数据。
  测试更改操作,在postman里选择GET方式,输入

localhost:9200/discusspost/doc_/231

可以看到id为231的帖子。往ElasticSearchTests里添加

@Test
    public void testUpdate(){
        DiscussPost post = discussMapper.selectDiscussPostById(231);
        post.setContent("我是新人,使劲灌水");
        discussRepository.save(post);
    }

执行完再在postman里查一下,就能看到数据已经被修改了。
  删除数据可以用

@Test
    public void testDelete(){
        discussRepository.deleteById(231);
//        discussRepository.deleteAll();
    }

  接着测试最重要的搜索功能

@Test
    public void testSearchByRepository(){
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.multiMatchQuery("互联网寒冬","title","content"))
                .withSort(SortBuilders.fieldSort("type").order(SortOrder.DESC))  //排序设置
                .withSort(SortBuilders.fieldSort("score").order(SortOrder.DESC))
                .withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
                .withPageable(PageRequest.of(0,10))
                .withHighlightFields(   //高亮显示设置
                        new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),
                        new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")
                ).build();
//        Repository底层是调用了elasticTemplate.queryForPage(searchQuery,class,SearchResultMapper)
        //底层获取到了高亮显示的值,但它没有返回,如果要整合起来,需要改写、并调用SearchResultMapper(太麻烦了,不如直接用elasticTemplate)
        Page<DiscussPost> page = discussRepository.search(searchQuery);
        System.out.println(page.getTotalElements());
        System.out.println(page.getTotalPages());
        System.out.println(page.getNumber());  //当前是第几页
        System.out.println(page.getSize());  //每页显示多少条
        for(DiscussPost post:page){
            System.out.println(post);
        }
    }

  改用elasticTemplate来查询

@Test
    public void testSearchByTemplate(){
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.multiMatchQuery("互联网寒冬","title","content"))
                .withSort(SortBuilders.fieldSort("type").order(SortOrder.DESC))
                .withSort(SortBuilders.fieldSort("score").order(SortOrder.DESC))
                .withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
                .withPageable(PageRequest.of(0,10))
                .withHighlightFields(
                        new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),
                        new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")
                ).build();
        Page<DiscussPost> page = elasticTemplate.queryForPage(searchQuery, DiscussPost.class, new SearchResultMapper() { //DiscussPost.class作为实体类传进去,而把SearchResultMapper类匿名实现
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
                SearchHits hits = response.getHits();
                if(hits.getTotalHits()<=0){
                    return null;
                }
                List<DiscussPost> list = new ArrayList<>();
                for(SearchHit hit:hits){
                    DiscussPost post = new DiscussPost();
                    String id = hit.getSourceAsMap().get("id").toString();
                    post.setId(Integer.valueOf(id));

                    String userId = hit.getSourceAsMap().get("userId").toString();
                    post.setUserId(Integer.valueOf(userId));

                    String title = hit.getSourceAsMap().get("title").toString();
                    post.setTitle(title);

                    String content = hit.getSourceAsMap().get("content").toString();
                    post.setContent(content);

                    String status = hit.getSourceAsMap().get("status").toString();
                    post.setStatus(Integer.valueOf(status));

                    String createTime = hit.getSourceAsMap().get("createTime").toString();
                    post.setCreateTime(new Date(Long.valueOf(createTime)));

                    String commentCount = hit.getSourceAsMap().get("commentCount").toString();
                    post.setCommentCount(Integer.valueOf(commentCount));

                    //处理高亮显示的结果
                    HighlightField titleField = hit.getHighlightFields().get("title");
                    if(titleField!=null){
                        post.setTitle(titleField.getFragments()[0].toString());
                    }

                    HighlightField contentField = hit.getHighlightFields().get("content");
                    if(contentField!=null){
                        post.setContent(contentField.getFragments()[0].toString());
                    }
                    list.add(post);
                }
                return new AggregatedPageImpl(list,pageable,
                        hits.getTotalHits(),response.getAggregations(),response.getScrollId(),hits.getMaxScore());
            }

            @Override
            public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
                return null;
            }
        });

        System.out.println(page.getTotalElements());
        System.out.println(page.getTotalPages());
        System.out.println(page.getNumber());  //当前是第几页
        System.out.println(page.getSize());  //每页显示多少条
        for(DiscussPost post:page){
            System.out.println(post);
        }
    }
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐