solr笔记
1. 什么是solr
solr是一个apache的全文检索引擎系统, 就是个war包, 部署到Tomcat下就可以独立运行, 我们使用它的客户端工具包
solrj来远程调用solr服务器, 完成对索引库的操作(对索引库的添加修改删除, 查询)
solr底层使用lucene编写.
2. 作用
对于大数据量搜索或者是查询, 速度非常快, 并且不会随着数据量的增大而减缓查询速度.
主要应用于大型的互联网项目中, 做大规模数据查询.
3. 同类型技术
elasticsearch是solr的同类型技术, elasticsearch在搜索的时候速度比solr要快.但是使用起来比solr要复杂,企业中现在elasticsearch比较流行
4. 全文检索算法(倒排索引表算法)
使用场景: 大数据量搜索查询, 例如: 京东, 天猫的搜索功能.
描述: 查询前先将查询的内容抽取出来组成文档(document), 也就相当于字典的正文, 然后进行切分词, 将切分出来的词组成索引(index)相当于字段的目录, 查询的时候先查询索引根据索引找文档, 这个过程叫做全文检索
总结: 和字典原理一样.
优点: 查询速度快, 并且不会随着查询的数据量增大而变慢, 查询结果精确
缺点: 索引会额外占用大量的磁盘空间.
5. 顺序扫描法
使用场景: 数据库中的like模糊查询就是用的这种算法
描述: 拿着需要查询的关键字, 到内容中逐字逐行的对比, 直到查询内容结束
优点: 查询准确
缺点: 查询速度慢, 并且会随着查询内容量增大越来越慢.
6. 切分词
将一句一句话, 切分成一个一个词, 去掉停用词(的, 地得, a,an,the等), 去掉空格和标点符号, 大写字母全部转成小写字母.
7. solr部署步骤(linux)
- 在/usr/local目录下创建solr文件夹
- 复制solr安装包, ik分词器包, tomcat包到这个目录下, 并且解压
- 将solr/example/webapps/solr.war复制到tomcat/webapps目录下
- 启动tomcat目的是对war包解压, 解压完成后关闭tomcat
- 到tomcat/webapps目录中删除solr.war
- 复制solr/example/lib/ext下的所有到 tomcat/webapps/solr/WEB-INf/lib目录下
- 复制solr/example/solr目录到 /usr/loca/solr目录下并且改名问solrhome
- 配置solrhome的位置到tomcat/webapps/solr/WEB-INF/web.xml中
- 启动tomcat, 浏览器访问http://服务器地址:端口/solr看到solr页面后证明部署成功
solrhome就是solr的家, 一个solr服务器只能有一个solrhome, 一个solrhome中可以有多个solr实例, 里面的collection1
文件夹就是默认的solr实例, 一个solrhome中可以同时有多个实例, 实例中有索引库, 实例和实例之间是互相隔离的.
8. 注意
注意:
- 域名要先定义后使用, 没有定义的域名直接使用会报错
ERROR: [doc=002] unknown field ‘sadfasdfasdfasdf’ - solr中添加数据的时候必须有主键域id, 没有会报错
Document is missing mandatory uniqueKey field: id" - solr中没有修改方法, 添加就是修改, 修改就是添加, 每次修改数据的时候, 都是根据id主键先去查询,
如果查到了, 将原有数据删除, 将新数据添加进去, 这就是修改, 如果没有根据id查询到数据, 则直接添加, 就成了添加.
9. 管理界面功能介绍
9.1 Dashboard
仪表盘,显示了该Solr实例开始启动运行的时间、版本、系统资源、jvm等信息。
9.2 Logging
Solr运行日志信息
9.3 Cloud
Cloud即SolrCloud,即Solr云(集群),当使用Solr Cloud模式运行时会显示此菜单
9.4 Core Admin
Solr Core的管理界面。在这里可以添加SolrCore实例(有bug,不推荐使用浏览器界面添加SolrCore)。
9.5 java properties
Solr在JVM 运行环境中的属性信息,包括类路径、文件编码、jvm内存设置等信息。
9.6 Tread Dump
显示Solr Server中当前活跃线程信息,同时也可以跟踪线程运行栈信息。
9.8 Core selector
选择一个SolrCore进行详细操作,如下:
9.9 Analysis
通过此界面可以测试索引分析器和搜索分析器的执行情况
9.10 dataimport
可以定义数据导入处理器,从关系数据库将数据导入到Solr索引库中。
默认没有配置,需要手工配置。
9.11 Document
通过/update表示更新索引,solr默认根据id(唯一约束)域来更新Document的内容,如果根据id值搜索不到id域则会执行添加操作,如果找到则更新。
通过此菜单可以创建索引、更新索引、删除索引等操作,界面如下:
- overwrite=“true” : solr在做索引的时候,如果文档已经存在,就用xml中的文档进行替换
- commitWithin=“1000” : solr 在做索引的时候,每隔1000(1秒)毫秒,做一次文档提交。为了方便测试也可以在Document中立即提交,后添加“”
9.12 Query
通过/select执行搜索索引,必须指定“q”查询条件方可搜索。
10. solr基本使用
10.1 schema.xml
schema.xml文件在SolrCore的conf目录下,在此配置文件中定义了域以及域的类型等一些配置。在solr中域必须先定义后使用。
10.1.1 field
<field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" />
- Name:域的名称
- Type:域的类型
- Indexed:是否索引
- Stored:是否存储
- Required:是否必须
- multiValued:是否是多值,存储多个值时设置为true,solr允许一个Field存储多个值,比如存储一个用户的好友id(多个),商品的图片(多个,大图和小图)
10.1.2 dynamicField(动态域)
<dynamicField name="*_s" type="string" indexed="true" stored="true" />
Name:动态域的名称,是一个表达式,*匹配任意字符,只要域的名称和表达式的规则能够匹配就可以使用。
例如:搜索时查询条件[product_i:钻石]就可以匹配这个动态域,可以直接使用,不用单独再定义一个product_i域。
10.1.3 uniqueKey
<uniqueKey>id</uniqueKey>
相当于主键,每个文档中必须有一个id域。
10.1.4 copyField(复制域)
<copyField source=“cat” dest=“text”/>
可以将多个Field复制到一个Field中,以便进行统一的检索。当创建索引时,solr服务器会自动的将源域的内容复制到目标域中。
- source:源域
- dest:目标域,搜索时,指定目标域为默认搜索域,可以提高查询效率。
定义目标域:copyField(复制域)
<field name="text" type="text_general" indexed="true" stored="false" multiValued="true"/>
目标域必须要使用:multiValued=“true”
10.1.4 fieldType(域类型)
<fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">
<analyzer type="index">
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
<analyzer type="query">
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
<filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
</fieldType>
- name:域类型的名称
- class:指定域类型的solr类型。
- analyzer:指定分词器。在FieldType定义的时候最重要的就是定义这个类型的数据在建立索引和进行查询的时候要使用的分析器analyzer,包括分词和过滤。
- type:index和query。Index 是创建索引,query是查询索引。
- tokenizer:指定分词器
- filter:指定过滤器
10.2 配置中文分析器
使用IKAnalyzer中文分析器
第一步:把IKAnalyzer2012FF_u1.jar添加到solr/WEB-INF/lib目录下。
第二步:复制IKAnalyzer的配置文件和自定义词典和停用词词典到solr的solr/WEB-INF/classes目录下。
复制IK分词器配置文件、自定义词典、停用词词典
粘贴到Tomcat的solr的/WEB-INF/classes目录下
第三步:在schema.xml中添加一个自定义的fieldType,使用中文分析器。
<!-- IKAnalyzer-->
<fieldType name="text_ik" class="solr.TextField">
<analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/>
</fieldType>
第四步:在schema.xml中添加field,指定field的type属性为text_ik
<!--IKAnalyzer Field-->
<field name="content_ik" type="text_ik" indexed="true" stored="true" />
第五步:重启tomcat
效果:
10.3 配置业务Field
10.3.1 需求
要使用solr实现网站中商品搜索,需要 将mysql数据库中数据在solr中创建索引。
1.需要在solr的schema.xml文件定义要存储的商品Field。
2.需要把MySQL的数据导入到solr索引库中
3.开发搜索功能
10.3.2 数据库添加数据
在数据库中运行solr.sql脚本
10.3.3 定义Field
先确定定义的商品document的Field域有哪些?
可以根据mysql数据库中商品表的字段来确定:
products商品表:
Schema.xml中配置业务域
<!--product-->
<field name="product_name" type="text_ik" indexed="true" stored="true"/>
<field name="product_price" type="float" indexed="true" stored="true"/>
<field name="product_description" type="text_ik" indexed="true" stored="false" />
<field name="product_picture" type="string" indexed="false" stored="true" />
<field name="product_catalog_name" type="string" indexed="true" stored="true" />
<field name="product_keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/>
<copyField source="product_name" dest="product_keywords"/>
<copyField source="product_description" dest="product_keywords"/>
11. Solrj的使用
11.1 什么是solrj
solrj是访问Solr服务的java客户端,提供索引和搜索的请求方法,如下图:
Solrj和图形界面操作的区别就类似于数据库中使用jdbc和mysql客户端的区别一样。
11.2 需求
使用solrj调用solr服务实现对索引库的增删改查操作。
11.3.环境准备
- Solr:4.10.3
- Jdk环境:1.7
- IDE环境:Eclipse Mars2
11.4 工程搭建
11.5 添加jar
solrj依赖包,\solr-4.10.3\dist\solrj-lib
Solr服务的依赖包,\solr\example\lib\ext
11.6 代码实现
11.6.1 添加&修改索引
步骤:
1、创建HttpSolrServer对象,通过它和Solr服务器建立连接。
2、创建SolrInputDocument对象,然后通过它来添加域。
3、通过HttpSolrServer对象将SolrInputDocument添加到索引库。
4、提交。
代码:
说明:根据id(唯一约束)域来更新Document的内容,如果根据id值搜索不到id域则会执行添加操作,如果找到则更新。
@Test
public void testCreateAndUpdateIndex() throws Exception {
// 1. 创建HttpSolrServer对象
// 设置solr服务接口,浏览器客户端地址http://127.0.0.1:8081/solr/#/
String baseURL = "http://127.0.0.1:8081/solr";
HttpSolrServer httpSolrServer = new HttpSolrServer(baseURL);
// 2. 创建SolrInputDocument对象
SolrInputDocument document = new SolrInputDocument();
document.addField("id", "c1001");
document.addField("content ", "Hello world!");
// 3. 把SolrInputDocument对象添加到索引库中
httpSolrServer.add(document);
// 4. 提交
httpSolrServer.commit();
}
11.6.2 删除索引
代码:
抽取HttpSolrServer 的创建代码
private HttpSolrServer httpSolrServer;
// 提取HttpSolrServer创建
@Before
public void init() {
// 1. 创建HttpSolrServer对象
// 设置solr服务接口,浏览器客户端地址http://127.0.0.1:8081/solr/#/
String baseURL = "http://127.0.0.1:8081/solr/";
this.httpSolrServer = new HttpSolrServer(baseURL);
}
删除索引逻辑,两种:
根据id删除
根据条件删除,根据条件删除
可以使用*:*作为条件,就是删除所有数据(慎用)
@Test
public void testDeleteIndex() throws Exception {
// 根据id删除索引数据
// this.httpSolrServer.deleteById("c1001");
// 根据条件删除(如果是*:*就表示全部删除,慎用)
this.httpSolrServer.deleteByQuery("*:*");
// 提交
this.httpSolrServer.commit();
}
11.6.3 查询索引
简单查询:
/**
* 简单搜索
*
* @throws Exception
*/
@Test
public void testSearchIndex1() throws Exception {
// 创建搜索对象
SolrQuery query = new SolrQuery();
// 设置搜索条件
query.setQuery("*:*");
// 发起搜索请求
QueryResponse response = this.httpSolrServer.query(query);
// 处理搜索结果
SolrDocumentList results = response.getResults();
System.out.println("搜索到的结果总数:" + results.getNumFound());
// 遍历搜索结果
for (SolrDocument solrDocument : results) {
System.out.println("----------------------------------------------------");
System.out.println("id:" + solrDocument.get("id"));
System.out.println("content" + solrDocument.get("content"));
}
}
12. Spring Data Solr入门
12.1 Spring Data Solr简介
虽然支持任何编程语言的能力具有很大的市场价值,你可能感兴趣的问题是:我如何将Solr的应用集成到Spring中?可以,Spring Data Solr就是为了方便Solr的开发所研制的一个框架,其底层是对SolrJ(官方API)的封装。
12.2 Spring Data Solr入门小Demo
12.2.1 搭建工程
(1)创建maven工程solrdemo jar项目,pom.xml中引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-solr</artifactId>
<version>1.5.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- java编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</build>
(2)在src/main/resources下创建 applicationContext-solr.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:solr="http://www.springframework.org/schema/data/solr"
xsi:schemaLocation="http://www.springframework.org/schema/data/solr
http://www.springframework.org/schema/data/solr/spring-solr-1.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- solr服务器地址 -->
<solr:solr-server id="solrServer" url="http://192.168.200.128:8080/solr" />
<!-- solr模板,使用solr模板可对索引库进行CRUD的操作 -->
<bean id="solrTemplate" class="org.springframework.data.solr.core.SolrTemplate">
<constructor-arg ref="solrServer" />
</bean>
</beans>
12.2.1 @Field 注解
创建 cn.itcast.pojo 包,将品优购的TbItem实体类拷入本工程 ,属性使用@Field注解标识 。 如果属性与配置文件定义的域名称不一致,需要在注解中指定域名称。
public class TbItem implements Serializable{
@Field
private Long id;
@Field("item_title")
private String title;
@Field("item_price")
private BigDecimal price;
@Field("item_image")
private String image;
@Field("item_goodsid")
private Long goodsId;
@Field("item_category")
private String category;
@Field("item_brand")
private String brand;
@Field("item_seller")
private String seller;
.......
}
12.2.2 增加(修改)
创建测试类TestTemplate.java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext-solr.xml")
public class TestTemplate {
@Autowired
private SolrTemplate solrTemplate;
@Test
public void testAdd(){
TbItem item=new TbItem();
item.setId(1L);
item.setBrand("华为");
item.setCategory("手机");
item.setGoodsId(1L);
item.setSeller("华为2号专卖店");
item.setTitle("华为Mate9");
item.setPrice(new BigDecimal(2000));
solrTemplate.saveBean(item);
solrTemplate.commit();
}
}
12.2.2 按主键查询
@Test
public void testFindOne(){
TbItem item = solrTemplate.getById(1, TbItem.class);
System.out.println(item.getTitle());
}
12.2.3 按主键删除
@Test
public void testDelete(){
solrTemplate.deleteById("1");
solrTemplate.commit();
}
12.2.4 分页查询
首先循环插入100条测试数据
@Test
public void testAddList(){
List<TbItem> list=new ArrayList();
for(int i=0;i<100;i++){
TbItem item=new TbItem();
item.setId(i+1L);
item.setBrand("华为");
item.setCategory("手机");
item.setGoodsId(1L);
item.setSeller("华为2号专卖店");
item.setTitle("华为Mate"+i);
item.setPrice(new BigDecimal(2000+i));
list.add(item);
}
solrTemplate.saveBeans(list);
solrTemplate.commit();
}
编写分页查询测试代码:
@Test
public void testPageQuery(){
Query query=new SimpleQuery("*:*");
query.setOffset(20);//开始索引(默认0)
query.setRows(20);//每页记录数(默认10)
ScoredPage<TbItem> page = solrTemplate.queryForPage(query, TbItem.class);
System.out.println("总记录数:"+page.getTotalElements());
List<TbItem> list = page.getContent();
showList(list);
}
//显示记录数据
private void showList(List<TbItem> list){
for(TbItem item:list){
System.out.println(item.getTitle() +item.getPrice());
}
}
12.2.4 条件查询
Criteria 用于对条件的封装:
@Test
public void testPageQueryMutil(){
Query query = new SimpleQuery("*:*");
Criteria criteria = new Criteria();
criteria = criteria.or("item_title").contains("2");
criteria = criteria.or("item_title").contains("5");
query.addCriteria(criteria);
ScoredPage<TbItem> page = solrTemplate.queryForPage(query, TbItem.class);
System.out.println("总记录数:"+page.getTotalElements());
List<TbItem> list = page.getContent();
showList(list);
}
12.2.5 删除全部数据
@Test
public void testDeleteAll(){
Query query=new SimpleQuery("*:*");
solrTemplate.delete(query);
solrTemplate.commit();
}
附录:
<field name="item_goodsid" type="long" indexed="true" stored="true"/>
<field name="item_title" type="text_ik" indexed="true" stored="true"/>
<field name="item_price" type="double" indexed="true" stored="true"/>
<field name="item_image" type="string" indexed="false" stored="true" />
<field name="item_category" type="string" indexed="true" stored="true" />
<field name="item_seller" type="text_ik" indexed="true" stored="true" />
<field name="item_brand" type="string" indexed="true" stored="true" />
<field name="item_updatetime" type="date" indexed="true" stored="true" />
<field name="item_keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/>
<copyField source="item_title" dest="item_keywords"/>
<copyField source="item_category" dest="item_keywords"/>
<copyField source="item_seller" dest="item_keywords"/>
<copyField source="item_brand" dest="item_keywords"/>
<dynamicField name="item_spec_*" type="string" indexed="true" stored="true" />
package com.rsw.pojo.item;
import org.apache.solr.client.solrj.beans.Field;
import org.springframework.data.solr.core.mapping.Dynamic;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;
public class Item implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 商品id,同时也是商品编号
*/
@Field
private Long id;
/**
* 商品标题
*/
@Field("item_title")
private String title;
/**
* 商品卖点
*/
private String sellPoint;
/**
* 商品价格,单位为:元
*/
@Field("item_price")
private BigDecimal price;
private Integer stockCount;
/**
* 库存数量
*/
private Integer num;
/**
* 商品条形码
*/
private String barcode;
/**
* 商品图片
*/
@Field("item_image")
private String image;
/**
* 所属类目,叶子类目
*/
private Long categoryid;
/**
* 商品状态,1-正常,2-下架,3-删除
*/
private String status;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
@Field("item_updatetime")
private Date updateTime;
private String itemSn;
private BigDecimal costPirce;
private BigDecimal marketPrice;
private String isDefault;
@Field("item_goodsid")
private Long goodsId;
private String sellerId;
private String cartThumbnail;
@Field("item_category")
private String category;
@Field("item_brand")
private String brand;
private String spec;
@Field("item_seller")
private String seller;
@Dynamic
@Field("item_spec_*")
private Map<String,String> specMap;
public Map<String, String> getSpecMap() {
return specMap;
}
public void setSpecMap(Map<String, String> specMap) {
this.specMap = specMap;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title == null ? null : title.trim();
}
public String getSellPoint() {
return sellPoint;
}
public void setSellPoint(String sellPoint) {
this.sellPoint = sellPoint == null ? null : sellPoint.trim();
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getStockCount() {
return stockCount;
}
public void setStockCount(Integer stockCount) {
this.stockCount = stockCount;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
public String getBarcode() {
return barcode;
}
public void setBarcode(String barcode) {
this.barcode = barcode == null ? null : barcode.trim();
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image == null ? null : image.trim();
}
public Long getCategoryid() {
return categoryid;
}
public void setCategoryid(Long categoryid) {
this.categoryid = categoryid;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status == null ? null : status.trim();
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public String getItemSn() {
return itemSn;
}
public void setItemSn(String itemSn) {
this.itemSn = itemSn == null ? null : itemSn.trim();
}
public BigDecimal getCostPirce() {
return costPirce;
}
public void setCostPirce(BigDecimal costPirce) {
this.costPirce = costPirce;
}
public BigDecimal getMarketPrice() {
return marketPrice;
}
public void setMarketPrice(BigDecimal marketPrice) {
this.marketPrice = marketPrice;
}
public String getIsDefault() {
return isDefault;
}
public void setIsDefault(String isDefault) {
this.isDefault = isDefault == null ? null : isDefault.trim();
}
public Long getGoodsId() {
return goodsId;
}
public void setGoodsId(Long goodsId) {
this.goodsId = goodsId;
}
public String getSellerId() {
return sellerId;
}
public void setSellerId(String sellerId) {
this.sellerId = sellerId == null ? null : sellerId.trim();
}
public String getCartThumbnail() {
return cartThumbnail;
}
public void setCartThumbnail(String cartThumbnail) {
this.cartThumbnail = cartThumbnail == null ? null : cartThumbnail.trim();
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category == null ? null : category.trim();
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand == null ? null : brand.trim();
}
public String getSpec() {
return spec;
}
public void setSpec(String spec) {
this.spec = spec == null ? null : spec.trim();
}
public String getSeller() {
return seller;
}
public void setSeller(String seller) {
this.seller = seller == null ? null : seller.trim();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName());
sb.append(" [");
sb.append("Hash = ").append(hashCode());
sb.append(", id=").append(id);
sb.append(", title=").append(title);
sb.append(", sellPoint=").append(sellPoint);
sb.append(", price=").append(price);
sb.append(", stockCount=").append(stockCount);
sb.append(", num=").append(num);
sb.append(", barcode=").append(barcode);
sb.append(", image=").append(image);
sb.append(", categoryid=").append(categoryid);
sb.append(", status=").append(status);
sb.append(", createTime=").append(createTime);
sb.append(", updateTime=").append(updateTime);
sb.append(", itemSn=").append(itemSn);
sb.append(", costPirce=").append(costPirce);
sb.append(", marketPrice=").append(marketPrice);
sb.append(", isDefault=").append(isDefault);
sb.append(", goodsId=").append(goodsId);
sb.append(", sellerId=").append(sellerId);
sb.append(", cartThumbnail=").append(cartThumbnail);
sb.append(", category=").append(category);
sb.append(", brand=").append(brand);
sb.append(", spec=").append(spec);
sb.append(", seller=").append(seller);
sb.append(", serialVersionUID=").append(serialVersionUID);
sb.append("]");
return sb.toString();
}
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (that == null) {
return false;
}
if (getClass() != that.getClass()) {
return false;
}
Item other = (Item) that;
return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
&& (this.getTitle() == null ? other.getTitle() == null : this.getTitle().equals(other.getTitle()))
&& (this.getSellPoint() == null ? other.getSellPoint() == null : this.getSellPoint().equals(other.getSellPoint()))
&& (this.getPrice() == null ? other.getPrice() == null : this.getPrice().equals(other.getPrice()))
&& (this.getStockCount() == null ? other.getStockCount() == null : this.getStockCount().equals(other.getStockCount()))
&& (this.getNum() == null ? other.getNum() == null : this.getNum().equals(other.getNum()))
&& (this.getBarcode() == null ? other.getBarcode() == null : this.getBarcode().equals(other.getBarcode()))
&& (this.getImage() == null ? other.getImage() == null : this.getImage().equals(other.getImage()))
&& (this.getCategoryid() == null ? other.getCategoryid() == null : this.getCategoryid().equals(other.getCategoryid()))
&& (this.getStatus() == null ? other.getStatus() == null : this.getStatus().equals(other.getStatus()))
&& (this.getCreateTime() == null ? other.getCreateTime() == null : this.getCreateTime().equals(other.getCreateTime()))
&& (this.getUpdateTime() == null ? other.getUpdateTime() == null : this.getUpdateTime().equals(other.getUpdateTime()))
&& (this.getItemSn() == null ? other.getItemSn() == null : this.getItemSn().equals(other.getItemSn()))
&& (this.getCostPirce() == null ? other.getCostPirce() == null : this.getCostPirce().equals(other.getCostPirce()))
&& (this.getMarketPrice() == null ? other.getMarketPrice() == null : this.getMarketPrice().equals(other.getMarketPrice()))
&& (this.getIsDefault() == null ? other.getIsDefault() == null : this.getIsDefault().equals(other.getIsDefault()))
&& (this.getGoodsId() == null ? other.getGoodsId() == null : this.getGoodsId().equals(other.getGoodsId()))
&& (this.getSellerId() == null ? other.getSellerId() == null : this.getSellerId().equals(other.getSellerId()))
&& (this.getCartThumbnail() == null ? other.getCartThumbnail() == null : this.getCartThumbnail().equals(other.getCartThumbnail()))
&& (this.getCategory() == null ? other.getCategory() == null : this.getCategory().equals(other.getCategory()))
&& (this.getBrand() == null ? other.getBrand() == null : this.getBrand().equals(other.getBrand()))
&& (this.getSpec() == null ? other.getSpec() == null : this.getSpec().equals(other.getSpec()))
&& (this.getSeller() == null ? other.getSeller() == null : this.getSeller().equals(other.getSeller()));
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
result = prime * result + ((getTitle() == null) ? 0 : getTitle().hashCode());
result = prime * result + ((getSellPoint() == null) ? 0 : getSellPoint().hashCode());
result = prime * result + ((getPrice() == null) ? 0 : getPrice().hashCode());
result = prime * result + ((getStockCount() == null) ? 0 : getStockCount().hashCode());
result = prime * result + ((getNum() == null) ? 0 : getNum().hashCode());
result = prime * result + ((getBarcode() == null) ? 0 : getBarcode().hashCode());
result = prime * result + ((getImage() == null) ? 0 : getImage().hashCode());
result = prime * result + ((getCategoryid() == null) ? 0 : getCategoryid().hashCode());
result = prime * result + ((getStatus() == null) ? 0 : getStatus().hashCode());
result = prime * result + ((getCreateTime() == null) ? 0 : getCreateTime().hashCode());
result = prime * result + ((getUpdateTime() == null) ? 0 : getUpdateTime().hashCode());
result = prime * result + ((getItemSn() == null) ? 0 : getItemSn().hashCode());
result = prime * result + ((getCostPirce() == null) ? 0 : getCostPirce().hashCode());
result = prime * result + ((getMarketPrice() == null) ? 0 : getMarketPrice().hashCode());
result = prime * result + ((getIsDefault() == null) ? 0 : getIsDefault().hashCode());
result = prime * result + ((getGoodsId() == null) ? 0 : getGoodsId().hashCode());
result = prime * result + ((getSellerId() == null) ? 0 : getSellerId().hashCode());
result = prime * result + ((getCartThumbnail() == null) ? 0 : getCartThumbnail().hashCode());
result = prime * result + ((getCategory() == null) ? 0 : getCategory().hashCode());
result = prime * result + ((getBrand() == null) ? 0 : getBrand().hashCode());
result = prime * result + ((getSpec() == null) ? 0 : getSpec().hashCode());
result = prime * result + ((getSeller() == null) ? 0 : getSeller().hashCode());
return result;
}
}
package com.rsw.service;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.rsw.pojo.item.Item;
import com.rsw.util.Constants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.solr.core.SolrTemplate;
import org.springframework.data.solr.core.query.*;
import org.springframework.data.solr.core.query.result.*;
import java.util.*;
@Service
public class ItemSearchServiceImpl implements ItemSearchService {
@Autowired
private SolrTemplate solrTemplate;
@Autowired
private RedisTemplate redisTemplate;
@Override
public Map<String, Object> search(Map searchMap) {
//1. 根据查询参数, 到solr中分页, 高亮, 过滤, 排序查询
Map<String, Object> resultMap = highlightSearch(searchMap);
//2. 根据查询参数, 到solr中获取对应的分类结果集, 由于分类重复, 所以需要分组去重
List<String> groupCategoryList = findGroupCatagoryList(searchMap);
//3.判断传入的参数中是否有分类名称
String category = String.valueOf(searchMap.get("category"));
if (category != null && !"".equals(category)) {
//4.如果有分类参数,则根据分类查询对应的品牌集合和规格集合
Map map = findBrandAndSpecList(category);
resultMap.putAll(map);
} else {
//5.如果没有默认根据第一个分类查询对应的品牌集合和规格集合
if (groupCategoryList != null && groupCategoryList.size() > 0) {
Map map = findBrandAndSpecList(groupCategoryList.get(0));
resultMap.putAll(map);
}
}
return resultMap;
}
/**
* 根据分类名称从Redis中查询品牌和规格集合
*
* @param category
* @return
*/
private Map findBrandAndSpecList(String category) {
//1. 根据分类名称到redis中查询对应的模板id
Long templateId = (Long) redisTemplate.boundHashOps(Constants.CATEGORY_LIST_REDIS).get(category);
//2. 根据模板id到redis中查询对应的品牌集合
List<Map> brandList = (List<Map>) redisTemplate.boundHashOps(Constants.BRAND_LIST_REDIS).get(templateId);
//3. 根据模板id到reids中查询对应的规格集合
List<Map> specList = (List<Map>) redisTemplate.boundHashOps(Constants.SPEC_LIST_REDIS).get(templateId);
//4. 将品牌集合和规格集合数据封装到Map中返回
Map resultMap = new HashMap();
resultMap.put("brandList", brandList);
resultMap.put("specList", specList);
return resultMap;
}
/**
* 根据查询参数, 到solr中获取对应的分类结果集, 由于分类重复, 所以需要分组去重
*
* @param searchMap
* @return
*/
private List<String> findGroupCatagoryList(Map searchMap) {
List<String> resultList = new ArrayList<>();
//获取查询关键字
String keywords = String.valueOf(searchMap.get("keywords"));
//创建查询对象
Query query = new SimpleQuery();
//创建查询条件对象
Criteria criteria = new Criteria("item_keywords").is(keywords);
//将查询条件放入查询对象中
query.addCriteria(criteria);
//创建分组对象
GroupOptions groupOptions = new GroupOptions();
//设置根据分类域进行分组
groupOptions.addGroupByField("item_category");
//将分组对象放入查询对象中
query.setGroupOptions(groupOptions);
//分组查询分类集合
GroupPage<Item> items = solrTemplate.queryForGroupPage(query, Item.class);
//获取结果集中分类域的集合
GroupResult<Item> item_category = items.getGroupResult("item_category");
//获取分类域的实体集合
Page<GroupEntry<Item>> groupEntries = item_category.getGroupEntries();
//遍历实体集合得到实体对象
for (GroupEntry<Item> groupEntry : groupEntries) {
String groupCategory = groupEntry.getGroupValue();
resultList.add(groupCategory);
}
return resultList;
}
/**
* 根据关键字, 分页, 高亮, 过滤, 排序查询, 并且将查询结果返回
*
* @param searchMap 从页面传入进来的查询参数
* @return
*/
private Map<String, Object> highlightSearch(Map searchMap) {
//关键字
String keywords = String.valueOf(searchMap.get("keywords"));
//当前页
Integer pageNo = Integer.parseInt(String.valueOf(searchMap.get("pageNo")));
//每页数据量
Integer pageSize = Integer.parseInt(String.valueOf(searchMap.get("pageSize")));
//获取页面点击的分类过滤条件
String category = String.valueOf(searchMap.get("category"));
//获取页面点击的品牌过滤条件
String brand = String.valueOf(searchMap.get("brand"));
//获取页面点击的规格过滤条件
String spec = String.valueOf(searchMap.get("spec"));
//获取页面点击的价格区间过滤条件
String price = String.valueOf(searchMap.get("price"));
//获取页面排序字段
String sortField = String.valueOf(searchMap.get("sortField"));
//获取页面排序规则
String sortType = String.valueOf(searchMap.get("sort"));
HighlightQuery query = new SimpleHighlightQuery();
// Query query=new SimpleQuery();
//查询条件对象
Criteria criteria = new Criteria("item_keywords").is(keywords);
//设置高亮
HighlightOptions options = new HighlightOptions();
options.addField("item_title");
options.setSimplePrefix("<em style=\"color:red\">");
options.setSimplePostfix("</em>");
query.setHighlightOptions(options);
query.addCriteria(criteria);
if (pageNo == null || pageNo <= 0) {
pageNo = 1;
}
//添加分页
//分页条件
Integer start = (pageNo - 1) * pageSize;
query.setOffset(start);
query.setRows(pageSize);
//过滤查询
//分类
if (category != null && !"".equals(category)) {
FilterQuery filterQuery = new SimpleFilterQuery();
Criteria filterCriteria = new Criteria("item_category").is(category);
filterQuery.addCriteria(filterCriteria);
query.addFilterQuery(filterQuery);
}
//品牌
//根据品牌顾虑查询
if (brand != null && !"".equals(brand)) {
//创建过滤查询对象
FilterQuery filterQuery = new SimpleFilterQuery();
//创建条件对象
Criteria filterCriteria = new Criteria("item_brand").is(brand);
//将条件对象放入过滤对象中
filterQuery.addCriteria(filterCriteria);
//过滤对象放入查询对象中
query.addFilterQuery(filterQuery);
}
//规格
if (spec != null && !"".equals(spec)) {
Map<String, String> specMap = JSON.parseObject(spec, Map.class);
if (specMap != null && specMap.size() > 0) {
Set<Map.Entry<String, String>> entries = specMap.entrySet();
for (Map.Entry<String, String> entry : entries) {
FilterQuery filterQuery = new SimpleFilterQuery();
Criteria fiCriteria = new Criteria("item_spec" + entry.getKey()).is(entry.getValue());
filterQuery.addCriteria(fiCriteria);
query.addFilterQuery(filterQuery);
}
}
}
//价格
if (price != null && !"".equals(price)) {
String[] split = price.split("-");
if (split != null && split.length == 2) {
if (!"0".equals(split[0])) {
FilterQuery filterQuery = new SimpleFilterQuery();
Criteria filCriteria = new Criteria("item_price").greaterThanEqual(split[0]);
filterQuery.addCriteria(filCriteria);
query.addFilterQuery(filterQuery);
}
if (!"*".equals(split[1])) {
FilterQuery filterQuery = new SimpleFilterQuery();
Criteria filCriteria = new Criteria("item_price").lessThanEqual(split[1]);
filterQuery.addCriteria(filCriteria);
query.addFilterQuery(filterQuery);
}
}
}
//排序
if (sortField != null && sortType != null && !"".equals(sortField) && !"".equals(sortType)) {
if ("ASC".equals(sortType)) {
Sort sort = new Sort(Sort.Direction.ASC, "item_" + sortField);
query.addSort(sort);
}
if ("DESC".equals(sortType)) {
Sort sort = new Sort(Sort.Direction.DESC, "item_" + sortField);
query.addSort(sort);
}
}
HighlightPage<Item> items = solrTemplate.queryForHighlightPage(query, Item.class);
List<HighlightEntry<Item>> highlighted = items.getHighlighted();
List<Item> itemList = new ArrayList<>();
for (HighlightEntry<Item> itemHighlightEntry : highlighted) {
Item entity = itemHighlightEntry.getEntity();
List<HighlightEntry.Highlight> highlights = itemHighlightEntry.getHighlights();
if (highlights.size() > 0 && highlights != null) {
List<String> snipplets = highlights.get(0).getSnipplets();
if (snipplets != null && snipplets.size() > 0) {
String title = snipplets.get(0);
entity.setTitle(title);
}
}
itemList.add(entity);
}
Map<String, Object> resultMap = new HashMap<>();
//查询到的结果集
resultMap.put("rows", itemList);
//查询到的总页数
resultMap.put("totalPages", items.getTotalPages());
//查询到的总条数
resultMap.put("total", items.getTotalElements());
return resultMap;
}
}