项目1在线交流平台-6.Elasticsearch分布式搜索引擎-2.Spring整合Elasticsearch测试

news/2024/7/7 5:57:49 标签: spring, java, spring boot, 后端, elasticsearch

文章目录

    • 一、导入依赖和配置
      • 1. 导入依赖
      • 2. 配置连接
      • 3. 解决启动Netty冲突
    • 二、SpringData集成ES测试
      • 1. 配置实体类与ES索引字段相对应
        • `@Document(indexName = "discusspost", type = "_doc", shards = 5, replicas = 2)`
        • `@Id`
        • `@Field(type = FieldType.Integer)`
        • `analyzer = "ik_max_word", searchAnalyzer = "ik_smart"`
      • 2. dao层接口定义
      • 3. 测试对ES服务器的CRUD基本操作
        • 3.1 向ES库中添加数据
          • `save(discussPostMapper.selectPost(241))`
          • `saveAll(discussPostMapper.getPosts(101, 0, 100))`
        • 3.2 修改ES库中的数据
        • 3.3 删除数据
          • `delete(discussPostMapper.selectDiscussPostById(231))`
          • `deleteAll()`
        • 3.4 搜索数据-ElasticsearchRepository
          • `SearchQuery`
          • `QueryBuilders.multiMatchQuery("互联网寒冬", "title", "content")`
          • `SortBuilders.fieldSort("type").order(SortOrder.DESC)`
          • `PageRequest.of(0, 10)`
          • `withHighlightFields()`
          • `new HighlightBuilder.Field("title").preTags("").postTags("")`
          • `search(searchQuery)`
          • `page.getTotalElements()`
          • `page.getTotalPages()`
          • `page.getNumber()`
          • `page.getSize()`
          • 测试结果:
        • 3.5 搜索数据-ElasticsearchTemplate
          • `new SearchResultMapper()`
          • `HighlightField`
          • `getFragments()`
          • 测试结果

参考牛客网高级项目教程
狂神说Elasticsearch教程笔记
尚硅谷Elasticsearch教程笔记

一、导入依赖和配置

1. 导入依赖

  • 版本在父依赖中已经指定,为6.3.0
<!--    整合elasticsearch-->
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
      </dependency>

2. 配置连接

  • 指定集群名称
  • TCP,客户端访问的端口是9300
# ElasticsearchProperties
spring.data.elasticsearch.cluster-name=community
#TCP访问端口
spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300

3. 解决启动Netty冲突

  • 如果不解决Netty冲突,启动ES时,会报错误

    在这里插入图片描述

  • 原因是ES启动时,会在Netty4Utils类的setAvailableProcessors方法中进行检查netty是否启动了

    • 如果已经启动了就会报错

    在这里插入图片描述

  • 解决策略是:将方法中留的set布尔变量设置为false,这样,就会中断检查

  • 因此,在整个项目启动函数CommunityApplication中,设置初始化函数,将set的布尔值设置为false

    java">@PostConstruct
    public void init() {
       // 解决netty启动冲突问题
       // see Netty4Utils.setAvailableProcessors()
       System.setProperty("es.set.netty.runtime.available.processors", "false");
    }
    

二、SpringData集成ES测试

1. 配置实体类与ES索引字段相对应

@Document(indexName = "discusspost", type = "_doc", shards = 5, replicas = 2)

  • indexName 对应的索引名称-没有创建,就自动创建一个
  • type 对应的类型,逐渐舍去
  • shards,replicas ,设定的分片和副本

@Id

  • 必须有 id,这里的 id 是全局唯一的标识,等同于 es 中的"/id"

@Field(type = FieldType.Integer)

  • 字段的注解,指定字段的数据类型

analyzer = "ik_max_word", searchAnalyzer = "ik_smart"

  • 指定储存分词器和搜索分词器的类型
java">@Document(indexName = "discusspost", type = "_doc", shards = 5, replicas = 2)
public class DiscussPost {
    //必须有 id,这里的 id 是全局唯一的标识,等同于 es 中的"_id"
    @Id
    private int id;
    /**
     * type : 字段数据类型
     * analyzer :       储存分词器类型:ik_max_word,最多分切
     * searchAnalyzer:  搜索分词器类型:ik_smart,最少分切
     * index : 是否索引(默认:true)
     * Keyword : 短语,不进行分词
     */
    @Field(type = FieldType.Integer)
    private int userId;
    
