elasticsearch7.14.0最新版的操作集合,以及对elasticsearch和mybatis-plus整合。
前言
- 就算不用这个框架,用这个来魔改一下也挺不错哦🤪
- 该框架是在springboot中对elasticsearch的方法进行包装。同时整合mybatis-plus和elasticsearch(利用mybatis-plus的拦截器来完成自动增删改)
- 不用在给每个类配置一个独一无二的增删改操作了。且能够自动的实现elasticsearch的增删改操作,以及自动创建index。还有es的搜索功能、支持高亮显示、must匹配和should匹配、list列表分页获取数据。
- 版本使用:
- elasticsearch 7.14.0 最新版本
- springboot 2.4.1
- 自定义的yml配置
smart-es:
client-config:
hostname: # 服务器ip
port: 9200
scheme: http
username: # es给你生成的用户名
password: # es给你生成的密码
service-config:
pre-tags: <span class="highlight">
post-tags: </span>
pkg: cn.omisheep.spook.entity # 实体类包名
1. 自动创建索引
如下,有三个注解,分别是@Index
,@IndexId
,@IndexField
。
@Data
@Index
@TableName("article")
@Accessors(chain = true)
public class Article {
@TableId(value = "id", type = IdType.AUTO)
@IndexId
private Integer id;
@IndexField(isSearch = false)
private String userAvatar;
@IndexField
private String type;
@IndexField
private String tags;
@IndexField
private String title;
@IndexField(searchAnalyzer = "ik_smart")
private String content;
@IndexField(isSearch = false)
private String firstPicture;
@IndexField
private String description;
@IndexField
private Integer views; // 浏览量
@IndexField
@TableField(value = "`like`")
private Integer like; // 浏览量
@IndexField
private Date createTime;
}
@Index
该注解用于实体类上面,被注解标记的实体类在springboot运行时会自动判断你的服务器有没有对应的索引,如果没有会创建,有的话会update,稍后说。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Index {
String value() default ""; // 索引名
}
-
@IndexId
该注解用于属性上,被该注解标记的实体类在创建index时或者数据自动增删改时会将其当作es的id,什么类型无所谓,类型会自动转成string。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IndexId {
}
-
@IndxField
该注解用于属性上,被该注解标记的实体类顾名思义就是index里面的属性。可以自定义搜索方式,分词方式,指定类型,指定名称,基础类型可以不写,其他的,比如String类型可以有text或者keyword。默认text。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IndexField {
String value() default ""; // 字段名,默认为属性名
String type() default ""; // 字段类型,如text等,可以不设置,会默认根据java类型来转换
String analyzer() default "ik_max_word"; // 在类型为text时生效
String searchAnalyzer() default "ik_max_word"; // 在类型为text时生效
/**
* 默认为true参与搜索。
* false时不参与搜索,只作为内容传递,但在指定should和must搜索下仍然能被搜索
*/
boolean isSearch() default true;
}
2. 增删改统一方法
如下,比如在ElasticSearchService类中,使用
@Autowired
private ArticleMapper articleMapper;
@Autowired
private ElasticSearchService elasticSearchService;
public int releaseArticle(Article article, User user) {
article.setUserId(user.getId()).setUserAvatar(user.getAvatar());
int insert = articleMapper.insert(article);
elasticSearchService.insert(article);
// elasticSearchService.update(article);
return insert;
}
众所周知,因为mybatis的特性,当你插入一个数据时,如果你想这个id由数据库生成,然后在你插入返还id给你,你得重写从对象里面取出来,比如:
articleMapper.insert(article);
int id = article.getId();
看上面,只需要一行代码就能实现插入。使用逻辑和mybatis-plus基本一致
当然,你可以不写,借助mybatis-plus给你的拦截器,你可以在所有mapper调用insert方法之后去拦截她,然后在拦截器中写上这个方法。因为所有的类使用都是一个模样elasticSearchService.insert(object);
。
- 拦截器如下
@Slf4j
@Intercepts(@Signature(type = Executor.class, method = "update", args = {
MappedStatement.class,
Object.class}))
public class ESAop implements Interceptor {
@Autowired
private ElasticSearchService elasticSearchService;
@Override
public Object intercept(Invocation invocation) throws Throwable {
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
Object proceed = invocation.proceed(); // 这个就是mapper的方法执行
fun(mappedStatement.getId(), invocation.getArgs()[1]);
return proceed;
}
public Object plugin(Object o) {
return Plugin.wrap(o, this);
}
public void setProperties(Properties properties) {
}
@SneakyThrows
private void fun(String method, Object parameter) {
int i = method.lastIndexOf(".");
String className = method.substring(0, i);
String methodName = method.substring(i + 1);
ParameterizedType parameterizedType =
(ParameterizedType) Class.forName(className)
.getGenericInterfaces()[0];
Class<?> clz = (Class<?>) parameterizedType.getActualTypeArguments()[0];
if (!elasticSearchService.isIndex(clz)) {
return;
}
switch (methodName) {
// 在这里添加自定义方法,如果不熟悉,可以打个断点
case "insert": {
if (clz == parameter.getClass()) {
elasticSearchService.insert(parameter);
}
break;
}
case "updateById":
Object o = ((MapperMethod.ParamMap) parameter).get("param1");
if (clz == o.getClass()) {
elasticSearchService.insert(parameter);
}
elasticSearchService.insert(o);
break;
case "deleteById": {
elasticSearchService.delete(clz.getName(), parameter + "");
break;
}
}
}
}
自定义的mapper方法需要拦截,你需要动动你的小手自己加上去。。。
- 再来看看insert这个方法。其他方法大同小异
/**
* 请在save之后调用此方法,否则获得不到id,这样在插入到es服务器中,id是随机的。
*/
@SneakyThrows
public <E> int insert(E o) {
log.info(" elastic service insert {}", o);
Meta meta = new Meta(o);
IndexRequest request = new IndexRequest(meta.getIndexName())
.id(meta.getIndexId())
.timeout("3s")
.source(meta.getSource());
return client.index(request, RequestOptions.DEFAULT).status().getStatus();
}
很明显。参数是不是固定的。所以可以实现插入任意的实体类。这个service有点长,在最后贴出来。
Meta meta = new Meta(o); // 这一行代码就是根据之前的一些注解来包装成一个对象。
还有一些其他的如update delete啥的放在最后了,或者去gitee上下载下来。https://gitee.com/iozxc/smart-es
3. 搜索查询
搜索就不看service的代码了。直接看controller如何调用的
@RestController
@RequestMapping
@Slf4j
public class SearchController {
@Autowired
private ElasticSearchService elasticSearchService;
@GetMapping(value = {
"/search", "/s"})
public Result search(@RequestParam(value = "kw", required = false) String keyword,
@RequestParam(required = false) Integer pageNo,
@RequestParam(required = false) Integer pageSize,
@RequestParam(required = false) String type,
@RequestParam(required = false) String index,
@RequestParam(required = false) String must,
@RequestParam(required = false) String should,
@RequestParam(value = "highlight", required = false, defaultValue = "true") Boolean isHighlight) {
if (keyword == null) {
return new Result(ResultCode.SUCCESS);
}
HashMap<String, List<Object>> search = elasticSearchService.search(keyword, pageNo, pageSize, type, index, must, should, isHighlight);
return new Result(ResultCode.SUCCESS, search);
}
@GetMapping(value = {
"/list", "/l"})
public Result list(@RequestParam(value = "i", required = false) String index,
@RequestParam(required = false) Integer pageNo,