EverydayOneCat
Cat记忆混乱…✍️🐈💦
🐈➡️🛏️💤🙏
Solr
1.什么是Solr
大多数搜索引擎应用都必须具有某种搜索功能,问题是搜索功能往往是巨大的资源消耗并且它们由于沉重的数据库加载而拖垮你的应用的性能。
Solr是一个开源搜索平台,用于构建搜索应用程序。 它建立在Lucene(全文搜索引擎)之上。 Solr是企业级的,快速的和高度可扩展的。 使用Solr构建的应用程序非常复杂,可提供高性能。
Solr可以和Hadoop一起使用。由于Hadoop处理大量数据,Solr帮助我们从这么大的源中找到所需的信息。不仅限于搜索,Solr也可以用于存储目的。像其他NoSQL数据库一样,它是一种非关系数据存储和处理技术。
总之,Solr是一个可扩展的,可 部署,搜索/存储引擎,优化搜索大量以文本为中心的数据。
2.Solr本地安装
Solr安装之前专门写过一篇文章介绍。《Linux下Solr的安装及配置》这里其实差不多,主要介绍几个关键点。
2.1中文分析器IK Analyzer
IK Analyzer 是一个开源的,基亍 java 语言开发的轻量级的中文分词工具包。从 2006年 12 月推出 1.0 版开始, IKAnalyzer 已经推出了 4 个大版本。最初,它是以开源项目Luence 为应用主体的,结合词典分词和文法分析算法的中文分词组件。从 3.0 版本开始,IK 发展为面向 Java 的公用分词组件,独立亍 Lucene 项目,同时提供了对 Lucene 的默认优化实现。在 2012 版本中,IK 实现了简单的分词歧义排除算法,标志着 IK 分词器从单纯的词典分词向模拟语义分词衍化。
3.配置域
域相当于数据库的表字段,用户存放数据,因此用户根据业务需要去定义相关的Field(域),一般来说,每一种对应着一种数据,用户对同一种数据进行相同的操作。
域的常用属性:
- name:指定域的名称
- type:指定域的类型
- indexed:是否索引
- stored:是否存储
- required:是否必须
- multiValued:是否多值
3.1普通域
修改solrhome的schema.xml 文件 根据需求设置业务系统 Field
1 | <field name="item_goodsid" type="long" indexed="true" stored="true"/> |
3.2复制域
复制域的作用在于将某一个Field中的数据复制到另一个域中,常用来把需要搜索的关键域结合起来形成一个新的域好搜索,但是不要存起来。
1 | <field name="item_keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/> |
3.2动态域
当我们需要动态扩充字段时,我们需要使用动态域。对于品优购,规格的值是不确定的,所以我们需要使用动态域来实现。需要实现的效果如下:
1 | <dynamicField name="item_spec_*" type="string" indexed="true" stored="true" /> |
4.Spring Data Solr入门
在前面的淘淘商城我们学习了SolrJ的实际使用,这里介绍Spring Data Solr,Spring Data Solr就是为了方便Solr的开发所研制的一个框架,其底层是对SolrJ(官方API)的封装。
4.1配置文件
pom.xml中引入依赖
1 | <dependency> |
resources下创建 applicationContext-solr.xml
1 |
|
4.2@Field 注解
将品优购的TbItem实体类拷入工程 ,属性使用@Field注解标识 。如果属性与配置文件定义的域名称不一致,需要在注解中指定域名称。
1 | public class TbItem implements Serializable{ |
4.3增加(修改)
1 |
|
4.4按主键查询
1 |
|
4.5按主键删除
1 |
|
4.6分页查询
首先循环插入100条测试数据,增加可以用saveBeans()方法加入集合
1 |
|
编写分页查询测试代码:
1 |
|
4.7条件查询
Criteria 用于对条件的封装:
1 | Query query=new SimpleQuery("*:*"); |
4.8删除全部数据
1 |
|
品优购
1.批量数据导入
编写专门的导入程序,将商品数据导入到Solr系统中
1.1搭建工程
创建pinyougou-solr-util(jar) ,引入pinyougou-dao 以及spring 相关依赖:
1 | <context:component-scan base-package="com.pinyougou.solrutil"> |
1.2实体类
上文解释过,需要修改pinyougou-pojo中的TbItem类添加必要的@Field注解
同时,为了让规格导入动态域,我们定义Map属性,具体代码如下:
1 |
|
1.3数据导入Solr索引库
创建com.pinyougou.solrutil包,创建类SolrUtil ,实现商品数据的查询(已审核商品)
1 |
|
运行结果:
2.关键字搜索
打开搜索页面,在搜索框输入要搜索的关键字,点击搜索按钮即可进行搜索,展示搜索结果
2.1搭建工程
创建pinyougou-search-interface模块(搜索服务接口),依赖pinyougou-pojo
创建war工程pinyougou-search-service ,引入pinyougou-search-interface spring dubbox 等相关依赖(参照其它服务工程)Tomcat运行端口9004
创建pinyougou-search-web 工程 ,引入依赖(参见其它web模块),tomcat运行端口9104
web.xml把search.html设为默认主页
1 | <welcome-file-list> |
2.2后端
创建业务接口
1 | public interface ItemSearchService { |
编写服务实现类ItemSearchServiceImpl.java
1 |
|
创建包com.pinyougou.search.controller 编写控制器类
1 |
|
2.3前端
pinyougou-search-web工程创建searchService.js
1 | //搜索服务层 |
pinyougou-search-web工程searchController.js
1 | app.controller('searchController',function($scope,searchService){ |
search.html绑定搜索框
1 | <div class="input-append"> |
循环显示数据
1 | <li class="yui3-u-1-5" ng-repeat="item in resultMap.rows"> |
3.高亮显示
将用户输入的关键字在标题中以红色的字体显示出来,就是搜索中常用的高亮显示。
3.1后端
修改服务层代码ItemSearchServiceImpl.java
为了方便模块化处理,创建私有方法,用于返回查询列表的结果(高亮)
1 | //根据关键字搜索列表 |
修改ItemSearchServiceImpl 的search方法,调用刚才编写的私有方法
1 |
|
测试搜索:
3.2前端
我们测试后发现高亮显示的html代码原样输出,这是angularJS为了防止html攻击采取的安全机制。
我们会用到$sce服务的trustAsHtml方法来实现转换。因为这个功能具有一定通用性,我们可以通过angularJS的过滤器来简化开发,这样只写一次,调用的时候就非常方便了。
(1)修改base.js
1 | // 定义模块: |
(2)使用过滤器
ng-bind-html指令用于显示html内容,竖线 |用于调用过滤器
1 | <div class="attr" ng-bind-html="item.title | trustHtml"></div> |
效果:华为手机(确信)
4.分类列表
根据搜索关键字查询商品分类名称列表
4.1后端
1 | //查询分类列表 |
search方法调用
1 |
|
4.2前端
修改search.html
1 | <div class="type-wrap" ng-if="resultMap.categoryList!=null"> |
5.品牌和规格数据
5.1缓存品牌和规格数据
将商品分类数据、品牌数据、和规格数据都放入Redis存储。
(1)当用户进入运营商后台的商品分类页面时,将商品分类数据放入缓存(Hash)。以分类名称作为key ,以模板ID作为值
(2)当用户进入运营商后台的模板管理页面时,分别将品牌数据和规格数据放入缓存(Hash)。以模板ID作为key,以品牌列表和规格列表作为值。
缓存商品分类数据:
1 |
|
缓存品牌和规格列表数据:
1 |
|
在查询分页方法(findPage) 时调用此方法
1 | public PageResult findPage(TbTypeTemplate typeTemplate, int pageNum, int pageSize) { |
5.2显示品牌和规格数据
后端:
修改ItemSearchServiceImpl.java ,增加方法
1 |
|
Search方法调用此方法
1 |
|
前端:
修改页面search.html,实现品牌列表和规格列表
1 | <div class="type-wrap logo" ng-if="resultMap.brandList!=null"> |
6.过滤构建和查询
6.1过滤条件构建
点击搜索面板上的分类、品牌和规格,实现查询条件的构建。查询条件以面包屑的形式显示。
当面包屑显示分类、品牌和规格时,要同时隐藏搜索面板对应的区域。
用户可以点击面包屑上的X 撤销查询条件。撤销后显示搜索面包相应的区域。
1、添加搜索项:
修改pinyougou-search-web的searchController.js
1 | $scope.searchMap={'keywords':'','category':'','brand':'','spec':{},'price':''};//搜索对象 |
修改pinyougou-search-web 的search.html ,为搜索面板添加点击事件
1 | <li class="tag" ng-if="searchMap.category!=''" ng-click="removeSearchItem('category')">商品分类:{{searchMap.category}}<i class="sui-icon icon-tb-close"></i></li> |
修改pinyougou-search-web 的search.html,用面包屑形式显示搜索条件
1 | <ul class="fl sui-breadcrumb">搜索条件:</ul> |
2、撤销搜索项:
修改pinyougou-search-web工程searchController.js
1 | //移除复合搜索条件 |
3、隐藏查询面板
修改search.html
1 | <div class="type-wrap" ng-if="resultMap.categoryList!=null && searchMap.category==''"> |
4、如果用户输入的是品牌的关键字,则隐藏品牌列表
修改searchController.js
1 | //判断关键字是不是品牌 |
修改页面
1 | <div class="type-wrap logo" ng-if="resultMap.brandList!=null&& searchMap.brand=='' && keywordsIsBrand()==false"> |
6.2过滤查询
根据分类查询品牌规格列表:
1 |
|
分类和品牌过滤:
1 | //按分类筛选 |
规格过滤:
实现思路:规格有多项,需要循环过滤。循环规格查询条件,根据key得到域名城,根据value设置过滤条件
1 | //过滤规格 |
按价格筛选:
1 | //按价格筛选 |
7.搜索结果分页
之前的分页我们都是用PageHelper来完成的,但是PageHelper做出来的界面不太美观,达不到我们想要的前台效果,适合后台。这里我们通过Spring Data Solr来完成分页。
7.1后端
修改pinyougou-search-service工程ItemSearchServiceImpl.java
1 | //分页查询 |
7.2构建分页标签
为搜索对象添加属性
1 | $scope.searchMap={'keywords':'','category':'','brand':'','spec':{},'price':'', |
实现页码的构建
1 | //构建分页标签(totalPages为总页数) |
在查询后调用此方法
7.3提交页码查询
在searchController.js增加方法,修改页码执行查询
1 | //根据页码查询 |
修改页码调用方法
1 | <div class="sui-pagination pagination-large"> |
修改search方法, 在执行查询前,转换为int类型,否则提交到后端有可能变成字符串
1 | //搜索 |
7.4显示省略号
1 | //构建分页栏 |
修改页面 :页码的省略号
1 | <li class="dotted" ng-if="firstDot==true"><span>...</span></li> |
7.5搜索起始页码处理
测试:如果我们先按照“手机”关键字进行搜索,得出的页数是19页,然后我们点击第18页进行查询,然后我们再根据“三星”关键字搜索,会发现没有结果显示。是因为当前页仍然为18,而三星的结果只有4页,所以无法显示。我们需要在每次点击查询时将页码设置为1 。
修改搜索按钮,调用搜索前将起始页码设置为1
1 | <button class="sui-btn btn-xlarge btn-danger" ng-click="searchMap.pageNo=1;search()" type="button">搜索</button> |
8.排序
8.1按价格排序
修改pinyougou-search-service的ItemSearchServiceImpl.java 添加排序的代码
1 | //排序 |
修改searchController.js
1 | $scope.searchMap={'keywords':'','category':'','brand':'','spec':{},'price':'','pageNo':1,'pageSize':40 ,'sortField':'','sort':'' };//搜索对象 |
修改页面search.html
1 | <div class="navbar-inner filter"> |
8.2按上架时间排序
修改solrhome的schema.xml 添加域定义
1 | <field name="item_updatetime" type="date" indexed="true" stored="true" /> |
修改实体类为updatetime属性添加注解
1 |
|
重新启动solr
安装pinyougou-pojo
重新运行pinyougou-solr-util
修改search.html
1 | <li> |
8.3按销量和评价排序
(1)增加域item_salecount 用于存储每个SKU的销量数据
(2)编写定时器程序,用于更新每个SKU的销量数据(查询近1个月的销量数据,不是累计数据)
(3)定时器每天只需执行一次,可以设定为凌晨开始执行。
定时器可以使用spring task技术来实现
评论分为好评、中评、差评,我们不能简单地将评论数相加,而是应该根据每种评论加权进行统计。比如好评的权重是3 ,中评的权重是1,而差评的权重是 -3,这样得出的是评价的综合得分。
9.搜索页与首页对接
用户在首页的搜索框输入关键字,点击搜索后自动跳转到搜索页查询
9.1首页传递关键字
修改pinyougou-portal-web的contentController.js
1 | //搜索跳转 |
修改pinyougou-portal-web的index.html
1 | <div class="input-append"> |
9.2搜索页接收关键字
修改pinyougou-search-web的searchController.js
添加location服务用于接收参数
1 | app.controller('searchController',function($scope,$location,searchService) |
接收参数并进行查询
1 | //加载查询字符串 |
在search页面初始化调用此方法
10.更新索引库
10.1查询审核商品(SKU)列表
修改pinyougou-sellergoods-interface的GoodsService.java,新增方法
1 | /** |
GoodsServiceImpl.java 实现该方法
1 |
|
10.2更新到索引库
修改pinyougou-search-interface的ItemSearchService.java
1 | /** |
ItemSearchServiceImpl.java实现该方法
1 |
|
修改pinyougou-manager-web工程的GoodsController.java
1 |
|
10.3商品删除同步索引数据
服务接口层
1 | /** |
服务实现层
1 |
|
控制层
1 | /** |
结语
天天35°C以上的高温,只想呆在空调里面裹着被子刷手机📱或者睡觉😴