Solr 是开放源码的企业搜索服务器(Enterprise Search Server)软件,由Apache软件基金会所研发。Solr 使用Lucene程式库以及需要Servlet容器作执行环境。Solr本身提供XML/HTTP与JSON的应用程式接口。 Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口。用户可以通过http post请求,向搜索引擎服务器提交一定格式的XML文件,生成索引;也可以用http get请求获取xml格式的返回数据。 总之是在lucene之上包装了一层http协议,功能强大而易用。
一、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下的也可以做类似修改!)
七、加入中文分词
待补充...
九、安全性配置
为了防止solr服务被暴漏后,防止被随便使用,增加了安全性配置,其实是用的resin配置,请参照http://sling2007.blog.163.com/blog/static/8473271320127821117148/的安全性配置一部分