赞
踩
对于elasticsearch的搭建,前面写了一篇文章有简单描述如何搭建es,本次主要介绍如何在项目里使用,主要使用ElasticsearchRepository和ElasticsearchRestTemplate操作es。
首先选择合适的项目组件版本,因为es版本和springboot版本有对应,如果不合适会报错。
这里以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>
主要还是第一个dependency配置,就是操作es的。
1.config类,主要用于配置扫描操作es所用的repository
@Configuration
@EnableElasticsearchRepositories(basePackages = "com.es.demo.repository")
public class ElasticsearchConfig {
}
2.对应的实体类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.创建repository包,这里防止所有的es对应的索引操作类。所有类继承org.springframework.data.elasticsearch.repository.ElasticsearchRepository,这里以ProductInfo为例:
@Component
public interface ProductInfoRepository extends ElasticsearchRepository<ProductInfo, Integer> {
}
第一个泛型是操作的索引类,第二个是唯一标识(id)对应的类型,
在这个类里按照一定的规则可以直接写一些方法名,不用写实现,ElasticsearchRepository会自动帮我们实现。具体我没有写,你们可以去网上搜一下怎么写。
4.因为ProductInfoRepository中实现了基本的增删改查,所以在这里可以直接使用
如何使用:
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; }
1.对于一些复杂的查询我是直接使用ElasticsearchRestTemplate这个类来操作的。写了一个公共类,类似mybatisplus的service抽象,具体service接口和实现类可以直接继承。也是看别的开源项目这么写自己总结出来的。
首先是接口层BasicEsService:
public interface BasicEsService<T> { /** * 保存数据 * * @param indexEnum 索引 * @param ts 数据 */ void save(IndexEnum indexEnum, T... ts); /** * 删除数据 * * @param indexEnum 索引 * @param id id */ void delete(IndexEnum indexEnum, String id); /** * 查询单个数据 * * @param indexEnum * @param id * @param clazz * @return */ T getById(IndexEnum indexEnum, String id, T clazz); /** * 查询所有数据,es限制最多返回10000条 * * @param indexEnum * @param clazz * @return */ List<T> getAllData(IndexEnum indexEnum, T clazz); /** * 动态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); /** * 分页查询,默认十条,支持多字段模糊匹配,多字段排序 * * @param pageNo 从0开始 * @param pageSize 每页记录数 * @param keyword 关键词 * @param clazz 转换的对象 * @param order 排序集合 * @param fields 搜索的列 * @return */ Page<T> pageList(Integer pageNo, Integer pageSize, String keyword, T clazz, Map<String, String> order, String... fields);
然后是实现类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()); } }
因为ElasticsearchRestTemplate这个操作需要传入具体的索引名称,所以我创建了一个公共枚举类存放es索引名称
IndexEnum
@Getter
@AllArgsConstructor
public enum IndexEnum {
PRODUCT_INFO("product-info", "_doc");
private String index;
private String type;
}
使用的话就直接继承就行了
接口层
public interface ProductService extends BasicEsService<ProductInfo>{
}
实现层
@Service
@Slf4j
public class ProductServiceImpl extends BasicEsServiceImpl<ProductInfo> implements ProductService {
}
后面如果有公共的方法也可以抽出来放在公共类里。
1.问题1:启动报错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]
这是因为springboot和es版本不对应导致的,我最开始用的是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}
这是因为我索引名称用了大写字母,而es规定索引名称不能使用大写,所以修改配置
@Document(indexName = “productInfo”),替换成了@Document(indexName = “product-info”)
最后在放一个我写的demo地址,有兴趣可以看下
https://gitee.com/wdvc/es-demo.git
唉,就这样吧,如果有问题请指出来我马上修改
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。