7 SpringBoot整合ElasticSearch
7.1 导入依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
7.2 写yml
yaml
spring:
elasticsearch:
rest:
uris: 127.0.0.1:9200 #地址
7.3 实体映射
java
package com.cxs.elasticsearch.entity;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import java.time.LocalDateTime;
/*
* @Author:cxs
* @Motto:放下杂念,只为迎接明天更好的自己
* */
@Data
@Document(indexName = "good_item_index")
public class GoodItem {
@Id
private Long id;
@Field(type = FieldType.Long)
private Long spu;
@Field(type = FieldType.Long)
private Long sku;
@Field(type = FieldType.Text)
private String title;
@Field(type = FieldType.Long)
private Long price;
@Field(type = FieldType.Text)
private String pic;
@Field(type = FieldType.Text)
private String url;
@Field(type = FieldType.Date, format = DateFormat.date)
private LocalDateTime createTime;
@Field(type = FieldType.Date, format = DateFormat.date)
private LocalDateTime updateTime;
}
7.4 Dao
java
package com.cxs.elasticsearch.dao;
import com.cxs.elasticsearch.entity.GoodItem;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
/*
* @Author:cxs
* @Motto:放下杂念,只为迎接明天更好的自己
* */
@Repository
public interface GoodItenDao extends ElasticsearchRepository<GoodItem, Long> {
}
7.5 Service
java
package com.cxs.service.impl;
import com.cxs.elasticsearch.dao.GoodItenDao;
import com.cxs.elasticsearch.entity.GoodItem;
import com.cxs.service.GoodService;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/*
* @Author:cxs
* @Motto:放下杂念,只为迎接明天更好的自己
* */
@Service
public class GoodServiceImpl implements GoodService {
@Autowired
private GoodItenDao goodItenDao;
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Override
public Boolean save(GoodItem goodItem) {
GoodItem save = goodItenDao.save(goodItem);
System.out.println(save);
return save != null;
}
@Override
public List<GoodItem> findAll() {
Iterable<GoodItem> all = goodItenDao.findAll();
List<GoodItem> list = new ArrayList<>();
all.forEach(a -> {
list.add(a);
});
return list;
}
@Override
public List<GoodItem> search(GoodItem goodItem) {
Iterable<GoodItem> search = goodItenDao.search(QueryBuilders.termsQuery("url", goodItem.getUrl()));
List<GoodItem> list = new ArrayList<>();
search.forEach(a -> {
list.add(a);
});
return list;
}
@Override
public List<GoodItem> query(GoodItem goodItem) {
List<GoodItem> list = new ArrayList<>();
GoodItem item = elasticsearchRestTemplate.get("", GoodItem.class);
return list;
}
}
7.6 测试
java
// ... 测试省略
7.7 复杂查询
ElasticsearchRepository
中许多方法已经被弃用,实现复杂操作就需要自己实现,注入
ElasticsearchRestTemplate
完成复杂操作,注意,原先的ElasticsearchTemplate已经弃用
7.7.1 使用ElasticsearchRestTemplate完成Match查询
java
// 匹配url is https://item.jd.com/100016034400.html
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("url", "https://item.jd.com/100016034400.html");
NativeSearchQuery query = new NativeSearchQuery(matchQueryBuilder);
SearchHits<GoodItem> search = elasticsearchRestTemplate.search(query, GoodItem.class);
List<SearchHit<GoodItem>> hits = search.getSearchHits();
List<GoodItem> list = new ArrayList<>();
// 总数
long totalHits = search.getTotalHits();
for (SearchHit<GoodItem> hit : hits) {
GoodItem content = hit.getContent();
list.add(content);
}
System.out.println(list);
7.7.2 使用ElasticsearchRestTemplate完成Bool查询
java
@Test
void contextLoads1() {
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("url", "https://item.jd.com/100016034400.html");
TermQueryBuilder termQuery = QueryBuilders.termQuery("url", "https://item.jd.com/100016034400.html");
boolQueryBuilder.should(matchQuery).should(termQuery);
NativeSearchQuery query = new NativeSearchQuery(boolQueryBuilder);
SearchHits<GoodItem> search = elasticsearchRestTemplate.search(query, GoodItem.class);
System.out.println(search);
List<SearchHit<GoodItem>> hits = search.getSearchHits();
List<GoodItem> list = new ArrayList<>();
long totalHits = search.getTotalHits();
for (SearchHit<GoodItem> hit : hits) {
GoodItem content = hit.getContent();
list.add(content);
}
System.out.println(list);
}
7.7.3 Es批量操作,存在即修改,不存在即插入
java
@Test
void contextLoads1() {
BulkRequest bulkRequest = new BulkRequest();
for (FinancialDoc document : documents) {
document.setProdId(getProductId(document));
document.setRefreshTime(LocalDateTime.now());
IndexRequest indexRequest = new IndexRequest(getIndex())
.id(document.getProdId())
.source(JSON.toJSONString(document), XContentType.JSON);
UpdateRequest request = new UpdateRequest(getIndex(), document.getProdId())
.doc(JSON.toJSONString(document), XContentType.JSON).upsert(indexRequest);
bulkRequest.add(request);
}
BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
return bulkResponse.status().equals(RestStatus.OK);
}
7.7.4 Es全量查询文档,总数大于10000条
使用scroll,每次1000条查询,可自行调整
java
public List<StockDoc> selectAllList() throws IOException {
SearchRequest request = new SearchRequest();
request.indices(getIndex());
List<StockDoc> result = new ArrayList<>();
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.size(1000).query(QueryBuilders.matchAllQuery());
Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1L));
request.scroll(scroll)
.source(builder);
SearchResponse searchResponse = client.search(request, RequestOptions.DEFAULT);
String scrollId = searchResponse.getScrollId();
SearchHit[] searchHits = searchResponse.getHits().getHits();
while (searchHits != null && searchHits.length > 0) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
result.add(JSON.parseObject(hit.getSourceAsString(), StockDoc.class));
}
SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
scrollRequest.scroll(scroll);
searchResponse = client.scroll(scrollRequest, RequestOptions.DEFAULT);
searchHits = searchResponse.getHits().getHits();
}
ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
clearScrollRequest.addScrollId(scrollId);
client.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
return result;
}
7.7.5 复杂搜索-权重,优先级排序
java
private void globalSearchConditionHandler(SearchDTO dto, SearchSourceBuilder builder) {
// 封装分页数据
builder.from((dto.getPageNum() - 1) * dto.getPageSize());
builder.size(dto.getPageSize());
Script script =
new Script("if (doc['code.keyword'].value == '" + dto.getKeyword() + "') {return doc['code.keyword'].value.length();} else {if(doc['codeType.keyword'].value == '4353' || doc['codeType.keyword'].value == '4609') {return 1;} else {return 0;}}");
Script codeTypeScript =
new Script("return Integer.parseInt(doc['codeType.keyword'].value)");
SortBuilder sortBuilder = SortBuilders.scriptSort(script, ScriptSortBuilder.ScriptSortType.NUMBER).order(SortOrder.DESC);
SortBuilder codeTypeScriptBuild = SortBuilders.scriptSort(codeTypeScript, ScriptSortBuilder.ScriptSortType.NUMBER).order(SortOrder.ASC);
SortBuilder scoreBuild = SortBuilders.fieldSort("_score").order(SortOrder.DESC);
SortBuilder codeBuild = SortBuilders.fieldSort("code.keyword").order(SortOrder.ASC);
builder.sort(scoreBuild);
builder.sort(sortBuilder);
builder.sort(codeTypeScriptBuild);
builder.sort(codeBuild);
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
if (StringUtils.hasLength(dto.getKeyword())) {
BoolQueryBuilder query = QueryBuilders.boolQuery();
query.should(QueryBuilders.wildcardQuery("code.keyword", swapLeftKey(dto.getKeyword())).boost(100.0f))
.should(QueryBuilders.wildcardQuery("code.keyword", swapKey(dto.getKeyword())).boost(90.0f))
.should(QueryBuilders.wildcardQuery("codeName.keyword", swapLeftKey(dto.getKeyword())).boost(10.0f))
.should(QueryBuilders.wildcardQuery("codeName.keyword", swapKey(dto.getKeyword())).boost(9.5f))
.should(QueryBuilders.wildcardQuery("pinYin.keyword", swapLeftKey(dto.getKeyword())).boost(9.0f))
.should(QueryBuilders.wildcardQuery("pinYin.keyword", swapLeftKey(dto.getKeyword().toUpperCase(Locale.ROOT))).boost(9.0f))
.should(QueryBuilders.wildcardQuery("pinYin.keyword", swapLeftKey(dto.getKeyword().toUpperCase(Locale.ROOT))).boost(9.0f))
.should(QueryBuilders.regexpQuery("pinYin.keyword", regexStr(dto.getKeyword().toUpperCase(Locale.ROOT))).boost(8.0f))
.should(QueryBuilders.regexpQuery("pinYin.keyword", regexStr(dto.getKeyword().toLowerCase(Locale.ROOT))).boost(8.0f))
.should(QueryBuilders.wildcardQuery("pinYin.keyword", swapKey(dto.getKeyword())).boost(7.0f))
.should(QueryBuilders.wildcardQuery("pinYin.keyword", swapKey(dto.getKeyword().toUpperCase(Locale.ROOT))).boost(7.0f))
.should(QueryBuilders.wildcardQuery("pinYin.keyword", swapKey(dto.getKeyword().toLowerCase(Locale.ROOT))).boost(7.0f));
boolQuery.must(query);
}
builder.query(boolQuery);
}