栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 前沿技术 > 大数据 > 大数据系统

从零开发短视频电商 电商商品全文搜索之Elasticsearch与SpringBoot集成实战

从零开发短视频电商 电商商品全文搜索之Elasticsearch与SpringBoot集成实战

文章目录
      • SpringBoot集成
        • 添加POM依赖
        • 配置文件
        • 创建映射对象
        • 创建Repository接口
      • 常见操作
        • 索引操作
        • 新增数据
        • 删除数据
        • 更新数据
        • 更新某几个字段
      • 查询
        • 普通查询
        • 分页查询
        • 高亮查询
        • 权重查询
        • 聚合查询
      • 监控管理
      • 问题总结
        • 索引映射创建
        • 排序查询报错

Elasticsearch部署请参考:【 https://laker.blog.csdn.net/article/details/120977484】

Elasticsearch官网文档:https://www.elastic.co/guide/en/elasticsearch/client/index.html

SpringBoot集成

我使用的版本为SpringBoot2.3.7RELEASE,对应的ES版本为7.6.2.

添加POM依赖

    org.springframework.boot
    spring-boot-starter-data-elasticsearch

SpringBoot版本以及对应Elasticsearch版本如下图:

以SpringBoot2.3.7RELEASE版本为例子。

Spring Data Elasticsearch从 4.0 版本起不推荐使用 ElasticsearchTemplate,请改用 ElasticsearchRestTemplate。

下表显示了Spring Data发布系列所使用的Elasticsearch版本和其中包含的Spring Data Elasticsearch版本,以及针对特定Spring Data发布系列的Spring Boot版本:

Spring Data Release TrainSpring Data ElasticsearchElasticsearchSpring frameworkSpring Boot
2021.1 (Q)4.3.x7.15.25.3.x2.5 .x
2021.0 (Pascal)4.2.x7.12.05.3.x2.5.x
2020.0 (Ockham)4.1.x7.9.35.3.22.4.x
Neumann4.0.x7.6.25.2.122.3.x
Moore3.2.x6.8.125.2.122.2.x
Lovelace3.1.x6.2.25.1.192.1.x
Kay3.0.x5.5.05.0.132.0.x
Ingalls2.1.x2.4.04.3.251.5.x
  • https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#elasticsearch.clients.rest

  • https://docs.spring.io/spring-data/elasticsearch/docs/4.0.6.RELEASE/reference/html/#new-features

  • https://github.com/spring-projects/spring-data-elasticsearch

配置文件
spring:
  elasticsearch:
    rest:
      # ES的连接地址,多个地址用逗号分隔
      uris: http://127.0.0.1:9200
      # 读取超时时间
      read-timeout: 30s
      # 连接超时时间
      connection-timeout: 1s
      # 用户名
      username:
      # 密码
      password:

注: 9300 是 Java 客户端的端口。9200 是支持 Restful HTTP 的接口。

创建映射对象
@Data
@document(indexName = "goods_index", shards = 1, replicas = 0, createIndex = true)
public class Goods {
    
    @Id
    private Long id;
    
    @Field(type = FieldType.Keyword)
    private String goodsCode;
    
    @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
    private String goodsName;
    
    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    @Field(type = FieldType.Date, format = DateFormat.basic_date_time)
    private Date createDate;
    
    @Field(type = FieldType.Integer)
    private Integer hotScore = 100;
    
    @Field(type = FieldType.Nested)
    private List attrs;
}

映射详情 :https://docs.spring.io/spring-data/elasticsearch/docs/4.0.6.RELEASE/reference/html/#elasticsearch.mapping.meta-model

  • @Id 作用在成员变量,标记一个字段作为id主键
  • @Field 作用在成员变量,标记为文档的字段,并指定字段映射属性:
    • type:字段类型,是枚举:FieldType,可以是text、long、short、date、integer、object等
      • text:存储数据时候,会自动分词,并生成索引
      • keyword:存储数据时候,不会分词建立索引
      • Numerical:数值类型,分两类
        • 基本数据类型:long、interger、short、byte、double、float、half_float
        • 浮点数的高精度类型:scaled_float
          • 需要指定一个精度因子,比如10或100。elasticsearch会把真实值乘以这个因子后存储,取出时再还原
      • Date:日期类型
        • elasticsearch可以对日期格式化为字符串存储,但是建议我们存储为毫秒值,存储为long,节省空间。
    • index:是否索引,是否设置分词,布尔类型,默认是true
    • store:是否存储,布尔类型,默认是false
    • analyzer:分词器名称,存储时使用的分词器,这里的ik_max_word即使用ik分词器
    • searchAnalyze:搜索时使用的分词器
