Solr简介

Solr是一个基于Lucene java库的企业级搜索服务器,包含XML/HTTP,JSON API, 高亮查询结果,faceted search,缓存,复制和一个WEB管理界面。Solr运行在Servlet容器中。所以Solr和Lucene的本质区别有以下三点:搜索服务器,企业级和管理。
Lucene本质上是搜索库,不是独立的应用程序,而Solr是。Lucene专注于搜索底层的建设,而Solr专注于企业应用。Lucene不负责支撑搜索服务所必须的管理,而Solr负责。
所以说,一句话概括:Solr是Lucene面向企业搜索应用的扩展。

solr的安装

环境介绍:tomcat7服务器,solr-4.7.2,IK Analyzer 2012FF_hf1文分词器

  1. 将solr解压文件夹下面的example\webapps的solr.war文件添加tomcat7/webapp的下面
  2. 新建一个solr文件夹作为solr的home文件夹,拷贝example\solr下面的所有文件到solr文件夹
  3. 启动tomcat解压war包
  4. 拷贝jar包(example\lib\ext下面所有的jar包)到tomcat7\lib目录下面
  5. 进入tomcat7\webapps\solr\WEB-INF文件夹修改web.xml,取消env-entry的注释,将env-entry-value值修改成为步骤2中的solr的路径

    <env-entry>
       <env-entry-name>solr/home</env-entry-name>
       <env-entry-value>D:/develop/tomcat7/solr</env-entry-value>
       <env-entry-type>java.lang.String</env-entry-type>
    </env-entry>
    
  6. 重启tomcat服务器,并进入http://localhost/solr/查看是否能够进去管理页面
    solr安装成功页面

solr中文分词

IK Analyzer分词器

  1. 解压缩IK Analyzer 2012FF_hf1压缩文件
  2. 把IKAnalyzer2012FF_u1.jar拷贝到solr服务的/tomcat7/webapps/solr/WEB-INF/lib下面。
  3. 把IKAnalyzer.cfg.xml、stopword.dic拷贝到需要使用分词器的/tomcat7/solr/collection1/conf下面,和schema.xml文件同一个目录。
  4. 修改schema.xml文件,在之间加入如下配置:

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

    如此,我们的中文分词器就配置好了
    schema.xml文件中type参数说明:
    name:标识
    class:和其他属性决定了这个fieldType的实际行为。
    sortMissingLast:设置成true没有该field的数据排在有该field的数据之后,而不管请求时的排序规则, 默认是设置成false。
    sortMissingFirst:跟上面倒过来呗。 默认是设置成false
    analyzer:字段类型指定的分词器
    type:当前分词用用于的操作.index代表生成索引时使用的分词器query代码在查询时使用的分词器
    tokenizer:分词器类
    filter:分词后应用的过滤器 过滤器调用顺序和配置相同

  5. 将schema.xml文件中配置的需要field类型中需要使用中的字段替换为text_ik即可
    如:

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

    将之前type="text_general"替换为type="text_ik"
    schema.xml文件中field参数说明:
    name:字段类型名
    class:java类名
    indexed:缺省true。 说明这个数据应被搜索和排序,如果数据没有indexed,则stored应是true。
    stored:缺省true。说明这个字段被包含在搜索结果中是合适的。如果数据没有stored,则indexed应是true。
    omitNorms:字段的长度不影响得分和在索引时不做boost时,设置它为true。一般文本字段不设置为true。
    termVectors:如果字段被用来做more like this 和highlight的特性时应设置为true。
    compressed:字段是压缩的。这可能导致索引和搜索变慢,但会减少存储空间,只有StrField和TextField是可以压缩,这通常适合字段的长度超过200个字符。
    multiValued:字段多于一个值的时候,可设置为true。

  6. 最后进入http://localhost/solr/#/collection1/analysis输入维多利亚之心,选择’text_ik‘分词器,点击’分析’,出现下图表示中文分词器配置成功
    solr中文分词

