乐优商城--服务(五) : 搜索微服务(LySearchApplication)

1. 引言

1.1 Elasticsearch

Elasticsearch:全文检索技术
在这里插入图片描述
如上所述,Elasticsearch具备以下特点:

  • 分布式,无需人工搭建集群(solr就需要人为配置,使用Zookeeper作为注册中心)
  • Restful风格,一切API都遵循Rest原则,容易上手
  • 近实时搜索,数据更新在Elasticsearch中几乎是完全同步的。

1.2 kibana

在这里插入图片描述
Kibana是一个基于Node.js的Elasticsearch索引库数据统计工具(发请求),可以利用Elasticsearch的聚合功能,生成各种图表,如柱形图,线状图,饼图等。

而且还提供了操作Elasticsearch索引数据的控制台,并且提供了一定的API提示

1.3 操作索引

Elasticsearch也是基于Lucene的全文检索库,本质也是存储数据,很多概念与MySQL类似的。

对比关系:
索引(indices)--------------------------------Databases 数据库
类型(type)-----------------------------Table 数据表

     索引(indices)--------------------------------Databases 数据库
     类型(type)-----------------------------Table 数据表
     文档(Document)----------------Row 行
	 字段(Field)-------------------Columns 列 

Elasticsearch采用Rest风格API(http请求接口),因此其API就是一次http请求,可以用任何工具发起http请求

索引的请求格式:

  • 请求方式:PUT(创建,修改合二为一)/ GET(查看)/ DELETE(删除)/ POST(可以向一个已经存在的索引库中添加数据)
  • 请求路径:/索引库名
  • 请求参数:json格式:
     {
   
          "settings": {
   
              "number_of_shards": 3,
              "number_of_replicas": 2
            }
      }
  • settings:索引库的设置
  • number_of_shards:分片数量
  • number_of_replicas:副本数量

1.4 测试

1.4.1 查询

@RunWith(SpringRunner.class)
@SpringBootTest
public class GoodsRepositoryTest {
   
	public void testQuery{
   
		// 1 创建查询构建器(spring提供的)
		NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
		// 2 结果过滤
		queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{
   "id", "subTitle", "skus"}, null));
		// 3 添加查询条件
		queryBuilder.withQuery(QueryBuilders.matchQuery(name:"title",text:"小米手机"));
		// 4 排序
		queryBuilder.withSort(SortBuilders.fieldSort("price"").order(SortOrder.DESC));
		// 5 分页
		queryBuilder.withPageable(PageRequest.of(page,size));
		// 6 查询
		Page<Goods> result = repository.search(queryBuilder.build());
		long total = result.getTotalElements();
		........
	}
}

采用类的字节码信息创建索引并映射:
Spring Data通过注解来声明字段的映射属性,有下面的三个注解:

  • @Document 作用在类(Goods),标记实体类为文档对象,一般有两个属性
    • indexName:对应索引库名称
    • type:对应在索引库中的类型
    • shards:分片数量,默认5
    • replicas:副本数量,默认1
  • @Id 作用在成员变量,标记一个字段作为id主键
  • @Field 作用在成员变量,标记为文档的字段,并指定字段映射属性:
    • type:字段类型,是是枚举:FieldType
    • index:是否索引,布尔类型,默认是true
    • store:是否存储,布尔类型,默认是false
    • analyzer:分词器名称
  • 增删改不用ElasticsearchTemplate,ElasticsearchTemplate一般会用来做原生的复杂查询,比如聚合,我们一般的普通增删改查用不到,而spring给我们提供了ElasticsearchRepository( Spring Data 的强大之处,就在于你不用写任何DAO处理,自动根据方法名或类的信息进行CRUD操作。只要你定义一个接口,然后继承Repository提供的一些子接口,就能具备各种基本的CRUD功能。)
  • 因此我们应该写个GoodsRepository 接口继承ElasticsearchRepository,第一个泛型是实体类,第二个是id类型,接下来就可以直接用了
  • Spring Data 的另一个强大功能,是根据方法名称自动实现功能。
    比如:你的方法名叫做:findByTitle,那么它就知道你是根据title查询,然后自动帮你完成,无需写实现类。当然,方法名称要符合一定的约定
public interface GoodsRepository extends ElasticsearchRepository<Goods, Long> {
   
}

QueryBuilder(spring提供的)可整合Elasticsearch原生的结果过滤、查询、排序、分页等,还有结果过滤,整合完之后利用spring data做一个搜索,它会帮我们封装成一个结果

1.4.2 聚合

