Fork me on GitHub

七.宜立方商城——Solr服务搭建

课程计划

1
2
3
4
1、Solr服务搭建
2、Solrj使用测试
3、把数据库中的数据导入索引库
4、搜索功能的实现

Solr服务搭建

Solr的环境

1
2
3
4
Solr是java开发。
需要安装jdk。
安装环境Linux。
需要安装Tomcat。

搭建步骤

  1. 把solr 的压缩包上传到Linux系统
  2. 解压solr。

image

image

image

image

image

  1. 安装Tomcat,解压缩即可。
  1. 把solr部署到Tomcat下。

image
image

  1. 解压缩war包。启动Tomcat解压。

image

image

image

  1. 把/root/solr-4.10.3/example/lib/ext目录下的所有的jar包,添加到solr工程中。

image

image

  1. 创建一个solrhome。/example/solr目录就是一个solrhome。复制此目录到/usr/local/solr/solrhome

image

image

  1. 关联solr及solrhome。需要修改solr工程的web.xml文件。
  1. 启动Tomcat

这里有个坑,你把zookeeper最好启动,因为这个里面有我之前部署的zookeeper的监控中心,就那个dubbo-admin,所以先把zookeeper开启,就不会报错了

访问solr服务
image

访问之前的dubbo-admin
image

配置业务域

schema.xml中定义

1
2
3
4
5
6
1、商品Id
2、商品标题
3、商品卖点
4、商品价格
5、商品图片
6、分类名称

创建对应的业务域。需要制定中文分析器。

创建步骤:

  1. 把中文分析器添加到工程中。

    ①.把IKAnalyzer2012FF_u1.jar添加到solr工程的lib目录下

    ②.把扩展词典、配置文件放到solr工程的WEB-INF/classes目录下。

image

image

image

image

image

  1. 配置一个FieldType,制定使用IKAnalyzer

    ①.修改schema.xml文件

    ②.修改Solr的schema.xml文件,添加FieldType:

image

image

  1. 配置业务域,type制定使用自定义的FieldType。

    设置业务系统Field
1
2
3
4
5
6
7
8
9
10
<field name="item_title" type="text_ik" indexed="true" stored="true"/>
<field name="item_sell_point" type="text_ik" indexed="true" stored="true"/>
<field name="item_price" type="long" indexed="true" stored="true"/>
<field name="item_image" type="string" indexed="false" stored="true" />
<field name="item_category_name" type="string" 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_sell_point" dest="item_keywords"/>
<copyField source="item_category_name" dest="item_keywords"/>

image

image

  1. 重启tomcat

image

image

搜索工程搭建

image

要实现搜索功能,需要搭建solr服务、搜索服务工程、搜索系统

搜索服务工程搭建

image

1
2
3
4
5
可以参考e3-manager创建。
e3-search(聚合工程pom)
|--e3-search-interface(jar)
|--e3-search-Service(war)
e3-search-web(war)

使用solrJ管理索引库

使用SolrJ可以实现索引库的增删改查操作。

添加文档

第一步:把solrJ的jar包添加到工程中。

image

第二步:创建一个SolrServer,使用HttpSolrServer创建对象。

image

image

第三步:创建一个文档对象SolrInputDocument对象。

第四步:向文档中添加域。必须有id域,域的名称必须在schema.xml中定义。

image

第五步:把文档添加到索引库中。

image

第六步:提交。

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 添加文档
* @throws Exception
*/
@Test
public void addDocument() throws Exception{
//创建一个SolrServer对象,创建一个连接。参数:solr服务的url(地址)
SolrServer solrServer = new HttpSolrServer("http://192.168.25.128:8080/solr/collection1");

//创建一个文档对象SolrInputDocument
SolrInputDocument document = new SolrInputDocument();

//向文档对象中。文档中必须包含一个id域,所有的域的名称必须在schema.xml中定义
document.addField("id","doc01");
document.addField("item_title","测试商品01");
document.addField("item_price",1000);

//把文档写入索引库
solrServer.add(document);

//提交
solrServer.commit();
}

删除文档

根据id删除

第一步:创建一个SolrServer对象。

第二步:调用SolrServer对象的根据id删除的方法。

第三步:提交。

image

