SpringBoot 2.5.0 集成 Elasticsearch

Maven 依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

ElasticsearchRepository<T, ID>

Spring 提供的便捷操作 Dao 层接口,通过继承此接口可以实现简单的增删改查操作。

T:与索引结构对应的 Model。

ID:索引里的 ID。

  • Dao 样例
@Repository
public interface GoodsSpuEsDao extends ElasticsearchRepository<GoodsSpuEs, Long> {

    List<GoodsSpuEs> findByTitleAndShopId(String title, Long shopId);

}
  • Model 样例
/** 创建索引使用,默认项目启动时自动创建索引 */
@Document(indexName = "goods_spu", createIndex = false)
/** 设置分片和副本 */
@Setting(shards = 3, replicas = 1)
public class GoodsSpuEs {

    /** 必须有 ID,这里的 ID 是全局唯一的标识,等同于 es 中的 "_id" */
    @Id
    @Field(name = "id", type = FieldType.Long)
    private Long id;

    /** 店铺 ID */
    @Field(name = "shop_id", type = FieldType.Long)
    private Long shopId;

    /** 商品分类 ID */
    @Field(name = "category_id", type = FieldType.Long)
    private Long categoryId;

    /** 商品店铺分类 ID */
    @Field(name = "shop_category_id", type = FieldType.Long)
    private Long shopCategoryId;

    /** 商品名称 */
    @Field(name = "title", type = FieldType.Text, analyzer = "ik_max_word")
    private String title;

    /** 商品的售价;此处价格应填写 SKU 的最低价格 */
    @Field(name = "price", type = FieldType.Double)
    private BigDecimal price;

    @Field(name = "main_image", type = FieldType.Keyword)
    private String mainImage;

    @Field(name = "sales", type = FieldType.Integer)
    private Integer sales;

    @Field(name = "base_activity", type = FieldType.Integer)
    private Integer baseActivity;

    @Field(name = "if_wholesale", type = FieldType.Boolean)
    private Boolean ifWholesale;

    @Field(name = "if_ziti", type = FieldType.Boolean)
    private Boolean ifZiti;

    @Field(name = "if_express", type = FieldType.Boolean)
    private Boolean ifExpress;

    @Field(name = "tag", type = FieldType.Text, analyzer = "ik_max_word")
    private String tag;

    /** 地理位置;格式:纬度,经度 */
    @Field(name = "location")
    @GeoPointField
    private String location;

}

这样一个基本的 Dao 就可以使用了,如下:

也可以以 find 开头的规范自己组合一些查询条件,如下:

还可以使用注解方式字节写一些 DSL,如下:

ElasticsearchRestTemplate

  • 更新
public void updateGoodsSpu(GoodsSpuEs goodsSpuEsIn) throws IllegalAccessException {
    log.info("【更新商品信息到 ES】- 入参:${}$", goodsSpuEsIn);
    // 索引
    IndexCoordinates indexCoordinates = IndexCoordinates.of(goodsSpuEsIn.findIndexName());

    // 更新信息
    Document document = Document.create();
    document.putAll(goodsSpuEsIn.objectToMap());

    // 构建更新对象
    UpdateQuery updateQuery = UpdateQuery.builder(String.valueOf(goodsSpuEsIn.getId()))
      .withDocument(document)
      .build();

    // 增量更新
    restTemplate.update(updateQuery, indexCoordinates);
}
  • 商品名称搜索
public List<GoodsSpuEsOut> queryByNearby(GoodsSpuSearch goodsSpuSearch) {
    log.info("【综合化查询商品列表】- 入参:${}$", goodsSpuSearch);

    // 先构建基础查询条件对象
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

    // 商品名称搜索条件构建
    if (StringUtils.isNotBlank(goodsSpuSearch.getTitle())) {
      boolQueryBuilder.must(QueryBuilders.matchQuery("title", goodsSpuSearch.getTitle()));
    }

    // 排序
    SortBuilder sortBuilder = SortBuilders.fieldSort("sales").order(SortOrder.DESC);

    // 分页条件
    PageRequest pageRequest = PageRequest.of(goodsSpuSearch.getPage(), goodsSpuSearch.getSize());

    // 组装条件
    NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
      .withQuery(boolQueryBuilder)
      .withPageable(pageRequest)
      .withSort(sortBuilder)
      .build();

    SearchHits<GoodsSpuEsOut> searchHits = restTemplate.search(nativeSearchQuery, GoodsSpuEsOut.class);
    log.info("【综合化查询商品列表】- 查询结果:${}$", searchHits);
    // 获取到商品结果集
    List<GoodsSpuEsOut> goodsSpuEsOutList = searchHits.stream().map(SearchHit::getContent).collect(Collectors.toList());
    return goodsSpuEsOutList;
}
  • 地理位置搜索
public List<GoodsSpuEsOut> queryByNearby(GoodsSpuSearch goodsSpuSearch) {
    log.info("【综合化查询商品列表】- 入参:${}$", goodsSpuSearch);

    // 先构建基础查询条件对象
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

    // 商品名称搜索条件构建
    if (StringUtils.isNotBlank(goodsSpuSearch.getTitle())) {
      boolQueryBuilder.must(QueryBuilders.matchQuery("title", goodsSpuSearch.getTitle()));
    }

    // 2. 地理位置
    if (StringUtils.isNotBlank(goodsSpuSearch.getLocation())) {
      // 处理经纬度
      String[] split = goodsSpuSearch.getLocation().split(",");
      String lot = Double.valueOf(split[0]);
      String lat = Double.valueOf(split[1]);

      boolQueryBuilder.filter(
        QueryBuilders.geoDistanceQuery("location")
        // 3. 商品范围
        .distance(goodsSpuSearch.getDistance().doubleValue(), DistanceUnit.KILOMETERS)
        .point(lat, lot)
      );
    }

    // 排序
    // 距离排
    SortBuilder sortBuilder = SortBuilders.fieldSort("price").order(SortOrder.ASC);

    // 分页条件
    PageRequest pageRequest = PageRequest.of(goodsSpuSearch.getPage(), goodsSpuSearch.getSize());

    // 组装条件
    NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
      .withQuery(boolQueryBuilder)
      .withPageable(pageRequest)
      .withSort(sortBuilder)
      .build();

    SearchHits<GoodsSpuEsOut> searchHits = restTemplate.search(nativeSearchQuery, GoodsSpuEsOut.class);
    log.info("【综合化查询商品列表】- 查询结果:${}$", searchHits);
    // 获取到商品结果集
    List<GoodsSpuEsOut> goodsSpuEsOutList = searchHits.stream().map(SearchHit::getContent).collect(Collectors.toList());
    return goodsSpuEsOutList;
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注