solr初步安装配置调用

Solr 是开放源码的企业搜索服务器(Enterprise Search Server)软件,由Apache软件基金会所研发。Solr 使用Lucene程式库以及需要Servlet容器作执行环境。Solr本身提供XML/HTTP与JSON的应用程式接口。 Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口。用户可以通过http post请求,向搜索引擎服务器提交一定格式的XML文件,生成索引;也可以用http get请求获取xml格式的返回数据。 总之是在lucene之上包装了一层http协议,功能强大而易用。

下面从hello world、简单配置、java客户端、主从配置、加入中文分词等方面分别阐述

一、linux下,Resin中配置solr-----HelloWorld

请参考http://wiki.apache.org/solr/SolrInstall ,有针对各种服务器的配置。

下面以linux下的resin为例,给出配置步骤。

1、  安装jdk1.6、resin3.0,解压apache-solr-4.0.0-BETA.zip

2、  把解压后solr目录下dist/apache-solr-4.0.0-BETA.war,拷贝到resin安装目录下的webapps目录下。

在resin的配置文件resin.conf中,增加一个应用

<host id="solr.test.com" root-directory=".">

<web-app id="/solr" document-directory="/home/bjsunling/server/solr" archive-path="/home/bjsunling/resin/webapps/solr.war" character-encoding="utf-8">

</web-app>

</host>

3、  mkdir –p /home/bjsunling/server/solr,

然后启动resin,会报错如下:

com.caucho.config.LineConfigException: WEB-INF/web.xml:23: <web-app xmlns="http://java.sun.com/xml/ns/javaee"> is an unexpected top-level tag.

这说明war包解压出的web.xml文件标签有错,我们把web-app标签的xmlns="http://java.sun.com/xml/ns/javaee"属性删除即可。

4、  再重启resin,报错如下:No /solr/home in JNDI,也就是说没有设置solr/home,有三种方式可以设置,我们这里使用jndi方式。

打开刚才的web.xml,找到如下内容,其中ent-entry-value需要设置为自己的路径,即solrhome。默认状态下,下面的是被注释的,需要把注释符号删除。

<env-entry>

<env-entry-name>solr/home</env-entry-name>

<env-entry-value>/home/bjsunling/server/solr/solrhome</env-entry-value>

<env-entry-type>java.lang.String</env-entry-type>

</env-entry>

5、  再重启resin会报错:Can't find resource 'solrconfig.xml’,说明我们的solr/home文档结构错误。

把solr解压后的example/solr下的文件,全部拷贝到solrhome目录下。

6、  再重启resin,会报错schema XML parser doesn't support XInclude option,也就是说schema.xml有错,我们需要把其中有关xinclude的地方先注了。具体在197、245、695行,分别是:

<!--   <dynamicField name="*_c"   type="currency" indexed="true"  stored="true"/>-->

<!--   <copyField source="price" dest="price_c"/>-->

<!--    <fieldType name="currency" class="solr.CurrencyField" precisionStep="8" defaultCurrency="USD" currencyConfig="currency.xml" />-->

7、 现在重启resin,没错了吧,^_^。

绑定  127.0.0.1 solr.test.com,访问http://solr.test.com:8080/solr/就看到控制台了

8、  solrhome目录下的solr.xml和solrconfig.xml、schema.xml可以深度定制。

9、 如果出现org.apache.solr.common.SolrException:

QueryElevationComponent requires the schema to have a uniqueKeyField implemented using StrField。

说明是主键问题,你需要在配置中增加一个主键,或者把solrconfig.xml中下面的内容注释掉

<!-- a search component that enables you to configure the top results for

a given query regardless of the normal lucene scoring.-->

<searchComponent name="elevator" >

<!-- pick a fieldType to analyze queries -->

<str name="queryFieldType">string</str>

<str name="config-file">elevate.xml</str>

</searchComponent>

 

<!-- a request handler utilizing the elevator component -->

<requestHandler name="/elevate" startup="lazy">

<lst name="defaults">

<str name="echoParams">explicit</str>

</lst>

<arr name="last-components">

<str>elevator</str>

