商城项目(八)
搜索系统搭建搜索功能需要发布服务共pc端、移动端使用。根据关键词搜索,得到json格式的搜索结果。
创建一个搜索系统,发布搜索服务。
系统架构
http://i2.muimg.com/1949/a3149f60f2737e92.png
创建搜索系统
参考taotao-rest创建。
框架整合
Solr服务的搭建
CentOS单机版安装
[*]第一步:安装jdk、安装tomcat
[*]第二步:解压solr压缩包。
[*]第三步:把dist/solr-4.10.3.war部署到tomcat下。
[*] 第四步:解压缩war包。启动tomcat解压。
tail -f logs/catalina.out 看日志
[*]第五步:需要把/root/solr-4.10.3/example/lib/ext目录下的所有的jar包添加到solr工程中。
[*]第六步:创建solrhome。把/root/solr-4.10.3/example/solr文件夹复制一份作为solrhome。
[*] 第七步:告诉solr服务solrhome的位置。需要修改web.xml
<env-entry> <env-entry-name>solr/home</env-entry-name>
<env-entry-value>/usr/local/solr/solrhome</env-entry-value>
<env-entry-type>java.lang.String</env-entry-type>
</env-entry>
[*]第八步:启动tomcat。
./startup.sh
http://i2.muimg.com/1949/3d606fe30312a736.png
配置中文分析器、自定义业务域
分析器使用IKAnalyzer。
使用方法:
[*]第一步:把IKAnalyzer依赖的jar包添加到solr工程中。把分析器使用的扩展词典添加到classpath中。
cp .jar /usr/local/solr/tomcat/webapps/solr/WEB-INF/lib
cp ext_stopword.dic IKAnalyzer.cfg.xml mydict.dic /usr/local/solr/tomcat/webapps/solr/WEB-INF/classes
[*]
[*]第二步:需要自定义一个FieldType。/usr/local/solr/solrhome/collection1/conf/Schema.xml中定义。可以在FieldType中指定中文分析器。
<fieldType name="text_ik"> <analyzer/>
</fieldType>
[*]第三步:自定义域。指定域的类型为自定义的FieldType。
Sql语句:
SELECT a.id,
a.title,
a.sell_point,
a.price,
a.image,
b.`name` category_name,
c.item_desc
FROM
tb_item a
LEFT JOIN tb_item_cat b ON a.cid = b.id
LEFT JOIN tb_item_desc c ON a.id = c.item_id
WHERE
a.`status` = 1
加入/usr/local/solr/solrhome/collection1/conf/Schema.xml中
<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_desc" type="text_ik" indexed="true" stored="false" />
<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"/>
<copyField source="item_desc" dest="item_keywords"/>
[*]第四步:重新启动tomcat
索引库中导入数据
dataimport插件
Solrj的使用
测试类
public>
@Test
public void testSolrJ() throws Exception {
// 创建连接
SolrServer solrServer = new HttpSolrServer("http://192.168.204.132:8080/solr");
// 创建一个文档对象
SolrInputDocument document = new SolrInputDocument();
document.addField("id", "solrtest01");
document.addField("item_title", "测试商品");
document.addField("item_sell_point", "卖点");
// 添加到索引库
solrServer.add(document);
// 提交
solrServer.commit();
}
@Test
public void testQuery() throws Exception {
// 创建连接
SolrServer solrServer = new HttpSolrServer("http://192.168.204.132:8080/solr");
// 创建一个查询对象
SolrQuery solrQuery = new SolrQuery();
solrQuery.setQuery("*:*");
// 执行查询
QueryResponse queryResponse = solrServer.query(solrQuery);
// 取查询结果
SolrDocumentList solrDocumentList = queryResponse.getResults();
for (SolrDocument solrDocument : solrDocumentList) {
System.out.println(solrDocument.get("id"));
System.out.println(solrDocument.get("item_title"));
System.out.println(solrDocument.get("item_sell_point"));
}
}
}
导入数据
清除原来的数据
http://i4.buimg.com/1949/a07ec85ed55c9a55.png
分析
从数据库中根据sql语句查询数据,遍历数据创建文档对象,把文档对象写入索引库。
Dao层
Sql语句:
SELECT a.id,
a.title,
a.sell_point,
a.price,
a.image,
b.`name` category_name,
c.item_desc
FROM
tb_item a
LEFT JOIN tb_item_cat b ON a.cid = b.id
LEFT JOIN tb_item_desc c ON a.id = c.item_id
WHERE
a.`status` = 1
创建一个Mapper文件
public interface ItemMapper {
List<SearchItem> getItemList();
}
<mapper namespace="com.taotao.search.mapper.ItemMapper" > <select resultType="com.taotao.search.pojo.SearchItem">
SELECT
a.id,
a.title,
a.sell_point,
a.price,
a.image,
b.`name` category_name,
c.item_desc
FROM
tb_item a
LEFT JOIN tb_item_cat b ON a.cid = b.id
LEFT JOIN tb_item_desc c ON a.id = c.item_id
WHERE
a.`status` = 1
</select>
</mapper>
<bean> <property name="basePackage" value="com.taotao.mapper, com.taotao.search.mapper" />
</bean>
Service层
取商品列表,遍历列表,创建文档对象,把文档对象写入索引库。
要操作索引库需要SolrServer对象,可以把SolrServer放到spring容器中,注入到Service。
<!--单机版solr客户端-->
<bean>
<constructor-arg name="baseURL" value="http://192.168.204.132:8080/solr"></constructor-arg>
</bean>
@Service
public>
@Autowired
private SolrServer solrServer;
@Autowired
private ItemMapper itemMapper;
@Override
public TaotaoResult importItems() throws Exception {
// 查询数据库获取商品列表
List<SearchItem> searchItems = itemMapper.getItemList();
// 遍历列表
for (SearchItem item : searchItems) {
// 创建文档对象
SolrInputDocument document = new SolrInputDocument();
document.addField("id", item.getId());
document.addField("item_title", item.getTitle());
document.addField("item_sell_point", item.getSell_point());
document.addField("item_price", item.getPrice());
document.addField("item_image", item.getImage());
document.addField("item_category_name", item.getCategory_name());
document.addField("item_desc", item.getItem_desc());
// 写入索引库
solrServer.add(document);
}
// 提交
solrServer.commit();
return TaotaoResult.ok();
}
}
Controller层
请求一个url,返回TaotaoResult。
@Controller
public>
@Autowired
private ItemService itemService;
@RequestMapping("/importall")
@ResponseBody
public TaotaoResult importAll() {
try {
TaotaoResult result = itemService.importItems();
return result;
} catch (Exception e) {
e.printStackTrace();
return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));
}
}
}
pom.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>
http://i4.buimg.com/1949/e0c052dc5d4ebad6.png
搜索系统的实现
搜索服务发布
调用服务传递过来一个查询条件,根据查询条件进行查询。返回查询结果。参数中包括分页条件。
参数:
[*]String queryString
[*]Int page
[*] Int rows
[*]返回结果:返回json数据。
[*]包含查询结果的列表。使用商品的pojo来描述。SearchItem
[*]包含查询结果总记录数。
[*]包含查询结果的总页数。
[*]包含当前页码。
[*]包含查询的状态。
[*] 包含错误信息。
创建一个SearchResult
包含四个属性:
[*]1、商品列表
[*]2、查询结果总记录数
[*]3、查询结果的总页数
[*]4、当前页码
public>
private List<SearchItem> itemList;
private Long recordCount;
private int pageCount;
private int curPage;
public List<SearchItem> getItemList() {
return itemList;
}
public void setItemList(List<SearchItem> itemList) {
this.itemList = itemList;
}
public Long getRecordCount() {
return recordCount;
}
public void setRecordCount(Long recordCount) {
this.recordCount = recordCount;
}
public int getPageCount() {
return pageCount;
}
public void setPageCount(int pageCount) {
this.pageCount = pageCount;
}
public int getCurPage() {
return curPage;
}
public void setCurPage(int curPage) {
this.curPage = curPage;
}
}
Dao层
根据查询条件进行查询,返回查询结果。
[*]参数:SolrQuery对象
返回结果:
[*]1、查询结果的商品列表
[*]2、查询结果的总记录数
返回SearchResult
@Service
public>
@Autowired
private SolrServer solrServer;
@Override
public SearchResult search(SolrQuery query) throws Exception {
// 执行查询
QueryResponse response = solrServer.query(query);
// 取查询结果列表
SolrDocumentList solrDocumentList = response.getResults();
List<SearchItem> itemList = new ArrayList<>();
for (SolrDocument solrDocument : solrDocumentList) {
// 创建一个SearchItem对象
SearchItem item = new SearchItem();
item.setCategory_name((String) solrDocument.get("item_category_name"));
item.setId((String) solrDocument.get("id"));
item.setImage((String) solrDocument.get("item_image"));
item.setPrice((Long) solrDocument.get("item_price"));
item.setSell_point((String) solrDocument.get("item_sell_point"));
//取高亮显示
Map<String, Map<String, List<String>>> highlighting = response.getHighlighting();
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");
}
item.setTitle(itemTitle);
//添加到列表
itemList.add(item);
}
SearchResult result = new SearchResult();
result.setItemList(itemList);
//查询结果总数量
result.setRecordCount(solrDocumentList.getNumFound());
return result;
}
}
Service层
1、接收查询条件、分页条件。
2、创建SolrQuery对象,设置查询条件、分页条件。
3、调用dao进行搜索
4、计算总页数,把总页数设置到SearchResult对象中,设置当前页属性。
5、返回SearchResult
参数:
1、查询条件
2、Page
3、Rows
返回结果:
SearchResult
@Service
public>
@Autowired
private SearchDao searchDao;
@Override
public SearchResult search(String queryString, int page, int rows) throws Exception {
//创建查询条件
SolrQuery query = new SolrQuery();
//设置查询条件
query.setQuery(queryString);
//设置分页条件
query.setStart((page-1)*rows);
query.setRows(rows);
//设置默认搜索域
query.set("df", "item_title");
//设置高亮
query.setHighlight(true);
query.addHighlightField("item_title");
query.setHighlightSimplePre("<font> query.setHighlightSimplePost("</font>");
//执行查询
SearchResult searchResult = searchDao.search(query);
//计算总页数
Long recordCount = searchResult.getRecordCount();
int pageCount = (int) (recordCount / rows);
if (recordCount % rows > 0) {
pageCount++;
}
searchResult.setPageCount(pageCount);
searchResult.setCurPage(page);
return searchResult;
}
}
Controller层
发布服务。
搜索服务的url:/search/q?keyword=xxx&page=1&rows=30
参数keyword、page、rows
返回结果:json数据,使用TaotaoResult包装SearchResult。
@Controller
public>
@Autowired
private SearchService searchService;
@RequestMapping("/q")
@ResponseBody
public TaotaoResult search(@RequestParam(defaultValue="")String keyword,
@RequestParam(defaultValue="1")Integer page,
@RequestParam(defaultValue="30")Integer rows) {
try {
//转换字符集
keyword = new String(keyword.getBytes("iso8859-1"), "utf-8");
SearchResult searchResult = searchService.search(keyword, page, rows);
return TaotaoResult.ok(searchResult);
} catch (Exception e) {
e.printStackTrace();
return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));
}
}
}
http://localhost:8093/search/q?keyword=%E6%89%8B%E6%9C%BA&page=1&rows=10
http://i1.piimg.com/1949/050122e7f92a7072.png
在portal中实现搜索
分析
调用taotao-search发布的服务,实现搜索。使用HttpClient调用服务。返回json数据。需要把json转换成java对象。把java对象传递给页面。
请求的url:http://localhost:8082/search.html
参数:q:查询条件
返回结果:jsp页面(search.jsp)
Search.jsp分析:
数据:
Query:查询条件
totalPages:总页数
itemList:商品列表(每个元素可以是SearchItem)
Page:当前页
Service层
参数:查询条件、page、rows。
根据查询调用taotao-search发布的服务,查询商品列表。得到json数据,需要把json转换成java对象,返回SearchResult。
@Service
public>
@Value("${SEARCH_BASE_URL}")
private String SEARCH_BASE_URL;
@Override
public SearchResult search(String keyword, int page, int rows) {
// 调用服务查询商品列表
Map<String, String> param = new HashMap<>();
param.put("keyword", keyword);
param.put("page", page + "");
param.put("rows", rows + "");
// 调用服务
String json = HttpClientUtil.doGet(SEARCH_BASE_URL, param);
// 转换成java对象
TaotaoResult taotaoResult = TaotaoResult.formatToPojo(json, SearchResult.class);
// 取返回结果
SearchResult searchResult = (SearchResult) taotaoResult.getData();
return searchResult;
}
}
Controller层
接收三个参数:查询条件、page、rows
调用服务查询商品列表。
把商品列表传递给jsp、参数回显。
返回逻辑视图(search.jsp)
请求的url:/search
@Controller
public>
@Autowired
private SearchService searchService;
@RequestMapping("/search")
public String search(@RequestParam("q") String keyword,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "60") Integer rows, Model model) {
// get乱码处理
try {
keyword = new String(keyword.getBytes("iso8859-1"), "utf-8");
} catch (UnsupportedEncodingException e) {
keyword = "";
e.printStackTrace();
}
SearchResult searchResult = searchService.search(keyword, page, rows);
// 参数传递给页面
model.addAttribute("query", keyword);
model.addAttribute("totalPages", searchResult.getPageCount());
model.addAttribute("itemList", searchResult.getItemList());
model.addAttribute("page", searchResult.getCurPage());
// 返回逻辑视图
return "search";
}
}
解决图片显示不出来的问题:
public String getImage() { if (image != null && image.equals("")) {
String[] strings = image.split(",");
return strings;
}
return image;
}
页:
[1]