创建Repository接口
@Repository
public interface GoodsRepository extends ElasticsearchRepository {
}
常见操作

我们在操作索引和数据时,需要引用下面的类

@Autowired 用于高级查询
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Autowired 用于基础的CRUD
private GoodsRepository goodsRepository;
@Autowired 
private RestHighLevelClient restHighLevelClient;
@Bean
RestHighLevelClient client() {
    ClientConfiguration clientConfiguration = ClientConfiguration.builder()
            .connectedTo("localhost:9200")
            .build();

    return RestClients.create(clientConfiguration)
            .rest();
}
索引操作
# 判断索引是否存在
elasticsearchRestTemplate.indexOps(Goods.class).exists();
elasticsearchRestTemplate.indexOps(IndexCoordinates.of(indexNames)).exists();
# 创建索引
elasticsearchRestTemplate.indexOps(Goods.class).create();
# 删除索引
elasticsearchRestTemplate.indexOps(IndexCoordinates.of(indexName)).delete();
if (!esService.indexExists()) {
    esService.indexCreate();
}
新增数据
Goods goods = new Goods();
goods.setId((long) i);
goods.setGoodsCode("中国人" + i);
goods.setCreateDate(new Date());
goods.setHotScore(10);
goods.setGoodsName("我是中国人" + i);
# 新增
goodsRepository.save(goods);
# 批量新增
goodsRepository.saveAll(goodsList);

删除数据
# 根据ID,删除数据
goodsRepository.deleteById(id);
# 根据对象删除数据,主键ID不能为空
goodsRepository.delete(bean);
# 根据对象集合,批量删除
goodsRepository.deleteAll(beanList);
# 删除所有
goodsRepository.deleteAll();
更新数据
Goods goods = new Goods();
goods.setId(id); // ID不能为空
goods.setGoodsCode("中国人");
goodsRepository.save(goods);// 根据主键更新 ID不能为空
更新某几个字段 查询

ElasticsearchRestTemplate的基本api

  • SearchQuery 总的查询
  • BoolQueryBuilder bool查询,可在后面加上must,mustNot,should等等
  • MatchQueryBuilder 匹配查询
  • TermQueryBuilder 倒排索引查询
  • HighlightBuilder 高亮查询,用于设置要高亮的field

查询结果

SearchHit

包含以下信息:

  • Id

  • Score

  • Sort Values

  • Highlight fields

  • The retrieved entity of type

SearchHits

包含以下信息:

  • Number of total hits
  • Total hits relation
  • Maximum score
  • A list of SearchHit objects
  • Returned aggregations

SearchPage

定义一个Spring Data Page 包含一个 SearchHits 元素,可以使用存储库方法进行分页访问

SearchScrollHits

由ElasticsearchRestTemplate中的底层滚动API函数返回,它用Elasticsearch滚动id充实了SearchHits。

SearchHitsIterator

由SearchOperations接口的流函数返回的迭代器。

QueryBuilders提供了大量的静态方法,用于生成各种不同类型的查询:

  • matchQuery:词条匹配,先分词然后在调用termQuery进行匹配
  • TermQuery:词条匹配,不分词
  • wildcardQuery:通配符匹配
  • fuzzyQuery:模糊匹配
  • rangeQuery:范围匹配
  • booleanQuery:布尔查询,组合查询

// 构建查询条件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 添加基本分词查询
queryBuilder.withQuery(QueryBuilders.matchQuery(“title”, “小米手机”));

BooleanQuery(组合查询)

注意点:

BooleanClause用于表示布尔查询子句关系的类,包括:BooleanClause.Occur.MUST,BooleanClause.Occur.MUST_NOT,BooleanClause.Occur.SHOULD。必须包含,不能包含,可以包含三种.有以下6种组合:

