• 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>
    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="/")
     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     }
    复制代码
  • 相关阅读:
    thinkphp6 模型中时间戳报错
    php获取真实的ip信息
    java 获取字符串长度
    thinkphp6 linux首次部署Access denied
    mysql5.7 分配子账户和解决进程错误
    springboot controller引用请求体和返回体方法
    微信公众号token 验证
    springboot常量定义
    python 抓接口 利用requests 增加 headers示例
    asp.net web api 如何设置允许后台跨域访问
  • 原文地址:https://www.cnblogs.com/Jansens520/p/7845465.html
Copyright © 2020-2023  润新知