    @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
    private String title;
    
    @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
    private String content;
    
    @Field(type = FieldType.Integer)
    private int type;   // '0-普通; 1-置顶;
    
    @Field(type = FieldType.Integer)
    private int status; // '0-正常; 1-精华; 2-拉黑;
    
    @Field(type = FieldType.Date)
    private Date createTime;
    
    @Field(type = FieldType.Integer)
    private int commentCount; // 评论数
    
    @Field(type = FieldType.Double)
    private double score;
    ...
}

2. dao层接口定义

ElasticsearchRepository<DiscussPost, Integer>

  • SpringData对ES封装的处理ES数据CRUD的接口,基本的操作都能完成
  • 接口中指定要操作的实体类型,以及文档id即实体类型主键的数据类型
java">@Repository
public interface DiscussPostRepository extends ElasticsearchRepository<DiscussPost, Integer> {
}

3. 测试对ES服务器的CRUD基本操作

3.1 向ES库中添加数据

save(discussPostMapper.selectPost(241))
  • 插入一条数据
java">/**
 * 测试添加文档数据-一条数据
 */
@Test
public void testPUT() {
    discussRepository.save(discussPostMapper.selectPost(241));
    discussRepository.save(discussPostMapper.selectPost(242));
    discussRepository.save(discussPostMapper.selectPost(243));
}
  • 测试结果:

    {
        "discusspost": {
            "aliases": {},
            "mappings": {
                "_doc": {
                    "properties": {
                        "commentCount": {
                            "type": "integer"
                        },
                        "content": {
                            "type": "text",
                            "analyzer": "ik_max_word",
                            "search_analyzer": "ik_smart"
                        },
                        "createTime": {
                            "type": "date"
                        },
                        "id": {
                            "type": "long"
                        },
                        "score": {
                            "type": "double"
                        },
                        "status": {
                            "type": "integer"
                        },
                        "title": {
                            "type": "text",
                            "analyzer": "ik_max_word",
                            "search_analyzer": "ik_smart"
                        },
                        "type": {
                            "type": "integer"
                        },
                        "userId": {
                            "type": "integer"
                        }
                    }
                }
            },
            "settings": {
                "index": {
                    "refresh_interval": "1s",
                    "number_of_shards": "5",
                    "provided_name": "discusspost",
                    "creation_date": "1648823705739",
                    "store": {
                        "type": "fs"
                    },
                    "number_of_replicas": "2",
                    "uuid": "u0zDxIykSbWUehgQXP3Wow",
                    "version": {
                        "created": "6040399"
                    }
                }
            }
        }
    }
    
saveAll(discussPostMapper.getPosts(101, 0, 100))
  • 一次性插入多条数据-从offset到limit的所有条数据
java">/**
 * 测试添加多条数据-
 */
