Elasticsearch7学习笔记(实战)


在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();
    }
}

特别提醒:扫码关注微信订阅号'起岸星辰',实时掌握IT业界技术资讯! 转载请保留原文中的链接!
 上一篇
下一篇 
设计模式之状态模式 设计模式之状态模式
状态模式(State Pattern):类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
2021-01-06
  目录