首页 > 代码库 > 搜索引擎之solr小结

搜索引擎之solr小结

      前几天被安排到一个项目组里,项目组需要用到搜索引擎技术,因此花了两天调研了一下,后来又说不用了,那就做个小结,写个博文。

一、Solr定义现在被应用的最多的搜索引擎就是solr,solr的定义先贴一下:

       Solr是一个基于Lucene的Java搜索引擎服务器。Solr 提供了层面搜索、命中醒目显示并且支持多种输出格式(包括 XML/XSLT 和 JSON 格式)。它易于安装和配置,而且附带了一个基于 HTTP 的管理界面。Solr已经在众多大型的网站中使用,较为成熟和稳定。Solr 包装并扩展了 Lucene,所以Solr的基本上沿用了Lucene的相关术语。更重要的是,Solr 创建的索引与 Lucene 搜索引擎库完全兼容。通过对Solr 进行适当的配置,某些情况下可能需要进行编码,Solr 可以阅读和使用构建到其他 Lucene 应用程序中的索引。此外,很多 Lucene 工具(如Nutch、 Luke)也可以使用Solr 创建的索引。

       我总结一句话:lucene是工具包,Solr就是lucene的服务化。

二、Solr的安装:

       1、solr下载

       现在最新的Solr版本是4.9,重点!这里要注意,4.9版本对应的jdk的版本要1.7。我就是在这吃了大亏,找了好久才发现这个问题。jdk1.6的版本要4.7以前的,我是采用的4.2.0的solr版本。

       下载地址:http://archive.apache.org/dist/lucene/solr/

我下了三个包,分别是 源码包、linux压缩包、win压缩包。

     2、Tomcat下载

下载地址:http://tomcat.apache.org/download-60.cgi

我们一般使用Core 核心包,Deployer的是给tomcat的开发爱好者用的,一般情况下用不上。


      3、windows下solr安装。

Solr文件分两部分,一部分是webApp,也就是管理页面,需要放到tomcat下发布。另一部分是 索引等数据存储地方,就类似数据库里存数据的位置。

那么,先找一个地方把下好的安装文件解压缩,我在D盘建立了路径 D:\solrHome,然后将zip包放到下面,解压缩。

solr-4.2.0里存放了我们需要的两部分:

webApp文件在D:\solrHome\solr-4.2.0\dist\solr-4.2.0.war

数据存储文件在D:\solrHome\solr-4.2.0\example\solr(整个solr文件夹都是)

(1)第一步安装Tomcat,我们下绿色版本就行


(2)将solr-4.2.0.war包,放到D:\apache-tomcat-solr\webapps下(重命名为solr.war,方便访问)。

(3)将solr的数据存储文件,解压到任意位置,我是放到了solrHome的下面,因为后面还要进行配置。

(4)编辑Tomcat下 \conf\server.xml文件,

这里8080端口随意改,还要添加“URIEncoding="UTF-8",因为要支持中文索引

(5)然后再Tomcat的conf文件夹里创建Catalina\localhost\solr.xml。

<?xml version="1.0" encoding="UTF-8"?>
<Context docBase="\solr.war"  crossContext="true" >
   <Environment name="solr/home" type="java.lang.String" value=http://www.mamicode.com/"D:/solrHome/solr" override="true" />>其中 docBase是我们放solr.war包的路径,这里也可以写绝对路径。

<Environment> 标签是启动tomcat的环境变量,这里name="solr/home"是固定写法,value=http://www.mamicode.com/"D:/solrHome/solr"是我们的存放数据文件的文职。

(6)运行 D:\apache-tomcat-solr\bin\startup.bat

启动成功。

      4、linux下solr安装。

理解了安装原理后,不在重复了。

我把数据文件放在:/opt/solr

webApp放在:/opt/tomcat/apache-tomcat-solr/webapps/solr.war

环境变量配置:vi /etc/profile 在profile文件下面添加export JAVA_OPTS="$JAVAOPTS -Dsolr.solr.home=/opt/solr"这一行。