@Test
public void testPUTList() {
    discussRepository.saveAll(discussPostMapper.getPosts(101, 0, 100));
    discussRepository.saveAll(discussPostMapper.getPosts(102, 0, 100));
    discussRepository.saveAll(discussPostMapper.getPosts(103, 0, 100));
    discussRepository.saveAll(discussPostMapper.getPosts(111, 0, 100));
    discussRepository.saveAll(discussPostMapper.getPosts(112, 0, 100));
    discussRepository.saveAll(discussPostMapper.getPosts(131, 0, 100));
    discussRepository.saveAll(discussPostMapper.getPosts(132, 0, 100));
    discussRepository.saveAll(discussPostMapper.getPosts(133, 0, 100));
    discussRepository.saveAll(discussPostMapper.getPosts(134, 0, 100));
}
  • 结果测试

    {
        "took": 18,
        "timed_out": false,
        "_shards": {
            "total": 5,
            "successful": 5,
            "skipped": 0,
            "failed": 0
        },
        "hits": {
            "total": 141,	// 一共查询到了141条数据
            "max_score": 1,
                ...
        }
    

3.2 修改ES库中的数据

  • 为保证数据同步,先在mysql中修改数据,再将数据重新保存覆盖进ES库中
java">/**
 * 修改数据
 * 为保证数据同步,先在mysql中修改数据,再将数据重新保存覆盖进ES库中
 */
@Test
public void testUpdate() {
    DiscussPost post = discussPostMapper.selectPost(231);
    post.setContent("我其实不是新人,哈哈哈");
    // discussPostMapper.update(231, post.getContent());
    discussRepository.save(post);
}

3.3 删除数据

delete(discussPostMapper.selectDiscussPostById(231))
  • 删除一条数据
deleteAll()
  • 删除所有数据
java">    /**
     * 删除数据
     */
    @Test
    public void testDelete() {
//        discussRepository.delete(discussPostMapper.selectDiscussPostById(231));
        discussRepository.deleteAll();
    }
  • 测试结果:

    {
        "took": 0,
        "timed_out": false,
        "_shards": {
            "total": 5,
            "successful": 5,
            "skipped": 0,
            "failed": 0
        },
        "hits": {
            "total": 0,	// 没有数据
            "max_score": null,
            "hits": []
        }
    }
    

3.4 搜索数据-ElasticsearchRepository

SearchQuery
  • 查询体-可以构建查询的规则

NativeSearchQueryBuilder

  • 查询的实现类

withQuery()

  • 查询规则的制订方法,返回NativeSearchQueryBuilder实体,可以重复设置

    在这里插入图片描述

QueryBuilders.multiMatchQuery("互联网寒冬", "title", "content")
  • 定义组合查询的条件
SortBuilders.fieldSort("type").order(SortOrder.DESC)
  • 定义组合查询后根据字段排序的规则
PageRequest.of(0, 10)
  • 查询结果分页的设定:
    • 当前页码-从0开始
    • 每页显示多少条数据:10条
withHighlightFields()
  • 设定高亮规则
new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>")
  • 对指定字段的前后添加标签,前端可以用css渲染高亮
search(searchQuery)
  • 根据之前设定的查询规则,进行搜索
  • 返回Page类型的文档集合
page.getTotalElements()
  • 一共匹配的数据条数
page.getTotalPages()
  • 一共多少页
page.getNumber()
  • 当前页码,默认从0开始
page.getSize()
  • 每页显示的数量
java">    /**
     * 使用ElasticsearchRepository搜索匹配的数据
     */
    @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))	// 分页规则:0页,每页10行
                .withHighlightFields( // 设定高亮规则
                        new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),
                        new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")
                ).build();

        // elasticTemplate.queryForPage(searchQuery, class, SearchResultMapper)
        // 底层获取得到了高亮显示的值, 但是没有返回.

        Page<DiscussPost> page = discussRepository.search(searchQuery);
        System.out.println(page.getTotalElements());    // 一共匹配的数据条数
        System.out.println(page.getTotalPages());       // 一共多少页
        System.out.println(page.getNumber());           // 当前页码,默认从0开始
        System.out.println(page.getSize());             // 每页显示的数量
        for (DiscussPost post : page) {                 // 当前页的10条数据
            System.out.println(post);
        }
    }
}
测试结果:

在这里插入图片描述

3.5 搜索数据-ElasticsearchTemplate

  • 使用ElasticsearchRepository,虽也查询到了高亮,但无法将高亮标签与原数据进行组合,因此,很难实现高亮效果
  • 因此,使用ElasticsearchTemplate 原生的API进行组装处理
new SearchResultMapper()
  • 创建新的结果映射规则,对结果自定义封装

    将搜索的结果处理后用一个集合装着
    获取的搜索结果是JSON格式,可以转为map,获取map中的每个字段
    对于高亮显示的title、content字段,先获取到原始数据,因为有可能没有匹配到,防止空指针
    最后再处理高亮部分的数据,如果有,就将原数据覆盖即可
    
HighlightField
  • 接受高亮的字段
getFragments()
  • getFragments()返回的是一个数组,因为匹配的不止一个,只高亮第一个匹配的结果即可
java">/**
 * 使用ElasticsearchTemplate 搜索匹配的数据
 */
