Solr是一个基于Lucene java库的企业级搜索服务器,包含XML/HTTP,JSON API, 高亮查询结果,faceted search,缓存,复制和一个WEB管理界面。Solr运行在Servlet容器中。所以Solr和Lucene的本质区别有以下三点:搜索服务器,企业级和管理。
Lucene本质上是搜索库,不是独立的应用程序,而Solr是。Lucene专注于搜索底层的建设,而Solr专注于企业应用。Lucene不负责支撑搜索服务所必须的管理,而Solr负责。
所以说,一句话概括:Solr是Lucene面向企业搜索应用的扩展。
solr的安装
环境介绍:tomcat7服务器,solr-4.7.2,IK Analyzer 2012FF_hf1文分词器
- 将solr解压文件夹下面的example\webapps的solr.war文件添加tomcat7/webapp的下面
- 新建一个solr文件夹作为solr的home文件夹,拷贝example\solr下面的所有文件到solr文件夹
- 启动tomcat解压war包
- 拷贝jar包(example\lib\ext下面所有的jar包)到tomcat7\lib目录下面
进入tomcat7\webapps\solr\WEB-INF文件夹修改web.xml,取消env-entry的注释,将env-entry-value值修改成为步骤2中的solr的路径
<env-entry> <env-entry-name>solr/home</env-entry-name> <env-entry-value>D:/develop/tomcat7/solr</env-entry-value> <env-entry-type>java.lang.String</env-entry-type> </env-entry>
- 重启tomcat服务器,并进入http://localhost/solr/查看是否能够进去管理页面
solr中文分词
IK Analyzer分词器
- 解压缩IK Analyzer 2012FF_hf1压缩文件
- 把IKAnalyzer2012FF_u1.jar拷贝到solr服务的/tomcat7/webapps/solr/WEB-INF/lib下面。
- 把IKAnalyzer.cfg.xml、stopword.dic拷贝到需要使用分词器的/tomcat7/solr/collection1/conf下面,和schema.xml文件同一个目录。
修改schema.xml文件,在
… 之间加入如下配置:<fieldType name="text_ik" class="solr.TextField"> <analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/> </fieldType>
如此,我们的中文分词器就配置好了
schema.xml文件中type参数说明:
name:标识
class:和其他属性决定了这个fieldType的实际行为。
sortMissingLast:设置成true没有该field的数据排在有该field的数据之后,而不管请求时的排序规则, 默认是设置成false。
sortMissingFirst:跟上面倒过来呗。 默认是设置成false
analyzer:字段类型指定的分词器
type:当前分词用用于的操作.index代表生成索引时使用的分词器query代码在查询时使用的分词器
tokenizer:分词器类
filter:分词后应用的过滤器 过滤器调用顺序和配置相同将schema.xml文件中配置的需要field类型中需要使用中的字段替换为text_ik即可
如:<field name="title" type="text_ik" indexed="true" stored="true" multiValued="true"/>
将之前
type="text_general"
替换为type="text_ik"
schema.xml文件中field参数说明:
name:字段类型名
class:java类名
indexed:缺省true。 说明这个数据应被搜索和排序,如果数据没有indexed,则stored应是true。
stored:缺省true。说明这个字段被包含在搜索结果中是合适的。如果数据没有stored,则indexed应是true。
omitNorms:字段的长度不影响得分和在索引时不做boost时,设置它为true。一般文本字段不设置为true。
termVectors:如果字段被用来做more like this 和highlight的特性时应设置为true。
compressed:字段是压缩的。这可能导致索引和搜索变慢,但会减少存储空间,只有StrField和TextField是可以压缩,这通常适合字段的长度超过200个字符。
multiValued:字段多于一个值的时候,可设置为true。最后进入http://localhost/solr/#/collection1/analysis输入
维多利亚之心
,选择’text_ik‘分词器,点击’分析’,出现下图表示中文分词器配置成功
smartcn 分词器
- 将发行包中contrib/analysis-extras/lucene-libs/lucene-analyzers-smartcn-4.7.2.jar中的复制到webapps\solr\WEB-INF\lib文件夹下面
配置schemal.xml文件,注册分词器,如下配置:
<fieldType name="text_smart" class="solr.TextField" positionIncrementGap="100"> <analyzer type="index"> <tokenizer class="solr.SmartChineseSentenceTokenizerFactory"/> <filter class="solr.SmartChineseWordTokenFilterFactory"/> </analyzer> <analyzer type="query"> <tokenizer class="solr.SmartChineseSentenceTokenizerFactory"/> <filter class="solr.SmartChineseWordTokenFilterFactory"/> </analyzer>
引用字段类型即可
4.最后进入http://localhost/solr/#/collection1/analysis输入维多利亚之心
,选择’text_smart‘分词器,点击’分析’,出现下图表示中文分词器配置成功
两种分词比较
- IKAnalyzer分词后的碎片太多
- 从自定义词库的角度考虑,因为smartcn在Lucene4.6中的版本,目前不支持自定义词库
solrJ的使用
准备工作
- 添加solr解压文件下面的solr-4.7.2/dist/solrj-lib文件夹下面所有jar文件
- 下载solr-solrj-4.7.2.jar放置到你的工程lib下面
- 在schema.xml设置好字段信息(名称,类型,索引,存储,分词等信息)
普通添加索引和删除索引
package cn.xiaoyu.solr;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.common.SolrInputDocument;
public class AddDocsDemo {
public static final String SOLR_URL = "http://localhost/solr";
public static void main(String[] args) {
AddDocs();
//delDocs();
}
public static void AddDocs() {
String[] words = {"人民","人民政府","民政","劳动人民","中华人民共和国"};
long start = System.currentTimeMillis();
Collection<SolrInputDocument> docs = new ArrayList<SolrInputDocument>();
for (int i=1;i<100;i++){
SolrInputDocument doc = new SolrInputDocument();
doc.addField("id","id"+i,1.0f);
doc.addField("name",words[i%5],1.0f);
doc.addField("price",10*i);
docs.add(doc);
}
try {
// 链接服务器
HttpSolrServer server = new HttpSolrServer(SOLR_URL);
server.setConnectionTimeout(100);
server.setDefaultMaxConnectionsPerHost(100);
server.setMaxTotalConnections(100);
/**
* 可以通过三种方式增加doc,其中server.add(docs.iterator())效率最高
*/
// 第一种提交方式:
// 增加后通过执行commit函数commit (936ms)
// server.add(docs);
// server.commit();
// 第二种提交方式:将提交方法设置为自动提交
// 增加doc后立即commit (946ms)
// UpdateRequest req = new UpdateRequest();
// req.setAction(ACTION.COMMIT, false, false);
// req.add(docs);
// UpdateResponse rsp = req.process(server);
// 第三种提交方式:
// the most optimal way of updating all your docs in one http request(432ms)
server.add(docs.iterator());
server.commit();
} catch (Exception e) {
System.out.println(e);
}
System.out.println("time elapsed(ms):" + (System.currentTimeMillis() - start));
}
// 删除 索引
public static void delDocs() {
long start = System.currentTimeMillis();
try {
HttpSolrServer server = new HttpSolrServer(SOLR_URL);
/**
* 这儿删除也有三种方式:通过id来删除,通过ids来删除,通过查询来删除
*/
// 第一种方式: 通过id来删除
// String id = "id1";
// server.deleteById(id);
// server.commit();
// 第二种方式:通过ids来删除
List<String> ids = new ArrayList<String>();
for (int i = 1; i < 100; i++) {
ids.add("id" + i);
}
server.deleteById(ids);
server.commit();
// 第三种方式:通过查询删除全部
// server.deleteByQuery("*:*");
// server.commit();
} catch (Exception e) {
System.out.println(e);
}
System.out.println("time elapsed(ms):" + (System.currentTimeMillis() - start));
}
}
普通查询索引
package cn.xiaoyu.solr;
import java.io.IOException;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrQuery.ORDER;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.BinaryRequestWriter;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.client.solrj.impl.XMLResponseParser;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
public class QueryDocsDemo {
public static final String SOLR_URL = "http://localhost/solr";
public static void main(String[] args) throws SolrServerException,
IOException {
HttpSolrServer server = new HttpSolrServer(SOLR_URL);
server.setMaxRetries(1); // defaults to 0. > 1 not recommended.
server.setConnectionTimeout(5000); // 5 seconds to establish TCP
// 正常情况下,以下参数无须设置
// 使用老版本solrj操作新版本的solr时,因为两个版本的javabin incompatible,所以需要设置Parser
server.setParser(new XMLResponseParser());
server.setSoTimeout(1000); // socket read timeout
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);
// 使用ModifiableSolrParams传递参数
// ModifiableSolrParams params = new ModifiableSolrParams();
// //
// 192.168.230.128:8983/solr/select?q=video&fl=id,name,price&sort=price
// asc&start=0&rows=2&wt=json
// // 设置参数,实现上面URL中的参数配置
// // 查询关键词
// params.set("q", "video");
// // 返回信息
// params.set("fl", "id,name,price,score");
// // 排序
// params.set("sort", "price asc");
// // 分页,start=0就是从0开始,rows=5当前返回5条记录,第二页就是变化start这个值为5就可以了
// params.set("start", 2);
// params.set("rows", 2);
// // 返回格式
// params.set("wt", "javabin");
// QueryResponse response = server.query(params);
// 使用SolrQuery传递参数,SolrQuery的封装性更好
server.setRequestWriter(new BinaryRequestWriter());
SolrQuery query = new SolrQuery();
query.setQuery("政府");
query.setFields("id", "name", "price", "score");
// 根据相关度排序
// query.setSort("score", ORDER.desc);
query.setSort("price", ORDER.asc);
query.setStart(0);
query.setRows(10);
//query.setRequestHandler("/select"); //默认就是/select
QueryResponse response = server.query(query);
// 搜索得到的结果数
System.out.println("Find:" + response.getResults().getNumFound());
// 输出结果
int iRow = 1;
for (SolrDocument doc : response.getResults()) {
System.out.println("----------" + iRow + "------------");
System.out.println("id: " + doc.getFieldValue("id").toString());
System.out.println("name: " + doc.getFieldValue("name").toString());
System.out.println("price: "
+ doc.getFieldValue("price").toString());
System.out.println("score: " + doc.getFieldValue("score"));
iRow++;
}
}
}
POJO对象
package cn.xiaoyu.solr;
import java.util.List;
import org.apache.solr.client.solrj.beans.Field;
public class NewsBean {
@Field
private String id;
@Field
private String name;
@Field
private String author;
@Field
private String description;
@Field("links")
private List<String> relatedLinks;
public NewsBean() {}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<String> getRelatedLinks() {
return relatedLinks;
}
public void setRelatedLinks(List<String> relatedLinks) {
this.relatedLinks = relatedLinks;
}
}
添加POJO对象索引
package cn.xiaoyu.solr;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import org.apache.solr.client.solrj.impl.BinaryRequestWriter;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
public class AddBeansDemo {
public static final String SOLR_URL = "http://localhost/solr";
public static void main(String[] args) {
// 通过浏览器查看结果
// 要保证bean中各属性的名称在conf/schema.xml中存在,如果查询,要保存被索引
// http://172.168.63.233:8983/solr/collection1/select?q=description%3A%E6%94%B9%E9%9D%A9&wt=json&indent=true
// delBeans();
AddBeans();
}
public static Random rand = new Random(47);
public static String[] authors = {"张三", "李四", "王五", "赵六", "张飞", "刘备","关云长"};
public static String[] links = {
"http://www.baidu.com",
"http://www.google.com",
"http://www.taobao.com",
"http://www.facebook.com",
"http://www.youtube.com" };
public static String getAuthors() {
List<String> list = Arrays.asList(authors).subList(0, rand.nextInt(6)+1);
String str = "";
for (String tmp : list) {
str += " " + tmp;
}
return str;
}
public static List<String> getLinks() {
return Arrays.asList(links).subList(0, rand.nextInt(4)+1);
}
public static void AddBeans() {
String[] words = {"人民","人民政府","民政","世界地图","中华人民共和国"};
long start = System.currentTimeMillis();
Collection<NewsBean> docs = new ArrayList<NewsBean>();
// DocumentObjectBinder binder = new DocumentObjectBinder();
for (int i = 1; i < 300; i++) {
NewsBean news = new NewsBean();
news.setId("id" + i);
news.setName("news" + i);
news.setAuthor(getAuthors());
news.setDescription(words[i % 5]);
news.setRelatedLinks(getLinks());
// SolrInputDocument doc = binder.toSolrInputDocument(news);
docs.add(news);
}
try {
HttpSolrServer server = new HttpSolrServer(SOLR_URL);
server.setRequestWriter(new BinaryRequestWriter());
// 可以通过二种方式增加docs,其中server.add(docs.iterator())效率最高
// 增加后通过执行commit函数commit (981ms)
// server.addBeans(docs);
// server.commit();
// the most optimal way of updating all your docs
// in one http request(481ms)
server.addBeans(docs.iterator());
server.commit();
server.optimize(); //优化Lucene 的索引文件以改进搜索性能。
} catch (Exception e) {
System.out.println(e);
}
System.out.println("time elapsed(ms):"
+ (System.currentTimeMillis() - start));
}
public static void delBeans() {
long start = System.currentTimeMillis();
try {
HttpSolrServer server = new HttpSolrServer(SOLR_URL);
server.deleteByQuery("*:*");
server.commit();
} catch (Exception e) {
System.out.println(e);
}
System.out.println("time elapsed(ms):" + (System.currentTimeMillis() - start));
}
}
查询POJO
package cn.xiaoyu.solr;
import java.io.IOException;
import java.util.List;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.beans.DocumentObjectBinder;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.client.solrj.response.FacetField;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
public class QueryBeanDemo {
public static final String SOLR_URL = "http://localhost/solr";
public static void main(String[] args) throws SolrServerException,
IOException {
// http://172.168.63.233:8983/solr/collection1/select?q=description%3A%E6%80%BB%E7%9B%AE%E6%A0%87&facet=true&facet.field=author_s
HttpSolrServer server = new HttpSolrServer(SOLR_URL);
server.setMaxRetries(1);
server.setMaxRetries(1); // defaults to 0. > 1 not recommended.
server.setConnectionTimeout(5000); // 5 seconds to establish TCP
// server.setRequestWriter(new BinaryRequestWriter());
SolrQuery query = new SolrQuery();
query.setQuery("description:人民");
query.setStart(0);
query.setRows(2);
query.setFacet(true);
query.addFacetField("author_s");
QueryResponse response = server.query(query);
// 搜索得到的结果数
System.out.println("Find:" + response.getResults().getNumFound());
// 输出结果
int iRow = 1;
// response.getBeans存在BUG,将DocumentObjectBinder引用的Field应该为
// org.apache.solr.client.solrj.beans.Field
SolrDocumentList list = response.getResults();
DocumentObjectBinder binder = new DocumentObjectBinder();
List<NewsBean> beanList = binder.getBeans(NewsBean.class, list);
for (NewsBean news : beanList) {
System.out.println(news.getId());
System.out.println(news.getAuthor());
System.out.println(news.getName());
System.out.println(news.getRelatedLinks());
}
for (SolrDocument doc : response.getResults()) {
System.out.println("----------" + iRow + "------------");
System.out.println("id: " + doc.getFieldValue("id").toString());
System.out.println("name: " + doc.getFieldValue("name").toString());
iRow++;
}
for (FacetField ff : response.getFacetFields()) {
System.out.println(ff.getName() + "," + ff.getValueCount() + "," + ff.getValues());
}
}
}
#参考
solr4.7中文分词器(ik-analyzer)配置
Solr JAVA客户端SolrJ 4.9使用示例教程
http://wiki.apache.org/solr/Solrj