然后,启动tomcat。

三、Solr的JAVA API应用:
1、pom文件:

      <dependency>
          <groupId>apache-solr-solrj</groupId>
          <artifactId>apache-solr-solrj</artifactId>
          <version>3.6.2</version>
      </dependency>
      <dependency>
          <groupId>org.apache.httpcomponents</groupId>
          <artifactId>httpclient</artifactId>
          <version>4.1.2</version>
      </dependency>
      <dependency>
          <groupId>org.apache.httpcomponents</groupId>
          <artifactId>httpmime</artifactId>
          <version>4.3</version>
      </dependency>
      <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-api</artifactId>
          <version>1.6.6</version>
      </dependency>
      <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-nop</artifactId>
          <version>1.7.5</version>
      </dependency>
这块我用的solr代码包版本有点低。这几个包都是必须的,否则就会各种报错~

2、服务初始化

百度上的都是最早的solr版本例子,大概是1.4版本的。所以我把demo贴过来报错,因此去看看源码,翻了翻,找到了新的服务获取方式。

    // solr服务
    public static SolrServer server;

    /**
     * 初始化
     */
    static {
        String url = "http://<span style="font-family:Microsoft YaHei;">xx</span>.<span style="font-family:Microsoft YaHei;">xx</span>.<span style="font-family:Microsoft YaHei;">xx</span>.<span style="font-family:Microsoft YaHei;">xx</span>:<span style="font-family:Microsoft YaHei;">xxxx</span>/solr";
        server = new HttpSolrServer(url);
    }
3、手动添加索引

    /**
     * 手动添加索引。
     * @throws IOException
     * @throws SolrServerException
     */
    public void addIndexFromFiled() throws IOException, SolrServerException {
        // 删除所有索引
        server.deleteByQuery( "*:*" );
        SolrInputDocument doc1 = new SolrInputDocument();
        doc1.addField( "id", "id1", 1.0f );
        doc1.addField( "name", "doc1", 1.0f );
        doc1.addField( "price", 10 );

        SolrInputDocument doc2 = new SolrInputDocument();
        doc2.addField( "id", "id2", 1.0f );
        doc2.addField( "name", "冰羽", 1.0f );
        doc2.addField( "price", 20 );

        Collection<SolrInputDocument> docs = new ArrayList<SolrInputDocument>();
        docs.add( doc1 );
        docs.add( doc2 );
        // 添加索引
        server.add( docs );
        // 提交索引
        server.commit();
        // 优化索引
        server.optimize();
    }
4、ListBean添加索引,这种事最常用的写法。

这里需要bean里的字段要加上标签@Field


    @Field
    private String id;
如果不加的话,不会报错,也不会添加索引。

这种有个重点,如果添加索引,可能需要修改solr数据文件下的 solr\collection1\conf\schema.xml

如图

   <!-- 自定义 -->
   <field name="age"  type="int"    indexed="true"  stored="true"/>
   <field name="type"  type="string"    indexed="true"  stored="true"/>
类型一定要跟JavaBean匹配上。

   /**
     * 通过编辑List<Bean>,设置索引
     */
    public void addIndexFromBean() {
        try{
            // 删除所有索引
            server.deleteByQuery( "*:*" );
            List<Item> list = new ArrayList<Item>();
            list.add(new Item("一号","dog1",5,"松狮"));
            list.add(new Item("2","dog2",3,"哈士奇"));
            list.add(new Item("3","dog3",1,"泰迪"));
            // 添加索引
            server.addBeans(list);
            // 提交索引
            server.commit();
            // 优化索引
            server.optimize();
        }  catch (SolrServerException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        } catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
    }