image

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 删除文档
* @throws Exception
*/
@Test
public void deleteDocument() throws Exception{
SolrServer solrServer = new HttpSolrServer("http://192.168.25.128:8080/solr/collection1");

//删除文档
solrServer.deleteById("doc01");
//solrServer.deleteByQuery("id:doc01");

//提交
solrServer.commit();
}
根据查询删除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    /**
* 删除文档
* @throws Exception
*/
@Test
public void deleteDocument() throws Exception{
SolrServer solrServer = new HttpSolrServer("http://192.168.25.128:8080/solr/collection1");

//删除文档
solrServer.deleteByQuery("id:doc01");

//提交
solrServer.commit();
}
}

把商品数据导入到索引库中

Dao层

Sql语句

1
2
3
4
5
6
7
8
9
10
11
select
a.id,
a.title,
a.sell_point,
a.price,
a.image,
b.name category_name
from tb_item a
LEFT JOIN tb_item_cat b
on a.cid = b.idl
where a.status = 1;

需要自己创建Mapper文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="yp.e3mall.search.mapper.ItemMapper">
<select id="getItemList" resultType="yp.e3mall.common.pojo.SearchItem">
select
a.id,
a.title,
a.sell_point,
a.price,
a.image,
b.name category_name
from tb_item a
LEFT JOIN tb_item_cat b
on a.cid = b.idl
where a.status = 1;
</select>
</mapper>

创建对应数据集的pojo

1
2
3
4
5
6
7
8
public class SearchItem implements Serializable{
private String id;
private String title;
private String sell_point;
private long price;
private String image;
private String category_name;
}

image

接口定义

1
2
3
4
public interface ItemMapper {

List<SearchItem> getItemList();
}

Mapper映射文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="yp.e3mall.search.mapper.ItemMapper">
<select id="getItemList" resultType="yp.e3mall.common.pojo.SearchItem">
select
a.id,
a.title,
a.sell_point,
a.price,
a.image,
b.name category_name
from tb_item a
LEFT JOIN tb_item_cat b
on a.cid = b.idl
where a.status = 1;
</select>
</mapper>

image

image

image

image

Service层

功能分析

1
2
3
4
5
6
1、查询所有商品数据。
2、循环把商品数据添加到索引库。使用solrJ实现。
3、返回成功。返回E3Result

参数:无
返回值:E3Result

solrJ添加索引库

1
2
3
4
5
6
1、把solrJ的jar包添加到工程。
2、创建一个SolrServer对象。创建一个和sorl服务的连接。HttpSolrServer。
3、创建一个文档对象。SolrInputDocument。
4、向文档对象中添加域。必须有一个id域。而且文档中使用的域必须在schema.xml中定义。
5、把文档添加到索引库
6、Commit。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* @author RickYinPeng
* @ClassName SearchItemServiceImpl
* @Description
* @date 2018/11/22/0:00
*/
@Service
public class SearchItemServiceImpl implements SearchItemService {

@Autowired
private ItemMapper itemMapper;

@Autowired
private SolrServer solrServer;

@Override
public E3Result importAllItems() {
try {
//查询商品列表
List<SearchItem> itemList = itemMapper.getItemList();
//遍历商品列表
for (SearchItem searchItem : itemList) {
//创建文档对象
SolrInputDocument document = new SolrInputDocument();
//向文档对象中添加域
document.addField("id", searchItem.getId());
document.addField("item_title", searchItem.getTitle());
document.addField("item_sell_point", searchItem.getSell_point());
document.addField("item_price", searchItem.getPrice());
document.addField("item_image", searchItem.getImage());
document.addField("item_category_name", searchItem.getCategory_name());
//把文档对象写入索引库
solrServer.add(document);
}
//提交
solrServer.commit();

//返回导入成功
return E3Result.ok();
} catch (Exception e) {
return E3Result.build(500, "数据导入失败");
}
}
}

此时我们的spring容器中还没有SolrServer对象,所以得配置一个

SolrServer的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.2.xsd ">

<bean id="httpSolrServer" class="org.apache.solr.client.solrj.impl.HttpSolrServer" >
<constructor-arg index="0" value="http://192.168.25.128:8080/solr/"/>
</bean>
</beans>

image

发布服务

image

表现层

后台管理工程中调用商品导入服务。

image

image

功能分析

image

image

image

请求的url:/index/item/import

