赞
踩
在ES6.0之后,前两个术语与MySQL对应逐步发生变化,删除了类型,变成一个索引对应一张表,但是保留了类型这个单词。
在ES7.0之后,逐步删除类型。
对于Elasticsearch
的下载,最好在对应项目中,找到父级依赖所确定的版本,因为这是经过测试,与当前Spring Boot
版本最匹配的版本。
下载完成后,解压到不含有中文的目录,目录结果如下图所示:
版本不一致,目录结构可能会有所区别。
配置主要是配置config
目录下的elasticsearch.yml
文件;配置内容如下所示:
# 集群名字
cluster.name: my-application
# 数据存储位置
path.data: E:\Data\elasticsearch\es-7.15.2\data
# 运行时产生日志 存储位置
path.logs: E:\Data\elasticsearch\es-7.15.2\logs
配置结果如下图所示:
进入配置环境变量界面步骤:系统->系统信息->高级系统设置->环境变量
在系统变量的Path中新建环境变量;如下图所示:
ES默认进行英文分词,需要安装中文分词插件来对中文进行分词,即可对中文关键词进行检索。
对应Elasticsearch版本来下载对应的中文分词插件。
首先在Elasticsearch安装目录下的,plugins目录下,新建一个ik
文件夹,然后将分词插件解压到ik目录下,如下图所示:
在config目录下,有许多dic
字典文件,里面包含很多中文词语,除此之外,若需要新增当前流行的"网络词语",需要在IKAnalyzer.cfg.xml
文件中进行配置。
该工具在操作和界面上与postman类似,但是功能比postman
更多,主要用来进行API设计、调试、测试等;且支持中文。
因为ES服务器,通过命令行存储某些数据;过长不方便,可以用ApiPost模拟网页,发送HTTP请求,往ES服务器中添加数据更为方便。
除此之外,当需要查询某些复杂数据时,也可以用ApiPost来简化数据查询。
可以通过双击bin目录下的elasticsearch.bat
文件直接启动,也可以在命令行启动。
若出现如下报错:
[DESKTOP-CO3SKTG] error updating geoip database [GeoLite2-ASN.mmdb]
则在配置文件中添加如下配置,再重新启动即可。
ingest.geoip.downloader.enabled: false
即禁止geoip
数据库的更新。
启动后结果如下:
因为配置过环境变量,所以可以直接在任意位置的命令行中,执行ES命令。
curl -X GET "localhost:9200/_cat/health?v"
ES默认端口为9200
,v
表示显示标题,使用GET
请求获取数据;执行结果如下所示:
第一行是标题,第二行是显示的数据。
green
表示很健康执行如下命令,查看集群节点;
curl -X GET "localhost:9200/_cat/nodes?v"
结果如下:
执行如下命令;
curl -X GET "localhost:9200/_cat/indices?v"
结果如下:
结果显示当前并未有索引。
新建索引采用的是PUT
请求,执行命令如下:
curl -X PUT "localhost:9200/test"
表示新建test
索引;执行结果如下图:
返回结果为JSON格式。
此时再次查询索引,则会显示出一条索引,且因为没有给索引进行分片和备份,所以健康状况会显示yellow
,结果如下图:
删除索引,使用DELETE
请求,执行命令如下所示:
curl -X DELETE "localhost:9200/test"
删除名为test
的索引;执行结果如下图所示:
此时再次查询索引则不存在名为test
的索引,如下图所示:
如图所示:
如图所示:
再次查询索引即可查到名为test
的索引,如下图所示:
如图所示:
如下图所示:
使用PUT请求,插入数据,会自动创建索引test
,_doc
插入数据类型,表示占位,1
则是插入数据的id
;插入数据格式为JSON。
如下图所示:
查询使用GET请求,表示查询索引为test
,占位为_doc
下id为1
的数据。
如下图所示:
修改数据其实与插入数据一致,在同样的位置修改数据,在底层就是先删除该位置原先存在的数据,并插入新的数据。
如下图所示:
删除数据使用的是DELETE请求,返回结果确认删除;此时再次查询,则数据不存在,如下图所示:
如图所示:
test
表示索引名。
如图所示:
q
表示查询的条件,title:互联网
则表示含有title
字段,且字段内容含有互联网
的数据。
且ES在查询时,会先将条件分割为多个词条,然后去查询包含对应字条的数据。
如图所示:
多字段查询格式如图所示;query
表示条件,multi_match
表示多个匹配,fields
则表示匹配条件的字段。
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-elasticsearch -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
在配置文件application.properties
中配置如下内容:
# 连接集群节点
spring.elasticsearch.uris=localhost:9200
主要是Redis与Elasticsearch都调用了NettyRuntime类的setAvailableProcessors方法。
在Application启动类中,添加如下内容:
@PostConstruct // 所注解的方法 会在构造器调用完以后调用
public void init() {
// 解决Netty启动冲突问题
// 由Netty4Utils.setAvailableProcessors()得
System.setProperty("es.set.netty.runtime.available.processors", "false");
}
即配置项目实体与ElasticSearch相对应;即可自动生成与某实体相对应的索引;具体实体类配置如下所示:
/** * @author 花木凋零成兰 * @date 2024/3/4 20:16 */ @Document(indexName = "discusspost") // 与Elasticsearch关联 设置索引 注意不能出现大写字母 public class DiscussPost { @Id // 与ES索引对应字段 private int id; @Field(type = FieldType.Integer) // type字段类型 private int userId; /** * analyzer时候的解析器 ik_max_word 尽可能的拆分 * searchAnalyzer搜索时候的解析器 ik_smart 灵活的拆分 */ @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; @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; }
配置完实体类后,还需要配置对ES操作接口,即接口内自动包含了与ES有关的API;接口配置如下所示:
/**
* ES操作接口
* @author 花木凋零成兰
* @date 2024/3/25 21:14
*/
@Repository
public interface DiscussPostRepository extends ElasticsearchRepository<DiscussPost, Integer> {
}
自定义接口继承ElasticsearchRepository<K, V>
类,自定义接口内即有关于ES操作的API,K
指操作的数据实体类型,V
指数据实体类型的id类型。
在ES7中,ElasticsearchRepository
主要用来实现简单的对数据增删改查,即主要用于实现简单操作;ElasticsearchRestTemplate
类则主要用来实现对数据的复杂查询等;即主要用户复杂的数据操作。
测试代码如下:
/** * @author 花木凋零成兰 * @date 2024/3/25 21:15 */ @SpringBootTest @ContextConfiguration(classes = Application.class) // 使用Application类的配置 public class ElasticsearchTests { @Autowired private DiscussPostMapper discussPostMapper; @Autowired() private DiscussPostRepository discussPostRepository; @Autowired private ElasticsearchRestTemplate elasticsearchRestTemplate; // 多用于复杂查询 @Test public void insertTest() { // 测试插入数据 若不存在索引 会自动创建 discussPostRepository.save(discussPostMapper.selectDiscussPostById(241)); // 每次插入一条数据 discussPostRepository.save(discussPostMapper.selectDiscussPostById(242)); discussPostRepository.save(discussPostMapper.selectDiscussPostById(243)); } @Test public void insertListTest() { discussPostRepository.saveAll(discussPostMapper.selectDiscussPosts(101, 0, 100)); // 一次性插入多条数据 discussPostRepository.saveAll(discussPostMapper.selectDiscussPosts(102, 0, 100)); // 一次性插入多条数据 discussPostRepository.saveAll(discussPostMapper.selectDiscussPosts(103, 0, 100)); // 一次性插入多条数据 discussPostRepository.saveAll(discussPostMapper.selectDiscussPosts(111, 0, 100)); // 一次性插入多条数据 discussPostRepository.saveAll(discussPostMapper.selectDiscussPosts(112, 0, 100)); // 一次性插入多条数据 discussPostRepository.saveAll(discussPostMapper.selectDiscussPosts(131, 0, 100)); // 一次性插入多条数据 discussPostRepository.saveAll(discussPostMapper.selectDiscussPosts(132, 0, 100)); // 一次性插入多条数据 discussPostRepository.saveAll(discussPostMapper.selectDiscussPosts(133, 0, 100)); // 一次性插入多条数据 discussPostRepository.saveAll(discussPostMapper.selectDiscussPosts(134, 0, 100)); // 一次性插入多条数据 } @Test public void updateTest() { DiscussPost discussPost = discussPostMapper.selectDiscussPostById(231); discussPost.setContent("我是Java程序员,我要好好学Java!"); discussPostRepository.save(discussPost); // 在同样id处重新插入数据 覆盖原先数据 } @Test public void deleteTest() { discussPostRepository.deleteById(231); // 根据id删除数据 } @Test public void deleteAllTest() { discussPostRepository.deleteAll(); // 一次性删除所有数据 } @Test public void testSearch() { // 构造搜索条件 NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.multiMatchQuery("互联网寒冬", "title", "content")) // 构建搜索条件 多字段查询内容 .withSorts( SortBuilders.fieldSort("type").order(SortOrder.DESC), // 构建排序顺序 先按照type倒序排 SortBuilders.fieldSort("score").order(SortOrder.DESC), // 再按score倒序排 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(); SearchHits<DiscussPost> searchHits = elasticsearchRestTemplate.search(searchQuery, DiscussPost.class); if (searchHits.getTotalHits() <= 0) { // 若查询无数据 new PageImpl<DiscussPost>(null, PageRequest.of(0, 20), 0); } List<DiscussPost> discussPostList = searchHits.stream().map(SearchHit::getContent).collect(Collectors.toList()); // 将查询的数据转化为List集合 Page<DiscussPost> page = new PageImpl<>(discussPostList, searchQuery.getPageable(), searchHits.getTotalHits()); System.out.println(page.getTotalElements()); // 获取总数 System.out.println(page.getNumber()); // 获取页码 System.out.println(page.getSize()); // 获取每页个数 System.out.println(page.getTotalPages()); // 分页总数 for (DiscussPost discussPost : page) { System.out.println(discussPost); // 输出查询结果 } } @Test public void testSearchByTemplateHighLight() { // 按条件查询数据 实现高亮 NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.multiMatchQuery("互联网寒冬", "title", "content")) // 构建搜索条件 多字段查询内容 .withSorts( SortBuilders.fieldSort("type").order(SortOrder.DESC), // 构建排序顺序 先按照type倒序排 SortBuilders.fieldSort("score").order(SortOrder.DESC), // 再按score倒序排 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(); SearchHits<DiscussPost> searchHits = elasticsearchRestTemplate.search(searchQuery, DiscussPost.class); // SearchPage<DiscussPost> page = SearchHitSupport.searchPageFor(searchHits, searchQuery.getPageable()); // 获取高亮结果集 List<DiscussPost> list = new ArrayList<>(); for (SearchHit<DiscussPost> searchHit : searchHits) { DiscussPost discussPost = searchHit.getContent(); if (searchHit.getHighlightFields().get("title") != null) { discussPost.setTitle(searchHit.getHighlightFields().get("title").get(0)); // discussPost.setContent(searchHit.getHighlightField("content").toString()); } if (searchHit.getHighlightFields().get("content") != null) { discussPost.setContent(searchHit.getHighlightFields().get("content").get(0)); // discussPost.setContent(searchHit.getHighlightField("content").toString()); } list.add(discussPost); } // 组装分页对象 Page<DiscussPost> pageInfo = new PageImpl<>(list, searchQuery.getPageable(), searchHits.getTotalHits()); System.out.println(pageInfo.getTotalElements()); // 获取查询得到数据总数 System.out.println(pageInfo.getTotalPages()); // 获取总页数 System.out.println(pageInfo.getNumber()); // 获取当前页码 System.out.println(pageInfo.getSize()); // 获取当前页面个数 // 输出分页结果 for (DiscussPost discussPost : pageInfo) { System.out.println(discussPost); } } }
因测试数据过多,此处只展示最后一个测试方法执行成功结果;如下所示:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。