FutureJava学习笔记solr

04_Solr的入门与使用

2017-09-02  本文已影响58人  明天你好向前奔跑

1. solr介绍

solr是apache开源项目,基于lucene,并默认运行在jetty这个serlvet容器中的优秀的全文检索服务器。可通过http请求实现索引和搜索功能。

solr 和lucene区别
lucene是一个全文检索的工具包,不能够进行独立的部署,对外提供服务,必须嵌入到系统中,高度耦合依托于系统,并提供搜索服务。
solr 是一个全文检索的搜索引擎(服务器/软件系统),能够独立部署,可以对外单独提供搜索服务。
solr 可以降低耦合度,lucene不可以。

下载地址:
    Solr官方网站:
    http://lucene.apache.org/solr/ 
    下载地址:
    http://archive.apache.org/dist/lucene/solr/

解压后的目录结构:

1.png

example下的目录结构:

2.png

1. tomcat整合solr运行

第一步:解压

1. 在D盘新建一个文件夹solr,将一个新的tomcat与solr工程解压到该文件夹
3.png
2. 在解压后的solr-4.10.3\example\webapps目录下拷贝solr.war到tomcat的webapp目录下解压,为方便统一管理,解压到solr的文件夹。解压完毕删除war包
4.png

第二步:拷贝jar包

3. 拷贝D:\solr\solr-4.10.3\example\lib\ext下的所有jar包到D:\solr\apache-tomcat-7.0.52\webapps\solr\WEB-INF\lib目录下

第三步:solrhome

4. 在solr目录下新建文件夹solrhome。
    概念:SolrHome是Solr运行的主目录,目录中包括了运行Solr实例所有的配置文件和数据文件,Solr实例就是SolrCore。example\solr下就是一个solr home目录结构。

拷贝D:\solr\solr-4.10.3\example\solr下的所有目录到D:\solr\solrhome下,完成一个solrCore的创建。可以在该目录下创建多个实例
5.png
5. 最后,修改D:\solr\apache-tomcat-7.0.52\webapps\solr\WEB-INF,告诉它solrhome的位置
6.png
到D:\solr\apache-tomcat-7.0.52\bin中启动tomcat,访问http://localhost:8080/solr,能够成功访问代表搭建成功
7.png
如果默认开启的tomcat不是当前路径下的tomcat,删除环境变量里的CATALINA_HOME的配置
1.1 新建solrcore
添加solrcore:
第一步:在D:\solr\solrhome复制collection1改名为collection2
第二步:修改core.properties。设置name=collection2
第三步:重启tomcat 
1.2 使用IKAnalyzer中文分析器。
第一步:把IKAnalyzer2012FF_u1.jar添加到tomcat中的solr/WEB-INF/lib目录下

第二步:复制IKAnalyzer的配置文件和自定义词典和停用词词典到solr项目的classpath下。
        在WEB-INF下如果没有classes 需要创建一个。
8.png
第三步:在schema.xml中添加一个自定义的fieldType,使用中文分析器。
<!-- IKAnalyzer-->
<fieldType name="text_ik" class="solr.TextField">
  <analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/>
</fieldType>

第四步:定义field,指定field的type属性为text_ik
<!--IKAnalyzer Field-->

<field name="title_ik" type="text_ik" indexed="true" stored="true" />
<field name="content_ik" type="text_ik" indexed="true" stored="false" multiValued="true"/>

第五步:重启tomcat,在analyze下输入中文进行测试
9.png

2. Solr管理索引库

2.1 维护索引
1. 添加单个文档(略)
2. 批量导入数据
    使用dataimport插件批量导入数据。如果collection1下没有lib  需要自己创建一个。

第一步:在D:\solr\solr-4.10.3\dist目录下把dataimport插件依赖的jar包添加到solrcore(collection1\lib)中,没有目录就新建目录

因为要批量根据数据库的数据创建索引,因此还需要导入数据库驱动
11.png
第二步:配置collection1\conf\下的solrconfig.xml文件,添加一个requestHandler。
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
<lst name="defaults">
  <str name="config">data-config.xml</str>
 </lst>
</requestHandler> 

第三步:在同级目录下创建一个data-config.xml,保存到collection1\conf\目录下
<?xml version="1.0" encoding="UTF-8" ?>  
<dataConfig>   
<dataSource type="JdbcDataSource"   
          driver="com.mysql.jdbc.Driver"   
          url="jdbc:mysql://localhost:3306/solr"   
          user="root"   
          password="root"/>   
