feat: 设置 Elasticsearch 搜索关键词高亮显示

This commit is contained in:
xiongxiaoyang 2022-05-24 19:53:51 +08:00
parent 23fa646cd6
commit a8e2e2d5c9
3 changed files with 126 additions and 19 deletions

View File

@ -1,7 +1,5 @@
package io.github.xxyopen.novel.core.constant; package io.github.xxyopen.novel.core.constant;
import lombok.Getter;
/** /**
* elasticsearch 相关常量 * elasticsearch 相关常量
* *
@ -10,19 +8,109 @@ import lombok.Getter;
*/ */
public class EsConsts { public class EsConsts {
private EsConsts() {
throw new IllegalStateException(SystemConfigConsts.CONST_INSTANCE_EXCEPTION_MSG);
}
/** /**
* ES 索引枚举类 * 小说索引
*/ * */
@Getter public static class BookIndex{
public enum IndexEnum {
BOOK("book"); private BookIndex() {
throw new IllegalStateException(SystemConfigConsts.CONST_INSTANCE_EXCEPTION_MSG);
IndexEnum(String name) {
this.name = name;
} }
private String name; /**
* 索引名
* */
public static final String INDEX_NAME = "book";
/**
* id
*/
public static final String FIELD_ID = "id";
/**
* 作品方向;0-男频 1-女频
*/
public static final String FIELD_WORK_DIRECTION = "workDirection";
/**
* 类别ID
*/
public static final String FIELD_CATEGORY_ID = "categoryId";
/**
* 类别名
*/
public static final String FIELD_CATEGORY_NAME = "categoryName";
/**
* 小说名
*/
public static final String FIELD_BOOK_NAME = "bookName";
/**
* 作家id
*/
public static final String FIELD_AUTHOR_ID = "authorId";
/**
* 作家名
*/
public static final String FIELD_AUTHOR_NAME = "authorName";
/**
* 书籍描述
*/
public static final String FIELD_BOOK_DESC = "bookDesc";
/**
* 评分;总分:10 真实评分 = score/10
*/
public static final String FIELD_SCORE = "score";
/**
* 书籍状态;0-连载中 1-已完结
*/
public static final String FIELD_BOOK_STATUS = "bookStatus";
/**
* 点击量
*/
public static final String FIELD_VISIT_COUNT = "visitCount";
/**
* 总字数
*/
public static final String FIELD_WORD_COUNT = "wordCount";
/**
* 评论数
*/
public static final String FIELD_COMMENT_COUNT = "commentCount";
/**
* 最新章节ID
*/
public static final String FIELD_LAST_CHAPTER_ID = "lastChapterId";
/**
* 最新章节名
*/
public static final String FIELD_LAST_CHAPTER_NAME = "lastChapterName";
/**
* 最新章节更新时间
*/
public static final String FIELD_LAST_CHAPTER_UPDATE_TIME = "lastChapterUpdateTime";
/**
* 是否收费;1-收费 0-免费
*/
public static final String FIELD_IS_VIP = "isVip";
} }
} }

View File