1.MUST和MUST:交集。

2.MUST和MUST_NOT:表示查询结果中不能包含MUST_NOT所对应得查询子句的检索结果。

3.SHOULD与MUST_NOT:连用时,功能同MUST和MUST_NOT。

4.SHOULD与MUST连用时,结果为MUST子句的检索结果,但是SHOULD可影响排序。

5.SHOULD与SHOULD:并集。

6.MUST_NOT和MUST_NOT:无意义,检索无结果。

public void testBooleanQuery(){
    NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
    builder.withQuery(QueryBuilders.boolQuery().must(QueryBuilders.termQuery("title","手机"))
                                               .must(QueryBuilders.termQuery("brand","小米"))
                     );
    Page list = this.itemRepository.search(builder.build());

排序

   NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
    // 添加基本分词查询
    queryBuilder.withQuery...
    // 排序
    queryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.ASC));
普通查询

可以使用goodsRepository操作

@Repository
public interface GoodsRepository extends ElasticsearchRepository {

    long countByGoodsCode(String goodsCode);

    long deleteByGoodsCode(String goodsCode);

    List removeByGoodsCode(String goodsCode);

    List findByGoodsCode(String goodsCode);

    Page findByGoodsCode(String goodsCode, Pageable pageable);

    Slice findByGoodsCode(String goodsCode, Pageable pageable);

    List findByGoodsCode(String goodsCode, Sort sort);

    List findFirst10ByGoodsCode(String goodsCode, Pageable pageable);
    
    @Query("{"bool": {"must": [{"match": {"tags": "?0"}}]}}")
    Page findFirst10ByGoodsCode(String tag, Pageable pageable);
    
    Page
findByAuthorsName(String name, Pageable pageable); @Query("{"bool": {"must": [{"match": {"authors.name": "?0"}}]}}") Page
findByAuthorsNameUsingCustomQuery(String name, Pageable pageable); @Query("{"bool": {"must": {"match_all": {}}, "filter": {"term": {"tags": "?0" }}}}") Page
findByFilteredTagQuery(String tag, Pageable pageable); @Query("{"bool": {"must": {"match": {"authors.name": "?0"}}, "filter": {"term": {"tags": "?1" }}}}") Page
findByAuthorsNameAndFilteredTagQuery(String name, String tag, Pageable pageable); }

API 接受Sort和Pageable。如果您不想应用任何排序或分页,请使用Sort.unsorted()和Pageable.unpaged(),不要传null。

Sort sort = Sort.by(“firstname”).ascending()

​ .and(Sort.by(“lastname”).descending());

与Elasticsearch 查询字符串映射如下:https://docs.spring.io/spring-data/elasticsearch/docs/4.0.6.RELEASE/reference/html/#elasticsearch.query-methods.criterions

分页查询
Page users = repository.findAll(PageRequest.of(1, 20));
MatchQueryBuilder builder = QueryBuilders.matchQuery(field, value);
SearchQuery searchQuery = new NativeSearchQuery(builder).setPageable(PageRequest.of(0, 100));
AggregatedPage page = restTemplate.queryForPage(searchQuery, EmployeeBean.class);
long totalElements = page.getTotalElements(); // 总记录数
int totalPages = page.getTotalPages();  // 总页数
int pageNumber = page.getPageable().getPageNumber(); // 当前页号
List beanList = page.toList();  // 当前页数据集
Set beanSet = page.toSet();  // 当前页数据集
高亮查询 权重查询 聚合查询 监控管理

ElasticHQ可以作为Docker容器使用。执行以下命令以使用ElasticHQ启动容器:

$ docker run -d --name elastichq -p 5000:5000 elastichq/elasticsearch-hq
问题总结 索引映射创建

方式一 在实体类上加**@document**,而在这个类中有一个createIndex属性,默认为true,意思是在启动应用时es中还没创建该索引,则进行初始化。

下面是默认创建的索引和映射,很明显里面的映射创建是不对。不建议使用这种方式。

es服务端版本为7.3.0。不知道是不是不完全匹配的原因。试了7.6.2版本也不对哈哈哈。