<document>   
    <entity name="product" query="SELECT pid,name,catalog_name,price,description,picture FROM products ">
         <field column="pid" name="id"/> 
         <field column="name" name="product_name"/> 
         <field column="catalog_name" name="product_catalog_name"/> 
         <field column="price" name="product_price"/> 
         <field column="description" name="product_description"/> 
         <field column="picture" name="product_picture"/> 
    </entity>   
</document>   
</dataConfig>


3. 上面name对应的是field域,自定义的域要自己在schema.xml中配置
如果不使用Solr提供的Field可以针对具体的业务需要自定义一套Field,如下是商品信息Field:
<!--product-->
<field name="product_name" type="text_ik" indexed="true" stored="true"/>
<field name="product_price"  type="float" indexed="true" stored="true"/>
<field name="product_description" type="text_ik" indexed="true" stored="false" />
<field name="product_picture" type="string" indexed="false" stored="true" />
<field name="product_catalog_name" type="string" indexed="true" stored="true" />

<field name="product_keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/>

<copyField source="product_name" dest="product_keywords"/>
<copyField source="product_description" dest="product_keywords"/>

4. 重启tomcat,完成批量索引的创建
12.png
2.2 删除文档
删除索引格式如下:

1) 删除指定ID的索引 
<delete>
    <id>8</id>
</delete>
需要加上
<commit />

2) 删除查询到的索引数据 
<delete>
    <query>product_catalog_name:幽默杂货</query>
</delete>
<commit />

3) 删除所有索引数据
 <delete>
    <query>*:*</query>
</delete>
<commit />
2.3 查询索引
通过/select搜索索引,Solr制定一些参数完成不同需求的搜索:

1. q - 查询字符串,必须的,如果查询所有使用*:*。
2. fq - (filter query)过虑查询,作用:在q查询符合结果中同时是fq查询符合的
    也可以在“q”查询条件中使用product_price:[1 TO 20]
    也可以使用“*”表示无限,例如:
    20以上:product_price:[20 TO *]
    20以下:product_price:[* TO 20]
13.png
3. sort - 排序
    product_price desc/asc

4. start - 分页显示使用,开始记录下标,从0开始
5. rows - 指定返回结果最多有多少条记录,配合start来实现分页。

6. fl - 指定返回那些字段内容,用逗号或空格分隔多个
7. df-指定一个默认搜索Field
8. wt - (writer type)指定输出格式,可以有 xml, json, php, phps,
9. hl 是否高亮 ,设置高亮Field,设置格式前缀和后缀

3. 使用soloJ管理索引库

solrj是访问Solr服务的java客户端,提供索引和搜索的请求方法,SolrJ通常在嵌入在业务系统中,通过SolrJ的API接口操作Solr服务,如下图:
14.png
3.1 依赖的jar包
依赖solrj及solrj依赖包.
15.png
以及lib下的扩展依赖包
16.png
3.1 使用solrj添加文档
第一步:创建一个java工程
第二步:导入jar包。包括solrJ的jar包,依赖包,扩展包(如上图所示)

代码开发步骤:
第三步:和Solr服务器建立连接。HttpSolrServer对象建立连接。
第四步:创建一个SolrInputDocument对象,然后添加域。
第五步:将SolrInputDocument添加到索引库。
第六步:提交。

//向索引库中添加索引
@Test
public void addDocument() throws Exception {
    //和solr服务器创建连接
    //参数:solr服务器的地址
    SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr");
    //创建一个文档对象
    SolrInputDocument document = new SolrInputDocument();
    //向文档中添加域
    //第一个参数:域的名称,域的名称必须是在schema.xml中定义的
    //第二个参数:域的值
    document.addField("id", "c0001");
    document.addField("title_ik", "使用solrJ添加的文档");  
    document.addField("product_name", "商品名称");
    //把document对象添加到索引库中
    solrServer.add(document);
    //提交修改
    solrServer.commit();    
}
3.2 删除文档
根据id删除
//删除文档,根据id删除
@Test
public void deleteDocumentByid() throws Exception {
    //创建连接
    SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr");
    //根据id删除文档
    solrServer.deleteById("c0001");
    //提交修改
    solrServer.commit();
}

根据查询删除
//根据查询条件删除文档
@Test
public void deleteDocumentByQuery() throws Exception {
    //创建连接
    SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr");
    //根据查询条件删除文档
    solrServer.deleteByQuery("*:*");
    //提交修改
    solrServer.commit();
}
3.3 修改文档
在solrJ中修改没有对应的update方法,只有add方法,只需要添加一条新的文档,和被修改的文档id一致就可以修改了。
本质上就是先删除后添加。