响应的结果:json数据。可以使用E3Result

Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* @author RickYinPeng
* @ClassName SearchItemController
* @Description 导入商品数据到索引库
* @date 2018/11/22/1:07
*/
@Controller
public class SearchItemController {

@Autowired
private SearchItemService searchItemService;

@RequestMapping("/index/item/import")
@ResponseBody
public E3Result importItemList(){
E3Result e3Result = searchItemService.importAllItems();
return e3Result;
}
}

image

image

解决Mapper映射文件不存在异常

在e3-search-service的pom文件中需要添加资源配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- 如果不添加此节点mybatis的mapper.xml文件都会被漏掉。 -->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>

image

成功导入!!!!

六.搜索功能实现

6.1 搭建前台搜索工程

image

image

image

image

image

image

使用sorlJ 查询索引库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Test
public void queryIndex() throws Exception{
//创建一个SolrServer对象
SolrServer solrServer = new HttpSolrServer("http://192.168.25.128:8080/solr/collection1");

//创建一个SolrQuery对象
SolrQuery solrQuery = new SolrQuery();

//设置查询条件
//solrQuery.setQuery("*:*");
solrQuery.set("q","*:*");

//执行查询,QueryResponse对象
QueryResponse queryResponse = solrServer.query(solrQuery);

//取文档列表,取查询结果的总记录数
SolrDocumentList results = queryResponse.getResults();
System.out.println("查询结果总记录数:"+results.getNumFound());

//遍历文档列表,从文档取域的内容
for(SolrDocument solrDocument: results){
System.out.println(solrDocument.get("id"));
System.out.println(solrDocument.get("item_title"));
System.out.println(solrDocument.get("item_sell_point"));
System.out.println(solrDocument.get("item_price"));
System.out.println(solrDocument.get("item_image"));
System.out.println(solrDocument.get("item_category_name"));
System.out.println("--------------------------------------------------");
}


}

image

image

image

image

image

image

代码高亮显示

image

image

image

image

image

image

image

image

image

image

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@Test
public void queryIndexFuza() throws Exception{
//创建一个SolrServer对象
SolrServer solrServer = new HttpSolrServer("http://192.168.25.128:8080/solr/collection1");

//创建一个SolrQuery对象
SolrQuery solrQuery = new SolrQuery();

//设置查询条件
solrQuery.setQuery("手机");
solrQuery.setStart(0);
solrQuery.setRows(20);
solrQuery.set("df","item_title");
solrQuery.setHighlight(true);
solrQuery.addHighlightField("item_title");
solrQuery.setHighlightSimplePre("<em>");
solrQuery.setHighlightSimplePost("</em>");

//执行查询,QueryResponse对象
QueryResponse queryResponse = solrServer.query(solrQuery);

//取文档列表,取查询结果的总记录数
SolrDocumentList results = queryResponse.getResults();
System.out.println("查询结果总记录数:"+results.getNumFound());

//取高亮
Map<String, Map<String, List<String>>> highlighting =
queryResponse.getHighlighting();

//遍历文档列表,从文档取域的内容
for(SolrDocument solrDocument: results){
System.out.println(solrDocument.get("id"));

List<String> strings = highlighting.get(solrDocument.get("id")).get("item_title");
String title = "";
if(strings!=null && strings.size()>0){
title = strings.get(0);
}else{
title = (String) solrDocument.get("item_title");
}
System.out.println(title);
System.out.println(solrDocument.get("item_title"));
System.out.println(solrDocument.get("item_sell_point"));
System.out.println(solrDocument.get("item_price"));
System.out.println(solrDocument.get("item_image"));
System.out.println(solrDocument.get("item_category_name"));
System.out.println("--------------------------------------------------");
}
}

功能分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
把搜索结果页面添加到工程中。

请求的url:/search
请求的方法:GET
参数:
keyword:查询条件
Page:页码。如果没有此参数,需要给默认值1。
返回的结果:
1)商品列表
2)总页数
3)总记录数
使用jsp展示,返回逻辑视图。

商品列表使用:SearchItem表示。
需要把查询结果封装到一个pojo中:
1)商品列表List<SearchItem>
2)总页数。Int totalPages。总记录数/每页显示的记录数向上取整。把每页显示的记录是配置到属性文件中。
3)总记录数。Int recourdCount
1
2
3
4
5
public class SearchResult implements Serializable {
private List<SearchItem> itemList;
private int totalPages;
private int recourdCount;
}