5、通过DB查询数据,添加索引。
    /**
     * 通过Db查询,设置索引
     */
    public void addIndexFromDb() {
        try{
            Collection<SolrInputDocument> docs = new ArrayList<SolrInputDocument>();
            // 此处的 dbResultList 应该是从db查询出来的结果集。
            List<Cat> dbResultList = new ArrayList<Cat>();
            for (int i = 0; i < dbResultList.size(); i++) {
                Cat cat = dbResultList.get(i);
                //设置每个字段不得为空,可以在提交索引前进行检查
                SolrInputDocument doc = new SolrInputDocument();
                //在这里请注意date的格式,要进行适当的转化,上文已提到
                doc.addField("id", cat.getName());
    //            …………
                docs.add(doc);
            }
            // 添加索引
            server.add( docs );
            // 提交索引
            server.commit();
            // 优化索引
            server.optimize();
        }  catch (SolrServerException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        } catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
    }
6、getFieldValue的查询方式。
    /**
     * getFieldValue的查询方式。
     * @throws SolrServerException
     */
    public void findByField(String queryStr) throws SolrServerException {
        SolrQuery query = new SolrQuery();
        if (queryStr == null)
            queryStr = "*:*";
        // 创建检索条件
        query.setQuery(queryStr);
        // 设置偏移量,从第0个开始。
        query.setStart(0);
        // 一次取几条,分页用
        query.setRows(10);
        // 按"price"索引 倒序
        query.addSortField( "price", SolrQuery.ORDER.desc );
        QueryResponse rsp = server.query( query );
        SolrDocumentList docsList = rsp.getResults();
        for(Iterator<SolrDocument> doc =docsList.iterator();doc.hasNext();){
            SolrDocument d = doc.next();
            System.out.print(d.getFieldValue("id")+"->");
            System.out.println(d.getFieldValue("name"));
        }
    }
7、findByBeans的查询方式。
    /**
     * findByBeans的查询方式。
     * @throws SolrServerException
     */
    public void findByBeans(String queryStr) throws SolrServerException {
        SolrQuery query = new SolrQuery();
        // 创建检索条件
        if (queryStr == null)
            queryStr = "*:*";
        query.setQuery(queryStr);
        // 设置偏移量,从第0个开始。
        query.setStart(0);
        // 一次取几条,分页用
        query.setRows(10);
        query.addSortField( "id", SolrQuery.ORDER.desc );
        QueryResponse rsp = server.query( query );
        // 此方法需要Item有无参构造函数
        List<Item> itemList = rsp.getBeans(Item.class);
        for (Item item : itemList) {
            System.out.print("id:" + item.getId() + " ");
            System.out.print("name:" + item.getName() + " ");
            System.out.print("age:" + item.getAge() + " ");
            System.out.println("type:" + item.getType());
        }
    }
8、删除索引

    /**
     * 删除所有索引
     * @throws IOException
     * @throws SolrServerException
     */
    public void delIndex() throws IOException, SolrServerException {
        // delete everything!
        // *:*第一个* 代表索引名称,第二个*代表索引内容。
        // "name:dog1"表示 索引里的name字段为dog1的。
        // "name"
        server.deleteByQuery( "*:*" );
    }
9、测试

    public static void main(String[] args) throws IOException, SolrServerException {
        SolrDemo demo = new SolrDemo();
        demo.delIndex();
        demo.addIndexFromBean();
        // 如果只传入一个参数,好像是默认查询field为name 的内容。待确认
        demo.findByBeans("*:*");
    }

四、总结:

       对solr的调研只有两天时间其中一天都浪费在版本包问题的解决上了。感觉效果不理想,只掌握了基本的使用手法,或者说知道solr是什么样一个东西了。一些solr的中高级用法也没有时间去看,太仓促的原因,solr的服务化的优势我也没有去体会。不过solr的使用上来讲,因为涉及到启动独立的服务,未必所有人都喜欢,而且内存的占用说实话也不小。


很多人可能更喜欢lucene的jar包的方式,来灵活的运用吧。我不方便评价二者究竟哪个更好,因为我并未来得及体会solr服务化的强大之处。

       最近JAVA的基础知识落下了,得先补几天JAVA基础知识和设计模式,solr有机会再深入吧。