smartcn 分词器

  1. 将发行包中contrib/analysis-extras/lucene-libs/lucene-analyzers-smartcn-4.7.2.jar中的复制到webapps\solr\WEB-INF\lib文件夹下面
  2. 配置schemal.xml文件,注册分词器,如下配置:

    <fieldType name="text_smart" class="solr.TextField" positionIncrementGap="100">
    <analyzer type="index">       
        <tokenizer class="solr.SmartChineseSentenceTokenizerFactory"/>              
        <filter class="solr.SmartChineseWordTokenFilterFactory"/>
    </analyzer>      
    <analyzer type="query">      
        <tokenizer class="solr.SmartChineseSentenceTokenizerFactory"/>                
        <filter class="solr.SmartChineseWordTokenFilterFactory"/>
    </analyzer>
    

  3. 引用字段类型即可


    4.最后进入http://localhost/solr/#/collection1/analysis输入维多利亚之心,选择’text_smart‘分词器,点击’分析’,出现下图表示中文分词器配置成功

两种分词比较

  1. IKAnalyzer分词后的碎片太多
  2. 从自定义词库的角度考虑,因为smartcn在Lucene4.6中的版本,目前不支持自定义词库

solrJ的使用

准备工作

  1. 添加solr解压文件下面的solr-4.7.2/dist/solrj-lib文件夹下面所有jar文件
  2. 下载solr-solrj-4.7.2.jar放置到你的工程lib下面
  3. 在schema.xml设置好字段信息(名称,类型,索引,存储,分词等信息)

普通添加索引和删除索引

package cn.xiaoyu.solr;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.common.SolrInputDocument;

public class AddDocsDemo {
    public static final String SOLR_URL = "http://localhost/solr";

    public static void main(String[] args) {
        AddDocs();
        //delDocs();
    }

    public static void AddDocs() {
        String[] words = {"人民","人民政府","民政","劳动人民","中华人民共和国"};
        long start = System.currentTimeMillis();
        Collection<SolrInputDocument> docs = new ArrayList<SolrInputDocument>();
        for (int i=1;i<100;i++){
            SolrInputDocument doc = new SolrInputDocument();
            doc.addField("id","id"+i,1.0f);
            doc.addField("name",words[i%5],1.0f);
            doc.addField("price",10*i);
            docs.add(doc);
        }
        try {
            // 链接服务器
            HttpSolrServer server = new HttpSolrServer(SOLR_URL);
            server.setConnectionTimeout(100);
            server.setDefaultMaxConnectionsPerHost(100);
            server.setMaxTotalConnections(100);

            /**
             * 可以通过三种方式增加doc,其中server.add(docs.iterator())效率最高
             */
//            第一种提交方式:
//            增加后通过执行commit函数commit (936ms)
            // server.add(docs);
            // server.commit();

//             第二种提交方式:将提交方法设置为自动提交
//            增加doc后立即commit (946ms)
            // UpdateRequest req = new UpdateRequest();
            // req.setAction(ACTION.COMMIT, false, false);
            // req.add(docs);
            // UpdateResponse rsp = req.process(server);

//              第三种提交方式:
//            the most optimal way of updating all your docs in one http request(432ms)
            server.add(docs.iterator());
            server.commit();

        } catch (Exception e) {
            System.out.println(e);
        }
        System.out.println("time elapsed(ms):" + (System.currentTimeMillis() - start));
    }
    // 删除 索引
    public static void delDocs() {
        long start = System.currentTimeMillis();
        try {
            HttpSolrServer server = new HttpSolrServer(SOLR_URL);
            /**
             * 这儿删除也有三种方式:通过id来删除,通过ids来删除,通过查询来删除
             */
            // 第一种方式: 通过id来删除
//            String id = "id1";
//            server.deleteById(id);
//            server.commit();
            // 第二种方式:通过ids来删除 
            List<String> ids = new ArrayList<String>();
            for (int i = 1; i < 100; i++) {
                ids.add("id" + i);
            }
            server.deleteById(ids);
            server.commit();
            // 第三种方式:通过查询删除全部
//            server.deleteByQuery("*:*");
//            server.commit();
        } catch (Exception e) {
            System.out.println(e);
        }
        System.out.println("time elapsed(ms):" + (System.currentTimeMillis() - start));
    }
}

普通查询索引

package cn.xiaoyu.solr;

import java.io.IOException;

import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrQuery.ORDER;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.BinaryRequestWriter;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.client.solrj.impl.XMLResponseParser;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;

public class QueryDocsDemo {
    public static final String SOLR_URL = "http://localhost/solr";