</arr>

</requestHandler>

二、solr的业务相关的配置

solr的和业务相关的配置文件,主要有3个:solr.xml、solrconfig.xml、schema.xml。

solr.xml是solr节点配置文件,负责节点部署

solrconfig.xml是solr的索引效率的参数配置文件,负责优化查询、索引的效率

schema.xml是solr的索引内容的配置文件,负责制定索引字段等。

2.1 schema.xml,

这个文件的结构是:Schema根节点

|  fields节点:表示需要索引的字段

| field 1 用于定义一个字段的类型、是否索引、存储等

| ……

| field N

|   uniqueKey节点:类似于数据库主键

|   copyField 节点:把正常索引字段,拷贝到这里一份,也用于索引

|   ……

|   copyField 节点:

|   types 节点:表示支持的索引字段的类型,比如int、string之类

| fieldType:

| ……

| fieldType:

|   similariry节点:用于相似查询

在第一级节点中,fields、uniqueKey、types节点是必须有的,其它可选。

1、  修改fields节点。

把自己业务中需要的字段写到这里,删除之前的,但是要保留两个节点,如下:

注意Fields中还必须有一个节点_version_,用于表示词条记录的版本,如果没有的话,会引起一些错误,比如deleteByQuery将无效。

<field name="_version_" type="long" indexed="true" stored="true"/>

fields节点中必须有一个名字是text的节点,否则会报缺失text节点的异常,

这是copyField节点的目的地:

<field name="text" type="text_general" indexed="true" stored="false" multiValued="true"/>

2、  删除copyField节点,如果需要额外的查询的话可以保留,

3、  Types节点,可以保持原样,在fields节点引用类型的时候,直接从这里取就可以了,

也可以定义自己的类型。

2.2 solrconfig.xml

参看solr的主从配置

2.3 Solr.xml

这里可以修改自己的core的名字defaultCoreName,或者配置多个core

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

<solr persistent="true">

<cores adminPath="/admin/cores" defaultCoreName="collection1" host="${host:}" hostPort="${jetty.port:}" zkClientTimeout="${zkClientTimeout:15000}">

<core name="collection1" instanceDir="collection1" />

</cores>

</solr>

三、Solrj---solr的java客户端配置

Solrj是基于httpclient的。下面直接给出solrj的增删改查代码了。注意引入需要的jar,

 

3.1 增加

package solr.index;

import java.io.IOException;

import java.util.ArrayList;

import java.util.Collection;

import java.util.Date;

import org.apache.solr.client.solrj.SolrServer;

import org.apache.solr.client.solrj.SolrServerException;

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

import org.apache.solr.common.SolrInputDocument;

/**

* 用http协议来修改索引

* @author bjsunling

*

*/

public class Add {

public static void main(String[] args) throws SolrServerException, IOException {

SolrServer server = new ConcurrentUpdateSolrServer ("http://solr.test.com:8082/solr/");

 

server.deleteByQuery( "*:*" );// CAUTION: deletes everything!

 

SolrInputDocument doc1 = new SolrInputDocument();

doc1.addField( "id", "bjsunling");

doc1.addField( "name", "孙岭");

doc1.addField( "age", 12);

doc1.addField( "sex", true);

doc1.addField( "birth_date", new Date());

doc1.addField( "weight", 160f);

doc1.addField( "uid", System.currentTimeMillis());

 

SolrInputDocument doc2 = new SolrInputDocument();

doc2.addField( "id", "chenmg");

doc2.addField( "name", "陈明刚");

doc2.addField( "age", 15);

doc2.addField( "sex", false);

doc2.addField( "birth_date", new Date());

doc2.addField( "weight", 160f);

doc2.addField( "uid", System.currentTimeMillis());

 

Collection<SolrInputDocument> docs = new ArrayList<SolrInputDocument>();

docs.add( doc1 );

docs.add( doc2 );

 

server.add( docs );

server.optimize();

server.commit();

 

}

}

3.2 删除

package solr.index;

import java.io.IOException;

import java.util.ArrayList;

import org.apache.solr.client.solrj.SolrServer;

