1. 学习计划
1、Solr服务搭建
2、Solrj使用测试
3、把数据库中的数据导入索引库
4、搜索功能的实现
2. Solr服务搭建
2.1. Solr的环境
Solr是java开发。
需要安装jdk。
安装环境Linux。
需要安装Tomcat。
2.2. 搭建步骤
第一步:把solr 的压缩包上传到Linux系统
第二步:解压solr。
第三步:安装Tomcat,解压缩即可,然后复制一个到/usr/local/solr下面。
[root@localhost //]# mkdir /usr/local/solr
[root@localhost heima]# cp -rf apache-tomcat-7 /usr/local/solr
第四步:把solr部署到Tomcat的webapps下。
第五步:启动Tomcat会自动解压缩war包。
第六步:把/heima/solr-4.10.3/example/lib/ext目录下的所有的jar包,添加到solr工程中。
[root@localhost ext]# cp * /usr/local/solr/tomcat/webapps/solr/WEB-INF/lib/
第七步:创建一个solrhome。/example/solr目录就是一个solrhome。复制此目录到/usr/local/solr/solrhome
[root@localhost example]# cp -r solr /usr/local/solr/solrhome
第八步:关联solr及solrhome。需要修改solr工程的web.xml文件。
第九步:启动Tomcat
访问显示以下界面即表示solr安装成功
2.3. 配置业务域
schema.xml中定义
1、商品Id
2、商品标题
3、商品卖点
4、商品价格
5、商品图片
6、分类名称
创建步骤:
第一步:把中文分析器添加到工程中。
1、把IKAnalyzer2012FF_u1.jar添加到solr工程的lib目录下
2、把扩展词典、配置文件放到solr工程的WEB-INF/classes目录下。
第二步:配置一个FieldType,制定使用IKAnalyzer
修改schema.xml文件
修改Solr的schema.xml文件,添加FieldType:
<fieldType name="text_ik" class="solr.TextField">
<analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/>
</fieldType>
第三步:配置业务域,type制定使用自定义的FieldType。
设置业务系统Field
<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"/>
第四步:重启tomcat
3. 搜索工程搭建
要实现搜索功能,需要搭建solr服务、搜索服务工程、搜索系统
搜索服务工程搭建
可以参考e3-manager创建。
e3-search(聚合工程pom)
|--e3-search-interface(jar)
|--e3-search-Service(war)
e3-search-web(war)
查东西不需要事务,所以不需要applicationContext-trans.xml
4. 使用solrJ管理索引库
使用SolrJ可以实现索引库的增删改查操作。
4.1.添加文档
第一步:把solrJ的jar包添加到工程中。
<!-- solr客户端 -->
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
</dependency>
第二步:创建一个SolrServer,使用HttpSolrServer创建对象。
第三步:创建一个文档对象SolrInputDocument对象。
第四步:向文档中添加域。必须有id域,域的名称必须在schema.xml中定义。
第五步:把文档添加到索引库中。
第六步:提交。
@Test
public void addDocument() throws Exception {
// 第一步:把solrJ的jar包添加到工程中。
// 第二步:创建一个SolrServer,使用HttpSolrServer创建对象。
SolrServer solrServer = new HttpSolrServer("http://192.168.25.128:8090/solr");
// 第三步:创建一个文档对象SolrInputDocument对象。
SolrInputDocument document = new SolrInputDocument();
// 第四步:向文档中添加域。必须有id域,域的名称必须在schema.xml中定义。
document.addField("id", "1");
document.addField("item_title", "测试商品");
document.addField("item_price", "199");
// 第五步:把文档添加到索引库中。
solrServer.add(document);
// 第六步:提交。
solrServer.commit();
}
4.2.删除文档
4.2.1. 根据id删除
第一步:创建一个SolrServer对象。
第二步:调用SolrServer对象的根据id删除的方法。
第三步:提交。
@Test
public void deleteDocumentById() throws Exception {
// 第一步:创建一个SolrServer对象。
SolrServer solrServer = new HttpSolrServer("http://192.168.25.128:8090/solr");
// 第二步:调用SolrServer对象的根据id删除的方法。
//solrServer.deleteById("1");
solrServer.deleteById("id:1");
// 第三步:提交。
solrServer.commit();
}
5. 把商品数据导入到索引库中
5.1.Dao层
5.1.1. Sql语句
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.id
WHERE a.`status`=1;
需要自己创建Mapper文件。
5.1.2. 创建对应数据集的pojo
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;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getSell_point() {
return sell_point;
}
public void setSell_point(String sell_point) {
this.sell_point = sell_point;
}
public long getPrice() {
return price;
}
public void setPrice(long price) {
this.price = price;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public String getCategory_name() {
return category_name;
}
public void setCategory_name(String category_name) {
this.category_name = category_name;
} public String[] getImages() {
if (image != null && !"".equals(image)) {
String[] strings = image.split(",");
return strings;
}
return null;
} }
5.1.3. 接口定义
public interface ItemMapper { List<SearchItem> getItemList();
}
5.1.4. Mapper映射文件
<?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="cn.e3mall.search.mapper.ItemMapper" > <select id="getItemList" resultType="cn.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.id
WHERE a.`status`=1
</select>
</mapper>
5.2.Service层
5.2.1. 功能分析
1、查询所有商品数据。
2、循环把商品数据添加到索引库。使用solrJ实现。
3、返回成功。返回E3Result
参数:无
返回值:E3Result
5.2.2. solrJ添加索引库
1、把solrJ的jar包添加到工程。
2、创建一个SolrServer对象。创建一个和sorl服务的连接。HttpSolrServer。
3、创建一个文档对象。SolrInputDocument。
4、向文档对象中添加域。必须有一个id域。而且文档中使用的域必须在schema.xml中定义。
5、把文档添加到索引库
6、Commit。
5.2.3. 代码实现
@Service
public class SearchServiceImpl implements SearchService{ @Autowired
private ItemMapper itemMapper;
@Autowired
private SolrServer solrServer; @Override
public E3Result importItemAlls() {
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) {
e.printStackTrace();
return E3Result.build(500, "商品导入失败");
}
} }
5.2.4. applicationContext-solr.xml的配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd"> <bean id="httpSolrServer" class="org.apache.solr.client.solrj.impl.HttpSolrServer">
<constructor-arg index="0" value="http://192.168.25.128:8090/solr"/>
</bean> </beans>
5.2.5. 发布服务
applicationContext-service.xml
5.3. 表现层
在e3-manager-web中导入searchService包
后台管理工程中调用商品导入服务。
<dubbo:reference interface="cn.e3mall.search.service.SearchService" id="searchServiceImpl" />
5.3.1. 功能分析
请求的url:/index/item/import
响应的结果:json数据。可以使用E3Result
5.3.2. Controller
@Controller
public class SearchItemController {
@Autowired
private SearchService searchItemService; @RequestMapping("/index/item/import")
@ResponseBody
public E3Result impotItemIndex() {
E3Result result = searchItemService.importItemAlls();
return result;
} }
5.4. 解决Mapper映射文件不存在异常
<!-- 如果不添加此节点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>
6. 搜索功能实现
6.1. 使用sorlJ 查询索引库
//使用solrJ实现查询
@Test
public void queryDocument() throws Exception {
//创建一个SolrServer对象
SolrServer solrServer = new HttpSolrServer("http://192.168.25.128:8090/solr");
//创建一个查询对象,可以参考solr的后台的查询功能设置条件
SolrQuery query = new SolrQuery();
//设置查询条件
query.setQuery("阿尔卡特");
// query.set("q","阿尔卡特");
//设置分页条件
query.setStart(1);
query.setRows(2);
//开启高亮
query.setHighlight(true);
query.addHighlightField("item_title");
query.setHighlightSimplePre("<em>");
query.setHighlightSimplePost("</em>");
//设置默认搜索域
query.set("df", "item_title");
//执行查询,得到一个QueryResponse对象。
QueryResponse queryResponse = solrServer.query(query);
//取查询结果总记录数
SolrDocumentList solrDocumentList = queryResponse.getResults();
System.out.println("查询结果总记录数:" + solrDocumentList.getNumFound());
//取查询结果
Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting();
for (SolrDocument solrDocument : solrDocumentList) {
System.out.println(solrDocument.get("id"));
//取高亮后的结果
List<String> list = highlighting.get(solrDocument.get("id")).get("item_title");
String title= "";
if (list != null && list.size() > 0) {
//取高亮后的结果
title = list.get(0);
} else {
title = (String) solrDocument.get("item_title");
}
System.out.println(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"));
} }
6.2. 功能分析
把搜索结果页面添加到工程中。
请求的url:/search
请求的方法:GET
参数:
keyword:查询条件
Page:页码。如果没有此参数,需要给默认值1。
返回的结果:
1)商品列表
2)总页数
3)总记录数
使用jsp展示,返回逻辑视图。
商品列表使用:SearchItem表示。
需要把查询结果封装到一个pojo中:
1)商品列表List<SearchItem>
2)总页数。Int totalPages。总记录数/每页显示的记录数向上取整。把每页显示的记录是配置到属性文件中。
3)总记录数。Int recourdCount
public class SearchResult implements Serializable {
private List<SearchItem> itemList;
private int totalPages;
private int recourdCount;
}
6.3. Dao层
跟据查询条件查询索引库,返回对应的结果。
参数:SolrQuery
返回结果:SearchResult
@Repository
public class SearchDao { @Autowired
private SolrServer solrServer; public SearchResult search(SolrQuery query) throws Exception {
//根据查询条件查询索引库
QueryResponse queryResponse = solrServer.query(query);
//取查询结果总记录数
SolrDocumentList solrDocumentList = queryResponse.getResults();
long numFound = solrDocumentList.getNumFound();
//创建一个返回结果对象
SearchResult result = new SearchResult();
result.setRecourdCount((int) numFound);
//创建一个商品列表对象
List<SearchItem> itemList = new ArrayList<>();
//取商品列表
//取高亮后的结果
Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting();
for (SolrDocument solrDocument : solrDocumentList) {
//取商品信息
SearchItem searchItem = new SearchItem();
searchItem.setCategory_name((String) solrDocument.get("item_category_name"));
searchItem.setId((String) solrDocument.get("id"));
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> list = highlighting.get(solrDocument.get("id")).get("item_title");
String itemTitle = "";
if (list != null && list.size() > 0) {
itemTitle = list.get(0);
} else {
itemTitle = (String) solrDocument.get("item_title");
}
searchItem.setTitle(itemTitle);
//添加到商品列表
itemList.add(searchItem);
}
//把列表添加到返回结果对象中
result.setItemList(itemList);
return result;
}
}
6.4. Service层
需要有一个接口一个实现类,需要对外发布服务。
参数:String keyWord
int page
int rows
返回值:SearchResult
业务逻辑:
1)根据参数创建一个查询条件对象。需要指定默认搜索域,还需要配置高亮显示。
2)调用dao查询。得到一个SearchResult对象
3)计算查询总页数,每页显示记录数就是rows参数。
@Autowired
private SearchDao searchDao; @Override
public SearchResult search(String keyWord, int page, int rows) throws Exception {
//创建一个SolrQuery对象
SolrQuery query = new SolrQuery();
//设置查询条件
query.setQuery(keyWord);
//设置分页条件
query.setStart((page - 1) * rows);
//设置rows
query.setRows(rows);
//设置默认搜索域
query.set("df", "item_title");
//设置高亮显示
query.setHighlight(true);
query.addHighlightField("item_title");
query.setHighlightSimplePre("<em style=\"color:red\">");
query.setHighlightSimplePost("</em>");
//执行查询
SearchResult searchResult = searchDao.search(query);
//计算总页数
int recourdCount = searchResult.getRecourdCount();
int pages = recourdCount / rows;
if (recourdCount % rows > 0) pages++;
//设置到返回结果
searchResult.setTotalPages(pages);
return searchResult;
}
发布服务
6.5. 表现层
6.5.1. 引用服务
在e3-search-web中添加接口依赖
Springmvc.xml
<dubbo:reference interface="cn.e3mall.search.service.SearchService" id="searchService" />
6.5.2. Controller
请求的url:/search
请求的方法:GET
参数:
keyword:查询条件
Page:页码。如果没有此参数,需要给默认值1。
返回的结果:
使用jsp展示,返回逻辑视图。
@Controller
public class SearchController { @Autowired
private SearchService searchService; @Value("${PAGE_ROWS}")
private Integer PAGE_ROWS; @RequestMapping("/search")
public String search(String keyword,@RequestParam(defaultValue="1") Integer page, Model model) throws Exception {
//需要转码
keyword = new String(keyword.getBytes("iso8859-1"), "utf-8");
//调用Service查询商品信息
SearchResult result = searchService.search(keyword, page, PAGE_ROWS);
//把结果传递给jsp页面
model.addAttribute("query", keyword);
model.addAttribute("totalPages", result.getTotalPages());
model.addAttribute("recourdCount", result.getRecourdCount());
model.addAttribute("page", page);
model.addAttribute("itemList", result.getItemList());
//返回逻辑视图
return "search";
} }
6.6.页面效果
在搜索框中,输入要买的手机
总结
问题:org.springframework.beans.factory.NoSuchBeanDefinitionException
解决:mapper扫描没有配置好,我们的mapper有多个路劲!
问题:Waiting server-side response timeout.
解决:发布服务时,记得加上timeout="600000",不然采取的是默认的1000毫秒
<dubbo:service interface="cn.e3mall.search.service.SearchService" ref="searchServiceImpl" timeout="600000"/>