    public static void main(String[] args) throws SolrServerException,
            IOException {
        HttpSolrServer server = new HttpSolrServer(SOLR_URL);
        server.setMaxRetries(1); // defaults to 0. > 1 not recommended.
        server.setConnectionTimeout(5000); // 5 seconds to establish TCP
        // 正常情况下,以下参数无须设置
        // 使用老版本solrj操作新版本的solr时,因为两个版本的javabin incompatible,所以需要设置Parser
        server.setParser(new XMLResponseParser());
        server.setSoTimeout(1000); // socket read timeout
        server.setDefaultMaxConnectionsPerHost(100);
        server.setMaxTotalConnections(100);
        server.setFollowRedirects(false); // defaults to false
        // allowCompression defaults to false.
        // Server side must support gzip or deflate for this to have any effect.
        server.setAllowCompression(true);

        // 使用ModifiableSolrParams传递参数
        // ModifiableSolrParams params = new ModifiableSolrParams();
        // //
        // 192.168.230.128:8983/solr/select?q=video&fl=id,name,price&sort=price
        // asc&start=0&rows=2&wt=json
        // // 设置参数,实现上面URL中的参数配置
        // // 查询关键词
        // params.set("q", "video");
        // // 返回信息
        // params.set("fl", "id,name,price,score");
        // // 排序
        // params.set("sort", "price asc");
        // // 分页,start=0就是从0开始,rows=5当前返回5条记录,第二页就是变化start这个值为5就可以了
        // params.set("start", 2);
        // params.set("rows", 2);
        // // 返回格式
        // params.set("wt", "javabin");
        // QueryResponse response = server.query(params);

        // 使用SolrQuery传递参数,SolrQuery的封装性更好
        server.setRequestWriter(new BinaryRequestWriter());
        SolrQuery query = new SolrQuery();
        query.setQuery("政府");
        query.setFields("id", "name", "price", "score");
        // 根据相关度排序
//        query.setSort("score", ORDER.desc);
        query.setSort("price", ORDER.asc);
        query.setStart(0);
        query.setRows(10);
        //query.setRequestHandler("/select"); //默认就是/select
        QueryResponse response = server.query(query);

        // 搜索得到的结果数
        System.out.println("Find:" + response.getResults().getNumFound());
        // 输出结果
        int iRow = 1;
        for (SolrDocument doc : response.getResults()) {
            System.out.println("----------" + iRow + "------------");
            System.out.println("id: " + doc.getFieldValue("id").toString());
            System.out.println("name: " + doc.getFieldValue("name").toString());
            System.out.println("price: "
                    + doc.getFieldValue("price").toString());
            System.out.println("score: " + doc.getFieldValue("score"));
            iRow++;
        }
    }
}

POJO对象

package cn.xiaoyu.solr;

import java.util.List;

import org.apache.solr.client.solrj.beans.Field;

public class NewsBean {
    @Field
    private String id;
    @Field
    private String name;
    @Field
    private String author;
    @Field
    private String description;
    @Field("links")
    private List<String> relatedLinks;

    public NewsBean() {}
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public List<String> getRelatedLinks() {
        return relatedLinks;
    }
    public void setRelatedLinks(List<String> relatedLinks) {
        this.relatedLinks = relatedLinks;
    }
}

添加POJO对象索引

package cn.xiaoyu.solr;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Random;

import org.apache.solr.client.solrj.impl.BinaryRequestWriter;
import org.apache.solr.client.solrj.impl.HttpSolrServer;

public class AddBeansDemo {
  public static final String SOLR_URL = "http://localhost/solr";

  public static void main(String[] args) {
    // 通过浏览器查看结果
    // 要保证bean中各属性的名称在conf/schema.xml中存在,如果查询,要保存被索引
    // http://172.168.63.233:8983/solr/collection1/select?q=description%3A%E6%94%B9%E9%9D%A9&wt=json&indent=true
//    delBeans();
    AddBeans();
  }

  public static Random rand = new Random(47);
  public static String[] authors = {"张三", "李四", "王五", "赵六", "张飞", "刘备","关云长"};
  public static String[] links = {
      "http://www.baidu.com",
      "http://www.google.com",
      "http://www.taobao.com",
      "http://www.facebook.com",
      "http://www.youtube.com" };

  public static String getAuthors() {
    List<String> list = Arrays.asList(authors).subList(0, rand.nextInt(6)+1);
    String str = "";
    for (String tmp : list) {
      str += " " + tmp;
    }
    return str;
  }