@ -63,7 +63,7 @@ public class BookToEsTask {
EsBookDto esBook = buildEsBook(book); EsBookDto esBook = buildEsBook(book);
br.operations(op -> op br.operations(op -> op
.index(idx -> idx .index(idx -> idx
.index(EsConsts.IndexEnum.BOOK.getName()) .index(EsConsts.BookIndex.INDEX_NAME)
.id(book.getId().toString()) .id(book.getId().toString())
.document(esBook) .document(esBook)
) )

View File

@ -10,6 +10,7 @@ import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit; import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.TotalHits; import co.elastic.clients.elasticsearch.core.search.TotalHits;
import co.elastic.clients.json.JsonData; import co.elastic.clients.json.JsonData;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import io.github.xxyopen.novel.core.common.resp.PageRespDto; import io.github.xxyopen.novel.core.common.resp.PageRespDto;
import io.github.xxyopen.novel.core.common.resp.RestResp; import io.github.xxyopen.novel.core.common.resp.RestResp;
@ -48,7 +49,8 @@ public class EsSearchServiceImpl implements SearchService {
SearchResponse<EsBookDto> response = esClient.search(s -> { SearchResponse<EsBookDto> response = esClient.search(s -> {
SearchRequest.Builder searchBuilder = s.index(EsConsts.IndexEnum.BOOK.getName()); SearchRequest.Builder searchBuilder = s.index(EsConsts.BookIndex.INDEX_NAME);
// 构建检索条件
buildSearchCondition(condition, searchBuilder); buildSearchCondition(condition, searchBuilder);
// 排序 // 排序
if (!StringUtils.isBlank(condition.getSort())) { if (!StringUtils.isBlank(condition.getSort())) {
@ -61,6 +63,11 @@ public class EsSearchServiceImpl implements SearchService {
// 分页 // 分页
searchBuilder.from((condition.getPageNum() - 1) * condition.getPageSize()) searchBuilder.from((condition.getPageNum() - 1) * condition.getPageSize())
.size(condition.getPageSize()); .size(condition.getPageSize());
// 设置高亮显示
searchBuilder.highlight(h -> h.fields(EsConsts.BookIndex.FIELD_BOOK_NAME
, t -> t.preTags("<em style='color:red'>").postTags("</em>"))
.fields(EsConsts.BookIndex.FIELD_AUTHOR_NAME
, t -> t.preTags("<em style='color:red'>").postTags("</em>")));
return searchBuilder; return searchBuilder;
}, },
@ -74,6 +81,12 @@ public class EsSearchServiceImpl implements SearchService {
for (Hit<EsBookDto> hit : hits) { for (Hit<EsBookDto> hit : hits) {
EsBookDto book = hit.source(); EsBookDto book = hit.source();
assert book != null; assert book != null;
if (!CollectionUtils.isEmpty(hit.highlight().get(EsConsts.BookIndex.FIELD_BOOK_NAME))) {
book.setBookName(hit.highlight().get(EsConsts.BookIndex.FIELD_BOOK_NAME).get(0));
}
if (!CollectionUtils.isEmpty(hit.highlight().get(EsConsts.BookIndex.FIELD_AUTHOR_NAME))) {
book.setAuthorName(hit.highlight().get(EsConsts.BookIndex.FIELD_AUTHOR_NAME).get(0));
}
list.add(BookInfoRespDto.builder() list.add(BookInfoRespDto.builder()
.id(book.getId()) .id(book.getId())
.bookName(book.getBookName()) .bookName(book.getBookName())
@ -87,8 +100,12 @@ public class EsSearchServiceImpl implements SearchService {
} }
assert total != null; assert total != null;
return RestResp.ok(PageRespDto.of(condition.getPageNum(), condition.getPageSize(), total.value(), list)); return RestResp.ok(PageRespDto.of(condition.getPageNum(), condition.getPageSize(), total.value(), list));
} }
/**
* 构建检索条件
*/
private void buildSearchCondition(BookSearchReqDto condition, SearchRequest.Builder searchBuilder) { private void buildSearchCondition(BookSearchReqDto condition, SearchRequest.Builder searchBuilder) {
BoolQuery boolQuery = BoolQuery.of(b -> { BoolQuery boolQuery = BoolQuery.of(b -> {
@ -96,7 +113,9 @@ public class EsSearchServiceImpl implements SearchService {
if (!StringUtils.isBlank(condition.getKeyword())) { if (!StringUtils.isBlank(condition.getKeyword())) {
// 关键词匹配 // 关键词匹配
b.must((q -> q.multiMatch(t -> t b.must((q -> q.multiMatch(t -> t
.fields("bookName^2","authorName^1.8","bookDesc^0.1") .fields(EsConsts.BookIndex.FIELD_BOOK_NAME + "^2"
, EsConsts.BookIndex.FIELD_AUTHOR_NAME + "^1.8"
, EsConsts.BookIndex.FIELD_BOOK_DESC + "^0.1")
.query(condition.getKeyword()) .query(condition.getKeyword())
) )
)); ));
@ -105,14 +124,14 @@ public class EsSearchServiceImpl implements SearchService {
// 精确查询 // 精确查询
if (Objects.nonNull(condition.getWorkDirection())) { if (Objects.nonNull(condition.getWorkDirection())) {
b.must(TermQuery.of(m -> m b.must(TermQuery.of(m -> m
.field("workDirection") .field(EsConsts.BookIndex.FIELD_WORK_DIRECTION)
.value(condition.getWorkDirection()) .value(condition.getWorkDirection())
)._toQuery()); )._toQuery());
} }
if (Objects.nonNull(condition.getCategoryId())) { if (Objects.nonNull(condition.getCategoryId())) {
b.must(TermQuery.of(m -> m b.must(TermQuery.of(m -> m
.field("categoryId") .field(EsConsts.BookIndex.FIELD_CATEGORY_ID)
.value(condition.getCategoryId()) .value(condition.getCategoryId())
)._toQuery()); )._toQuery());
} }
@ -120,21 +139,21 @@ public class EsSearchServiceImpl implements SearchService {
// 范围查询 // 范围查询
if (Objects.nonNull(condition.getWordCountMin())) { if (Objects.nonNull(condition.getWordCountMin())) {
b.must(RangeQuery.of(m -> m b.must(RangeQuery.of(m -> m
.field("wordCount") .field(EsConsts.BookIndex.FIELD_WORD_COUNT)
.gte(JsonData.of(condition.getWordCountMin())) .gte(JsonData.of(condition.getWordCountMin()))
)._toQuery()); )._toQuery());
} }
if (Objects.nonNull(condition.getWordCountMax())) { if (Objects.nonNull(condition.getWordCountMax())) {
b.must(RangeQuery.of(m -> m b.must(RangeQuery.of(m -> m
.field("wordCount") .field(EsConsts.BookIndex.FIELD_WORD_COUNT)
.lt(JsonData.of(condition.getWordCountMax())) .lt(JsonData.of(condition.getWordCountMax()))
)._toQuery()); )._toQuery());
} }
if (Objects.nonNull(condition.getUpdateTimeMin())) { if (Objects.nonNull(condition.getUpdateTimeMin())) {
b.must(RangeQuery.of(m -> m b.must(RangeQuery.of(m -> m
.field("lastChapterUpdateTime") .field(EsConsts.BookIndex.FIELD_LAST_CHAPTER_UPDATE_TIME)
.gte(JsonData.of(condition.getUpdateTimeMin().getTime())) .gte(JsonData.of(condition.getUpdateTimeMin().getTime()))
)._toQuery()); )._toQuery());
} }