import org.apache.solr.client.solrj.SolrServerException;

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

import org.apache.solr.common.SolrInputDocument;

public class Delete {

public static void main(String[] args) throws SolrServerException,

IOException {

SolrServer server = new ConcurrentUpdateSolrServer (

"http://solr.test.com:8082/solr/");

server.deleteByQuery("*:*");//删除所有

server.optimize();

server.commit();

}

}

3.3 更新

package solr.index;

import java.io.IOException;

import java.util.ArrayList;

import java.util.Collection;

import org.apache.solr.client.solrj.SolrServer;

import org.apache.solr.client.solrj.SolrServerException;

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

import org.apache.solr.common.SolrInputDocument;

public class Update {

public static void main(String[] args) throws SolrServerException,

IOException {

SolrServer server = new ConcurrentUpdateSolrServer (

"http://solr.test.com:8082/solr/");

SolrInputDocument doc1 = new SolrInputDocument();

doc1.addField("id", "bjsunling");

doc1.addField("name", "孙岭  "+new java.util.Date());

doc1.addField("age", 19);

SolrInputDocument doc2 = new SolrInputDocument();

doc2.addField("id", "chenmg");

doc2.addField("name", "陈明刚");

doc2.addField("weight", 130f);

SolrInputDocument doc3 = new SolrInputDocument();

doc3.addField("id", "chen-wei", 1.0f);

doc3.addField("name", "陈伟", 1.0f);

doc3.addField("age", 20);

Collection<SolrInputDocument> docs = new ArrayList<SolrInputDocument>();

docs.add(doc1);

docs.add(doc2);

docs.add(doc3);

server.add(docs);

server.optimize();

server.commit();

}

}

3.4 查询

package solr.search;

import java.util.Iterator;

import org.apache.solr.client.solrj.SolrQuery;import org.apache.solr.client.solrj.SolrServer;

import org.apache.solr.client.solrj.SolrServerException;

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

import org.apache.solr.client.solrj.response.QueryResponse;

import org.apache.solr.common.SolrDocument;

import org.apache.solr.common.SolrDocumentList;

public class Searcher {

//      static SolrServer server = new HttpSolrServer("http://solr.slave.test.com:8082/solr/collection1");

static SolrServer server = new HttpSolrServer("http://solr.test.com:8082/solr/collection1");

static void searchAll() throws SolrServerException {

SolrQuery query = new SolrQuery();

query.setQuery("*:*");

query.addSortField("age", SolrQuery.ORDER.desc);

 

QueryResponse rsp = server.query(query);

SolrDocumentList docs = rsp.getResults();

print(docs);

}

static void search1() throws SolrServerException{

SolrQuery query = new SolrQuery();

query.setQuery("id:bjsunling");

query.addSortField("uid", SolrQuery.ORDER.asc);

query.setHighlight(true).setHighlightSnippets(1); // set other params as

QueryResponse rsp = server.query(query);

SolrDocumentList docs = rsp.getResults();

print(docs);

}

static void search2() throws SolrServerException{

SolrQuery query = new SolrQuery();

query.setQuery("name:孙");

query.addSortField("age", SolrQuery.ORDER.asc);

query.setHighlight(true).setHighlightSnippets(1); // set other params as

QueryResponse rsp = server.query(query);

SolrDocumentList docs = rsp.getResults();

print(docs);

}

static void print(SolrDocumentList docs) {

Iterator<SolrDocument> iter = docs.iterator();

while (iter.hasNext()) {

System.out.println(iter.next());

}

}

public static void main(String[] args) throws SolrServerException {

searchAll();

System.out.println("===========================");

search1();

System.out.println("===========================");

search2();

System.out.println("===========================");

search3();

}}

3.5 查询参数备忘录:

solr 查询参数说明备忘

