欢迎投稿

今日深度:

浅尝Solr~~,

浅尝Solr~~,


由于最近项目组有需求,大致意思是做一个对数据全面的统一搜索。于是乎,就研究了一哈Solr

什么是Solr?

Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口。用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引;也可以通过Http Get操作提出查找请求,并得到XML格式的返回结果。
Solr是一个高性能,采用Java5开发,Solr基于Lucene的全文搜索服务器。同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展并对查询性能进行了优化,并且提供了一个完善的功能管理界面,是一款非常优秀的全文搜索引擎。

为什么选择Solr?

除了Solr,ES(Elasticsearch),也是个不错的选择对于搜索引擎,并且,目前的应用是广于Solr的。
ES是一个实时的分布式搜索和分析引擎。具体特性,我也不甚了解,主要特点,分布式引擎,实时搜索,高性能采集。显然,在大数据方面上,ES是个不错选择。但在对索引进行搜索上,Solr是远远优于ES的。我目前的项目,数据级在万以内,并且目前不打算分布式部署搜索引擎,所以我选择了Solr。

Solr部署

 本次部署的话,是另开了个项目,采用了Jetty做容器。以下是Jetty的配置。(我萌萌哒的项目经理配的,我只是搬运工~)
  public class JettySolrStart {
    private static Server server;

    public static void main(String[] args) throws Exception {
        // change the default file encoding to utf-8
        // need add the -Dfile.encoding=utf-8 to command line in deploy
        // environment
        int port=8983;
        int listenerport=8984;
        String path="/";
        String rootdir="./webroot";
        if (args!=null && args.length>0){
            try{
                port=Integer.parseInt(args[0]);
            }catch(Exception e){
            }
        }
        if (args!=null && args.length>1){
            try{
                listenerport=Integer.parseInt(args[1]);
            }catch(Exception e){
            }
        }

        if (args!=null && args.length>2){
            path=args[2].trim();
        }

        if (args!=null && args.length>3){
            rootdir=args[3].trim();
        }

        System.setProperty("file.encoding", "utf-8");
        System.setProperty("solr.solr.home",  rootdir+"/WEB-INF/solr");
        //System.setProperty("solr.solr.home",  rootdir);
        server = new Server();

        QueuedThreadPool threadPool = new QueuedThreadPool();
        server.setThreadPool(threadPool);
        Connector connector = new SelectChannelConnector();
        connector.setPort(port);
        server.setConnectors(new Connector[] { connector });
        WebAppContext context = new WebAppContext(rootdir, path);
        HandlerCollection handlers = new HandlerCollection();
        ContextHandlerCollection contexts = new ContextHandlerCollection();
        RequestLogHandler requestLogHandler = new RequestLogHandler();
        handlers.setHandlers(new Handler[] { contexts, new DefaultHandler(),
                requestLogHandler });
        contexts.addHandler(context);
        server.setHandler(handlers);

        server.setStopAtShutdown(true);
        server.setSendServerVersion(true);

        Thread monitor = new MonitorThread(listenerport);
        monitor.start();
        server.start();
        server.join();
    }

    private static class MonitorThread extends Thread {

        private ServerSocket socket;

        public MonitorThread(int listenerport) {
            setDaemon(true);
            setName("StopMonitor");
            try {
                socket = new ServerSocket(listenerport, 1, InetAddress
                        .getByName("127.0.0.1"));
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        @Override
        public void run() {
            System.out.println("*** running jetty 'stop' thread");
            Socket accept;
            try {
                accept = socket.accept();
                BufferedReader reader = new BufferedReader(
                        new InputStreamReader(accept.getInputStream()));
                reader.readLine();
                System.out.println("*** stopping jetty embedded server");
                server.stop();
                accept.close();
                socket.close();
                System.exit(0);
            } catch (Exception e) {
                System.out.println(e.getMessage());
                throw new RuntimeException(e);
            }
        }
    }
}  
public class JettySolrStop {

    public static void main(String[] args) throws Exception {
        int port=8984;
        if (args!=null && args.length>0){
            try{
                port=Integer.parseInt(args[0]);
            }catch(Exception e){

            }
        }

        Socket s = new Socket(InetAddress.getByName("127.0.0.1"), port);
        OutputStream out = s.getOutputStream();
        System.out.println("*** sending jetty stop request");
        out.write(("\r\n").getBytes());
        out.flush();
        s.close();
    }

}

类似于很多的Jetty配置,大家需要关注的点是2个端口设置,

    int port=8983;
    int listenerport=8984;

8983是启动端口 8984是监听端口,另外还要关注的是对Solr目录的读取,

System.setProperty("solr.solr.home",  rootdir+"/WEB-INF/solr");

这个目录结构:
以上文件大家可以在Solr的JAR包里获取,http://lucene.apache.org/solr/ 里下载即可,到这里大致部署是ok了。

Solr索引采集

这个点是我僵了最久了的~ 后来在看了N篇博客后,我大致有了思路。我采取,定时任务去跑数据库全量与增量导入,同时在任何基础请求进行后进行采集索引。
这里重点讲下前一点。基础的solr-solrj-4.7.1.jar是不支持数据库导入索引的,需要引入

接下来是对data-config.xml进行配置:

<?xml version="1.0" encoding="UTF-8" ?>

<dataConfig>  
    <dataSource type="JdbcDataSource" driver="com.mysql.jdbc.Driver"  
        url="jdbc:mysql://localhost/test" user="test" password="test"/>  
  <document name="company">  
    <entity name="users" query="select * from users">  
        <field column="UserID"   name="UserID"/>
        <field column="Number"   name="Number"/>
        <field column="RealName" name="RealName"/>
        <field column="Leavel" name="Leavel"/>
        <field column="Degrees" name="Degrees"/>
        <field column="GraduateSchool" name="GraduateSchool"/>
        <field column="Professional" name="Professional"/>
        <field column="HouseholdAdd" name="HouseholdAdd"/>
        <field column="Residenc" name="Residenc"/>

        <entity name="tq_sys_userHasRoles" query="select roleId from tq_sys_userHasRoles where userId='${users.UserId}'">
            <field column="roleId" name="roleId"/>

            <entity name="tq_sys_roles" query="select roleName,orgId from tq_sys_roles where id='${tq_sys_userHasRoles.roleId}'">
               <field column="roleName" name="roleName"/>  
               <field column="orgId" name="orgId"/>

               <entity name="tq_sys_organizations" query="select orgName from tq_sys_organizations where id='${tq_sys_roles.orgId}'">
                    <field column="orgName" name="orgName"/> 
               </entity>
            </entity>
        </entity>
    </entity>
  </document>  
</dataConfig> 

这是部分配置,可看出Solr的数据库导入是支持多表关联查询,然后封装成一个entity的。一个document下可以存在多个entity。(!!!这里的SQ语句复杂程度直接影响导入索引的速度)。
接下来是schema.xml,这文件比较繁杂,截取部分配置用到的吧。

<!-- users -->
  <field name="UserId"    type="int"      indexed="true"  stored="true"  multiValued="false"/>
  <field name="number"    type="int"      indexed="true"  stored="true"  multiValued="false"/> 
  <field name="RealName"  type="text_ik"   indexed="true"  stored="true"  multiValued="false"/> 
  <field name="Leavel"    type="string"   indexed="true"  stored="true"  multiValued="false"/>
  <field name="Degrees" type="string"      indexed="true"  stored="true"  multiValued="false"/>
  <field name="GraduateSchool"   type="text_ik"      indexed="true"  stored="true"  multiValued="false"/>
  <field name="Professional"  type="text_ik"   indexed="true"  stored="true"  multiValued="false"/>
  <field name="HouseholdAdd"  type="text_ik"   indexed="true"  stored="true"  multiValued="false"/>
  <field name="Residenc"  type="text_ik"   indexed="true"  stored="true"  multiValued="false"/>
  <field name="roleId"  type="int"   indexed="true"  stored="true"  multiValued="false"/>
  <field name="roleName"  type="string"   indexed="true"  stored="true"  multiValued="false"/>
  <field name="orgName"  type="string"   indexed="true"  stored="true"  multiValued="false"/>
  <field name="orgId"  type="int"   indexed="true"  stored="true"  multiValued="false"/>

显然name与data-config.xml查出的字段名一一对应 ,solr支持各种type,普遍的string、int等都是可用,也支持自己构造type(例如 text_ik)。
介绍下另外3个属性吧,indexed是否可以查询,stored是否可以内容存储 ,multiValued是否复合索引。
这样配置完,可以打开solr的界面看下效果了。

点开dataimport

这里的clean是清空之前所有索引!!!慎勾!!!然后点击execute 你就可以在Query模块查到你所导入的索引啦~(≧▽≦)/~啦啦啦!

另一个数据实时支持,在任何基础请求进行后进行采集索引。这方面的话,我就贴下代码吧,也是比较简单的。

List<SolrInputDocument> docs = new ArrayList<SolrInputDocument>();  
SolrInputDocument solrDocument = new SolrInputDocument();  solrDocument.addField("id", userInfo.getId().toString());   docs.add(solrDocument);  
SolrUtil.addOrUpdate(docs,GetHttpSolrServer.getInstance()); 

Solr调用

呃~既然有索引了那接下来就好办了。由于Solr项目跟主项目分离了,我是采取了发送HTTP请求的方式调用Solr。这方面SolrJ提供了HttpSolrServer十分好用。不废话贴代码吧。

HttpSolrServer solr = SolrUtil.getSolrConnection();
SolrQuery params = new SolrQuery();
params.set("qt", "/select");
params.set("q", "RealName:"+q);
params.set("wt", "json");
params.setRows(Integer.MAX_VALUE);
params.setHighlight(true);                //开启高亮  
params.setHighlightFragsize(200);          //返回的字符个数 
params.setHighlightRequireFieldMatch(true);  
params.setHighlightSimplePost("<em>");    //前缀  
params.setHighlightSimplePre("</em>");    //后缀  
//高亮字段  
params.addHighlightField("RealName");
QueryResponse query = solr.query(params);
QueryResponse req = solr.query(params);  
SolrDocumentList results = query.getResults();

在最后得到K-V数据处理方面我也是困惑了很久。
为了给前端大哥相对好处理的数据集,我采用了封装成对象,然后根据数据动态映射set方法。(这方面涉及method/invoke的知识,网上还是蛮多的,大家可以自行研究下。)
这里眼尖的 有可能看到了高亮,solr对高亮是有很健壮的支持的,以及之前的分词。我贴下我的配置代码

<!-- Highlighting defaults -->
       <!-- hl是指定是否使用高亮;hl.fl,指定对哪些域进行高亮,对多个域进行高亮的话,好像是用逗号隔开;
       f.name.hl.fragsize是指摘要的长度,默认0代表不做摘要。而hl.simple.pre和hl.simple.post则是指定高亮时显示的格式,默认是<em></em> -->
       <str name="hl">on</str>
       <str name="hl.fl">content features title name</str>
       <str name="hl.encoder">html</str>
       <str name="hl.simple.pre">&lt;font color=&quot;red&quot;&gt;</str>
       <str name="hl.simple.post">&lt;/b&gt;</str>
       <str name="f.title.hl.fragsize">100</str>
       <str name="f.title.hl.alternateField">title</str>
       <str name="f.name.hl.fragsize">0</str>
       <str name="f.name.hl.alternateField">name</str>
       <str name="f.content.hl.snippets">3</str>
       <str name="f.content.hl.fragsize">200</str>
       <str name="f.content.hl.alternateField">content</str>
       <strname="f.content.hl.maxAlternateFieldLength">750</str>

新建一个IKAnalyzer.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">  
<properties>  
    <comment>IK Analyzer 扩展配置</comment>
    <!--用户可以在这里配置自己的扩展字典 
    <entry key="ext_dict">ext.dic;</entry> 
    -->
    <!--用户可以在这里配置自己的扩展停止词字典-->
    <entry key="ext_stopwords">stopword.dic;</entry> 

</properties>

以及

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

下班了写的有点急 有什么不对欢迎大家指出来~

www.htsjk.Com true http://www.htsjk.com/solr/37299.html NewsArticle 浅尝Solr~~, 由于最近项目组有需求,大致意思是做一个对数据全面的统一搜索。于是乎,就研究了一哈Solr 什么是Solr? Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web...
相关文章
    暂无相关文章
评论暂时关闭