@Test
public void testUpdate() throws IOException, SolrServerException {
    //连接SolrServer服务器
    SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr");
    //获取一个文档对象,添加域
    SolrInputDocument document = new SolrInputDocument();
    document.addField("id","002");
    document.addField("name","testtest22222");
    //更新
    solrServer.add(document);
    //提交修改
    solrServer.commit();
}

4. 查询文档

4.1 简单查询
//查询索引
@Test
public void queryIndex() throws Exception {
    //创建连接
    SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr");
    //创建一个query对象
    SolrQuery query = new SolrQuery();
    //设置查询条件
    query.setQuery("*:*");
    //执行查询
    QueryResponse queryResponse = solrServer.query(query);
    //取查询结果
    SolrDocumentList solrDocumentList = queryResponse.getResults();
    //共查询到商品数量
    System.out.println("共查询到商品数量:" + solrDocumentList.getNumFound());
    //遍历查询的结果
    for (SolrDocument solrDocument : solrDocumentList) {
        System.out.println(solrDocument.get("id"));
        System.out.println(solrDocument.get("product_name"));
        System.out.println(solrDocument.get("product_price"));
        System.out.println(solrDocument.get("product_catalog_name"));
        System.out.println(solrDocument.get("product_picture"));
    }
}
4.2 复杂查询
@Test
//其中包含查询、过滤、分页、排序、高亮显示等处理。
public void testQuery2() throws SolrServerException {
    //获取SolrServer连接
    SolrServer solrServer = new HttpSolrServer("http://localhost:8080/solr");
    //获取查询对象
    SolrQuery query = new SolrQuery();
    //设置查询条件
    //query.set("q","id:1");
    //query.setQuery("1");
    query.setQuery("情侣");
    //设置过滤条件
    query.setFilterQueries("product_catalog_name:幽默杂货");

    //排序(按价格从低到高排序)
    query.addSort("product_price", SolrQuery.ORDER.asc);
    //分页处理
    query.setStart(0);
    query.setRows(20);

    //结果中显示域的列表
    query.setFields("id", "product_name", "product_price", "product_catalog_name", "product_picture");

    //设置默认搜索域
    query.set("df", "product_name");//这里以商品名称作为默认搜索域

    //高亮显示,开启高亮开关
    query.setHighlight(true);
    //高亮显示的域
    query.addHighlightField("product_keywords");
    //高亮显示的前缀
    query.setHighlightSimplePost("<em style='color:red'>");
    //高亮显示的后缀
    query.setHighlightSimplePre("</em>");

    //执行查询
    QueryResponse queryResponse = solrServer.query(query);
    //获取查询的结果集
    SolrDocumentList solrDocumentList = queryResponse.getResults();

    //供查询到商品数量
    System.out.println(solrDocumentList.getNumFound());

    //遍历结果集
    for (SolrDocument document : solrDocumentList) {
        //高亮显示的存储位置不同,取出高亮显示的商品名
        Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting();
        //以id存在大Map中,再根据高亮显示的域从小Map中取出高亮显示的List
        List<String> list = highlighting.get(document.get("id")).get(document.get("product_name"));

        //判断是否有高亮内容
        String product_name = null;
        if (list != null) {//有高亮显示内容
            product_name = list.get(0);
        } else {//没有,就从document里面取
            product_name = (String) document.get("product_name");
        }

        System.out.println(product_name);
        System.out.println(document.get("id"));
        System.out.println(document.get("product_price"));
        System.out.println(document.get("product_catalog_name"));
        System.out.println(document.get("product_picture"));
    }
}

4. solr案例:模拟京东站内搜索

4.1 使用上面已经创建的索引库
4.2 搭建java程序环境
第一步:创建一个web工程导入jar包
1、springmvc的相关jar包
2、solrJ的核心包和依赖包
3、Example\lib\ext下的jar包
4、拷贝静态资源到工程项目webContent下

分析,此案例暂时不用到持久化相关的比如mybatis相关的配置项。而是使用solr索引库
所以可以使用springmvc.xml作为spring容器进行配置,因为springmvc是spring的子容器。


第二步:配置文件与包结构的创建

包结构:

17.png
web.xml:

    <!--解决Post请求乱码-->
    <filter>
        <filter-name>encoding</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!--springmvc前端控制器-->
    <servlet>
        <servlet-name>jd-springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:config/springmvc.xml</param-value>
        </init-param>
        <!--tomcat容器一启动就加载springmvc前端控制器-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>jd-springmvc</servlet-name>
        <url-pattern>*.action</url-pattern>
    </servlet-mapping>