@RunWith(SpringRunner.class)
@SpringBootTest
public class GoodsRepositoryTest {
   
	public void testAgg{
   
		// 1 创建查询构建器(spring提供的)
		NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
		String aggName = "popularBrand";
		// 2 聚合
		queryBuilder.addAggregation(AggregationBuilders.terms(CategoryAggName).field("brand"));
		// 3 查询并返回带聚合结果
	    AggregatedPage<Goods> result = template.queryForPage(queryBuilder.build(), Goods.class);
		// 4 解析聚合
		Aggregations aggs = result.getAggregations();
		// 5 获取指定名称的聚合
		StringTerms terms  = aggs.getName(aggName);
		// 6 获取桶
		List<StringTerms.Bucket> buckets = terms.getBuckets();
		for(StringTerms.Bucket bucket:buckets){
   
			bucket.getKeyAsString();
			...
		}
		........
	}
}

2. 搭建项目

用户访问我们的首页,一般都会直接搜索来寻找自己想要购买的商品。
而商品的数量非常多,而且分类繁杂。如果能正确的显示出用户想要的商品,并进行合理的过滤,尽快促成交易,是搜索系统要研究的核心。

面对这样复杂的搜索业务和数据量,一般我们都会使用全文检索技术: Elasticsearch。

2.1 引入依赖

 <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>leyou</artifactId>
        <groupId>com.leyou.parent</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.leyou.page.service</groupId>
    <artifactId>ly-search</artifactId>

    <dependencies>
        <!--eureka-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--elasticsearch-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
        <!--feign 服务间调用-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--springboot启动器的测试功能-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!--商品实体类的接口-->
        <dependency>
            <groupId>com.leyou.service</groupId>
            <artifactId>ly-item-interface</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2.2 配置

server:
  port: 8083
spring:
  application:
    name: search-service
  data:
    elasticsearch:
      cluster-name: elasticsearch
      cluster-nodes: 192.168.184.130:9300
  jackson:
    default-property-inclusion: non_null  #排除返回结构中字段值为null的属性
  rabbitmq:
    host: 192.168.184.130
    username: leyou
    password: leyou
    virtual-host: /leyou
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
      registry-fetch-interval-seconds: 10
  instance:
    #lease-renewal-interval-in-seconds: 5 # 每隔5秒发送一次心跳
    #lease-expiration-duration-in-seconds: 10 # 10秒不发送就过期
    prefer-ip-address: true
    ip-address: 127.0.0.1

2.3 启动类

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@EnableFeignClients
public class LySearchApplication {
   

    public static void main(String[] args) {
   
        SpringApplication.run(LySearchApplication.class, args);
    }
}

3. 索引库数据格式分析

接下来,我们需要商品数据导入索引库,便于用户搜索。

那么问题来了,我们有SPU和SKU,到底如何保存到索引库?

3.1 以结果为导向

我们来看下搜索结果页:

在这里插入图片描述

可以看到,每一个搜索结果都有至少1个商品,当我们选择大图下方的小图,商品会跟着变化。

因此,搜索的结果是SPU,即多个SKU的集合

既然搜索的结果是SPU,那么我们索引库中存储的应该也是SPU,但是却需要包含SKU的信息。

3.2 需要什么数据

由上图可以直观能看到的:图片、价格、标题、副标题(属于SKU数据,用来展示的);暗藏的数据:spu的id,sku的id

另外,页面还有过滤条件:
在这里插入图片描述
这些过滤条件也都需要存储到索引库中,包括:商品分类、品牌、可用来搜索的规格参数等

综上所述,我们需要的数据格式有:

spuId、SkuId、商品分类id、品牌id、图片、价格、商品的创建时间、sku信息集、可搜索的规格参数

3.3 最终的数据结构

我们创建一个类,封装要保存到索引库的数据,并设置映射属性:

@Data
@Document(indexName = "goods", type = "docs", shards = 1)
public class Goods {
   
    @Id
    private Long id; // spuId

    @Field(type = FieldType.text,analyzer = "ik_max_word")
    private String all; // 所有需要被搜索的信息,包含标题,分类,甚至品牌

    @Field(type = FieldType.keyword, index = false)//不进行搜索,不进行分词
    private String subTitle;// 卖点

    private Long brandId;// 品牌id
    private Long cid1;// 1级分类id
    private Long cid2;// 2级分类id
    private Long cid3;// 3级分类id
    private Date createTime;// 创建时间
    private Set<Long> price;// 价格,对应到elasticsearch/json中是数组,一个spu有多个sku,就有多个价格

    @Field(type = FieldType.keyword, index = false)
    pri
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值