关于 Elasticsearch
Elasticsearch(以下简称为 ES)是一个非常强大的搜索引擎,目前广泛用于各大 IT 公司,由Elastic公司创建。
源码开源在GitHub,目前已收获 60k+ 星标。GitHub 地址:
Elasticsearch 与 Logstash、Kibana 共同组成了大家应该有所耳闻的 ELK 技术栈。Logstash 负责数据的采集、处理,Kibana 负责数据展示、分析、管理、监督及应用,Elasticsearch 可以帮我们对数据进行快速地搜索及分析。
简单地说, Elasticsearch 是一个分布式的使用 REST 接口的搜索引擎。我们可以将它用作项目中的文章搜索功能——大材小用的典型例子,哈哈哈,不过学习嘛,咱也不可能有海量数据,但技术还是要学的,对吧?
安装配置 Elasticsearch
直接通过 ES 官方地址下载
https://www.elastic.co/cn/downloads/past-releases/elasticsearch-7-10-0
不同的操作系统有不同的版本。
进入解压后的目录,运行下面的命令,启动 ES。
./bin/elasticsearch
在浏览器地址栏里输入 http://localhost:9200 确认 ES 服务是否启动成功:
为了实现数据可视化,我们最好再安装一下 Kibana。
进入解压后的目录,运行下面的命令,启动 Kibana。
./bin/kibana
访问后可以看到以下界面。
关于中文分词器
分词是将全文本转换为一系列单词的过程,这些单词称为 term,而这个过程称为分词。
分词是通过分词器(Analyzer) 来实现的,比如用于中文分词的 IK 分词器等。
ES 内置的分词器对中文并不友好,只会一个字一个字的分,比如说“我喜欢冰淇淋”,会分成“我”、“喜”、“欢”、“冰”、“淇”、“淋”。
那我们预期的应该是“我”、“喜欢”、“冰淇淋”,对吧?
中文分词中,使用比较多的是 IK 分词器,它的算法是基于词典的,支持自定义词典和词典热更新。可通过以下网址查看最新的 release 版本。
目前最新的是 v8.2.0 版本。我们需要找到 v7.10.0 版本。
下载完成后,进入 ES 安装目录,新建 ik 目录。把刚刚下载好的 ik 中文分词器插件解压到该目录。
然后重启 ES。
关于 Spring Data Elasticsearch
Spring Data Elasticsearch 是 Spring Data 项目下的一个子模块,在下面的网址下可以查看对应的信息。
- 支持基于 Spring @Configuration 的Java配置方式。
- 提供了用于操作ES的便捷工具类ElasticsearchTemplate。
- 利用Spring的数据转换服务可以实现功能强大的对象映射。
- 可以根据持久层接口自动生成对应实现方法,无需手工编写样板操作代码。
Spring Data Elasticsearch 通过注解来声明字段的映射属性,有下面三个注解需要特别说明下:
1)@Document,作用在类上,标记实体类为文档对象。
public @interface Document {
//相当于mysql中数据库的概念
String indexName();
}
2)@Id,作用在成员变量上,标记一个字段作为id主键。
//表示是文档的id,可以认为是mysql中主键的概念
public @interface Id {
}
3)@Field,作用在成员变量上,标记为文档的字段,并指定字段映射属性。
public @interface Field {
//文档中字段的类型
FieldType type() default FieldType.Auto;
//分词器名次
String analyzer() default "";
}
其中 FieldType 有以下这些关键选项:
public enum FieldType {
Auto("auto"),//自动判断字段类型
Text("text"),//会进行分词并建了索引的字符类型
Keyword("keyword"),//不会进行分词建立索引的类型
}
整合 Elasticsearch 实现文章搜索
第一步,在 pom.xml 中添加 Spring Data Elasticsearch 的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
第二步,新建 ES 配置类,连上我们本地的 ES 服务。
@Configuration
@EnableElasticsearchRepositories
public class ESConfig {
@Bean
public RestHighLevelClient client() {
ClientConfiguration clientConfiguration
= ClientConfiguration.builder()
.connectedTo("localhost:9200")
.build();
return RestClients.create(clientConfiguration).rest();
}
@Bean
public ElasticsearchOperations elasticsearchTemplate() {
return new ElasticsearchRestTemplate(client());
}
}
第三步,准备实体类 Posts.java。
@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="Posts对象", description="文章")
@TableName(autoResultMap = true)
public class Posts implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "posts_id")
@TableId(value = "posts_id", type = IdType.AUTO)
private Long postsId;
@ApiModelProperty(value = "正文")
private String postContent;
@ApiModelProperty(value = "标题")
private String postTitle;
@ApiModelProperty(value = "摘录")
private String postExcerpt;
}
在此基础上,添加 Spring Data Elasticsearch 文档注解@Document、@Id、@Field:
@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="Posts对象", description="文章")
@TableName(autoResultMap = true)
@Document(indexName = "posts")
public class Posts implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "posts_id")
@TableId(value = "posts_id", type = IdType.AUTO)
@Id
private Long postsId;
@ApiModelProperty(value = "正文")
private String postContent;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
@ApiModelProperty(value = "标题")
private String postTitle;
@ApiModelProperty(value = "摘录")
private String postExcerpt;
}
第四步,新建 EsPostRepository 接口,继承 ElasticsearchRepository 类,这样EsPostRepository就具备基本的 ES 操作能力了。
我们可以在接口中直接定义 ES 的操作方法而无需实现,比如说我要从文章名中查询关键字,那么方法就是:
Page<EsPosts> findByPostTitle(String postTitle, Pageable pageable);
比如说我要从文章名或者文章内容中查询关键字,那么方法就是:
Page<EsPosts> findByPostTitleOrPostContent(String postTitle,String postContent,Pageable pageable);
在这个过程中,Intellij IDEA 还可以给出提示:
第五步,新建 IEsPostService 接口,定义 Service 的操作方法:
public interface IEsPostService {
/**
* 从数据库导入所有文章到 ES
* @return
*/
int importAll();
void delete(Long id);
EsPosts create(Long id);
/**
* 根据关键字查询
*
* @param keyword
* @param pageNum
* @param pageSize
* @return
*/
Page<EsPosts> search(String keyword, Integer pageNum, Integer pageSize);
}
第六步,新建 EsPostServiceImpl 实现类。
@Service
@Slf4j
public class EsPostServiceImpl implements IEsPostService {
@Autowired
private EsPostRepository esPostRepository;
@Autowired
private EsPostMapper esPostMapper;
@Override
public int importAll() {
log.info("导入所有文章到 ES");
List<EsPosts> list = esPostMapper.getAll();
esPostRepository.saveAll(list);
return list.size();
}
@Override
public void delete(Long id) {
log.info("删除 id {}",id);
esPostRepository.deleteById(id);
}
@Override
public EsPosts create(Long id) {
log.info("新增:{}", id);
EsPosts post = esPostMapper.selectById(id);
EsPosts result = esPostRepository.save(post);
return result;
}
@Override
public Page<EsPosts> search(String keyword, Integer pageNum, Integer pageSize) {
Pageable pageable = PageRequest.of(pageNum, pageSize);
return esPostRepository.findByPostTitle(keyword,pageable);
}
}
第七步,新建 EsPostController 控制器:
@RestController
@Api(tags = "文章 ES")
@RequestMapping("/esPost")
public class EsPostController {
@Autowired
private EsPostServiceImpl esPostService;
@ApiOperation(value = "导入所有文章到 ES")
@RequestMapping(value = "/importAll", method = RequestMethod.POST)
public ResultObject importAll() {
int count = esPostService.importAll();
return ResultObject.success(count);
}
@ApiOperation(value = "简单搜索")
@RequestMapping(value = "/search/simple", method = RequestMethod.GET)
public ResultObject<Page<EsPosts>> search(@RequestParam(required = false) String keyword,
@RequestParam(required = false, defaultValue = "0") Integer pageNum,
@RequestParam(required = false, defaultValue = "5") Integer pageSize) {
Page<EsPosts> page = esPostService.search(keyword,pageNum,pageSize);
return ResultObject.success(page);
}
}
文章搜索接口测试
启动项目,在浏览器地址栏中访问:
可以看到整合 ES 后的文章操作接口:
先把数据库中的所有文章信息导入到 ES 中:
在 Kibana 中也可以看到导入后的 ES 文档:
来执行 ES 关键字查询:
参考链接:
回复