--------------------------------------------------------------------------------

springmvc.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:p="http://www.springframework.org/schema/p"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
            http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    
    
        <!--配置组件扫描-->
        <context:component-scan base-package="main.com.itdream.jd"/>
    
        <!--配置注解驱动,相当于配置了处理器映射器,处理器适配器-->
        <mvc:annotation-driven/>
    
        <!--视图解析器-->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/jsp/"/>
            <property name="suffix" value=".jsp"/>
        </bean>
    </beans>

    

第二步:Pojo:
    商品对象模型:
    public class ProductModel {
        // 商品编号
        private Integer pid;
        // 商品名称
        private String name;
        // 商品分类名称
        private String catalog_name;
        // 价格
        private Float price;    
        // 图片名称
        private String picture;
    }
    
    返回值对象模型
    public class ResultModel {
        // 商品列表
        private List<ProductModel> productList;
        // 商品总数
        private Long recordCount;
        // 总页数
        private Integer pageCount;
        // 当前页
        private Integer curPage;
        // 每页的行数
        private Integer rows;
    }

查询条件对象QueryVo:
public class QueryVo implements Serializable {

    private String queryString;//关键字搜索
    private String catalog_name;//分类搜索
    private String price;//价格区间搜索
    private String sort;//排序

    private Integer page;//当前页
    private Integer rows;//每页显示条数
}
    

环境搭建完成。
4.2 代码开发
从dao层开始开发:
    功能:接收service层传递过来的参数,根据参数查询索引库,返回查询结果。
    参数:SolrQuery对象
    返回值:一个商品列表List<ProductModel>,还需要返回查询结果的总数量。
    返回:ResultModel
    方法定义:ResultModel search (SolrQuery query) throws Exception;

SearchDao:
/**
 * 根据查询条件进行搜索
 *
 * @param solrQuery
 * @return
 */
ResultModel search(SolrQuery solrQuery) throws Exception;

SearchDaoImpl :
@Repository
public class SearchDaoImpl implements SearchDao {

    @Autowired
    private SolrServer solrServer;

    @Override
    public ResultModel search(SolrQuery solrQuery) throws Exception {

        //1. 连接SolrServer索引库服务器,交由Spring管理
        //2. 根据查询条件执行查询
        QueryResponse queryResponse = solrServer.query(solrQuery);
        //根据查询条件获取结果集
        SolrDocumentList solrDocumentList = queryResponse.getResults();

        //获取高亮内容
        Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting();

        //构建ResultModel中的商品列表的ProductList集合
        List<ProductModel> productList = new ArrayList<>();
        ProductModel productModel = null;

        //遍历结果集
        for(SolrDocument document : solrDocumentList) {
            productModel = new ProductModel();

            //获取高亮
            List<String> list = highlighting.get(document.get("id")).get("product_name");

            String product_name = null;
            if(list != null && list.size() >0) { //有高亮
                product_name = list.get(0);
            }else {
                product_name = (String) document.get("product_name");
            }

            //获取ProductModel的其他内容
            //获取id
            Integer id = null;
            Object idObj = document.get("id");
            if(idObj != null) {
                id = Integer.parseInt(idObj.toString());
            }

            //获取分类名称
            String product_catalog_name = (String)document.get("product_catalog_name");
            //获取价格
            Float product_price = (Float) document.get("product_price");
            //获取图片名称
            String product_picture = (String) document.get("product_picture");

            //设置到ProductModel中
            productModel.setPid(id);
            productModel.setName(product_name);
            productModel.setCatalog_name(product_catalog_name);
            productModel.setPrice(product_price);
            productModel.setPicture(product_picture);

            //将该ProductModel添加到List中
            productList.add(productModel);
        }

        //3. 构建ResultModel实例将查询结果设置进去
        ResultModel model = new ResultModel();

        //设置总页数
        //设置当前页
        //设置每页的行数
        //-------以上在service层设置----------

        //设置商品列表
        model.setProductList(productList);
        //设置商品总数
        model.setRecordCount(solrDocumentList.getNumFound());
        return model;
    }
}

---------------------------------------------------------------------------

ProductService :
public interface ProductService {

    /**
     * 根据查询条件查询ResultMoedl
     *
     * @param vo
     * @return
     */
    ResultModel getResultModelByQuery(QueryVo vo) throws Exception;
}


