背景
我用Lucene3.6,为php语言搭建的平台提供一套搜索服务,但是lucene只是个提供索引操作的库,需要一个web-service来提供给java外的语言,使之可以通过http方式发送搜索请求并得到如json, xml格式的查询结果集数据。而Solr是一个二次包装了Lucene库的搜索服务容器,兼容lucene的api,可以在jetty或者Tomcat这样的容器里以web服务的方式启动并提供一套自己的索引建立,更新,删除,查询接口,以及更高级更全面的接口服务。
关于Solr
Solr版本随着lucene版本的更新而兼容更新,目前已经发布4.0,我使用的是3.6版本。在配置中文分词库以及使用Solrj来访问solr服务的时候,也因为版本比较新,遇到了一些问题,我会在下面详细说明。我初步感受,Solr和lucene有以下一些区别:
Solr的索引建立主要有通过xml文件读取需要建索引的数据、通过solrj用api来addDocument,此外还有读取CVS文件,获取post来的json数据以及直接从数据库读(DIH)。来讲讲主要的索引建立方法。其实通过Solrj用api里的类和函数来建索引是类似于Lucene的做法的,差别是两者的api长的不一样。但凡使用过lucene的人,就会觉得solrj的关键类和使用方法还是一目了然,比如下面这样:
String url = "http://localhost:8080/solr"; SolrServer server = new HttpSolrServer(url); SolrQuery query = new SolrQuery(); 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", "doc2", 1.0f ); doc2.addField( "price", 20 ); Collection<SolrInputDocument> docs = new ArrayList<SolrInputDocument>(); docs.add( doc1 ); docs.add( doc2 ); server.add( docs ); server.commit();
下载solr后,在 {solr.home}/example/exampledocs 内会有一些xml,就是通过第一种建索引的方式用xml文件导入数据。比如下面这样:
<add><doc> <field name="id">3007WFP</field> <field name="name">Dell Widescreen UltraSharp 3007WFP</field> <field name="manu">Dell, Inc.</field> <field name="cat">electronics</field> <field name="cat">monitor</field> <field name="features">30" TFT active matrix LCD, 2560 x 1600, .25mm dot pitch, 700:1 contrast</field> <field name="includes">USB cable</field> <field name="weight">401.6</field> <field name="price">2199</field> <field name="popularity">6</field> <field name="inStock">true</field> <!-- Buffalo store --> <field name="store">43.17614,-90.57341</field> </doc></add>这两者都有个共同的问题,关于不同Filed的分词,STORE,Analyze是怎么处理的?
solr有两个重要的配置文件:scheme.xml和solrconfig.xml
scheme.xml主要配置:
每个fieldtype来自哪个类,是solr自带的主要类型,还是你自己要加入的类,比如中文分词的类,各举一个例子:
<fieldType name="string" class="solr.StrField" sortMissingLast="true" /> <fieldType name="boolean" code" ><fieldType name="text_ik" class="solr.TextField"> <analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/> </fieldType>上面第二个配置是我加的中文分词,使用的是IKAnalyzer,版本是2012u6,最新的版本。配置了filedtype之后,要在scheme.xml里配置将要建立的索引字段的类型和信息:
<field name="goods_id" type="string" indexed="false" stored="true"/> <field name="brand_name" type="text_ik" indexed="true" stored="true"/>这样应该解释清楚了。至于,solrconfig.xml,主要是一些系统依赖,缓存配置,各种http的requestHandler的请求处理类等。
总结,就是solr的索引配置主要在xml里,甚至数据也可以写成xml,然后在cmd里添加数据文件,这就是封装了lucene的结果,比lucene更高层一点,而我认为这样的配置不怎么方便,我的选择是把自己用lucene建出来的索引块直接丢到solr索引目录里,并在scheme.xml里添加好字段,包括配置好中文分词工具,因为solr和lucene是兼容的,而包装到来的效果就是api不够底层。
再举几个小的方面,其实也可以说是solr比lucene进步的地方。lucene源码里,core包之外的随后整合的附加功能包highlighting在solr里是已经配置完整,并且solr的搜索url里可以整合排序和高亮等要求,给几个例子(hl是highlighting缩写):
q=video&sort=score desc q=video&sort=inStock asc, score desc q=video card&fl=name,id&hl=true&hl.fl=name,features
solr其实就是把lucene包装之后,变成了一个web的服务,放在容器里run起来后,可以通过http访问,作为一个中间件的服务存在。如果你自己做,也就是把lucene开发好之后写一些servlet的东西,也放在tomcat,jetty里面跑,然后接受url,做相应的response。solr的搜索请求和数据导入方式比较多样,适合不同开发者选用方便的特性适应需求,在Solr Tutorial里可以看到。
Solr的安装和配置
网上有很多资料,Solr3.6版本的配置也没有什么特别的。下载解压之后,可以选择直接进入 {solr.home}/example 目录,用 java -jar start.jar在8983端口启动服务,是在jetty容器里启动的,在 {solr.home}/example 子目录下已经为你部署好了jetty,jsp的jar包以及环境配置。你也可以启动自己的tomcat,把solr.war放到tomcat的webapp目录下,注意的是需要在 {tomcat.home}/conf/Catalina/localhost 下手动加一个solr.xml文件,作用是指向你的{solr.home}路径,让tomcat找到你要启动的web服务。比如我的:
<Context docBase="E:\softs2\apache-tomcat-6.0.36\webapps\solr.war" debug="0" crossContext="true" > <Environment name="solr/home" type="java.lang.String" value="E:\softs2\apache-solr-3.6.2\example\solr" override="true" /> </Context>
启动solr后,通过浏览器就可以访问到你的solr服务,具体别的自己慢慢研究吧。
首先是中文分词器。我使用的是IKAnalyzer2012_u6版本,IK,庖丁和mmseg是主流的三个支持lucene的分词库。但是中文分词库注定悲剧的地方在于总要跟着国外开源软件的更新走,一旦lucene,solr更新了,某些基础类改变或者接口变动,分词器可能就不再支持了。IKTokenizerFactory继承自solr的BaseTokenizerFactory,但是BaseTokenizerFactory在solr3.6之后版本已经不存在了,如果你按照以前的配置方式在scheme里配置:
<fieldType name="text" class="solr.TextField" > <analyzer type="index"> <tokenizer class="org.wltea.analyzer.solr.IKTokenizerFactory" useSmart ="false"/> </analyzer> <analyzer type="query"> <tokenizer class="org.wltea.analyzer.solr.IKTokenizerFactory" useSmart ="false"/> </analyzer> </fieldType>启动solr后报错提示BaseTokenizerFactory类是找不到了的。所以只能这样配置:
<fieldType name="text_ik" class="solr.TextField"> <analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/> </fieldType>再者是solrj版本。我的solr是3.6版本,我在IDE里使用的solrj是3.6.0版本的。通过maven的pom.xml里的Dependencis查找solrj,并自动获取相关包之后,还是会出现问题。原因是HttpSolrServer类还依赖别的包。那就是httpcomponents的httpclient和httpmime包,应该4.1后的版本是可以的,我依赖的是4.1.3。总之最后的pom.xml里的配置是这样的:
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId&
1、找到你的tomcat安装路径——conf——tomcat-user.xml文件将你登录名用户修改一下
修改前:
<tomcat-users>
<role rolename="manager"/>
<role rolename="admin"/>
<user username="admin" password="admin" roles="admin"/>
</tomcat-users>
这种的使用admin/admin登录是有效果的,localhost,但是将localhost换成127.0.0.1就会出现错误
修改后:
<tomcat-users>
<role rolename="manager"/>
<role rolename="admin"/>
<user username="admin" password="admin" roles="manager"/>
</tomcat-users>
这样就可以使用127.0.0.1访问了,
2、问题所在:角色设置问题,后期进一步说明原因
#include<stdio.h> #include<string.h> int main() { char c, s[20], *p; int a=1234, *i; float f=3.141592653589; double x=0.12345678987654321; p="How do you do"; strcpy(s, "Hello, Comrade"); *i=12; c='\x41'; printf("a=%d\n", a); /*结果输出十进制整数a=1234*/ printf("a=%6d\n", a); /*结果输出6位十进制数a= 1234*/ printf("a=%06d\n", a); /*结果输出6位十进制数a=001234*/ printf("a=%2d\n", a); /*a超过2位, 按实际值输出a=1234*/ printf("*i=%4d\n", *i); /*输出4位十进制整数*i= 12*/ printf("*i=%-4d\n", *i); /*输出左对齐4位十进制整数*i=12*/ printf("i=%p\n", i); /*输出地址i=06E4*/ printf("f=%f\n", f); /*输出浮点数f=3.141593*/ printf("f=6.4f\n", f); /*输出6位其中小数点后4位的浮点数f=3.1416*/ printf("x=%lf\n", x); /*输出长浮点数x=0.123457*/ printf("x=%18.16lf\n", x);/*输出18位其中小数点后16位的长浮点数x=0.1234567898765432*/ printf("c=%c\n", c); /*输出字符c=A*/ printf("c=%x\n", c); /*输出字符的ASCII码值c=41*/ printf("s[]=%s\n", s); /*输出数组字符串s[]=Hello, Comrade*/ printf("s[]=%6.9s\n", s);/*输出最多9个字符的字符串s[]=Hello,Co*/ printf("s=%p\n", s); /*输出数组字符串首字符地址s=FFBE*/ printf("*p=%s\n", p); /* 输出指针字符串p=How do you do*/ printf("p=%p\n", p); /*输出指针的值p=0194*/ getch(); retunr 0; }