zoukankan      html  css  js  c++  java
  • Solr和Spring Data Solr

    一.Solr概述与安装

    1.Solr简介

      Solr是一个开源搜索平台,用于构建搜索应用程序。 它建立在Lucene(全文搜索引擎)之上。 Solr是企业级的,快速的和高度可扩展的。Solr可以和Hadoop(http://www.yiibai.com/hadoop/)一起使用。由于Hadoop处理大量数据,Solr帮助我们从这么大的源中找到所需的信息。不仅限于搜索,Solr也可以用于存储目的。像其他NoSQL数据库一样,它是一种非关系数据存储和处理技术。总之,Solr是一个可扩展的,可部署,搜索/存储引擎,优化搜索大量以文本为中心的数据。

    2.Linux下Solr的安装

    1:把Tomcat安装包导入Linux系统,解压 Tomcat并重命名tomcat-solr

    2:导入solr的Linux安装包到Linux服务器,解压 solr-4.10.3.tgz.tgz。

    命令:tar -zxvf solr-4.10.3.tgz.tgz

    3:把 solr 下的example/webapps/solr.war部署到 Tomcat-solrwebapps下(去掉版本号)。

    命令:cp solr-4.10.3/example/webapps/solr.war tomcat-solr/webapps/

    4:使用命令解压:unzip -oq solr.war -d solr

    5:把solr下example/lib/ext 目录下的所有的 jar 包,添加到 solr 的工程中(WEB-INFlib目录下)。

    命令:cp solr-4.10.3/example/lib/ext/* tomcat-solr/webapps/solr/WEB-INF/lib/

    6:创建一个索引仓库。solr 下的/example/solr 目录就是一个索引仓库。复制此目录【solr】到任意磁盘即可完 成自定义索引仓库创建。

    命令:cp -r solr-4.10.3/example/solr . (把solr索引库仓库文件夹拷贝当前目录; . 表示当前目录)

    7:关联tomcat服务器中 solr项目 和 索引仓库solr。修改Tomcat/bin/catalina.sh文件指定索引仓库路径即可

     //在Tomcat/bin/catalina.sh第二行指定索引仓库路径即可
        export JAVA_OPTS="-Dsolr.solr.home=/usr/local/dintalk/singleSolr/solr"

    8:启动 Tomcat

    http://IP:8080/solr/

    9.加载contrib,dist依赖类库

    把solr-4.10.3/contrib,dist 文件夹拷贝 索引仓库目录下(和索引库在同一级目录)

    命令:cp -r solr-4.10.3/contrib/ solr-4.10.3/dist/ solr

    配置solr索引库中核心配置文件:solrConfig.xml 加载contrib,dist依赖jar包

    3.中文分析器IK Analyzer的安装

    IK Analyzer 是一个开源的,基亍 java 语言开发的轻量级的中文分词工具包。最初,它是以开源项目Luence 为应用主体的,结合词典分词和文法分析算法的中文分词组件。从 3.0 版本开始,IK 发展为面向 Java 的公用分词组件,独立亍 Lucene 项目,同时提供了对 Lucene 的默认优化实现。在 2012 版本中,IK 实现了简单的分词歧义排除算法,标志着 IK 分词器从单纯的词典分词向模拟语义分词衍化。

    3.1IK Analyzer配置

    1、把IKAnalyzer2012FF_u1.jar 添加到 solr 工程的 lib 目录下

    2、创建WEB-INF/classes(手动创建)文件夹 把扩展词典、停用词词典、配置文件放到 solr 工程的 WEB- INF/classes 目录下:

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

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

    4.业务域配置

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

    域的常用属性:

    • name:指定域的名称

    • type:指定域的类型

    • indexed:是否索引

    • stored:是否存储

    • required:是否必须

    • multiValued:是否多值

    4.1普通域

    修改solrhome的schema.xml 文件  设置业务系统 Field :

    <!-- 域的配置 :普通域 -->
    <!--name:字段名称  type:字段类型 stored:是否存储分词前内容(复制域选择false) 
    required:是否必填  indexed:索引(是否进行查询)  -->
    <field name="item_goodsid" type="long" indexed="true" stored="true"/>
    <field name="item_title" type="text_ik" indexed="true" stored="true"/>
    <field name="item_price" type="double" indexed="true" stored="true"/>
    <field name="item_image" type="string" indexed="false" stored="true" />
    <field name="item_category" type="string" indexed="true" stored="true" />
    <field name="item_seller" type="text_ik" indexed="true" stored="true" />
    <field name="item_brand" type="string" indexed="true" stored="true" />

    4.2复制域

    复制域的作用在于将某一个Field中的数据复制到另一个域中 :

    <!-- 域的配置 :复制域 -->
    <!--multiValued 是否有多值  stored="false" 不需要存储 -->
    <field name="item_keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/>
    <copyField source="item_title" dest="item_keywords"/>
    <copyField source="item_category" dest="item_keywords"/>
    <copyField source="item_seller" dest="item_keywords"/>
    <copyField source="item_brand" dest="item_keywords"/>

    4.3动态域

    当我们需要动态扩充字段时,我们需要使用动态域。所以我们需要使用动态域来实现。需要实现的效果示例如下 :

    <!-- 域的配置:动态域 -->
    <!--因为数据中的item_spec_后面的内容不固定所以用*号代替(即动态查询条件匹配) -->
    <dynamicField name="item_spec_*" type="string" indexed="true" stored="true" />  

    二.Spring Data Solr

    如何将Solr的应用集成到Spring中?Spring Data Solr就是为了方便Solr的开发所研制的一个框架,其底层是对SolrJ(官方API)的封装。

    1.Spring Data Solr 使用步骤

    第一步:导入相关坐标

    <!-- SpringDataSolr的坐标 -->
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-solr</artifactId>
        <version>1.5.5.RELEASE</version>
    </dependency>

    第二步:resources下编写配置文件(applicationContext-solr.xml)

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:solr="http://www.springframework.org/schema/data/solr"
           xsi:schemaLocation="http://www.springframework.org/schema/data/solr
              http://www.springframework.org/schema/data/solr/spring-solr-1.0.xsd
            http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans.xsd">
        
        <!-- 0.配置包扫描(注解) 
        <context:component-scan base-package="cn.dintalk.solrutil"></context:component-scan>
        -->
        
        <!-- 1.solr服务器地址 -->
        <solr:solr-server id="solrServer" url="http://127.0.0.1:8080/solr"/>
    
        <!-- 2.solr模板,使用solr模板可对索引库进行CRUD的操作 -->
        <bean id="solrTemplate" class="org.springframework.data.solr.core.SolrTemplate">
            <constructor-arg ref="solrServer"/>
        </bean>
    </beans>

    第三步:为操作的实体类添加@Filed注解

    public class TbItem implements Serializable{
        
        // id 和solr中的主键名称一致可以不用写
        @Field
        private Long id;
        
        @Field("item_title")
        private String title;
    
        @Field("item_image")
        private String image;
        ...

    第四步:基本的CRUD

    /**
     * 测试Solr的简单操作
     * @author Mr.song
     * @date 2019/06/13 16:55
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext-solr.xml")
    public class SolrTest {
    
        @Autowired
        private SolrTemplate solrTemplate;
    
        @Test   //增加item
        public void testAddItemToSolrHome(){
            TbItem item = new TbItem();
            item.setId(1L);
            item.setTitle("测试Title");
            item.setImage("这是图片");
            solrTemplate.saveBean(item);
            //增删改需要提交
            solrTemplate.commit();
        }
    
        //重新赋值便是更新
    
        @Test //根据主键查询
        public void testQueryById(){
            TbItem item = solrTemplate.getById(1L, TbItem.class);
            System.out.println(item.getTitle());
        }
    
        @Test  //条件查询
        public void testQuery(){
            //查询所有, *:*  前面是字段  后面是条件
            SimpleQuery simpleQuery = new SimpleQuery("*:*");
            //设置分页
            simpleQuery.setOffset(5);//开始索引
            simpleQuery.setRows(5);//每页记录数
            //拼接条件
            Criteria criteria = new Criteria("item_title");
            criteria = criteria.contains("苹果");
            simpleQuery.addCriteria(criteria);
    
            ScoredPage<TbItem> items = solrTemplate.queryForPage(simpleQuery, TbItem.class);
            for (TbItem item : items) {
                System.out.println(item.getTitle());
            }
        }
    
        @Test  //删除item(根据主键删除)
        public void testDelItem(){
            solrTemplate.deleteById("1");
            solrTemplate.commit();
        }
    
        @Test //清空
        public void delSolrHome(){
            SimpleQuery query = new SimpleQuery("*:*");
            solrTemplate.delete(query);
            solrTemplate.commit();
        }
    }

    三.SpringDataSolr的常用API示例

    /**
     * @author Mr.song
     * @date 2019/06/13 18:22
     */
    @Service
    public class ItemSearchServiceImpl implements ItemSearchService {
    
        @Autowired
        private TbItemMapper itemMapper;
    
        @Autowired
        private SolrTemplate solrTemplate;
    
        /**
         * 根据关键字查询solr索引库
         * @param searchMap 前端的查询条件,封装为Map
         * @return 查询的结果,不封装为实体,返回Map效果一样
         */
        @Override
        public Map search(Map searchMap) {
            
            //1.关键字查询
            String keywords = searchMap.get("keywords").toString();
            //2.封装查询条件(高亮查询)
            SimpleHighlightQuery simpleHighlightQuery = new SimpleHighlightQuery();
            if (keywords != null) {
                //这是查询的域
                Criteria criteria = new Criteria("item_keywords");
                criteria = criteria.contains(keywords);
                simpleHighlightQuery.addCriteria(criteria);
            }
    
            //1.高亮查询的高亮设置(即查询关键字的高亮显示)
            HighlightOptions options = new HighlightOptions();
            //设置高亮的头部和尾部
            options.setSimplePrefix("<em style='color:red'>");
            options.setSimplePostfix("</em>");
            //设置高亮的域名字,设置查询的选项
            options.addField("item_title");
            simpleHighlightQuery.setHighlightOptions(options);
    
            //2.过滤查询条件=====分类相关
            String categoryStr = (String) searchMap.get("category");
            if (categoryStr != null){
                //2.1按照传入的分类字符串进行查询,创建过滤查询对象
                SimpleFilterQuery filterQuery = new SimpleFilterQuery();
                //2.2创建条件,设置域的名称
                Criteria criteria = new Criteria("item_category");
                //2.3设置匹配字符串
                criteria = criteria.contains(categoryStr);
                //2.4过滤查询添加条件
                filterQuery.addCriteria(criteria);
                //2.5查询添加过滤查询
                simpleHighlightQuery.addFilterQuery(filterQuery);
                System.out.println("分类过滤查询:"+categoryStr);
            }
    
            //3.过滤查询条件=====品牌相关
            String brand = (String) searchMap.get("brand");
            if (brand != null){
                SimpleFilterQuery filterQuery = new SimpleFilterQuery();
                Criteria criteria = new Criteria("item_brand");
                criteria = criteria.contains(brand);
                filterQuery.addCriteria(criteria);
                simpleHighlightQuery.addFilterQuery(filterQuery);
                System.out.println("品牌过滤查询:"+brand);
            }
    
            //4.过滤查询条件=====规格相关
            Map<String,String> specMap = (Map) searchMap.get("spec");
            if (specMap != null){
                for (String s : specMap.keySet()) {
                    SimpleFilterQuery filterQuery = new SimpleFilterQuery();
                    Criteria criteria = new Criteria("item_spec_"+s);
                    criteria = criteria.contains(specMap.get(s));
                    filterQuery.addCriteria(criteria);
                    simpleHighlightQuery.addFilterQuery(filterQuery);
                    System.out.println("规格滤查询:"+s);
                }
            }
    
            //4.过滤条件查询=====价格相关
            String price = (String) searchMap.get("price");//0-199
            if (price != null){
                //1500-2000
                String[] prices = price.split("-");
    
                //过滤条件增加起始价格
                SimpleFilterQuery filterQuery1 = new SimpleFilterQuery();
                Criteria criteria1 = new Criteria("item_price");
                criteria1 = criteria1.greaterThanEqual(prices[0]); //大于等于起始价格
                filterQuery1.addCriteria(criteria1);
                simpleHighlightQuery.addFilterQuery(filterQuery1);
    
                //过滤条件增加结束价格
                if(!"*".equals(prices[1])){  //防止3000元以上的*号
                    SimpleFilterQuery filterQuery2 = new SimpleFilterQuery();
                    Criteria criteria2 = new Criteria("item_price");
                    criteria2 = criteria2.lessThanEqual(prices[1]); //小于等于结束价格
                    filterQuery2.addCriteria(criteria2);
                    simpleHighlightQuery.addFilterQuery(filterQuery2);
                }
                System.out.println("价格过滤查询:"+price);
            }
    
            //5.价格升降序=======价格升降序,前端设置默认是desc
            String sort = (String) searchMap.get("sort");
            //定义排序对象
            Sort orders = null;
            if ("DESC".equalsIgnoreCase(sort)){
                //降序查询 :参数1-指定升降序   参数2-指定域
                orders = new Sort(Sort.Direction.DESC,"item_price");
                System.out.println("价格降序过滤查询:"+sort);
            }else {
                orders = new Sort(Sort.Direction.ASC,"item_price");
                System.out.println("价格升序过滤查询:"+sort);
            }
            //为查询添加排序
            simpleHighlightQuery.addSort(orders);
    
            //6.分页相关
            Integer pageSize = (Integer) searchMap.get("pageSize");
            Integer pageNo = (Integer) searchMap.get("pageNo");
            //设置分页参数 :起始索引和每页条数
            simpleHighlightQuery.setOffset((pageNo-1)*pageSize);
            simpleHighlightQuery.setRows(pageSize);
    
            //查询
            HighlightPage<TbItem> tbItems = solrTemplate.
                queryForHighlightPage(simpleHighlightQuery, TbItem.class);
            //将高亮设置后的属性赋值给查出的内容
            List<TbItem> contents = tbItems.getContent();
            for (TbItem content : contents) {
                List<HighlightEntry.Highlight> highlights = tbItems.getHighlights(content);
                if (highlights.size()>0){//防止数组空内容
                    List<String> snipplets = highlights.get(0).getSnipplets();
                    if (snipplets.size()>0){//防止空内容
                        content.setTitle(snipplets.get(0));
                    }
                }
            }
    
            //构建返回的map对象
            Map returnMap = new HashMap();
            returnMap.put("rows", contents);
            //分页需要总记录数
            returnMap.put("total",tbItems.getNumberOfElements());
            return returnMap;
        }
    
        /**
         * 根据商品的id(spu)将商品(sku)导入到solr索引库中
         * @param ids 商品的id数组(spu)
         */
        @Override
        public void importItemsByGoodsId(Long[] ids) {
            //goods的所有ids,查询其列表导入到solr索引库
            TbItemExample example = new TbItemExample();
            example.createCriteria().andGoodsIdIn(Arrays.asList(ids));
            List<TbItem> tbItems = itemMapper.selectByExample(example);
            //将数据存入solr索引库
            solrTemplate.saveBeans(tbItems);
            solrTemplate.commit();
            System.out.println("solr库导入了商品:"+tbItems.size()+"个");
        }
    
    
        /**
         * 根据商品的id(spu)将商品(sku)从solr索引库中删除
         * @param ids 商品的id数组(spu)
         */
        @Override
        public void delItemsByGoodsId(Long[] ids) {
            //id是goods的id
            TbItemExample example = new TbItemExample();
            example.createCriteria().andGoodsIdIn(Arrays.asList(ids));
            List<TbItem> tbItems = itemMapper.selectByExample(example);
            //循环删除sku(solr索引库中的)
            for (TbItem tbItem : tbItems) {
                solrTemplate.deleteById(tbItem.getId().toString());
            }
            solrTemplate.commit();
            System.out.println("solr库删除了商品:"+tbItems.size()+"个");
        }
    }

    关注微信公众号,  随时随地学习

  • 相关阅读:
    awesome-blazor
    SQlite+dapper操作
    HashMap和HashTable的区别
    Linux常见命令大全
    多态的典型例题
    Hbase的安装及配置
    利用线程和管道的方式从客户端向服务的进行传送照片
    对TreeSet中的元素"HashSet"、"ArrayList"、"TreeMap"、"HashMap"、"TreeSet"、"LinkedList"进行升序 * 使用静态内部类实现
    对TreeSet中的元素"HashSet"、"ArrayList"、"TreeMap"、"HashMap"、"TreeSet"、"LinkedList"进行升序 *使用匿名内部类实现
    使用TreeSet和Comparator,写TreeSetTest1 要求:对TreeSet中的元素"HashSet"、"ArrayList"、"TreeMap"、 "HashMap"、"TreeSet"、"LinkedList"进行升序和倒序排列
  • 原文地址:https://www.cnblogs.com/dintalk/p/11028739.html
Copyright © 2011-2022 走看看