ProductServiceImpl 实现类:
@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private SearchDao searchDao;

    @Override
    public ResultModel getResultModelByQuery(QueryVo vo) throws Exception {

        //构建查询条件,封装查询条件
        SolrQuery solrQuery = new SolrQuery();

        //判断queryString是否为空
        if (!StringUtils.isEmpty(vo.getQueryString())) {
            //根据关键字搜索
            solrQuery.setQuery(vo.getQueryString());
        } else {
            solrQuery.setQuery("*:*");
            //solrQuery.set("q", "*:*");
        }

        //判断分类搜索条件是否为空
        if (!StringUtils.isEmpty(vo.getCatalog_name())) {
            //分类搜索不为空
            solrQuery.setFilterQueries("product_catalog_name:" + vo.getCatalog_name());
        }

        //判断价格搜索
        if (!StringUtils.isEmpty(vo.getPrice())) {
            //价格搜索不为空
            String[] split = vo.getPrice().split("-");
            solrQuery.setFilterQueries("product_price:[" + split[0] + " TO " + split[1] + "]");
        }

        //判断排序
        String sort = vo.getSort();
        if (!StringUtils.isEmpty(sort)) {
            if ("0".equals(sort)) {//升序
                solrQuery.addSort("product_price", SolrQuery.ORDER.asc);
            } else {//降序
                solrQuery.addSort("product_price", SolrQuery.ORDER.desc);
            }
        }
        //指定默认搜索域
        solrQuery.set("df", "product_keywords");

        //设置分页条件查询
        Integer page = vo.getPage();//当前页
        Integer rows = vo.getRows();//每页显示条数

        //设置第几条开始查询
        Integer start = (page - 1) * rows;
        solrQuery.setStart(start);
        //设置差多少条
        solrQuery.setRows(rows);

        //开启高亮
        solrQuery.setHighlight(true);
        //指定高亮显示的域
        solrQuery.addHighlightField("product_name");
        //设置高亮显示文本的前缀
        solrQuery.setHighlightSimplePre("<em style=\"color:red\">");
        //设置高亮显示文本的后缀
        solrQuery.setHighlightSimplePost("</em>");

        //-------查询条件封装完毕:包括默认域查询,过滤查询,排序,分页-------
        //调用dao查询结果,获取结果模型
        ResultModel resultModel = searchDao.search(solrQuery);

        //------在service层设置---------
        //设置当前页
        resultModel.setCurPage(page);
        //设置每页的行数
        resultModel.setRows(rows);
        //设置总页数
        Long recordCount = resultModel.getRecordCount();//总记录数
        Long pageCount = (recordCount + rows - 1) / rows;
        resultModel.setPageCount(pageCount);

        return resultModel;
    }
}

@Controller
public class ProductController {

    @Autowired
    private ProductService productService;

    @RequestMapping("list.action")
    public String listByQuery(QueryVo vo, Model model) {
        try {

            //如果页面未传值,给一定的值给定默认值
            if(vo.getPage() == null) {
                vo.setPage(1);
            }
            if(vo.getRows() == null) {
                vo.setRows(16);
            }
            if(vo.getSort() == null) {
                vo.setSort("1");
            }

            ResultModel resultModel = productService.getResultModelByQuery(vo);
            //添加数据,根据页面确定key
            model.addAttribute("result",resultModel);

            //回显查询条件,也是根据页面确定key
            model.addAttribute("queryString",vo.getQueryString());
            model.addAttribute("catalog_name",vo.getCatalog_name());
            model.addAttribute("price",vo.getPrice());
            model.addAttribute("page",vo.getPage());
            model.addAttribute("sort",vo.getSort());

        } catch (Exception e) {
            e.printStackTrace();
        }
        return "product_list";
    }
}


-----
代码开发完成
4.3 注意的问题
1. solr启动的tomcat可能会与程序运行的tomcat冲突,在solr启动的tomcat中修改端口号,我这里修改了8090.
要注意的是:这里修改之后,springmvc.xml中配置的solrServer连接服务器的配置记得更改:指定到具体的solrCore实例,否则会报错:
    Expected mime type application/octet-stream but got text/html

springmvc.xml配置:
    <!--注册SolrServer连接服务器-->
    <bean id="solrServer" class="org.apache.solr.client.solrj.impl.HttpSolrServer">
        <constructor-arg name="baseURL" value="http://localhost:8090/solr/collection1" />
    </bean>

2. 使用价格条件筛选的时候,格式是  product_pric:[10 TO *],闭括号代表包括,大括号代表不包括,星号代表无穷大或无穷小。
要注意的是:条件拼接要注意 TO 的两边要有空格

展示效果:

18.png
上一篇下一篇

猜你喜欢

热点阅读