前面已經講過 如果安裝及配置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 //上架
@Autowired private SolrServer solrServer; 2 public void isShow(Long[] ids){ 3 Product product = new Product(); 4 product.setIsShow(true); 5 for (Long id : ids) { 6 //上下架狀態 7 product.setId(id); 8 productDao.updateByPrimaryKeySelective(product); 9 10 //TODO 保存商品信息到Solr服務器 11 SolrInputDocument doc = new SolrInputDocument(); 12 //ID 13 doc.setField("id", id); 14 //名稱 15 Product p = productDao.selectByPrimaryKey(id); 16 doc.setField("name_ik", p.getName()); 17 //圖片URL 18 doc.setField("url", p.getImgUrls()[0]); 19 //品牌 ID 20 doc.setField("brandId", p.getBrandId()); 21 //價格 sql查詢語句: select price from bbs_sku where product_id = ? order by price asc limit 1 22 SkuQuery skuQuery = new SkuQuery(); 23 skuQuery.createCriteria().andProductIdEqualTo(id); 24 skuQuery.setOrderByClause("price asc"); 25 skuQuery.setPageNo(1); 26 skuQuery.setPageSize(1); 27 List<Sku> skus = skuDao.selectByExample(skuQuery); 28 doc.setField("price", skus.get(0).getPrice()); 29 //...時間等 剩下的省略 30 31 try { 32 solrServer.add(doc); 33 solrServer.commit(); 34 } catch (Exception e) { 35 // TODO Auto-generated catch block 36 e.printStackTrace(); 37 } 38 //TODO 靜態化 39 } 40 }
這里使用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上面都加了注釋, 相信還是比較容易理解的.