{
	"state": "open",
	"settings": {
		"index": {
			"refresh_interval": "1s",
			"number_of_shards": "1",
			"provided_name": "goods_index",
			"creation_date": "1640695057235",
			"store": {
				"type": "fs"
			},
			"number_of_replicas": "0",
			"uuid": "La3XlrTTTu6-SoNtsuPl1g",
			"version": {
				"created": "7030099"
			}
		}
	},
	"mappings": {
		"_doc": {
			"properties": {
				"hotScore": {
					"type": "long"
				},
				"_class": {
					"type": "text",
					"fields": {
						"keyword": {
							"ignore_above": 256,
							"type": "keyword"
						}
					}
				},
				"goodsCode": {
					"type": "text",
					"fields": {
						"keyword": {
							"ignore_above": 256,
							"type": "keyword"
						}
					}
				},
				"id": {
					"type": "long"
				},
				"goodsName": {
					"type": "text",
					"fields": {
						"keyword": {
							"ignore_above": 256,
							"type": "keyword"
						}
					}
				},
				"createDate": {
					"type": "text",
					"fields": {
						"keyword": {
							"ignore_above": 256,
							"type": "keyword"
						}
					}
				}
			}
		}
	}

方式二 使用postman、elasticsearch-head等提交put请求创建索引和映射。

这个就不用讲了哈,最原始的了。

方式三 设置**@document**,的createIndex属性为false,然后使用代码初始化索引和映射。

 public boolean indexAndMappingCreate() {
        IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(Goods.class);
        boolean exists = indexOperations.exists();
        if (exists) {
            return true;
        }
        boolean index = indexOperations.create();
        document mapping = indexOperations.createMapping(Goods.class);
        boolean putMapping = indexOperations.putMapping(mapping);
        if (index && putMapping) {
            return true;
        } else {
            return false;
        }
    }

不支持fielddata = true

@Field(type = FieldType.Integer, fielddata = true)
private Integer hotScore = 100;

结果如下:

{
	"state": "open",
	"settings": {
		"index": {
			"refresh_interval": "1s",
			"number_of_shards": "1",
			"provided_name": "goods_index",
			"creation_date": "1640697770606",
			"store": {
				"type": "fs"
			},
			"number_of_replicas": "0",
			"uuid": "_N7I0GAJRsitjTbs9XnOcQ",
			"version": {
				"created": "7030099"
			}
		}
	},
	"mappings": {
		"_doc": {
			"properties": {
				"hotScore": {
					"type": "integer"
				},
				"goodsCode": {
					"type": "keyword"
				},
				"goodsName": {
					"search_analyzer": "ik_smart",
					"analyzer": "ik_max_word",
					"type": "text"
				},
				"attrs": {
					"type": "nested"
				},
				"createDate": {
					"format": "basic_date_time",
					"type": "date"
				}
			}
		}
	},

方式四 使用RestHighLevelClient发起请求,类似于postman发起put请求。

@Autowired
@Qualifier("elasticsearchClient")
protected RestHighLevelClient client;

方式五

排序查询报错

排序字段【goodsCode】需要设置fielddata=true. 需要对 string类型的字段,单独设置加载到内存中,才能排序。

Fielddata is disabled on text fields by default. Set fielddata=true on [goodsCode] in order to load fielddata in memory by uninverting the inverted index

查询时错误如下:

Caused by: ElasticsearchException[Elasticsearch exception [type=illegal_argument_exception, reason=Fielddata is disabled on text fields by default. Set fielddata=true on [goodsCode] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead.]]
	at org.elasticsearch.ElasticsearchException.innerFromXContent(ElasticsearchException.java:496)
	at org.elasticsearch.ElasticsearchException.fromXContent(ElasticsearchException.java:407)
	at org.elasticsearch.ElasticsearchException.innerFromXContent(ElasticsearchException.java:437)
	... 46 more

参考:

  • https://www.baeldung.com/spring-data-elasticsearch-queries

  • https://blog.csdn.net/weixin_43814195/article/details/85281287

  • https://www.cnblogs.com/huanshilang/p/14382279.html

  • https://github.com/eugenp/tutorials/tree/master/persistence-modules/spring-data-elasticsearch

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/690006.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号