前面已經講過 如果安裝及配置Solr伺服器了, 那麼現在我們就來正式在代碼中使用Solr.
1,這裡Solr主要是怎麼使用的呢?
當我們在前台頁面搜尋商品名稱關鍵詞時, 我們這時是在Solr庫中去查找相應的商品資訊, 然後将搜尋關鍵詞高亮.
2,那麼Solr庫中的商品資訊又是如何添加的呢?
當我們在給商品上架的時候, 将商品資訊update 到mysql資料庫中的bbs_product表中, 然後同樣的将相應的資訊 添加到Solr庫中.
接下來就看代碼的具體實作吧:
一, 商品上架
我們在這裡點選上架按鈕:
list.jsp:
1 <div style="margin-top:15px;"><input class="del-button" type="button" value="删除" onclick="optDelete();"/><input class="add" type="button" value="上架" onclick="isShow('${name}', '${brandId }', '${isShow }' ,'${pagination.pageNo }')"/><input class="del-button" type="button" value="下架" onclick="isHide();"/></div>
複制
點選上架觸發isShow事件:
1 <script type="text/javascript">
2 //上架
3 function isShow(name,brandId,isShow,pageNo){
4 //請至少選擇一個
5 var size = $("input[name='ids']:checked").size();
6 if(size == 0){
7 alert("請至少選擇一個");
8 return;
9 }
10 //你确定上架嗎
11 if(!confirm("你确定上架嗎")){
12 return;
13 }
14 //送出 Form表單
15 $("#jvForm").attr("action","/product/isShow.do?name="+ name +"&brandId="+brandId+"&isShow="+isShow+"&pageNo="+pageNo);
16 $("#jvForm").attr("method","post");
17 $("#jvForm").submit();
18
19 }
20 </script>
複制
接着到Controller層:
ProductController.java:
1 //添加頁面
2 @RequestMapping("/isShow.do")
3 public String isShow(Long[] ids, Model model){
4 productService.isShow(ids);
5 return "forward:/product/list.do";
6 }
複制
接着看Service層:
ProdcutServiceImpl.java:
1 //上架
複制
這裡使用SolrInputDocument 來儲存商品資訊, 其中doc.setField("name_ik", p.getName());的name_ik 是我們在solr 配置檔案配置的IK 分詞器的字段, doc.setField("url", p.getImgUrls()[0]); 這裡我們也隻是取第一張圖檔的url用來展示.
這裡我們還用到了skuQuery, 因為一個商品中不同的顔色不同的尺碼都可能有不同的價格, 我們在這裡 是取到同一個productId下價格最小的來給顯示~
然後再就是将我們已經設定好的SolrInputDocument 通過SolrServer 來送出到Solr伺服器. SolrServer是已經在spring中注冊好了的, 在這裡直接注入即可使用.
spring來管理Solr:
到了這裡上架的功能就做好了, 這也是給後面Solr查詢做好鋪墊.
二, 前台使用Solr查詢
到了這裡就開始檢視前台頁面了, 前台頁面是扒的網上的, 具體業務邏輯是自己修改的, 頁面如下:
這裡需要特殊說明一下, 我們配置的全局攔截器變成了: / , 而且過濾掉靜态資源, 配置如下:
首先是babasport-portal project下的web.xml檔案:
1 <?xml version="1.0" encoding="UTF-8"?>
2 <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
5 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
6
7 <!-- Post過濾器 -->
8 <filter>
9 <filter-name>encoding</filter-name>
10 <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
11 <init-param>
12 <param-name>encoding</param-name>
13 <param-value>UTF-8</param-value>
14 </init-param>
15 </filter>
16
17 <filter-mapping>
18 <filter-name>encoding</filter-name>
19 <url-pattern>/</url-pattern>
20 </filter-mapping>
21
22 <!-- 前端控制器 -->
23 <servlet>
24 <servlet-name>portal</servlet-name>
25 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
26 <init-param>
27 <param-name>contextConfigLocation</param-name>
28 <!-- 預設讀取的是 WEB-INF/console-servlet.xml -->
29 <param-value>classpath:springmvc-portal.xml</param-value>
30 </init-param>
31 <load-on-startup>1</load-on-startup>
32 </servlet>
33
34 <servlet-mapping>
35 <servlet-name>portal</servlet-name>
36 <!--
37 /*: 攔截視圖請求: .jsp .js .css 幾乎不用,配置靜态資源過濾
38 /: 攔截所有,不攔截.jsp 檔案, 但是同樣攔截.js .css 如果使用也需要配置靜态資源過濾(前台系統使用)
39 *.do:攔截所有以.do請求, 背景開發應用*.do
40 -->
41 <url-pattern>/</url-pattern>
42 </servlet-mapping>
43 </web-app>
複制
第二個就是babasport-portal project下的spring配置檔案中設定過濾掉靜态資源:
1 <!-- 過濾靜态資源 -->
2 <mvc:resources location="/js/" mapping="/js/*.*"/>
3 <mvc:resources location="/css/" mapping="/css/*.*"/>
4 <mvc:resources location="/images/" mapping="/images/*.*"/>
複制
這樣就就可以直接通路了.
當我們輸入2016 點選查詢後會出現什麼? 我把已經做好的頁面展示一下:
那麼就進入到實際的開發當中:
當我們在搜尋框輸入2016 且點選 搜尋時:
然後到Controller層去找到search方法:
1 @Autowired
2 private SearchService searchService;
3
4 //去首頁
5 @RequestMapping(value="/")
6 public String index(){
7 return "index";
8 }
9
10 //搜尋
11 @RequestMapping(value="/search")
12 public String search(Integer pageNo, String keyword, String price, Long brandId, Model model){
13 //品牌結果集 Redis中
14 List<Brand> brands = searchService.selectBrandListFromRedis();
15 model.addAttribute("brands", brands);
16
17 //map 裝已經選擇的條件
18 Map<String, String> map = new HashMap<String, String>();
19 if(null != brandId){
20 for (Brand brand : brands) {
21 if(brandId.equals(brand.getId())){
22 map.put("品牌", brand.getName());
23 break;
24 }
25 }
26 }
27 //價格 0-99 1600以上
28 if(null != price){
29 String[] split = price.split("-");
30 //如果切割後的長度等于2 就說明這是一個價格區間
31 if(split.length == 2){
32 map.put("價格", price);
33 }else {
34 map.put("價格", price + "以上");
35 }
36 }
37 model.addAttribute("map", map);
38
39 Pagination pagination = searchService.selectPaginationFromSolr(pageNo, keyword, price, brandId);
40 model.addAttribute("pagination", pagination);
41 model.addAttribute("keyword", keyword);
42 model.addAttribute("price", price);
43 model.addAttribute("brandId", brandId);
44
45 return "search";
46 }
複制
提示: 這裡使用到了SolrService, 相信看我以前博文的朋友都知道這個地方還需要配置dubbo, 就是服務提供方和适用方, 這裡為了簡便直接略過, 實際開發中是必須要配置的, 否則就調用不了SolrService中的方法了.
這個controller 中往search.jsp中put了很多東西, 具體這些東西什麼用我們可以先不管, 我們先看下search.jsp頁面.
而且這個controller中查詢brand 是從redis中查詢出來的, 我們會在下面講到這個.
1 <c:if test="${fn:length(map) != 0 }">
2 <div class="sl-b-selected J_brandSelected">
3 <span class="crumbs-arrow">已選條件:</span>
4 <c:forEach items="${map }" var="m">
5 <a title="依琦蓮(yiqilian)" href="javascript:;" class="crumb-select-item">
6 <b>${m.key }:</b><em>${m.value }</em><i></i>
7 </a>
8 </c:forEach>
9 </div>
10 </c:if>
複制
上面這個地方就是為何要在controller設定map值了, 這個是顯示已選擇的過濾條件.
1 <c:if test="${empty brandId }">
2 <div class="J_selectorLine s-brand">
3 <div class="sl-wrap">
4 <div class="sl-key"><strong>品牌:</strong></div>
5 <div class="sl-value">
6 <div class="sl-v-list">
7 <ul class="J_valueList v-fixed">
8 <c:forEach items="${brands }" var="brand">
9 <li id="brand-38118" data-initial="j" style="display:block;">
10 <a href="javascript:;" onclick="fqBrand('${brand.id }')" title="${brand.name }"><i></i>${brand.name }</a>
11 </li>
12 </c:forEach>
13 </ul>
14 </div>
15 </div>
16 </div>
17 </div>
18 </c:if>
19 <c:if test="${empty price }">
20 <div id="J_selectorPrice" class="J_selectorLine s-line">
21 <div class="sl-wrap">
22 <div class="sl-key"><span>價格:</span></div>
23 <div class="sl-value">
24 <div class="sl-v-list">
25 <ul class="J_valueList">
26 <li>
27 <a href="javascript:;" onclick="fqPrice('0-99')"><i></i>0-99</a>
28 </li>
29 <li>
30 <a href="javascript:;" onclick="fqPrice('100-299')"><i></i>100-299</a>
31 </li>
32 <li>
33 <a href="javascript:;" onclick="fqPrice('300-599')"><i></i>300-599</a>
34 </li>
35 <li>
36 <a href="javascript:;" onclick="fqPrice('600-999')"><i></i>600-999</a>
37 </li>
38 <li>
39 <a href="javascript:;" onclick="fqPrice('1000-1599')"><i></i>1000-1599</a>
40 </li>
41 <li>
42 <a href="javascript:;" onclick="fqPrice('1600')"><i></i>1600以上</a>
43 </li>
44 </ul>
45 </div>
46 </div>
47 </div>
48 </div>
49 </c:if>
複制
接下來我們來看下對應的js方法:
1 <script type="text/javascript">
2 var price = '${price}';
3 var brandId = '${brandId}';
4 //過濾品牌id
5 function fqBrand(id){
6 if('' != price){
7 window.location.href="/search?keyword="+ ${keyword} + "&brandId="+ id+"&price="+price;
8 }else{
9 window.location.href="/search?keyword="+ ${keyword} + "&brandId="+ id;
10 }
11 }
12
13 //過濾價格
14 function fqPrice(id){
15 if('' != brandId){
16 window.location.href = "/search?keyword=${keyword}" + "&brandId=" + brandId + "&price=" + id;
17 }else{
18 window.location.href = "/search?keyword=${keyword}" + "&price=" + id;
19 }
20 }
21 </script>
複制
這個就可以實作 添加 過濾條件的選項了.
三, 使用Redis 取出商品品牌清單
首先 當我們在背景添加或者修改品牌時, 我們應該同樣将這個品牌添加到Redis中, 格式類似于: {"brandId":"brandName"}
controller層:(當我們在背景添加或者修改品牌)
1 @Autowired
2 private Jedis jedis;
3 //修改
4 public void updateBrandById(Brand brand){
5 //儲存或修改 時修改Redis中的品牌, hmset适合批量添加品牌
6 /*Map<String, String> map = new HashMap<String,String>();
7 map.put(String.valueOf(brand.getId()), brand.getName());
8 jedis.hmset("brand", map);*/
9 jedis.hset("brand", String.valueOf(brand.getId()), brand.getName());
10 brandDao.updateBrandById(brand);
11 }
複制
redis中有了品牌清單後, 然後就是查詢了:
1 @Autowired
2 private Jedis jedis;
3 //查詢Redis中的品牌結果集
4 public List<Brand> selectBrandListFromRedis(){
5 List<Brand> brands = new ArrayList<Brand>();
6 Map<String, String> hgetAll = jedis.hgetAll("brand");
7 Set<Entry<String, String>> entrySet = hgetAll.entrySet();
8 for (Entry<String, String> entry : entrySet) {
9 Brand brand = new Brand();
10 brand.setId(Long.parseLong(entry.getKey()));
11 brand.setName(entry.getValue());
12 brands.add(brand);
13 }
14
15 return brands;
16 }
複制
到了這裡redis查詢brand就完成了, 那麼繼續看下關于solr 是如何加入過濾條件的吧:
1 @Autowired
2 private SolrServer solrServer;
3 //查詢商品資訊從Solr
4 public Pagination selectPaginationFromSolr(Integer pageNo, String keyword, String price, Long brandId){
5 ProductQuery productQuery = new ProductQuery();
6 //目前頁
7 productQuery.setPageNo(Pagination.cpn(pageNo));
8 //每頁數
9 productQuery.setPageSize(8);
10
11 SolrQuery solrQuery = new SolrQuery();
12 //關鍵詞 商品名稱
13 solrQuery.set("q", "name_ik:"+keyword);
14 //回顯資料
15 StringBuilder params = new StringBuilder();
16 params.append("keyword=").append(keyword);
17
18 //排序
19 solrQuery.addSort("price", ORDER.asc);
20
21 //高亮
22 //1,設定, 打開高亮的開關
23 solrQuery.setHighlight(true);
24 //2, 設定高亮字段
25 solrQuery.addHighlightField("name_ik");
26 //3, 設定關鍵字高亮的樣式 <span style='color:red'>2016</span>
27 //設定字首和字尾
28 solrQuery.setHighlightSimplePre("<span style='color:red'>");
29 solrQuery.setHighlightSimplePost("</span>");
30
31 //過濾條件 品牌
32 if(null != brandId){
33 solrQuery.addFilterQuery("brandId:"+brandId);
34 params.append("&brandId=").append(brandId);
35 }
36 //過濾價格 0-99 1600
37 if(null != price){
38 String[] split = price.split("-");
39 //如果切割後的長度等于2 就說明這是一個價格區間
40 if(split.length == 2){
41 solrQuery.addFilterQuery("price:["+split[0]+" TO "+split[1]+"]");
42 }else {
43 solrQuery.addFilterQuery("price:["+split[0]+" TO *]");
44 }
45 params.append("&price=").append(price);
46 }
47
48 //分頁 limit 開始行,每頁數
49 solrQuery.setStart(productQuery.getStartRow());
50 solrQuery.setRows(productQuery.getPageSize());
51
52 QueryResponse response = null;
53 try {
54 response = solrServer.query(solrQuery);
55
56 } catch (Exception e) {
57 e.printStackTrace();
58 }
59 //分析這個Map
60 //第一層Map: Key String == ID : Value: Map
61 //第二層Map: Key String == name_ik : Value: List
62 //擷取到List: String 0,1,2....
63 Map<String, Map<String, List<String>>> highlighting = response.getHighlighting();
64
65
66 List<Product> products = new ArrayList<Product>();
67 //結果集
68 SolrDocumentList docs = response.getResults();
69 //總條數
70 long numFound = docs.getNumFound();
71 for (SolrDocument doc : docs) {
72 Product product = new Product();
73 //商品的ID
74 String id = (String)doc.get("id");
75 product.setId(Long.parseLong(id));
76
77 //取第二層Map
78 Map<String, List<String>> map = highlighting.get(id);
79 //取List集合
80 List<String> list = map.get("name_ik");
81
82 //商品名稱
83 //String name = (String)doc.get("name_ik");
84 //product.setName(name);
85 product.setName(list.get(0)); //list.get(0) 中的name是已經設定為高亮的
86
87 //圖檔
88 String url = (String)doc.get("url");
89 product.setImgUrl(url);
90 //價格 這裡的價格本身是儲存在bbs_sku表中的, 而我們在這裡将price屬性直接添加到了Product中
91 //因為我們在做上架的時候, 查詢的是bbs_sku中price最小的值 然後儲存到solr中的, 是以這裡我們就直接将price屬性添加到product中了
92 //這裡的價格隻有一個值
93 //Float price = (Float)doc.get("price");
94 product.setPrice((Float)doc.get("price"));
95 //品牌ID
96 //Integer brandId = (Integer)doc.get("brandId");
97 product.setBrandId(Long.parseLong(String.valueOf((Integer)doc.get("brandId"))));
98 products.add(product);
99 }
100
101 Pagination pagination = new Pagination(
102 productQuery.getPageNo(),
103 productQuery.getPageSize(),
104 (int)numFound,
105 products
106 );
107 //頁面展示
108 String url = "/search";
109 pagination.pageView(url, params.toString());
110
111 return pagination;
112 }
複制
這個就是本博文的重中之重了, code上面都加了注釋, 相信還是比較容易了解的.