Solr
是一种可供企业使用的、基于 Lucene 的搜索服务器,它支持层面搜索、命中醒目显示和多种输出格式。在这篇文章中,将介绍
Solr 并展示如何轻松地将其表现优异的全文本搜索功能加入到 Web 应用程序中。
开发环境:
System:Windows
WebBrowser:IE6+、Firefox3+
JDK:1.6+
JavaEE Server:tomcat5.0.2.8、tomcat6
IDE:eclipse、MyEclipse 8
开发依赖库:
JavaEE 5、solr 3.4
一、配置和安装solr
1、 首先去apache官方网站下载solr,下载地址
http://labs.renren.com/apache-mirror//lucene/solr/3.4.0/
目前最新的是3.4的版本
2、 下载后解压目录如下
client是一个ruby实现的示例,这个我们暂时不管
contrib有一些功能模块是需要的jar包
dist是打包发布好的工程war包
docs是帮助文档
example是示例,里面有打包部署好的solr工程示例和servlet容器jetty。如果你没有tomcat可以直接使用Jetty服务器部署你的solr示例。
3、 发布、部署solr示例
A、 利用自带的Jetty服务器
首先在dos命令中进入到下载好的solr解压的目录apache-solr-3.4.0的example目录
cd E:\JAR\solr\apache-solr-3.4.0\example
然后利用java命令,启动jetty服务器。Java –jar start.jar
启动Jetty成功后,如果没有看到错误消息,你可以看到端口信息。
如果你的端口冲突了,你可以到解压的solr示例包的example/etc的jetty.xml中,修改端口port信息。
<Set name="port">
<SystemProperty name="jetty.port" default="8983"/>
</Set> |
B、 利用tomcat发布solr示例
将下载的solr解压后,进入apache-solr-3.4.0\dist目录,将里面的solr.war放到D:\tomcat-6.0.28\webapps目录下,启动tomcat会自动解压。(当然,你也可以手动解压放到wabapps目录下)
当然你也可以设置context指向你的solr工程,在D:\tomcat-6.0.28\conf\Catalina\localhost目录加入solr.xml配置,配置如下:
<Context docBase="D:\solr.war" debug="0" crossContext="true" >
<Environment name="solr/home" type="java.lang.String" value="D:\solr" override="true" />
</Context> |
上面的2步都是一样的,这样还没有完。启动后你可能会看到如下错误:
我们需要将一些配置和index库文件也放到解压好的solr工程下。我们到解压的apache-solr-3.4.0\example\solr目录下,将里面的conf和data目录copy到刚才我们部署的D:\tomcat-6.0.28\webapps\solr工程目录下。或是copy到你的solr.xml中的context指定的路径下工程目录中。
重启tomcat就ok了。
4、 这个时候你就可以访问http://localhost:8983/solr/admin/你就可以看到如下界面:
在Query String中输入solr,点击Search就可以查询到相应的结果,结果以xml形式返回。当然你也可以设置返回数据类型为json。
二、Solr理论
1、 solr基础
因为 Solr 包装并扩展了 Lucene,所以它们使用很多相同的术语。更重要的是,Solr 创建的索引与
Lucene 搜索引擎库完全兼容。通过对 Solr 进行适当的配置,某些情况下可能需要进行编码,Solr
可以阅读和使用构建到其他 Lucene 应用程序中的索引。
在 Solr 和 Lucene 中,使用一个或多个 Document 来构建索引。Document
包括一个或多个 Field。Field 包括名称、内容以及告诉 Solr 如何处理内容的元数据。例如,Field
可以包含字符串、数字、布尔值或者日期,也可以包含你想添加的任何类型,只需用在solr的配置文件中进行相应的配置即可。Field
可以使用大量的选项来描述,这些选项告诉 Solr 在索引和搜索期间如何处理内容。现在,查看一下表 1 中列出的重要属性的子集:
2、 solr索引操作
在 Solr 中,通过向部署在 servlet 容器中的 Solr Web 应用程序发送 HTTP 请求来启动索引和搜索。Solr
接受请求,确定要使用的适当 SolrRequestHandler,然后处理请求。通过 HTTP 以同样的方式返回响应。默认配置返回
Solr 的标准 XML 响应。你也可以配置 Solr 的备用响应格式,如json、csv格式的文本。
索引就是接受输入元数据(数据格式在schema.xml中进行配置)并将它们传递给 Solr,从而在 HTTP
Post XML 消息中进行索引的过程。你可以向 Solr 索引 servlet 传递四个不同的索引请求:
add/update 允许您向 Solr 添加文档或更新文档。直到提交后才能搜索到这些添加和更新。
commit 告诉 Solr,应该使上次提交以来所做的所有更改都可以搜索到。
optimize 重构 Lucene 的文件以改进搜索性能。索引完成后执行一下优化通常比较好。如果更新比较频繁,则应该在使用率较低的时候安排优化。一个索引无需优化也可以正常地运行。优化是一个耗时较多的过程。
delete 可以通过 id 或查询来指定。按 id 删除将删除具有指定 id 的文档;按查询删除将删除查询返回的所有文档。
Lucene中操作索引也有这几个步骤,但是没有更新。Lucene更新是先删除,然后添加索引。因为更新索引在一定情况下,效率没有先删除后添加的效率好。
3、 搜索
添加文档后,就可以搜索这些文档了。Solr 接受 HTTP GET 和 HTTP POST 查询消息。收到的查询由相应的
SolrRequestHandler 进行处理。
solr查询参数描述:
4、 solr模式
上面有提到schema.xml这个配置,这个配置可以在你下载solr包的安装解压目录的apache-solr-3.4.0\example\solr\conf中找到,它就是solr模式关联的文件。打开这个配置文件,你会发现有详细的注释。
模式组织主要分为三个重要配置
types 部分是一些常见的可重用定义,定义了 Solr(和 Lucene)如何处理 Field。也就是添加到索引中的xml文件属性中的类型,如int、text、date等
fileds是你添加到索引文件中出现的属性名称,而声明类型就需要用到上面的types
其他配置有
uniqueKey 唯一键,这里配置的是上面出现的fileds,一般是id、url等不重复的。在更新、删除的时候可以用到。
defaultSearchField默认搜索属性,如q=solr就是默认的搜索那个字段
solrQueryParser查询转换模式,是并且还是或者(and/or)
schema配置类型
<fieldType name="text" class="solr.TextField" positionIncrementGap="100"> <analyzer type="index"> <tokenizer class="solr.WhitespaceTokenizerFactory" /> <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" /> <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0" /> <filter class="solr.LowerCaseFilterFactory" /> <filter class="solr.EnglishPorterFilterFactory" protected="protwords.txt" /> <filter class="solr.RemoveDuplicatesTokenFilterFactory" /> </analyzer> <analyzer type="query"> <tokenizer class="solr.WhitespaceTokenizerFactory" /> <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true" /> <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" /> <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="0" catenateNumbers="0" catenateAll="0" /> <filter class="solr.LowerCaseFilterFactory" /> <filter class="solr.EnglishPorterFilterFactory" protected="protwords.txt" /> <filter class="solr.RemoveDuplicatesTokenFilterFactory" /> </analyzer> </fieldType>
|
上面就是一个type了,然后你在fields配置field的时候就可以用这个type。
首先,上面的fieldType的配置中有两个analyzer,它是分词器。主要把我们的数据进行分割成一个个的词语。词干提取、停止词删除以及相似的操作都被应用于标记,然后才进行索引和搜索,导致使用相同类型的标记。
上面的应用程序的 Solr 的fieldType配置按以下步骤进行设置:
根据空白进行断词,然后删除所有的公共词(StopFilterFactory)
使用破折号处理特殊的大小写、大小写转换等等。(WordDelimiterFilterFactory);将所有条目处理为小写(LowerCaseFilterFactory)
使用 Porter Stemming 算法进行词干提取(EnglishPorterFilterFactory)
删除所有的副本(RemoveDuplicatesTokenFilterFactory)
Schema属性、字段
<field name="id" type="string" indexed="true" stored="true" required="true" /> <field name="sku" type="text_en_splitting_tight" indexed="true" stored="true" omitNorms="true" /> <field name="name" type="text_general" indexed="true" stored="true" /> <field name="alphaNameSort" type="alphaOnlySort" indexed="true" stored="false" /> <field name="manu" type="text_general" indexed="true" stored="true" omitNorms="true" /> <field name="cat" type="string" indexed="true" stored="true" multiValued="true" /> <field name="features" type="text_general" indexed="true" stored="true" multiValued="true" /> <field name="includes" type="text_general" indexed="true" stored="true" termVectors="true" termPositions="true" termOffsets="true" />
|
属性是在添加索引、查询的时候必须的配置,如果你不加这些配置。是无法完成索引的创建的。
首先id属性是未经分析的字符串类型,是可以索引、存储的,并且是唯一的。
sku是一个经过分词器分析出来的英文切割的类型字符,可以索引、存储、不要存储规范
multiValued 属性是一个特殊的例子,指 Document 可以拥有一个相同名称添加了多次的
Field。
omitNorms 属性告诉 Solr(和 Lucene)不要存储规范。
介绍一下字段声明下方的 <dynamicField> 声明。动态字段是一些特殊类型的字段,可以在任何时候将这些字段添加到任何文档中,由字段声明定义它们的属性。动态字段和普通字段之间的关键区别在于前者不需要在
schema.xml 中提前声明名称。Solr 将名称声明中的 glob-like 模式应用到所有尚未声明的引入的字段名称,并根据其
<dynamicField> 声明定义的语义来处理字段。例如,<dynamicField
name="*_i" type="sint" indexed="true"
stored="true"/> 指一个 myRating_i 字段被 Solr
处理为 sint,尽管并未将其声明为字段。这种处理比较方便,例如,当需要用户定义待搜索内容的时候。
5、 索引配置
Solr 性能因素,来了解与各种更改相关的性能权衡。
表 1 概括了可控制 Solr 索引处理的各种因素:
6、 查询处理配置
<maxBooleanClauses> 标记定义了可组合在一起形成一个查询的子句数量的上限。对于大多数应用程序而言,默认的
1024 就应该已经足够;然而,如果应用程序大量使用了通配符或范围查询,增加这个限值将能避免当值超出时,抛出
TooManyClausesException。
若应用程序预期只会检索 Document 上少数几个 Field,那么可以将 <enableLazyFieldLoading>
属性设置为 true。懒散加载的一个常见场景大都发生在应用程序返回和显示一系列搜索结果的时候,用户常常会单击其中的一个来查看存储在此索引中的原始文档。初始的显示常常只需要显示很短的一段信息。若考虑到检索大型
Document 的代价,除非必需,否则就应该避免加载整个文档。
<query> 部分负责定义与在 Solr 中发生的事件相关的几个选项。Searcher
的 Java 类来处理 Query 实例。要改进这一设计和显著提高性能,把这些新的 Searcher 联机以便为现场用户提供查询服务之前,先对它们进行
“热身”。<query> 部分中的 <listener> 选项定义 newSearcher
和 firstSearcher 事件,您可以使用这些事件来指定实例化新搜索程序或第一个搜索程序时应该执行哪些查询。如果应用程序期望请求某些特定的查询,那么在创建新搜索程序或第一个搜索程序时就应该反注释这些部分并执行适当的查询。
solrconfig.xml 文件的剩余部分,除 <admin>
之外,涵盖了与 缓存、复制 和 扩展或定制 Solr 有关的项目。admin 部分让您可以定制管理界面。有关配置
admin 节的更多信息,请参看solrconfig.xml 文件中的注释。
7、 监视、记录和统计数据
用于监视、记录和统计数据的 Solr 管理选项
8、 智能缓存
智能缓存是让 Solr 得以成为引人瞩目的搜索服务器的一个关键性能特征。Solr
提供了四种不同的缓存类型,所有四种类型都可在 solrconfig.xml 的 <query>
部分中配置。solrconfig.xml 文件中所用的标记名列出了这些缓存类型:
每个缓存声明都接受最多四个属性:
class 是缓存实现的 Java 名。
size 是最大的条目数。
initialSize 是缓存的初始大小。
autoWarmCount 是取自旧缓存以预热新缓存的条目数。如果条目很多,就意味着缓存的
hit 会更多,只不过需要花更长的预热时间。
三、利用SolrJ操作solr API,完成index操作
使用SolrJ操作Solr会比利用httpClient来操作Solr要简单。SolrJ是封装了httpClient方法,来操作solr的API的。SolrJ底层还是通过使用httpClient中的方法来完成Solr的操作。
1、 首先,你需要添加如下jar包
其中apache-solr-solrj-3.4.0.jar、slf4j-api-1.6.1.jar可以在下载的apache-solr-3.4.0的压缩包中的dist中能找到。
2、 其次,建立一个简单的测试类,完成Server对象的相关方法的测试工作,代码如下:
package com.hoo.test; import java.io.IOException; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collection; import java.util.List; 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.CommonsHttpSolrServer; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.client.solrj.response.UpdateResponse; import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.SolrInputDocument; import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.params.SolrParams; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.hoo.entity.Index; /** * <b>function:</b> Server TestCase * @author hoojo * @createDate 2011-10-19 下午01:49:07 * @file ServerTest.java * @package com.hoo.test * @project SolrExample * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public class ServerTest { private SolrServer server; private CommonsHttpSolrServer httpServer; private static final String DEFAULT_URL = "http://localhost:8983/solr/"; @Before public void init() { try { server = new CommonsHttpSolrServer(DEFAULT_URL); httpServer = new CommonsHttpSolrServer(DEFAULT_URL); } catch (MalformedURLException e) { e.printStackTrace(); } } @After public void destory() { server = null; httpServer = null; System.runFinalization(); System.gc(); } public final void fail(Object o) { System.out.println(o); } /** * <b>function:</b> 测试是否创建server对象成功 * @author hoojo * @createDate 2011-10-21 上午09:48:18 */ @Test public void server() { fail(server); fail(httpServer); } /** * <b>function:</b> 根据query参数查询索引 * @author hoojo * @createDate 2011-10-21 上午10:06:39 * @param query */ public void query(String query) { SolrParams params = new SolrQuery(query); try { QueryResponse response = server.query(params); SolrDocumentList list = response.getResults(); for (int i = 0; i < list.size(); i++) { fail(list.get(i)); } } catch (SolrServerException e) { e.printStackTrace(); } } }
|
测试运行server case方法,如果成功创建对象,那你就成功的链接到。
注意:在运行本方法之前,请启动你的solr官方自动的项目。http://localhost:8983/solr/保证能够成功访问这个工程。因为接下来的所有工作都是围绕这个solr工程完成的。如果你现在还不知道,怎么部署、发布官方solr工程,请参考前面的具体章节。
3、 Server的有关配置选项参数,server是CommonsHttpSolrServer的实例
server.setSoTimeout(1000); // socket read timeout server.setConnectionTimeout(100); 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); server.setMaxRetries(1); // defaults to 0. > 1 not recommended. //sorlr J 目前使用二进制的格式作为默认的格式。对于solr1.2的用户通过显示的设置才能使用XML格式。 server.setParser(new XMLResponseParser()); //二进制流输出格式 //server.setRequestWriter(new BinaryRequestWriter());
|
4、 利用SolrJ完成Index Document的添加操作
/** * <b>function:</b> 添加doc文档 * @author hoojo * @createDate 2011-10-21 上午09:49:10 */ @Test public void addDoc() { //创建doc文档 SolrInputDocument doc = new SolrInputDocument(); doc.addField("id", 1); doc.addField("name", "Solr Input Document"); doc.addField("manu", "this is SolrInputDocument content"); try { //添加一个doc文档 UpdateResponse response = server.add(doc); fail(server.commit());//commit后才保存到索引库 fail(response); fail("query time:" + response.getQTime()); fail("Elapsed Time:" + response.getElapsedTime()); fail("status:" + response.getStatus()); } catch (SolrServerException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } query("name:solr"); }
|
在apache-solr-3.4.0\example\solr\conf目录下的schema.xml中可以找到有关于field属性的配置,schema.xml中的field就和上面Document文档中的field(id、name、manu)对应。如果出现ERROR:unknown
field 'xxxx'就表示你设置的这个field在schema.xml中不存在。如果一定要使用这个field,请你在schema.xml中进行filed元素的配置。具体请参考前面的章节。
注意:在schema.xml中配置了uniqueKey为id,就表示id是唯一的。如果在添加Document的时候,id重复添加。那么后面添加的相同id的doc会覆盖前面的doc,类似于update更新操作,而不会出现重复的数据。
5、 利用SolrJ添加多个Document,即添加文档集合
/** * <b>function:</b> 添加docs文档集合 * @author hoojo * @createDate 2011-10-21 上午09:55:01 */ @Test public void addDocs() { Collection<SolrInputDocument> docs = new ArrayList<SolrInputDocument>(); SolrInputDocument doc = new SolrInputDocument(); doc.addField("id", 2); doc.addField("name", "Solr Input Documents 1"); doc.addField("manu", "this is SolrInputDocuments 1 content"); docs.add(doc); doc = new SolrInputDocument(); doc.addField("id", 3); doc.addField("name", "Solr Input Documents 2"); doc.addField("manu", "this is SolrInputDocuments 3 content"); docs.add(doc); try { //add docs UpdateResponse response = server.add(docs); //commit后才保存到索引库 fail(server.commit()); fail(response); } catch (SolrServerException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } query("solr"); }
|
就是添加一个List集合
6、 添加JavaEntity Bean,这个需要先创建一个JavaBean,然后来完成添加操作;
JavaBean:Index的代码
/** * <b>function:</b> JavaEntity Bean;Index需要添加相关的Annotation注解,便于告诉solr哪些属性参与到index中 * @author hoojo * @createDate 2011-10-19 下午05:33:27 * @file Index.java * @package com.hoo.entity * @project SolrExample * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public class Index { //@Field setter方法上添加Annotation也是可以的 private String id; @Field private String name; @Field private String manu; @Field private String[] cat; @Field private String[] features; @Field private float price; @Field private int popularity; @Field private boolean inStock; public String getId() { return id; } @Field public void setId(String id) { this.id = id; } //getter、setter方法 public String toString() { return this.id + "#" + this.name + "#" + this.manu + "#" + this.cat; } }
|
注意上面的属性是和在apache-solr-3.4.0\example\solr\conf目录下的schema.xml中可以找到有关于field属性的配置对应的。如果你Index
JavaBean中出现的属性在schema.xml的field配置无法找到,那么出出现unknown filed错误。
添加Bean完成doc添加操作
/** * <b>function:</b> 添加JavaEntity Bean * @author hoojo * @createDate 2011-10-21 上午09:55:37 */ @Test public void addBean() { //Index需要添加相关的Annotation注解,便于告诉solr哪些属性参与到index中 Index index = new Index(); index.setId("4"); index.setName("add bean index"); index.setManu("index bean manu"); index.setCat(new String[] { "a1", "b2" }); try { //添加Index Bean到索引库 UpdateResponse response = server.addBean(index); fail(server.commit());//commit后才保存到索引库 fail(response); } catch (SolrServerException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } queryAll(); }
|
7、 添加Bean集合
/** * <b>function:</b> 添加Entity Bean集合到索引库 * @author hoojo * @createDate 2011-10-21 上午10:00:55 */ @Test public void addBeans() { Index index = new Index(); index.setId("6"); index.setName("add beans index 1"); index.setManu("index beans manu 1"); index.setCat(new String[] { "a", "b" }); List<Index> indexs = new ArrayList<Index>(); indexs.add(index); index = new Index(); index.setId("5"); index.setName("add beans index 2"); index.setManu("index beans manu 2"); index.setCat(new String[] { "aaa", "bbbb" }); indexs.add(index); try { //添加索引库 UpdateResponse response = server.addBeans(indexs); fail(server.commit());//commit后才保存到索引库 fail(response); } catch (SolrServerException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } queryAll(); }
|
8、 删除索引Document
/** * <b>function:</b> 删除索引操作 * @author hoojo * @createDate 2011-10-21 上午10:04:28 */ @Test public void remove() { try { //删除id为1的索引 server.deleteById("1"); server.commit(); query("id:1"); //根据id集合,删除多个索引 List<String> ids = new ArrayList<String>(); ids.add("2"); ids.add("3"); server.deleteById(ids); server.commit(true, true); query("id:3 id:2"); //删除查询到的索引信息 server.deleteByQuery("id:4 id:6"); server.commit(true, true); queryAll(); } catch (SolrServerException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
|
9、 查询索引
/** * <b>function:</b> 查询所有索引信息 * @author hoojo * @createDate 2011-10-21 上午10:05:38 */ @Test public void queryAll() { ModifiableSolrParams params = new ModifiableSolrParams(); // 查询关键词,*:*代表所有属性、所有值,即所有index params.set("q", "*:*"); // 分页,start=0就是从0开始,,rows=5当前返回5条记录,第二页就是变化start这个值为5就可以了。 params.set("start", 0); params.set("rows", Integer.MAX_VALUE); // 排序,,如果按照id 排序,,那么将score desc 改成 id desc(or asc) params.set("sort", "score desc"); // 返回信息 * 为全部 这里是全部加上score,如果不加下面就不能使用score params.set("fl", "*,score"); try { QueryResponse response = server.query(params); SolrDocumentList list = response.getResults(); for (int i = 0; i < list.size(); i++) { fail(list.get(i)); } } catch (SolrServerException e) { e.printStackTrace(); } }
|
10、 其他和Server有关方法
/** * <b>function:</b> 其他server相关方法测试 * @author hoojo * @createDate 2011-10-21 上午10:02:03 */ @Test public void otherMethod() { fail(server.getBinder()); try { fail(server.optimize());//合并索引文件,可以优化索引、提供性能,但需要一定的时间 fail(server.ping());//ping服务器是否连接成功 Index index = new Index(); index.setId("299"); index.setName("add bean index199"); index.setManu("index bean manu199"); index.setCat(new String[] { "a199", "b199" }); UpdateResponse response = server.addBean(index); fail("response: " + response); queryAll(); //回滚掉之前的操作,rollback addBean operation fail("rollback: " + server.rollback()); //提交操作,提交后无法回滚之前操作;发现addBean没有成功添加索引 fail("commit: " + server.commit()); queryAll(); } catch (SolrServerException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
|
11、 文档查询
/** * <b>function:</b> query 基本用法测试 * @author hoojo * @createDate 2011-10-20 下午04:44:28 */ @Test public void queryCase() { //AND 并且 SolrQuery params = new SolrQuery("name:apple AND manu:inc"); //OR 或者 params.setQuery("name:apple OR manu:apache"); //空格 等同于 OR params.setQuery("name:server manu:dell"); //params.setQuery("name:solr - manu:inc"); //params.setQuery("name:server + manu:dell"); //查询name包含solr apple params.setQuery("name:solr,apple"); //manu不包含inc params.setQuery("name:solr,apple NOT manu:inc"); //50 <= price <= 200 params.setQuery("price:[50 TO 200]"); params.setQuery("popularity:[5 TO 6]"); //params.setQuery("price:[50 TO 200] - popularity:[5 TO 6]"); //params.setQuery("price:[50 TO 200] + popularity:[5 TO 6]"); //50 <= price <= 200 AND 5 <= popularity <= 6 params.setQuery("price:[50 TO 200] AND popularity:[5 TO 6]"); params.setQuery("price:[50 TO 200] OR popularity:[5 TO 6]"); //过滤器查询,可以提高性能 filter 类似多个条件组合,如and //params.addFilterQuery("id:VA902B"); //params.addFilterQuery("price:[50 TO 200]"); //params.addFilterQuery("popularity:[* TO 5]"); //params.addFilterQuery("weight:*"); //0 < popularity < 6 没有等于 //params.addFilterQuery("popularity:{0 TO 6}"); //排序 params.addSortField("id", ORDER.asc); //分页:start开始页,rows每页显示记录条数 //params.add("start", "0"); //params.add("rows", "200"); //params.setStart(0); //params.setRows(200); //设置高亮 params.setHighlight(true); // 开启高亮组件 params.addHighlightField("name");// 高亮字段 params.setHighlightSimplePre("<font color='red'>");//标记,高亮关键字前缀 params.setHighlightSimplePost("</font>");//后缀 params.setHighlightSnippets(1);//结果分片数,默认为1 params.setHighlightFragsize(1000);//每个分片的最大长度,默认为100 //分片信息 params.setFacet(true) .setFacetMinCount(1) .setFacetLimit(5)//段 .addFacetField("name")//分片字段 .addFacetField("inStock"); //params.setQueryType(""); try { QueryResponse response = server.query(params); /*List<Index> indexs = response.getBeans(Index.class); for (int i = 0; i < indexs.size(); i++) { fail(indexs.get(i)); }*/ //输出查询结果集 SolrDocumentList list = response.getResults(); fail("query result nums: " + list.getNumFound()); for (int i = 0; i < list.size(); i++) { fail(list.get(i)); } //输出分片信息 List<FacetField> facets = response.getFacetFields(); for (FacetField facet : facets) { fail(facet); List<Count> facetCounts = facet.getValues(); for (FacetField.Count count : facetCounts) { System.out.println(count.getName() + ": " + count.getCount()); } } } catch (SolrServerException e) { e.printStackTrace(); } }
|
12、 分片查询、统计
/** * <b>function:</b> 分片查询, 可以统计关键字及出现的次数、或是做自动补全提示 * @author hoojo * @createDate 2011-10-20 下午04:54:25 */ @Test public void facetQueryCase() { SolrQuery params = new SolrQuery("*:*"); //排序 params.addSortField("id", ORDER.asc); params.setStart(0); params.setRows(200); //Facet为solr中的层次分类查询 //分片信息 params.setFacet(true) .setQuery("*:*") .setFacetMinCount(1) .setFacetLimit(5)//段 //.setFacetPrefix("electronics", "cat") .setFacetPrefix("cor")//查询manu、name中关键字前缀是cor的 .addFacetField("manu") .addFacetField("name");//分片字段 try { QueryResponse response = server.query(params); //输出查询结果集 SolrDocumentList list = response.getResults(); fail("Query result nums: " + list.getNumFound()); for (int i = 0; i < list.size(); i++) { fail(list.get(i)); } fail("All facet filed result: "); //输出分片信息 List<FacetField> facets = response.getFacetFields(); for (FacetField facet : facets) { fail(facet); List<Count> facetCounts = facet.getValues(); for (FacetField.Count count : facetCounts) { //关键字 - 出现次数 fail(count.getName() + ": " + count.getCount()); } } fail("Search facet [name] filed result: "); //输出分片信息 FacetField facetField = response.getFacetField("name"); List<Count> facetFields = facetField.getValues(); for (Count count : facetFields) { //关键字 - 出现次数 fail(count.getName() + ": " + count.getCount()); } } catch (SolrServerException e) { e.printStackTrace(); } }
|
分片查询在某些统计关键字的时候还是很有用的,可以统计关键字出现的次数,可以通过统计的关键字来搜索相关文档的信息。
四、Document文档和JavaBean相互转换
这里转换的Bean是一个简单的User对象
package com.hoo.entity; import java.io.Serializable; import org.apache.solr.client.solrj.beans.Field; /** * <b>function:</b> User Entity Bean;所有被添加Annotation @Field 注解的属性将参与index操作 * @author hoojo * @createDate 2011-10-19 下午04:16:00 * @file User.java * @package com.hoo.entity * @project SolrExample * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public class User implements Serializable { /** * @author Hoojo */ private static final long serialVersionUID = 8606788203814942679L; //@Field private int id; @Field private String name; @Field private int age; /** * 可以给某个属性重命名,likes就是solr index的属性;在solrIndex中将显示like为likes */ @Field("likes") private String[] like; @Field private String address; @Field private String sex; @Field private String remark; public int getId() { return id; } //setter 方法上面也可以 @Field public void setId(int id) { this.id = id; } public String getName() { return name; } //getter、setter @Override public String toString() { return this.id + "#" + this.name + "#" + this.age + "#" + this.like + "#" + this.address + "#" + this.sex + "#" + this.remark; } }
|
测试类代码如下
package com.hoo.test; import org.apache.solr.client.solrj.beans.DocumentObjectBinder; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.SolrInputDocument; import org.apache.solr.common.SolrInputField; import org.junit.Test; import com.hoo.entity.User; /** * <b>function:</b>SolrInputDocument implements Map, Iterable * @author hoojo * @createDate 2011-10-19 下午03:54:54 * @file SolrInputDocumentTest.java * @package com.hoo.test * @project SolrExample * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */ public class SolrInputDocumentTest { public final void fail(Object o) { System.out.println(o); } /** * <b>function:</b> 创建SolrInputDocument * @author hoojo * @createDate 2011-10-21 下午03:38:20 */ @Test public void createDoc() { SolrInputDocument doc = new SolrInputDocument(); doc.addField("id", System.currentTimeMillis()); doc.addField("name", "SolrInputDocument"); doc.addField("age", 22, 2.0f); doc.addField("like", new String[] { "music", "book", "sport" }); doc.put("address", new SolrInputField("guangzhou")); doc.setField("sex", "man"); doc.setField("remark", "china people", 2.0f); fail(doc); } /** * <b>function:</b> 利用DocumentObjectBinder对象将SolrInputDocument 和 User对象相互转换 * @author hoojo * @createDate 2011-10-21 下午03:38:40 */ @Test public void docAndBean4Binder() { SolrDocument doc = new SolrDocument(); doc.addField("id", 456); doc.addField("name", "SolrInputDocument"); doc.addField("likes", new String[] { "music", "book", "sport" }); doc.put("address", "guangzhou"); doc.setField("sex", "man"); doc.setField("remark", "china people"); DocumentObjectBinder binder = new DocumentObjectBinder(); User user = new User(); user.setId(222); user.setName("JavaBean"); user.setLike(new String[] { "music", "book", "sport" }); user.setAddress("guangdong"); fail(doc); // User ->> SolrInputDocument fail(binder.toSolrInputDocument(user)); // SolrDocument ->> User fail(binder.getBean(User.class, doc)); SolrDocumentList list = new SolrDocumentList(); list.add(doc); list.add(doc); //SolrDocumentList ->> List fail(binder.getBeans(User.class, list)); } /** * <b>function:</b> SolrInputDocument的相关方法 * @author hoojo * @createDate 2011-10-21 下午03:44:30 */ @Test public void docMethod() { SolrInputDocument doc = new SolrInputDocument(); doc.addField("id", System.currentTimeMillis()); doc.addField("name", "SolrInputDocument"); doc.addField("age", 23, 1.0f); doc.addField("age", 22, 2.0f); doc.addField("age", 24, 0f); fail(doc.entrySet()); fail(doc.get("age")); //排名有用,类似百度竞价排名 doc.setDocumentBoost(2.0f); fail(doc.getDocumentBoost()); fail(doc.getField("name")); fail(doc.getFieldNames());//keys fail(doc.getFieldValues("age")); fail(doc.getFieldValues("id")); fail(doc.values()); } }
|
|