@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() {
        @Override
        public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> aClass, Pageable pageable) {
            // 获取搜索的结果
            SearchHits hits = response.getHits();
            if (hits.getTotalHits() <= 0) {
                return null;
            }
		   // 将搜索的结果处理后用一个集合装着
            // 获取的搜索结果是JSON格式,可以转为map,获取map中的每个字段
            // 对于高亮显示的title、content字段,先获取到原始数据,因为有可能没有匹配到,防止空指针
            // 最后再处理高亮部分的数据,如果有,就将原数据覆盖即可
            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) {	// 判空,没有匹配到,就不处理
                    // getFragments()返回的是一个数组,因为匹配的不止一个,只高亮第一个匹配的结果即可
                    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());
        }
    });

    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);
    }
}
测试结果

在这里插入图片描述


http://www.niftyadmin.cn/n/584076.html

相关文章

一个将阿拉伯数字转换为中文数字近似读法的程序

偶尔突发奇想&#xff0c;写了一个将阿拉伯数字转换为中文数字近似值的程序&#xff1a; #include <stdlib.h> #include <stdio.h> #include <limits.h> #include <string.h> #include <stdbool.h> #include <windows.h>const char *Chine…

项目1在线交流平台-6.Elasticsearch分布式搜索引擎-3.ES结合Kafka应用-开发社区搜索功能

文章目录功能需求一、Service层处理操作ES服务器的数据二、Controller层处理帖子添加和评论事件请求1.添加帖子时-触发事件-发布消息2. 添加评论时-触发发帖事件-发布消息三、kafka消费者订阅消息并提交到ES服务器四、处理搜索结果1. Controller处理搜索请求2. 处理模板页面主页…

一个小巧的反汇编引擎

从内核反汇编hook中截取的反汇编代码&#xff0c;非常小巧&#xff1a; #include <stdlib.h> #include <stdio.h> #include <stdbool.h> //#include <windows.h> #include "libdasm.h"typedef unsigned char byte; byte bin[] {0x55,0x89,0…

项目1在线交流平台-7.构建安全高效的企业服务-1.SpringSecurity入门-认识与测试案例

文章目录一、认识SpringSecurity1. 简介2. 历史3. 特点与Shiro的比较4. 底层原理FilterSecurityInterceptor&#xff1a;ExceptionTranslationFilter&#xff1a;UsernamePasswordAuthenticationFilter &#xff1a;二、SpringSecurity演示示例1. 导包2. 用户权限的设定2.1 授权…

一个比较精确计算cpu主频的程序

以前写的一段计算cpu主频的代码&#xff0c;考虑到了某些cpu的负载平衡能力。 #include <stdlib.h> #include <stdio.h> #include <stdbool.h> #include <math.h> #include <windows.h>static ULONGLONG g_pf_s,g_pf_e,g_sys_freq {0}; static …

项目1在线交流平台-7.构建安全高效的企业服务-2.使用Security自定义社区网页认证与授权

文章目录功能需求一、 废弃登录检查的拦截器二、授权配置1. 导包2. Security配置2.1 WebSecurity2.2HttpSecurity http.authorizeRequests().antMatchers()-要验证的请求路径.hasAnyAuthority( )-需要的权限.anyRequest().permitAll()-请他请求无需权限访问http.exceptionHandl…

在linux下监听转发后的端口

ubuntu12.04下nc -l -p port已经无效,将-p丢掉直接nc -l port即可. 我们要得效果为将telnet的端口转发到端口2222. 首先监听端口 : nc -l 2222 开新终端输入 : sudo iptables -t nat -A PREROUTING -p tcp --dport 23 -j REDIRECT --to-ports 2222 然后在win7系统上使用putty t…

GCC的内嵌汇编语法

1 Overview开发一个OS&#xff0c;尽管绝大部分代码只需要用C/C等高级语言就可以了&#xff0c;但至少和硬件相关部分的代码需要使用汇编语言&#xff0c;另外&#xff0c;由于启动部分的代码有大小限制&#xff0c;使用精练的汇编可以缩小目标代码的Size。另外&#xff0c;对于…