常用

  • q - 查询字符串,必须的。
  • fl - 指定返回那些字段内容,用逗号或空格分隔多个。
  • start - 返回第一条记录在完整找到结果中的偏移位置,0开始,一般分页用。
  • rows - 指定返回结果最多有多少条记录,配合start来实现分页。
  • sort - 排序,格式:sort=<field name>+<desc|asc>[,<field name>+<desc|asc>]… 。示例:(inStock desc, price asc)表示先 “inStock” 降序, 再 “price” 升序,默认是相关性降序。
  • wt - (writer type)指定输出格式,可以有 xml, json, php, phps, 后面 solr 1.3增加的,要用通知我们,因为默认没有打开。
  • fq - (filter query)过虑查询,作用:在q查询符合结果中同时是fq查询符合的,例如:q=mm&fq=date_time:[20081001 TO 20091031],找关键字mm,并且date_time是20081001到20091031之间的。官方文档:http://wiki.apache.org/solr/CommonQueryParameters#head-6522ef80f22d0e50d2f12ec487758577506d6002

不常用

  • q.op - 覆盖schema.xml的defaultOperator(有空格时用"AND"还是用"OR"操作逻辑),一般默认指定
  • df - 默认的查询字段,一般默认指定
  • qt - (query type)指定那个类型来处理查询请求,一般不用指定,默认是standard。

其它

  • indent - 返回的结果是否缩进,默认关闭,用 indent=true|on 开启,一般调试json,php,phps,ruby输出才有必要用这个参数。
  • version - 查询语法的版本,建议不使用它,由服务器指定默认值。

其实所有的查询操作都可以用 and or组合(类似sql的),不必一些复杂的语法

四、Solr的主从配置

下面以一主一从,来示例solr的主从配置。

Solr的主从配置可以用rsync方式,但是配置稍微麻烦;solr4提供了基于httpclient的同步。

注意除了主从还有solrcloud可以用哦。

1、  先按照第一步的步骤,搭建起两个solr

2、  修改主节点的solrconfig.xml

<requestHandler name="/replication" >
<lst name="master">
<str name="replicateAfter">commit</str>
<str name="replicateAfter">startup</str>
<str name="confFiles">schema.xml,stopwords.txt</str>
</lst>
</requestHandler>

replicateAfter : SOLR会自行在以下操作行为发生后执行复制: 'commit', 'startup' 'optimize',这里我们选择commit , 即SOLR每一次接受到commit请求后,会执行复制策略。

confFiles : 待分发的配置文件,solr 也会将主服务器上的字段配置文件:schema.xml和stopwords.txt,固排文件: elevate.xml同步到辅服务器上。

commitReserveDuration: 每次commit之后,保留增量索引的周期时间,这里设置为5分钟。

3、修改从节点的solrconfig.xml

<requestHandler name="/replication" >
<lst name="slave">
<str name="masterUrl">http://solr.test.com:8002/solr/replication</str><!--主搜索引擎服务地址-->
<str name="pollInterval">00:00:60</str><!--同步频率,1分钟一次-->
</lst>
</requestHandler>

?  masterUrl : 主服务器同步URL地址

?  pollInterval:从服务器同步间隔,即每隔多长时间同步一次主服务器

?  httpConnTimeout:设置连接超时(单位:毫秒)

?  httpReadTimeout:如果设置同步索引文件过大,则应适当提高此值。(单位:毫秒)

?  httpBasicAuthUser:验证用户名,需要和主服务器一致

?  httpBasicAuthPassword:验证密码,需和主服务器一致

?  compression:external or internal 使用SOLR自己的压缩算法或应用容器的

注意重启应用之前,把从节点的solrhome/data/下的索引文件删了

五、Solrj—主从的客户端调用

增删改操作还是在主节点上进行,和之前一样。

查询操作则可以分配到不同的节点上,这样会分担从客户端过来的请求负载。

这里可以用ngxin的负载均衡等,也可以使用solrj内置的均衡工具LBHttpSolrServer。

5.1 查询:

package solr.search;
import java.net.MalformedURLException;

import java.util.Iterator;

import org.apache.solr.client.solrj.SolrQuery;

import org.apache.solr.client.solrj.SolrServer;

import org.apache.solr.client.solrj.SolrServerException;

import org.apache.solr.client.solrj.impl.LBHttpSolrServer;

import org.apache.solr.client.solrj.response.QueryResponse;

