在SpringBoot中集成Elasticsearch,需要添加如下maven依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
注意在这里引入es的jar包版本要比elasticsearch版本相等或大于(最好相等),否则可能会出现一些奇奇怪怪的问题。
在application.yml配置文件中添加elasticsearch的配置(可以配置多个)
spring:
elasticsearch:
rest:
uris: http://192.168.111.55:9200
如果没有什么特殊的配置的话,直接使用springboot自动配置的即可;自定义配置时,可以参考springboot的中配置;
spring文档:https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html
示例:查询以某个字符开头的数据
这里我们实现类似12306查询站点时的智能提示功能,即输入北京会智能显示北京开头的站点。下面的示例使用的是webflux
12306火车站点信息数据拉取
站点数据:https://www.12306.cn/index/script/core/common/station_name_v10095.js
数据解析程序如下:
private static final String STATION_NAME_URL = "https://www.12306.cn/index/script/core/common/station_name_v10095.js";
/**
* 导入站点数据
*/
public Mono<String> importTrainStationName() {
return WebClient.create(STATION_NAME_URL).get().retrieve()
.onStatus(HttpStatus::is4xxClientError, clientResponse-> Mono.error(new BizException("数据拉取失败")))
.bodyToMono(String.class)
.flatMap(body->{
List<TrainStationName> list = extractStationName(body);
return Mono.just(batchInsert(list));
});
}
/**
* 提取数据
*/
private List<TrainStationName> extractStationName(String str){
String[] arr = str.split("\\|");
List<TrainStationName> list = new ArrayList<>();
for(int i=1; i<arr.length; i++){
TrainStationName stationName = new TrainStationName();
stationName.setCnName(arr[i++]);
stationName.setCode(arr[i++]);
stationName.setPinyin(arr[i++]);
stationName.setPinyinShort(arr[i++]);
list.add(stationName);
}
return list;
}
/**
* 导入数据到库中
*/
private String batchInsert(List<TrainStationName> list){
int add = 0;
int totalPull = list.size();
for(TrainStationName stationName:list){
LambdaQueryWrapper<TrainStationName> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(TrainStationName::getCnName, stationName.getCnName());
TrainStationName stationNameDb = trainStationNameMapper.selectOne(queryWrapper);
if(stationNameDb==null){
add++;
trainStationNameMapper.insert(stationName);
stationNameDb = stationName;
}
if(stationNameDb.getId()!=null){
boolean exists = trainStationNameRepository.existsById(stationNameDb.getId());
if(!exists){
trainStationNameRepository.save(TrainStationNameDTO.cloneTrainStationName(stationNameDb));
}
}
}
return String.format("共计拉取:%d条数据,新增:%d条数据", totalPull, add);
}
站点数据pojo
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
@Data
@TableName("train_station_name")
public class TrainStationName implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 三字码
*/
@TableField("code")
private String code;
/**
* 拼音
*/
@TableField("pinyin")
private String pinyin;
/**
* 拼音简写
*/
@TableField("pinyin_short")
private String pinyinShort;
/**
* 中文名称
*/
@TableField("cn_name")
private String cnName;
}
es中的实体类映射
import lombok.Data;
import org.springframework.beans.BeanUtils;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import top.vchar.train.entity.TrainStationName;
import java.io.Serializable;
@Data
@Document(indexName = "train_station_name")
public class TrainStationNameDTO implements Serializable {
@Id
private Long id;
/**
* 三字码
*/
@Field(type = FieldType.Keyword)
private String code;
/**
* 拼音
*/
@Field(type = FieldType.Keyword)
private String pinyin;
/**
* 拼音简写
*/
@Field(type = FieldType.Keyword)
private String pinyinShort;
/**
* 中文名称
*/
@Field(type = FieldType.Keyword)
private String cnName;
public static TrainStationNameDTO cloneTrainStationName(TrainStationName stationName){
TrainStationNameDTO dto = new TrainStationNameDTO();
BeanUtils.copyProperties(stationName, dto);
return dto;
}
}
es的查询语句
在不知道如何在代码中拼写语句时,可以先使用es的查询语句试哈。之后再敲代码会清晰许多。
GET /train_station_name/_search
{
"query": {
"bool": {
"should": [
{
"prefix": {
"cnName.keyword": {
"value": "beib"
}
}
},
{
"match_phrase_prefix": {
"pinyin": "beib"
}
}
]
}
}
}
Java代码实现
spring封装的ElasticsearchRestTemplate实现,没有特殊查询时建议直接用spring封装的工具类
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
public Flux<TrainStationNameDTO> findTrainStationName(String keywords) {
BoolQueryBuilder boolQuery = new BoolQueryBuilder();
QueryBuilder cnNameQuery = new PrefixQueryBuilder("cnName.keyword", keywords);
boolQuery.should(cnNameQuery);
QueryBuilder pinyinQuery = new MatchPhrasePrefixQueryBuilder("pinyin", keywords);
boolQuery.should(pinyinQuery);
Query query = new NativeSearchQuery(boolQuery);
return Flux.fromIterable(elasticsearchRestTemplate.search(query, TrainStationNameDTO.class)).map(SearchHit::getContent);
}
spring封装的orm框架,做简单的crud操作时强烈建议就用这种方式。类似mybatis
@Component
@Document(indexName = "train_station_name")
public interface TrainStationNameRepository extends ElasticsearchRepository<TrainStationNameDTO, Long> {
/**
* 查询以keywords开头的站点信息
* @param cnName 中文
* @param pinyin 拼音
* @return 返回结果
*/
List<TrainStationNameDTO> findByCnNameStartingWithOrPinyinStartingWith(String cnName, String pinyin);
}
最开始的RestHighLevelClient实现
@Autowired
private RestHighLevelClient client;
private void useClient(String keywords) {
SearchRequest request = new SearchRequest("train_station_name");
request.source(SearchSourceBuilder.searchSource()
.query(QueryBuilders.boolQuery()
.should(QueryBuilders.prefixQuery("cnName.keyword", keywords))
.should(QueryBuilders.matchPhrasePrefixQuery("pinyin", keywords))
)
);
try {
SearchResponse search = client.search(request, RequestOptions.DEFAULT);
log.info(JSONObject.toJSONString(search.getHits()));
} catch (IOException e) {
e.printStackTrace();
}
}