zoukankan      html  css  js  c++  java
  • Spring Data Solr —— 快速入门

      Solr是基于Lucene(全文检索引擎)开发,它是一个独立系统,运行在Tomcat或Jetty(solr6以上集成了jetty,无需再部署到servlet容器上),但其原生中文的分词词功能不行,需要集成第三方分词器(如IK Analyzer)。

      Solr的安装可上网搜一下,很简单。下面开始演示,如何集成IK Analyzer、配置相关域以及使用Spring Data Solr进行操作。

    一、集成 IK Analyzer 分词器

    步骤:
    1、把IKAnalyzer2012FF_u1.jar 添加到 solr 工程的 lib 目录下
    2、创建WEB-INF/classes文件夹把扩展词典、停用词词典、配置文件放到 solr 工程的 WEB-INF/classes 目录下。

     

    3、修改 Solrhome 的 schema.xml 文件,配置一个 FieldType,使用 IKAnalyzer

    <fieldType name="text_ik" class="solr.TextField">
        <analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/>
    </fieldType>

     二、配置域

      域相当于数据库的表字段,用户存放数据,因此用户根据业务需要去定义相关的Field(域),一般来说,每一种对应着一种数据,用户对同一种数据进行相同的操作。

    域的常用属性:
    • name:指定域的名称
    • type:指定域的类型
    • indexed:是否索引
    • stored:是否存储
    • required:是否必须
    • multiValued:是否多值

    1、域

      Solr中默认定义唯一主键key为id域,如下:

    <uniqueKey>id</uniqueKey>

      Solr在删除、更新索引时使用id域进行判断,也可以自定义唯一主键。
      注意在创建索引时必须指定唯一约束。

    1 <field name="item_goodsid" type="long" indexed="true" stored="true"/>
    2 <field name="item_title" type="text_ik" indexed="true" stored="true"/>
    3 <field name="item_price" type="double" indexed="true" stored="true"/>
    4 <field name="item_image" type="string" indexed="false" stored="true" />
    5 <field name="item_category" type="string" indexed="true" stored="true" />
    6 <field name="item_seller" type="text_ik" indexed="true" stored="true" />
    7 <field name="item_brand" type="string" indexed="true" stored="true" />

    2、copyField复制域

      copyField复制域,可以将多个Field复制到一个Field中,以便进行统一的检索:
      比如,根据关键字只搜索item_keywords域的内容就相当于搜索item_title、item_category、item_seller、item_brand,即将item_title、item_category、item_seller、item_brand复制到item_keywords域中。

      目标域必须是多值的。

    1 <field name="item_keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/>
    2 <copyField source="item_title" dest="item_keywords"/>
    3 <copyField source="item_category" dest="item_keywords"/>
    4 <copyField source="item_seller" dest="item_keywords"/>
    5 <copyField source="item_brand" dest="item_keywords"/>

     3、dynamicField(动态字段)

      动态字段就是不用指定具体的名称,只要定义字段名称的规则,例如定义一个 dynamicField,name 为*_i,定义它的type为text,那么在使用这个字段的时候,任何以_i结尾的字段都被认为是符合这个定义的,例如:name_i,gender_i,school_i等。

      自定义Field名为:product_title_t,“product_title_t”和scheam.xml中的dynamicField规则匹配成功。

      如:

    配置:<dynamicField name="item_spec_*" type="string" indexed="true" stored="true" />

     三、使用Spring Data Solr对Solr进行操作

      Spring Data Solr就是为了方便Solr的开发所研制的一个框架,其底层是对SolrJ(官方API)的封装。

     1、环境的搭建

    项目目录结构:

    ①搭建SSM框架,参考:http://www.cnblogs.com/gdwkong/p/8784780.html

    ②添加依赖

     1     <!-- solr客户端 -->
     2     <dependency>
     3       <groupId>org.apache.solr</groupId>
     4       <artifactId>solr-solrj</artifactId>
     5       <version>${solrj.version}</version>
     6     </dependency>
     7     <dependency>
     8       <groupId>com.janeluo</groupId>
     9       <artifactId>ikanalyzer</artifactId>
    10       <version>2012_u6</version>
    11     </dependency>
    12     <!--spring data solr-->
    13     <dependency>
    14       <groupId>org.springframework.data</groupId>
    15       <artifactId>spring-data-solr</artifactId>
    16       <version>1.5.5.RELEASE</version>
    17     </dependency>

    ③创建mysql表,通过通用mapper生成TbItemMapper.java、TbItemMapper.xml、实体类TbItem.java、TbItemExample.java

     1 CREATE TABLE `tb_item` (
     2   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '商品id,同时也是商品编号',
     3   `title` varchar(100) NOT NULL COMMENT '商品标题',
     4   `sell_point` varchar(500) DEFAULT NULL COMMENT '商品卖点',
     5   `price` decimal(20,2) NOT NULL COMMENT '商品价格,单位为:元',
     6   `stock_count` int(10) DEFAULT NULL,
     7   `num` int(10) NOT NULL COMMENT '库存数量',
     8   `barcode` varchar(30) DEFAULT NULL COMMENT '商品条形码',
     9   `image` varchar(2000) DEFAULT NULL COMMENT '商品图片',
    10   `categoryId` bigint(10) NOT NULL COMMENT '所属类目,叶子类目',
    11   `status` varchar(1) NOT NULL COMMENT '商品状态,1-正常,2-下架,3-删除',
    12   `create_time` datetime NOT NULL COMMENT '创建时间',
    13   `update_time` datetime NOT NULL COMMENT '更新时间',
    14   `item_sn` varchar(30) DEFAULT NULL,
    15   `cost_pirce` decimal(10,2) DEFAULT NULL,
    16   `market_price` decimal(10,2) DEFAULT NULL,
    17   `is_default` varchar(1) DEFAULT NULL,
    18   `goods_id` bigint(20) DEFAULT NULL,
    19   `seller_id` varchar(30) DEFAULT NULL,
    20   `cart_thumbnail` varchar(150) DEFAULT NULL,
    21   `category` varchar(200) DEFAULT NULL,
    22   `brand` varchar(100) DEFAULT NULL,
    23   `spec` varchar(200) DEFAULT NULL,
    24   `seller` varchar(200) DEFAULT NULL,
    25   PRIMARY KEY (`id`),
    26   KEY `cid` (`categoryId`),
    27   KEY `status` (`status`),
    28   KEY `updated` (`update_time`)
    29 ) ENGINE=InnoDB AUTO_INCREMENT=1369286 DEFAULT CHARSET=utf8 COMMENT='商品表'

    ④修改实体类TbItem.java,添加solr注解,映射索引字段

     1 public class TbItem implements Serializable {
     2     @Field
     3     private Long id;
     4 
     5     @Field("item_title")
     6     private String title;
     7 
     8     private String sellPoint;
     9 
    10     @Field("item_price")
    11     private BigDecimal price;
    12 
    13     private Integer stockCount;
    14 
    15     private Integer num;
    16 
    17     private String barcode;
    18 
    19     @Field("item_image")
    20     private String image;
    21 
    22     private Long categoryid;
    23 
    24     private String status;
    25 
    26     private Date createTime;
    27 
    28     private Date updateTime;
    29 
    30     private String itemSn;
    31 
    32     private BigDecimal costPirce;
    33 
    34     private BigDecimal marketPrice;
    35 
    36     private String isDefault;
    37 
    38     @Field("item_goodsid")
    39     private Long goodsId;
    40 
    41     private String sellerId;
    42 
    43     private String cartThumbnail;
    44 
    45     @Field("item_category")
    46     private String category;
    47 
    48     @Field("item_brand")
    49     private String brand;
    50 
    51     private String spec;
    52 
    53     @Field("item_seller")
    54     private String seller;
    55 
    56     @Dynamic
    57     @Field("item_spec_*")
    58     private Map<String,String> specMap;
    59 
    60     private static final long serialVersionUID = 1L;
    61 
    62     .....
    63 }

    ⑤配置applicationContext-solr.xml配置文件

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4        xmlns:p="http://www.springframework.org/schema/p"
     5        xmlns:context="http://www.springframework.org/schema/context"
     6        xmlns:solr="http://www.springframework.org/schema/data/solr"
     7        xsi:schemaLocation="http://www.springframework.org/schema/data/solr
     8         http://www.springframework.org/schema/data/solr/spring-solr-1.0.xsd
     9         http://www.springframework.org/schema/beans
    10         http://www.springframework.org/schema/beans/spring-beans.xsd
    11         http://www.springframework.org/schema/context
    12         http://www.springframework.org/schema/context/spring-context.xsd">
    13 
    14     <bean class="com.cenobitor.solr.SolrUtil" id="solrUtil"></bean>
    15 
    16     <!--solr服务器地址-->
    17     <solr:solr-server id="solrServer" url="http://127.0.0.1:8280/solr"/>
    18     <!-- solr模板,使用solr模板可对索引库进行CRUD的操作 -->
    19     <bean id="solrTemplate" class="org.springframework.data.solr.core.SolrTemplate">
    20         <constructor-arg ref="solrServer"/>
    21     </bean>
    22 
    23 </beans>

    ⑥运行该类生成索引

     1 @Component
     2 public class SolrUtil {
     3 
     4     @Autowired
     5     private TbItemMapper itemMapper;
     6 
     7     @Autowired
     8     private SolrTemplate solrTemplate;
     9 
    10     /**
    11      * 导入商品数据到索引库中
    12      */
    13     public void importItemData(){
    14         TbItemExample example = new TbItemExample();
    15         TbItemExample.Criteria criteria = example.createCriteria();
    16         criteria.andStatusEqualTo("1");//已审核的
    17 
    18         List<TbItem> itemList = itemMapper.selectByExample(example);
    19         System.out.println("==商品列表==");
    20         for (TbItem item : itemList) {
    21             Map specMap = JSON.parseObject(item.getSpec(),Map.class);//将spec字段中的json字符串转换为map
    22             item.setSpecMap(specMap);//给带注解的字段赋值
    23             System.out.println(item.getTitle());
    24         }
    25         solrTemplate.saveBeans(itemList);
    26         solrTemplate.commit();
    27         System.out.println("==结束==");
    28     }
    29 
    30     public static void main(String[] args) {
    31         ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-*.xml");
    32         context.getBean(SolrUtil.class).importItemData();
    33     }
    34 
    35 }

     2、使用Spring Data Solr对Solr进行操作

     ①创建ItemSearchServiceImpl.java

      1 @Service
      2 public class ItemSearchServiceImpl implements ItemSearchService {
      3 
      4     @Autowired
      5     private SolrTemplate solrTemplate;
      6 
      7     /**
      8      * 添加
      9      * @param tbItem
     10      */
     11     @Override
     12     public void add(TbItem tbItem) {
     13         solrTemplate.saveBean(tbItem);
     14         solrTemplate.commit();
     15     }
     16 
     17     /**
     18      * 按主键查询
     19      * @param id
     20      * @return
     21      */
     22     @Override
     23     public TbItem searchById(int id) {
     24         TbItem tbItem = solrTemplate.getById(id, TbItem.class);
     25         return tbItem;
     26     }
     27 
     28     /**
     29      * 按主键删除
     30      * @param id
     31      * @return
     32      */
     33     @Override
     34     public void deleteById(String id) {
     35         solrTemplate.deleteById(id);
     36     }
     37 
     38     /**
     39      * 分页查询
     40      * @param start
     41      * @param size
     42      */
     43     @Override
     44     public ScoredPage<TbItem> pageQuery(int start,int size){
     45         Query query=new SimpleQuery("*:*");
     46         query.setOffset(start);//开始索引(默认0)start:(page-1)*rows
     47         query.setRows(size);//每页记录数(默认10)//rows:rows
     48         return solrTemplate.queryForPage(query, TbItem.class);
     49     }
     50 
     51     /**
     52      * 删除所有
     53      */
     54     @Override
     55     public void deleteAll() {
     56         Query query = new SimpleQuery("*:*");
     57         solrTemplate.delete(query);
     58         solrTemplate.commit();
     59     }
     60 
     61     /**
     62      * 搜索
     63      * @param searchMap
     64      * @return
     65      */
     66     @Override
     67     public Map<String, Object> search(Map searchMap) {
     68         Map<String, Object> resultMap = new HashMap<>();
     69         //1.先获取从页面传递过来的参数的值   通过KEY获取
     70         String keywords = (String)searchMap.get("keywords");//获取主查询的条件
     71 
     72         //2.设置主查询的条件
     73         HighlightQuery query =  new SimpleHighlightQuery();
     74         Criteria criteria = new Criteria("item_keywords");
     75         criteria.is(keywords);
     76         query.addCriteria(criteria);
     77         //3.设置高亮查询的条件   设置高亮显示的域  设置前缀  设置后缀
     78         HighlightOptions hightoptions = new HighlightOptions();
     79         hightoptions.addField("item_title");//设置高亮显示的域
     80         hightoptions.setSimplePrefix("<em style="color:red">");
     81         hightoptions.setSimplePostfix("</em>");
     82         query.setHighlightOptions(hightoptions);
     83 
     84         //4.设置过滤条件  商品分类的过滤
     85         if (searchMap.get("category") != null && !"".equals(searchMap.get("category"))) {
     86             Criteria fiterCriteria = new Criteria("item_category").is(searchMap.get("category"));
     87             FilterQuery filterQuery = new SimpleFilterQuery(fiterCriteria);
     88             query.addFilterQuery(filterQuery);
     89         }
     90 
     91         //5.设置品牌的过滤
     92         if (searchMap.get("brand") != null && !"".equals(searchMap.get("brand"))) {
     93             Criteria fitercriteria = new Criteria("item_brand").is(searchMap.get("brand"));
     94             FilterQuery filterquery = new SimpleFilterQuery(fitercriteria);
     95             query.addFilterQuery(filterquery);
     96         }
     97 
     98         //6.设置规格的过滤条件
     99         if (searchMap.get("spec") != null) {
    100             Map<String,String> spec = (Map<String, String>) searchMap.get("spec");
    101 
    102             for (String key : spec.keySet()) {
    103                 String value = spec.get(key);
    104                 Criteria fiterCriteria = new Criteria("item_spec_"+key).is(value);//item_spec_网络:3G
    105                 FilterQuery filterquery = new SimpleFilterQuery(fiterCriteria);
    106                 query.addFilterQuery(filterquery);//
    107             }
    108         }
    109 
    110         //7.按照价格筛选
    111         if (StringUtils.isNotBlank((CharSequence) searchMap.get("price"))){
    112             //item_price:[10 TO 20]
    113             String[] split = searchMap.get("price").toString().split("-");
    114             SimpleFilterQuery filterQuery = new SimpleFilterQuery();
    115             Criteria itemPrice = new Criteria("item_price");
    116             //如果有* 语法是不支持的
    117             if(!split[1].equals("*")){
    118                 itemPrice.between(split[0],split[1],true,true);
    119             }else {
    120                 itemPrice.greaterThanEqual(split[0]);
    121             }
    122             filterQuery.addCriteria(itemPrice);
    123             query.addFilterQuery(filterQuery);
    124         }
    125         //8.分页查询
    126         Integer pageNo = (Integer) searchMap.get("pageNo");//提取页面
    127 
    128         if (pageNo==null){
    129             pageNo =1;
    130         }
    131         Integer pageSize = (Integer) searchMap.get("pageSize");//每页记录数
    132         if (pageSize==null){
    133             pageSize=20;
    134         }
    135         query.setOffset((pageNo-1)*pageSize);//从第几条记录查询
    136         query.setRows(pageSize);
    137 
    138         //9.排序
    139         String sortValue = (String) searchMap.get("sort");
    140         String sortField = (String) searchMap.get("sortField");//排序字段
    141         if (StringUtils.isNotBlank(sortField)){
    142             if (sortValue.equals("ASC")){
    143                 Sort sort = new Sort(Sort.Direction.ASC, "item_" + sortField);
    144                 query.addSort(sort);
    145             }
    146             if (sortValue.equals("DESC")){
    147                 Sort sort = new Sort(Sort.Direction.DESC, "item_" + sortField);
    148                 query.addSort(sort);
    149             }
    150         }
    151 
    152         //10.执行查询 获取高亮数据
    153         HighlightPage<TbItem> highlightPage = solrTemplate.queryForHighlightPage(query, TbItem.class);
    154 
    155         List<HighlightEntry<TbItem>> highlighted = highlightPage.getHighlighted();
    156         for (HighlightEntry<TbItem> tbItemHighlightEntry : highlighted) {
    157             TbItem entity = tbItemHighlightEntry.getEntity();//实体对象 现在是没有高亮的数据的
    158 
    159             List<HighlightEntry.Highlight> highlights = tbItemHighlightEntry.getHighlights();
    160             //如有高亮,就取高亮
    161             if(highlights!=null && highlights.size()>0 && highlights.get(0)!=null &&  highlights.get(0).getSnipplets()!=null && highlights.get(0).getSnipplets().size()>0) {
    162                 entity.setTitle(highlights.get(0).getSnipplets().get(0));
    163             }
    164         }
    165         List<TbItem> tbItems = highlightPage.getContent();//获取高亮的文档的集合
    166         //11.执行查询
    167         System.out.println("结果"+tbItems.size());
    168         //12.获取结果集  返回
    169         resultMap.put("rows",tbItems);
    170         resultMap.put("totalPages",highlightPage.getTotalPages());//返回总页数
    171         resultMap.put("total",highlightPage.getTotalElements());//返回总记录数
    172         return resultMap;
    173     }
    174 }

    ②测试相关方法

     1 @RunWith(SpringRunner.class)
     2 @ContextConfiguration("classpath:spring/applicationContext-*.xml")
     3 public class ItemSearchServiceImplTest {
     4 
     5     @Autowired
     6     private ItemSearchService itemSearchService;
     7 
     8     @Test
     9     public void search() {
    10         //构造json字符串
    11         String searchStr = new String("{'keywords':'华为','category':'手机','brand':'华为'," +
    12                 "'spec':{'机身内存':'16G','网络':'联通3G'},'price':'1000-3000'," +
    13                 "'pageNo':1,'pageSize':10,'sortField':'price','sort':'ASC'}");
    14         Map searchMap = JSON.parseObject(searchStr, Map.class);
    15         //根据条件进行搜索过滤
    16         Map<String, Object> search = itemSearchService.search(searchMap);
    17         for (String s : search.keySet()) {
    18             System.out.println(s+":"+search.get(s));
    19         }
    20         Assert.assertNotEquals(0,search.size());
    21         //结果9
    22         /*total:9
    23         totalPages:1
    24         rows:[
    25         TbItem{id=1041685, title='<em style="color:red">华为</em> 麦芒B199 白 电信3G手机 双卡双待双通', price=1249.0, image='http://**.jpg', goodsId=1, category='手机', brand='华为', seller='华为', specMap={网络=联通3G, 机身内存=16G}},
    26         TbItem{id=1060844, title='<em style="color:red">华为</em> Ascend P6S 碳素黑 联通3G手机 双卡双待', price=1259.0,image='http://**.jpg', goodsId=1, category='手机', brand='华为', seller='华为', specMap={网络=联通3G, 机身内存=16G}},
    27         TbItem{id=1075409, title='<em style="color:red">华为</em> 麦芒B199 深灰 电信3G手机 双卡双待双通', price=1269.0, image='http://**.jpg', goodsId=1, category='手机', brand='华为', seller='华为', specMap={网络=联通3G, 机身内存=16G}},
    28         TbItem{id=1082721, title='<em style="color:red">华为</em> 麦芒B199 深灰色 电信3G手机 双模双待双通',price=1269.0, image='http://**.jpg',goodsId=1, category='手机', brand='华为', seller='华为', specMap={网络=联通3G, 机身内存=16G}},
    29         TbItem{id=917460, title='<em style="color:red">华为</em> P6 (P6-C00) 黑 电信3G手机 双卡双待双通',price=1288.0, image='http://**.jpg', goodsId=1, category='手机', brand='华为', seller='华为', specMap={网络=联通3G, 机身内存=16G}},
    30         TbItem{id=917461, title='<em style="color:red">华为</em> P6 (P6-C00) 白 电信3G手机 双卡双待双通',price=1299.0, image='http://**.jpg', goodsId=1, category='手机', brand='华为', seller='华为', specMap={网络=联通3G, 机身内存=16G}},
    31         TbItem{id=1060847, title='<em style="color:red">华为</em> Ascend P6S 阿尔卑斯白 联通3G手机 双卡双待',price=1328.0, image='http://**.jpg', goodsId=1, category='手机', brand='华为', seller='华为', specMap={网络=联通3G, 机身内存=16G}},
    32         TbItem{id=1075413, title='<em style="color:red">华为</em> 麦芒B199 金 电信3G手机 双卡双待双通', price=1329.0, image='http://**.jpg', goodsId=1, category='手机', brand='华为', seller='华为', specMap={网络=联通3G, 机身内存=16G}},
    33         TbItem{id=917770, title='<em style="color:red">华为</em> P6-C00 电信3G手机(粉色) CDMA2000/GSM 双模双待双通', image='http://**.jpg', goodsId=1, category='手机', brand='华为', seller='华为', specMap={网络=联通3G, 机身内存=16G}}]
    34     */
    35     }
    36 
    37     @Test
    38     public void add(){
    39         TbItem item=new TbItem();
    40         item.setId(1L);
    41         item.setBrand("华为");
    42         item.setCategory("手机");
    43         item.setGoodsId(1L);
    44         item.setSeller("华为2号专卖店");
    45         item.setTitle("华为Mate9");
    46         item.setPrice(new BigDecimal(2000));
    47         itemSearchService.add(item);
    48     }
    49 
    50     @Test
    51     public void searchById(){
    52         TbItem tbItem = itemSearchService.searchById(536563);
    53         System.out.println(tbItem.getTitle());
    54     }
    55 
    56     @Test
    57     public void deleteById(){
    58         itemSearchService.deleteById("536563");
    59     }
    60 
    61     @Test
    62     public void pageQuery(){
    63         ScoredPage<TbItem> page = itemSearchService.pageQuery(2, 10);
    64         System.out.println("总记录数:"+page.getTotalElements());
    65         List<TbItem> list = page.getContent();
    66         for(TbItem item:list){
    67             System.out.println(item.getTitle() +item.getPrice());
    68         }
    69     }
    70 
    71     @Test
    72     public void deleteAll(){
    73         itemSearchService.deleteAll();
    74     }
    75 }

    到此为止,Spring Data Solr的基本使用基本就是这样。

  • 相关阅读:
    Android系统四层架构分享
    tracebace用法
    Hadoop生态系统入门进阶之一
    Eclipse无法显示Android设计界面解决方案
    va_start和va_end使用详解
    Android实现左右滑动效果
    Android实现网络多线程断点续传下载
    基于TCP Socket的简单网络通信
    Android通过手势实现图像拖拽功能
    SHELLEXECUTEINFO 结构详解
  • 原文地址:https://www.cnblogs.com/gdwkong/p/8997406.html
Copyright © 2011-2022 走看看