import org.apache.solr.common.SolrDocument;

import org.apache.solr.common.SolrDocumentList;

/**

* 用内置的LBHttpSolrServer来做负载均衡,实际是轮询各个slave

* 当然也可用代理等

* @author bjsunling

*

*/

public class SlaveSearcher {

static SolrServer server;

static {

try {

server = new LBHttpSolrServer("http://solr.slave.test.com:8082/solr/collection1","http://solr.test.com:8082/solr/collection1");

} catch (MalformedURLException e) {

e.printStackTrace();

}

}

static void searchAll() throws SolrServerException {

SolrQuery query = new SolrQuery();

query.setQuery("*:*");

query.addSortField("age", SolrQuery.ORDER.desc);

QueryResponse rsp = server.query(query);

SolrDocumentList docs = rsp.getResults();

print(docs);

}

static void search1() throws SolrServerException{

SolrQuery query = new SolrQuery();

query.setQuery("id:bjsunling");

query.addSortField("uid", SolrQuery.ORDER.asc);

query.setHighlight(true).setHighlightSnippets(1); // set other params as

QueryResponse rsp = server.query(query);

SolrDocumentList docs = rsp.getResults();

print(docs);

}

static void search2() throws SolrServerException{

SolrQuery query = new SolrQuery();

query.setQuery("name:孙");

query.addSortField("age", SolrQuery.ORDER.asc);

query.setHighlight(true).setHighlightSnippets(1); // set other params as

QueryResponse rsp = server.query(query);

SolrDocumentList docs = rsp.getResults();

print(docs);

}

static void search3() throws SolrServerException{

SolrQuery query = new SolrQuery();

query.setQuery("name:孙");

query.setQuery("age:1");

query.addSortField("age", SolrQuery.ORDER.asc);

query.setHighlight(true).setHighlightSnippets(1); // set other params as

QueryResponse rsp = server.query(query);

SolrDocumentList docs = rsp.getResults();

print(docs);

}

static void print(SolrDocumentList docs) {

Iterator<SolrDocument> iter = docs.iterator();

while (iter.hasNext()) {

System.out.println(iter.next());

}

}

public static void main(String[] args) throws SolrServerException {

searchAll();

System.out.println("===========================");

search1();

System.out.println("===========================");

search2();

System.out.println("===========================");

search3();

}

}

六、MultiCore的部署

如果Single Core的solr已经部署成功,那么MultiCore只是两个拷贝命令,没错就这么简单。

1、首先找到core的配置文件,/home/bjsunling/server/solrslave/solrhome/solr.xml

2、把<cores adminPath="/admin/cores" defaultCoreName="autopic" host="${host:}" hostPort="${jetty.port:}" zkClientTimeout="${zkClientTimeout:15000}">
<core name="core1" instanceDir="core1" />
</cores>
</solr>

修改成

<cores adminPath="/admin/cores" defaultCoreName="autopic" host="${host:}" hostPort="${jetty.port:}" zkClientTimeout="${zkClientTimeout:15000}">
<core name="core1" instanceDir="core1" />
<core name="core2" instanceDir="core2" />
</cores>
</solr>

3、然后把core1文件夹的内容全部拷贝到core2(或者从resin/webapp中拷贝一份也可以),

cp -r /home/bjsunling/server/solr/solrhome/core1/ /home/bjsunling/server/solr/solrhome/core2/

4、最后core2下的内容就可以自由修改了,比如schema.xml、solrconfig.xml,删除原有的数据文件夹data等

(slave下的也可以做类似修改!)

七、加入中文分词

待补充...

八、异常
1、solr服务器端报这个异常,
org.apache.solr.common.SolrException: Unexpected EOF; was expecting a close tag for element <stream>,
虽然报了这个异常,但数据正常,所以暂时没有理会这个异常。。。

九、安全性配置
为了防止solr服务被暴漏后,防止被随便使用,增加了安全性配置,其实是用的resin配置,请参照http://sling2007.blog.163.com/blog/static/8473271320127821117148/的安全性配置一部分

分享到:

发表评论