SpringBoot中集成Elasticsearch
,要注意一些依赖的版本和Elasticsearch服务器版本一致。
<properties>
<elasticsearch.version>7.13.4elasticsearch.version>
properties>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-elasticsearchartifactId>
<exclusions>
<exclusion>
<groupId>org.elasticsearch.clientgroupId>
<artifactId>elasticsearch-rest-high-level-clientartifactId>
exclusion>
<exclusion>
<groupId>org.elasticsearch.clientgroupId>
<artifactId>elasticsearch-rest-clientartifactId>
exclusion>
<exclusion>
<groupId>org.elasticsearchgroupId>
<artifactId>elasticsearchartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.elasticsearch.clientgroupId>
<artifactId>elasticsearch-rest-high-level-clientartifactId>
<version>${elasticsearch.version}version>
dependency>
<dependency>
<groupId>org.elasticsearch.clientgroupId>
<artifactId>elasticsearch-rest-clientartifactId>
<version>${elasticsearch.version}version>
dependency>
<dependency>
<groupId>org.elasticsearchgroupId>
<artifactId>elasticsearchartifactId>
<version>${elasticsearch.version}version>
dependency>
application配置文件:
#elasticsearch
spring.elasticsearch.rest.uris=124.223.44.254:9200
spring.elasticsearch.rest.username=elasticUser
spring.elasticsearch.rest.password='Password123.'
程序启动会默认据此实体类自动创建对应索引,
@Data
@Document(indexName = PrescriptionPo.indexName)
public class PrescriptionPo implements Serializable {
private static final long serialVersionUID = 4138692353689910309L;
public static final String indexName = "prescription";
@Id
@JSONField(ordinal = 1)
private String id;
@Field(type = FieldType.Keyword)
@JSONField(ordinal = 2)
private String code;
@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_max_word")
@JSONField(ordinal = 3)
private String detailContent;
@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@JSONField(ordinal = 4)
private Date publishTime;
@Field(type = FieldType.Keyword)
@JSONField(ordinal = 5)
private String authorId;
@Field(index = true, type = FieldType.Text, analyzer = "ik_smart", store = false, searchAnalyzer = "ik_smart")
@JSONField(ordinal = 6)
private String authorName;
@JSONField(ordinal = 7)
private List<String> picUrls;
@Field(type = FieldType.Keyword)
@JSONField(ordinal = 8)
private String requestUrl;
@Field(type = FieldType.Long)
@JSONField(ordinal = 9)
private Long batchId;
}
若因某些ES服务版本导致不能生成正确的索引映射,也可通过DSL
提前手动创建索引,
PUT prescription
{
"mappings" : {
"properties" : {
"authorId" : {
"type" : "keyword"
},
"authorName" : {
"type" : "text",
"analyzer" : "ik_smart"
},
"batchId" : {
"type" : "long"
},
"code" : {
"type" : "keyword"
},
"detailContent" : {
"type" : "text",
"analyzer" : "ik_max_word"
},
"id" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"picUrls" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"publishTime" : {
"type" : "date",
"format" : "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
},
"requestUrl" : {
"type" : "keyword"
}
}
}
}
SpringBoot里操作Elasticsearch有很多种方式,可以使用SpringData的ElasticsearchRepository
实现简单的操作,可以使用SpringData的ElasticsearchRestTemplate
实现更复杂的操作如搜索高亮和批量导入等操作,也可以自己封装一个通过操作DSL
的Restful客户端,更加自由。
public void sync(List<PrescriptionPo> prescriptionPoList) {
List<IndexQuery> queries = new ArrayList<>();
IndexCoordinates indexCoordinates = IndexCoordinates.of(PrescriptionPo.indexName);
int count = 0;
for (PrescriptionPo po : prescriptionPoList) {
IndexQuery query = new IndexQuery();
query.setId(po.getId());
query.setSource(JSON.toJSONString(po));
queries.add(query);
// 每批次1000条提交索引
if (count != 0 && count % 1000 == 0) {
elasticsearchRestTemplate.bulkIndex(queries, indexCoordinates);
queries.clear();
}
count++;
}
// 不足一批次的最后提交
if (queries.size() > 0) {
elasticsearchRestTemplate.bulkIndex(queries, indexCoordinates);
}
elasticsearchRestTemplate.indexOps(indexCoordinates).refresh();
log.info("Finished bulk {}!", prescriptionList.size());
}
使用ElasticsearchRestTemplate
配合SearchHits
实现分页搜索并高亮显示,
@Override
public Page<PrescriptionDto> searchPrescription(String keyword, Integer pageNum, Integer pageSize) {
Pageable pageable = PageRequest.of(pageNum - 1, pageSize);
NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field(new HighlightBuilder.Field("detailContent"));
highlightBuilder.preTags("").postTags("");
searchQueryBuilder.withPageable(pageable)
.withQuery(QueryBuilders.matchQuery("detailContent", keyword))
.withHighlightBuilder(highlightBuilder);
SearchHits<PrescriptionPo> searchHits = elasticsearchRestTemplate.search(searchQueryBuilder.build(), PrescriptionPo.class);
// 创建list对象
List<PrescriptionDto> prescriptionDtoList = new ArrayList<>();
searchHits.forEach(item -> {
PrescriptionPo prescriptionPo = item.getContent();
prescriptionDtoList.add(prescriptionPo.makeDto(item));
});
// 组装分页对象
Page<PrescriptionDto> page = new PageImpl<>(prescriptionDtoList, pageable, searchHits.getTotalHits());
return page;
}
使用ElasticsearchRepository
则不能同时实现分页搜索和高亮,
public interface PrescriptionRepository extends ElasticsearchRepository<PrescriptionPo, String> {
// 用DSL,对detailContent分词后搜索
@Query("{\"match\": {\"detailContent\": \"?0\"}}")
@Highlight(
fields = {@HighlightField(name = "detailContent")},
parameters = @HighlightParameters(preTags = {""}, postTags = {""}, numberOfFragments = 0)
)
List<SearchHit<PrescriptionPo>> matchByContentWithDsl(String detailContent, Pageable pageable);
// 支持分页,不支持高亮
@Query("{\"match\": {\"detailContent\": \"?0\"}}")
Page<PrescriptionPo> matchByContentWithDsl2(String detailContent, Pageable pageable);
Optional<PrescriptionPo> findByCode(String code);
}
GET _cat/indices?v
GET _cat/plugins
GET _search
{
"query": {
"match_all": {}
}
}
GET /_analyze
{
"text":"中华人民共和国国徽",
"analyzer":"ik_smart"
}
DELETE prescription
GET prescription/_mappings
GET prescription/_count
GET prescription/_search?pretty&from=0&size=2
GET prescription/_search?pretty&from=0&size=2
{
"query": {
"match": {
"detailContent": "咳嗽"
}
},
"highlight" : {
"pre_tags" : [""],
"post_tags" : [""],
"fields" : {
"detailContent" : {}
}
}
}
GET prescription/_search?pretty
{
"query": {
"wildcard": {
"code": "*LaONPnzhG"
}
},
"sort": [
{
"publishTime": {
"order": "desc"
}
}
]
}
GET prescription/_search?pretty&from=0
{
"query": {
"bool": {
"must": [
{
"term": {
"code": "Lbzt9cKrB"
}
}
]
}
}
}
GET prescription/_search?q=*&sort=publishTime:desc&pretty
# 指定字段更新
POST prescription/_update/1642665269755_RTX4FX
{
"doc": {
"detailContent": "xx"
}
}
# 全字段更新
POST prescription/_doc/9gUwDn0BA5KB_D7SWQLN
{
"detailContent": "xxx"
}
# 分组统计每批各自总数
GET prescription/_search?pretty
{
"size": 0,
"aggs": {
"group_by_batchId": {
"terms": {
"field": "batchId"
}
}
}
}
# 并计算每批的平均发布时间,因为请求setsize=0,所以响应仅包含聚合结果
GET prescription/_search?pretty
{
"size": 0,
"aggs": {
"group_by_batchId": {
"terms": {
"field": "batchId"
},
"aggs": {
"average_publishTime": {
"avg": {
"field": "publishTime"
}
}
}
}
}
}
# 备份到新索引
POST _reindex
{
"source": {
"index": "prescription"
},
"dest": {
"index": "prescription_copy20220110"
}
}
分享一个搜索头疼脑热和中药信息的网址,
https://124.223.54.92/ 或 http://124.223.54.92/,
Ps,其中所有信息仅供娱乐!
Ps,其中所有信息仅供娱乐!
Ps,其中所有信息仅供娱乐!
因使用了自己生成的ssl证书,可能谷歌浏览器打不开网址,提示您的连接不是私密连接
,
在浏览器页面键入thisisunsafe
即可。