Dao层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
* @author RickYinPeng
* @ClassName SearchDao
* @Description 商品搜索Dao
* @date 2018/11/23/0:31
*/
@Repository
public class SearchDao {

@Autowired
private SolrServer solrServer;

/**
* 根据查询条件查询索引
* @param solrQuery
* @return
*/
public SearchResult search(SolrQuery solrQuery) throws SolrServerException {
//根据query查询索引库
QueryResponse queryResponse = solrServer.query(solrQuery);
//取查询结果。
SolrDocumentList solrDocumentList = queryResponse.getResults();
//取查询结果总记录数
long numFound = solrDocumentList.getNumFound();

SearchResult searchResult = new SearchResult();
searchResult.setRecordCount(numFound);

//取商品列表,需要取高亮显示
Map<String, Map<String, List<String>>> highlighting =
queryResponse.getHighlighting();

List<SearchItem> searchItems = new ArrayList<>();
for (SolrDocument solrDocument:solrDocumentList){
SearchItem searchItem = new SearchItem();
searchItem.setId((String) solrDocument.get("id"));
searchItem.setCategory_name((String) solrDocument.get("item_category_name"));
searchItem.setImage((String) solrDocument.get("item_image"));
searchItem.setPrice((Long) solrDocument.get("item_price"));
searchItem.setSell_point((String) solrDocument.get("item_sell_point"));

//取高亮显示
List<String> strings = highlighting.get(solrDocument.get("id")).get("item_title");
String title = "";
if(strings!=null && strings.size()>0){
title = strings.get(0);
}else{
title = (String) solrDocument.get("item_title");
}

searchItem.setTitle(title);
//添加到商品列表
searchItems.add(searchItem);
}
searchResult.setItemList(searchItems);

//返回结果
return searchResult;
}

}

Service层

1
2
3
4
5
6
7
8
9
需要有一个接口一个实现类,需要对外发布服务。
参数:String keyWord
int page
int rows
返回值:SearchResult
业务逻辑:
1)根据参数创建一个查询条件对象。需要指定默认搜索域,还需要配置高亮显示。
2)调用dao查询。得到一个SearchResult对象
3)计算查询总页数,每页显示记录数就是rows参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
* @author RickYinPeng
* @ClassName SearchServiceImpl
* @Description 商品搜索Service
* @date 2018/11/23/0:52
*/
@Service
public class SearchServiceImpl implements SearchService{

@Autowired
private SearchDao searchDao;

@Override
public SearchResult search(String keyword, int page, int rows) throws SolrServerException {
//创建一个SolrQuery对象
SolrQuery solrQuery = new SolrQuery();
//设置查询条件
solrQuery.setQuery(keyword);

//设置分页条件
if(page <= 0){
page = 1;
}
solrQuery.setStart((page-1)*rows);
solrQuery.setRows(rows);

//设置默认搜索域
solrQuery.set("df","item_title");

//开启高亮显示
solrQuery.setHighlight(true);
solrQuery.addHighlightField("item_title");
solrQuery.setHighlightSimplePre("<em style=\"color:red\">");
solrQuery.setHighlightSimplePost("</em>");

//调用Dao
SearchResult search = searchDao.search(solrQuery);

//计算总页数
long recordCount = search.getRecordCount();
int totalPage = (int) (recordCount/rows);
if(recordCount%rows>0){
totalPage++;
}
search.setTotalPages(totalPage);
//返回结果

return search;
}
}

发布服务

表现层

引用服务

Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* @author RickYinPeng
* @ClassName SearchController
* @Description 商品搜索Controller
* @date 2018/11/23/17:29
*/
@Controller
public class SearchController {

@Autowired
private SearchService searchService;

@RequestMapping("/search")
public String searchItemList(String keyword,
@RequestParam(defaultValue = "1") Integer page,
Model model
) throws Exception {

keyword = new String(keyword.getBytes("iso-8859-1"),"utf-8");

//查询商品列表
SearchResult searchResult = searchService.search(keyword, page, 60);

//将商品列表传入页面
model.addAttribute("query",keyword);
model.addAttribute("totalPages",searchResult.getTotalPages());
model.addAttribute("page",page);
model.addAttribute("recourdCount",searchResult.getRecordCount());
model.addAttribute("itemList",searchResult.getItemList());

//返回逻辑视图
return "search";
}
}

image

image

image

image

image

image

image

image

image

image

image

image