文章目录
一、定义document
@Data
@EqualsAndHashCode(callSuper = false)
@Document(indexName = "syni_sjb_encyclopedia_entry")
public class SjbEncyclopediaEntryDocument implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private String id;
/**
* 词条名称
*/
@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer="ik_max_word")
private String name;
/**
* 词条解释,富文本类型
*/
@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer="ik_max_word")
private String entryExplain;
/**
* 初始浏览人数
*/
@Field(type = FieldType.Integer)
private Integer initPeopleCount;
/**
* 补增浏览人数
*/
@Field(type = FieldType.Integer)
private Integer complementPeopleCount;
/**
* 真实浏览人数,非实时
*/
@Field(type = FieldType.Integer)
private Integer realPeopleCount;
/**
* 初始人数+补增人数+真实浏览人数
*/
@Field(type = FieldType.Integer)
private Integer sumPeopleCount;
/**
* 词条生成的h5文件url
*/
@Field(type = FieldType.Keyword)
private String link;
/**
* 来源
*/
@Field(type = FieldType.Keyword)
private String createWay;
/**
* 创建人
*/
@Field(type = FieldType.Keyword)
private String creator;
/**
* 创建时间
*/
@Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern ="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private LocalDateTime createTime;
/**
* 更新时间
*/
@Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern ="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private LocalDateTime updateTime;
}
这里有个坑:时间字段使用
@Field(type = FieldType.Date, format = DateFormat.custom, pattern = “yyyy-MM-dd HH:mm:ss”)
调用find时时间会反序列化失败,不知道是不是版本问题
我的版本是2.3.4 RELEASE
二、手动put es mapping(类似于建表,定义表结构)
put http://xxx.xxx.xx:9200/syni_sjb_encyclopedia_entry
{
"mappings": {
"properties": {
"_class": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"complementPeopleCount": {
"type": "integer"
},
"createTime": {
"type": "date",
"format": "yyyy-MM-dd'T'HH:mm:ss||yyyy-MM-dd||epoch_millis"
},
"createWay": {
"type": "keyword"
},
"creator": {
"type": "keyword"
},
"entryExplain": {
"type": "text",
"analyzer": "ik_max_word"
},
"initPeopleCount": {
"type": "integer"
},
"link": {
"type": "keyword"
},
"name": {
"type": "text",
"analyzer": "ik_max_word"
},
"realPeopleCount": {
"type": "integer"
},
"sumPeopleCount": {
"type": "integer"
},
"updateTime": {
"type": "date",
"format": "yyyy-MM-dd'T'HH:mm:ss||yyyy-MM-dd||epoch_millis"
}
}
}
}
ES自带有自动建表的功能,那为什么还要手动put?
因为字段的类型可能不会合你意,比如时间格式只有yyyy-MM-dd’T’HH:mm:ss,但实际上还可能需要yyyy-MM-dd
三、创建respository,并定义方法(可省略)
public interface SjbEncyclopediaEntryRepository extends ElasticsearchRepository<SjbEncyclopediaEntryDocument,String> {
List<SjbEncyclopediaEntryDocument> findByNameAndIdNot(String name, String id);
}
方法定义类似JPA(虽然我也没用过JPA,大概意思就是按照一定规范定义方法,框架就自动帮你实现,比如findByName)
这个不太好用,因为用这个就不知道怎么分页,怎么排序了。
四、ElasticsearchOperations使用及与Mybatis-plus方法的转换
我的需求是把mysql的数据转移到ES,功能不变。所以需要把Mybatis-plus的方法转化成ES
- 增删改
增:xxxService.save(entity) => elasticsearchOperations.save(entity)
改:xxxService.update(entity) => elasticsearchOperations.save(entity)
删:xxxService.removeById(id) => elasticsearchOperations.deleteById(id)
增和改同样是使用save,只要id一样就会自动覆盖。
- 查
查比较复杂,上一段代码大家看看前后对比
public Page<SjbEncyclopediaEntryVO> queryByParam(SjbEncyclopediaEntryQueryDTO dto) {
// 分页
Page<SjbEncyclopediaEntry> page = new Page<>(dto.getPageNo(), dto.getPageSize());
QueryWrapper<SjbEncyclopediaEntry> queryWrapper = new QueryWrapper<>();
if(StringUtils.isNotBlank(dto.getName())) {
queryWrapper.lambda().eq(SjbEncyclopediaEntry::getName, dto.getName());
}
if(dto.getStartDate() != null && dto.getEndDate() != null) {
queryWrapper.lambda().between(SjbEncyclopediaEntry::getCreateTime, dto.getStartDate(), dto.getEndDate().plusDays(1));
}
// 排序
queryWrapper.lambda().orderByDesc(SjbEncyclopediaEntry::getRealPeopleCount);
page = page(page, queryWrapper);
Page<SjbEncyclopediaEntryVO> result = new Page<>(dto.getPageNo(), dto.getPageSize());
BeanUtils.copyProperties(page, result);
result.setRecords(page.getRecords().stream().map(sjbEncyclopediaEntry -> {
SjbEncyclopediaEntryVO vo = new SjbEncyclopediaEntryVO();
BeanUtils.copyProperties(sjbEncyclopediaEntry, vo);
vo.setRealPeopleCount(getRedisRealPeopleCount(sjbEncyclopediaEntry.getId()));
return vo;
}).collect(Collectors.toList()));
return result;
}
public Page<SjbEncyclopediaEntryVO> queryByParam(SjbEncyclopediaEntryQueryDTO dto) {
Pageable pageable = PageRequest.of(dto.getPageNo()-1, dto.getPageSize());
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
if(StringUtils.isNotBlank(dto.getName())) {
boolQueryBuilder.must(QueryBuilders.termsQuery("name", dto.getName()));
}
if(dto.getStartDate() != null && dto.getEndDate() != null) {
boolQueryBuilder.filter(QueryBuilders.rangeQuery("createTime").from(dto.getStartDate()).to(dto.getEndDate().plusDays(1)));
}
SortBuilder sortBuilder = SortBuilders.fieldSort("createTime").order(SortOrder.DESC);
NativeSearchQuery build = new NativeSearchQueryBuilder()
.withQuery(boolQueryBuilder)
.withPageable(pageable)
.withSort(sortBuilder)
.build();
SearchHits<SjbEncyclopediaEntryDocument> search = elasticsearchOperations.search(build, SjbEncyclopediaEntryDocument.class);
Page<SjbEncyclopediaEntryVO> result = new Page<>(dto.getPageNo(), dto.getPageSize());
result.setTotal(search.getTotalHits());
List<SearchHit<SjbEncyclopediaEntryDocument>> collect = search.get().collect(Collectors.toList());
result.setRecords(collect.stream().map(sjbEncyclopediaEntry -> {
SjbEncyclopediaEntryVO vo = new SjbEncyclopediaEntryVO();
BeanUtils.copyProperties(sjbEncyclopediaEntry.getContent(), vo);
vo.setRealPeopleCount(getRedisRealPeopleCount(sjbEncyclopediaEntry.getId()));
return vo;
}).collect(Collectors.toList()));
return result;
}
可以大致理解mybatis-plus与es的实体与api对应关系为:
QueryWrapper<?> => NativeSearchQuery
Page<?> => Pageable
wrapper.orderByDesc => SortBuilder
Page<?> result => List<SearchHit<?>>
page(page, queryWrapper); => elasticsearchOperations.search(build, SjbEncyclopediaEntryDocument.class);
五、一些坑
boolQueryBuilder.filter(QueryBuilders.rangeQuery(“publishTime”).lt(LocalDateTime.now()));
这句话又报错了,估计是lt里的时间值转字符串时多了毫秒值,试了mapping里加毫秒格式也不行。
解决方案:直接用字符串
String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern(“yyyy-MM-dd’T’HH:mm:ss”));
boolQueryBuilder.filter(QueryBuilders.rangeQuery(“publishTime”).lt(format));