首页 > 代码库 > [项目构建 七]babasport 商品上架及Solr使用实例.

[项目构建 七]babasport 商品上架及Solr使用实例.

前面已经讲过 如果安装及配置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             //ID13             doc.setField("id", id);14             //名称15             Product p = productDao.selectByPrimaryKey(id);16             doc.setField("name_ik", p.getName());17             //图片URL18             doc.setField("url", p.getImgUrls()[0]);19             //品牌 ID20             doc.setField("brandId", p.getBrandId());21             //价格 sql查询语句: select price from bbs_sku where product_id = ? order by price asc limit 122             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 block36                 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请求, 后台开发应用*.do40          -->41         <url-pattern>/</url-pattern>42     </servlet-mapping>43 </web-app>
View Code

第二个就是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="http://www.mamicode.com/") 6     public String index(){ 7         return "index"; 8     } 9     10     //搜索11     @RequestMapping(value="http://www.mamicode.com/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="http://www.mamicode.com/search?keyword="+ ${keyword} + "&brandId="+ id+"&price="+price; 8         }else{ 9             window.location.href="http://www.mamicode.com/search?keyword="+ ${keyword} + "&brandId="+ id;10         }11     }12     13     //过滤价格14     function fqPrice(id){15         if(‘‘ != brandId){16             window.location.href = "http://www.mamicode.com/search?keyword=${keyword}" + "&brandId=" + brandId + "&price=" + id;17         }else{18             window.location.href = "http://www.mamicode.com/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                     products106                 );107         //页面展示108         String url = "/search";109         pagination.pageView(url, params.toString());110         111         return pagination;112     }

这个就是本博文的重中之重了, code上面都加了注释, 相信还是比较容易理解的.

 



 

[项目构建 六]babasport Redis使用实例.

[项目构建 七]babasport 商品上架及Solr使用实例.