  public static List<String> getLinks() {
    return Arrays.asList(links).subList(0, rand.nextInt(4)+1);
  }

  public static void AddBeans() {
    String[] words = {"人民","人民政府","民政","世界地图","中华人民共和国"};
    long start = System.currentTimeMillis();
    Collection<NewsBean> docs = new ArrayList<NewsBean>();
//  DocumentObjectBinder binder = new DocumentObjectBinder();
    for (int i = 1; i < 300; i++) {
      NewsBean news = new NewsBean();
      news.setId("id" + i);
      news.setName("news" + i);
      news.setAuthor(getAuthors());
      news.setDescription(words[i % 5]);
      news.setRelatedLinks(getLinks());
//      SolrInputDocument doc = binder.toSolrInputDocument(news);
      docs.add(news);
    }
    try {
      HttpSolrServer server = new HttpSolrServer(SOLR_URL);
      server.setRequestWriter(new BinaryRequestWriter());
      // 可以通过二种方式增加docs,其中server.add(docs.iterator())效率最高
      // 增加后通过执行commit函数commit (981ms)
      // server.addBeans(docs);
      // server.commit();

      // the most optimal way of updating all your docs
      // in one http request(481ms)
      server.addBeans(docs.iterator());
      server.commit();
      server.optimize(); //优化Lucene 的索引文件以改进搜索性能。
    } catch (Exception e) {
      System.out.println(e);
    }
    System.out.println("time elapsed(ms):"
        + (System.currentTimeMillis() - start));
  }

  public static void delBeans() {
    long start = System.currentTimeMillis();
    try {
      HttpSolrServer server = new HttpSolrServer(SOLR_URL);
      server.deleteByQuery("*:*");
      server.commit();
    } catch (Exception e) {
      System.out.println(e);
    }
    System.out.println("time elapsed(ms):" + (System.currentTimeMillis() - start));
  }
}

查询POJO

package cn.xiaoyu.solr;

import java.io.IOException;
import java.util.List;

import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.beans.DocumentObjectBinder;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.client.solrj.response.FacetField;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;

public class QueryBeanDemo {
    public static final String SOLR_URL = "http://localhost/solr";

    public static void main(String[] args) throws SolrServerException,
            IOException {
        // http://172.168.63.233:8983/solr/collection1/select?q=description%3A%E6%80%BB%E7%9B%AE%E6%A0%87&facet=true&facet.field=author_s
        HttpSolrServer server = new HttpSolrServer(SOLR_URL);
        server.setMaxRetries(1);
        server.setMaxRetries(1); // defaults to 0. > 1 not recommended.
        server.setConnectionTimeout(5000); // 5 seconds to establish TCP
        // server.setRequestWriter(new BinaryRequestWriter());

        SolrQuery query = new SolrQuery();
        query.setQuery("description:人民");
        query.setStart(0);
        query.setRows(2);
        query.setFacet(true);
        query.addFacetField("author_s");

        QueryResponse response = server.query(query);
        // 搜索得到的结果数
        System.out.println("Find:" + response.getResults().getNumFound());
        // 输出结果
        int iRow = 1;

        // response.getBeans存在BUG,将DocumentObjectBinder引用的Field应该为
        // org.apache.solr.client.solrj.beans.Field
        SolrDocumentList list = response.getResults();
        DocumentObjectBinder binder = new DocumentObjectBinder();
        List<NewsBean> beanList = binder.getBeans(NewsBean.class, list);
        for (NewsBean news : beanList) {
            System.out.println(news.getId());
            System.out.println(news.getAuthor());
            System.out.println(news.getName());
            System.out.println(news.getRelatedLinks());
        }

        for (SolrDocument doc : response.getResults()) {
            System.out.println("----------" + iRow + "------------");
            System.out.println("id: " + doc.getFieldValue("id").toString());
            System.out.println("name: " + doc.getFieldValue("name").toString());
            iRow++;
        }

        for (FacetField ff : response.getFacetFields()) {
            System.out.println(ff.getName() + "," + ff.getValueCount() + "," + ff.getValues());
        }
    }
}

#参考
solr4.7中文分词器(ik-analyzer)配置
Solr JAVA客户端SolrJ 4.9使用示例教程
http://wiki.apache.org/solr/Solrj