寫在前面
對于elasticsearch的搭建,前面寫了一篇文章有簡單描述如何搭建es,本次主要介紹如何在項(xiàng)目里使用,主要使用ElasticsearchRepository和ElasticsearchRestTemplate操作es。
搭建項(xiàng)目環(huán)境和選擇合適版本
首先選擇合適的項(xiàng)目組件版本,因?yàn)閑s版本和springboot版本有對應(yīng),如果不合適會報(bào)錯(cuò)。
這里以es7.6.x版本、springboot2.7.12,java8、maven3.8寫出來的demo。
首先引入maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.7.12</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
主要還是第一個(gè)dependency配置,就是操作es的。
具體的代碼實(shí)現(xiàn)(1)繼承ProductInfoRepository
1.config類,主要用于配置掃描操作es所用的repository
@Configuration
@EnableElasticsearchRepositories(basePackages = "com.es.demo.repository")
public class ElasticsearchConfig {
}
2.對應(yīng)的實(shí)體類ProductInfo
@Data
@Document(indexName = "product-info")
public class ProductInfo implements Serializable {
@Id
private Integer id;
@Field(type = FieldType.Text, searchAnalyzer = "ik_max_word", analyzer = "ik_smart")
private String productName;
@Field(type = FieldType.Text, searchAnalyzer = "ik_max_word", analyzer = "ik_smart")
private String description;
@Field(type = FieldType.Keyword)
private Date createTime;
@Field(type = FieldType.Keyword)
private BigDecimal price;
@Field(type = FieldType.Integer)
private Integer num;
}
3.創(chuàng)建repository包,這里防止所有的es對應(yīng)的索引操作類。所有類繼承org.springframework.data.elasticsearch.repository.ElasticsearchRepository,這里以ProductInfo為例:
@Component
public interface ProductInfoRepository extends ElasticsearchRepository<ProductInfo, Integer> {
}
第一個(gè)泛型是操作的索引類,第二個(gè)是唯一標(biāo)識(id)對應(yīng)的類型,
在這個(gè)類里按照一定的規(guī)則可以直接寫一些方法名,不用寫實(shí)現(xiàn),ElasticsearchRepository會自動幫我們實(shí)現(xiàn)。具體我沒有寫,你們可以去網(wǎng)上搜一下怎么寫。
4.因?yàn)镻roductInfoRepository中實(shí)現(xiàn)了基本的增刪改查,所以在這里可以直接使用
如何使用:
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductInfoRepository productInfoRepository;
@Override
public Boolean save(ProductInfo... productInfo) {
productInfoRepository.saveAll(Arrays.asList(productInfo));
return true;
}
@Override
public Boolean delete(Integer id) {
productInfoRepository.deleteById(id);
return null;
}
@Override
public ProductInfo getById(Integer id) {
Optional<ProductInfo> byId = productInfoRepository.findById(id);
return byId.orElse(null);
}
@Override
public List<ProductInfo> getAll() {
List<ProductInfo> list = new ArrayList<>();
productInfoRepository.findAll().forEach(list::add);
return list;
}
具體的代碼實(shí)現(xiàn)(2)使用ElasticsearchRestTemplate操作
1.對于一些復(fù)雜的查詢我是直接使用ElasticsearchRestTemplate這個(gè)類來操作的。寫了一個(gè)公共類,類似mybatisplus的service抽象,具體service接口和實(shí)現(xiàn)類可以直接繼承。也是看別的開源項(xiàng)目這么寫自己總結(jié)出來的。
首先是接口層BasicEsService:
public interface BasicEsService<T> {
/**
* 保存數(shù)據(jù)
*
* @param indexEnum 索引
* @param ts 數(shù)據(jù)
*/
void save(IndexEnum indexEnum, T... ts);
/**
* 刪除數(shù)據(jù)
*
* @param indexEnum 索引
* @param id id
*/
void delete(IndexEnum indexEnum, String id);
/**
* 查詢單個(gè)數(shù)據(jù)
*
* @param indexEnum
* @param id
* @param clazz
* @return
*/
T getById(IndexEnum indexEnum, String id, T clazz);
/**
* 查詢所有數(shù)據(jù),es限制最多返回10000條
*
* @param indexEnum
* @param clazz
* @return
*/
List<T> getAllData(IndexEnum indexEnum, T clazz);
/**
* 動態(tài)sql查詢
*
* @param sql
* @param clazz
* @return
*/
List<Map<String, Object>> query(String sql, T clazz);
/**
* 范圍查詢
*
* @param pageNo
* @param pageSize
* @param indexEnum
* @param order
* @param rangeParam
* @return
*/
Page<T> rangeQuery(Integer pageNo, Integer pageSize, IndexEnum indexEnum, Map<String, String> order, Map<String, Object[]> rangeParam, T clazz);
/**
* 分頁查詢,默認(rèn)十條,支持多字段模糊匹配,多字段排序
*
* @param pageNo 從0開始
* @param pageSize 每頁記錄數(shù)
* @param keyword 關(guān)鍵詞
* @param clazz 轉(zhuǎn)換的對象
* @param order 排序集合
* @param fields 搜索的列
* @return
*/
Page<T> pageList(Integer pageNo, Integer pageSize, String keyword, T clazz, Map<String, String> order, String... fields);
然后是實(shí)現(xiàn)類BasicEsServiceImpl
@Service
public class BasicEsServiceImpl<T> implements BasicEsService<T> {
@Autowired
protected ElasticsearchRestTemplate elasticsearchRestTemplate;
@Override
public void save(IndexEnum indexEnum, T... ts) {
T[] save = elasticsearchRestTemplate.save(ts, IndexCoordinates.of(indexEnum.getIndex()));
return;
}
@Override
public void delete(IndexEnum indexEnum, String id) {
String delete = elasticsearchRestTemplate.delete(id, IndexCoordinates.of(indexEnum.getIndex()));
}
@Override
public T getById(IndexEnum indexEnum, String id, T clazz) {
elasticsearchRestTemplate.get(id, clazz.getClass(), IndexCoordinates.of(indexEnum.getIndex()));
return null;
}
@Override
public List<T> getAllData(IndexEnum indexEnum, T clazz) {
Query query = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchAllQuery()).build();
SearchHits<T> searchHits = (SearchHits<T>) elasticsearchRestTemplate.search(query, clazz.getClass(), IndexCoordinates.of(indexEnum.getIndex()));
return searchHits.get().map(SearchHit::getContent).collect(Collectors.toList());
}
@Override
public List<Map<String, Object>> query(String sql, T clazz) {
throw new AbstractMethodError();
}
@Override
public Page<T> rangeQuery(Integer pageNo, Integer pageSize, IndexEnum indexEnum, Map<String, String> order, Map<String, Object[]> rangeParam, T clazz) {
//范圍查詢
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
rangeParam.keySet().forEach(field -> {
boolQueryBuilder.must(new RangeQueryBuilder(field).gt(rangeParam.get(field)[0]).lt(rangeParam.get(field)[1]));
});
PageRequest of = PageRequest.of(pageNo, pageSize);
List<FieldSortBuilder> sortBuilderList = order.keySet().stream()
.map(field -> SortBuilders.fieldSort(field).order(SortOrder.valueOf(order.get(field))))
.collect(Collectors.toList());
Query searchQuery = new NativeSearchQueryBuilder()
.withQuery(boolQueryBuilder)
.withPageable(of)
.withSorts((SortBuilder<?>) sortBuilderList)
.build();
SearchHits<T> searchHits = (SearchHits<T>) elasticsearchRestTemplate.search(searchQuery, clazz.getClass());
SearchPage<T> searchHits1 = SearchHitSupport.searchPageFor(searchHits, searchQuery.getPageable());
return new PageImpl<>(searchHits.get().map(SearchHit::getContent).collect(Collectors.toList()), searchHits1.getPageable(), searchHits1.getTotalElements());
}
@Override
public Page<T> pageList(Integer pageNo, Integer pageSize, String keyword, T clazz, Map<String, String> order, String... fields) {
//分頁,頁碼從0開始
PageRequest of = PageRequest.of(pageNo, pageSize);
List<FieldSortBuilder> collect = order.keySet().stream()
.map(field -> SortBuilders.fieldSort(field).order(SortOrder.valueOf(order.get(field))))
.collect(Collectors.toList());
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
Arrays.asList(fields).forEach(e -> boolQueryBuilder.should(QueryBuilders.fuzzyQuery(e, keyword)));
Query searchQuery = new NativeSearchQueryBuilder()
//條件
.withQuery(boolQueryBuilder)
//分頁
.withPageable(of)
//排序
.withSorts((SortBuilder<?>) collect)
.build();
SearchHits<T> searchHits = (SearchHits<T>) elasticsearchRestTemplate.search(searchQuery, clazz.getClass());
SearchPage<T> searchHits1 = SearchHitSupport.searchPageFor(searchHits, searchQuery.getPageable());
return new PageImpl<>(searchHits.get().map(SearchHit::getContent).collect(Collectors.toList()), searchHits1.getPageable(), searchHits1.getTotalElements());
}
}
因?yàn)镋lasticsearchRestTemplate這個(gè)操作需要傳入具體的索引名稱,所以我創(chuàng)建了一個(gè)公共枚舉類存放es索引名稱
IndexEnum
@Getter
@AllArgsConstructor
public enum IndexEnum {
PRODUCT_INFO("product-info", "_doc");
private String index;
private String type;
}
使用的話就直接繼承就行了
接口層
public interface ProductService extends BasicEsService<ProductInfo>{
}
實(shí)現(xiàn)層
@Service
@Slf4j
public class ProductServiceImpl extends BasicEsServiceImpl<ProductInfo> implements ProductService {
}
后面如果有公共的方法也可以抽出來放在公共類里。
問題總結(jié)
1.問題1:啟動報(bào)錯(cuò)org.elasticsearch.client.ResponseException: method [PUT], host [http://10.0.180.100:9200], URI [/productInfo], status line [HTTP/1.1 406 Not Acceptable]
org.elasticsearch.client.ResponseException: method [PUT], host [http://10.0.180.100:9200], URI [/productInfo], status line [HTTP/1.1 406 Not Acceptable]
這是因?yàn)閟pringboot和es版本不對應(yīng)導(dǎo)致的,我最開始用的是springboot3和es7.6,后來把springboot版本降到了2.7.x。
問題2:“type”:“invalid_index_name_exception”
org.elasticsearch.client.ResponseException: method [PUT], host [http://10.0.180.100:9200], URI [/productInfo?master_timeout=30s&timeout=30s], status line [HTTP/1.1 400 Bad Request]
{"error":{"root_cause":[{"type":"invalid_index_name_exception","reason":"Invalid index name [productInfo], must be lowercase","index_uuid":"_na_","index":"productInfo"}],"type":"invalid_index_name_exception","reason":"Invalid index name [productInfo], must be lowercase","index_uuid":"_na_","index":"productInfo"},"status":400}
這是因?yàn)槲宜饕Q用了大寫字母,而es規(guī)定索引名稱不能使用大寫,所以修改配置
@Document(indexName = “productInfo”),替換成了@Document(indexName = “product-info”)文章來源:http://www.zghlxwxcb.cn/news/detail-478488.html
最后放個(gè)demo
最后在放一個(gè)我寫的demo地址,有興趣可以看下
https://gitee.com/wdvc/es-demo.git
唉,就這樣吧,如果有問題請指出來我馬上修改文章來源地址http://www.zghlxwxcb.cn/news/detail-478488.html
到了這里,關(guān)于項(xiàng)目中使用es(一):使用springboot操作elasticsearch的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!