一、 Introduction
Solr是一个独立的企业级搜索应用服务器,对外提供类似web-service的api接口。它是高性能的,基于lucene的,提供了完善的功能管理界面,是一款非常优秀的全文搜索引擎。一些大型门户网站,电子平台网站都需要站内搜索功能,传统的数据库查询方式已经无法满足一些高级的搜索需求,比如要求速度要快,搜索结果按相关度排序,搜索内容格式不固定,这要需要使用全文检索技术来实现。单独的使用lucene来实现,工作量大,索引维护、索引性能优化、搜索性能优化都需要很大的工作量。Solr提供了完备的搜索引擎解决方案,可以减少工作量。与lucene相比,solr是一个搜索引擎服务,可以独立运行,通过solr可以快速的构建企业的搜索引擎,也可以高效的完成站内搜索功能。
Solr可以运行在jetty,tomcat等这些servlet容器中,solr索引的实现方式是,用post方法向solr服务器发送一个描述field及其内容的xml文档,solr根据xml文档添加、删除、更新索引。Solr搜索只需要发送http get请求,然后对solr返回xml、json等格式的查询结果进行解析,组织页面布局。Solr不提供构建ui的功能。Solr提供了一个管理界面,通过管理界面可以查询solr的配置和运行情况。
二、 Solr的安装和配置
1. 从solr官网下载solr的包,linux和windows不同的系统下不同的包。Solr的使用指南参考:https://wiki.apache.org/solr/FrontPage。
打开压缩包,solr的文件夹结构如下图:
Bin |
运行脚本 |
Contrib. |
一些贡献软件/插件,用于增强solr的功能 |
Dist |
Build过程中产生的war和jar文件,以及相关的依赖文件 |
Docs |
Solr的api文档 |
Example |
Solr工程的例子目录。 Example/lib:依赖包。 Example/solr:该目录是一个包含了默认信息的solr的core目录。索引库存放的地方。Solr下面的collection目录是一个核,可以有多个核,核的概念相当于数据库的仓库。 Ecapmle/multicore:solr的multicore中设置的多个core目录。 Example/webapps:一个solr.war,可作为solr的运行实例工程。 |
2. 运行环境
要求jdk使用1.7以上,需要一个servlet容器,如jetty、tomcat。Solr默认提供jetty。一般,也可以整合tomcat。
3. Solr整合tomcat搭建solr服务器
1) 创建一个用于整合的文件夹solr,里面放一份未使用的tomcat解压后的目录,将solr包的中的工程包solr.war包放到webapps里,通过解压war或者启动tomcat将这个war包解压。
2) 将solr包中的/example/lib/ext下所有的jar包放到上面解压的solr工程的lib目录。
3) 在整合的solr文件夹下创建solrhome文件夹,用于存放持久化的库。将solr安装包中的/example/solr下的文件全部复制到这个solrhome中。
4) 配置tomcat中solr工程的WEB-INF中的web.xml文件
去掉
solr/home
F:\Solr\solrhome
java.lang.String
5) 启动tomcat测试,访问http://127.0.0.1:8080/solr/。
三、 Solr后台界面
Solr后台界面基本组成如下表:
Dashboard |
仪表盘,显示了solr实例开始启动运行的时间、版本、系统资源、jvm等信息。 |
||||||||||||||||
Logging |
Solr运行日志信息 |
||||||||||||||||
Cloud |
即lolrcloud,solr云(集群),使用solr cloud模式运行时显示此菜单。 |
||||||||||||||||
Core admin |
Solr core的管理界面。Solr core是solr的一个独立运行实例单位,对外提供索引和搜索服务,一个solrcore就相当于一个库,一个solr工程可以运行多个solrcore,一个core对应一个索引目录。 添加solrcore的步骤: 复制collection1给名为collection2,修改core.properties,name=collection2,重启tomcat。 |
||||||||||||||||
Java properties |
Solr在jvm运行环境中的属性信息,包括类路径、文件编码、jvm内存设置等。 |
||||||||||||||||
Thread dump |
显示solr server中当前活跃线程信息,同时也可以跟踪线程运行栈信息。 |
||||||||||||||||
Core selector |
选择一个solrcore进行详细操作。
|
四、 配置中文分析器
在solrcore的conf目录下有两个重要的配置文件,schema.xml和solrconfig.xml。
(一)Shema.xml
这个文件主要包括fiedtypes、fields和其他一些缺省配置。
1. Fieldtype
Fieldtype标签用了定义域的类型。
形如:
Name表示类型名
class表示类型的实际类型(也就是lucene中的field的实现类)
positionIncrementGap是可选属性,定义在同一个文档中此类型数据的空白间隔,避免短语匹配错误,此值相当于Lucene的短语查询设置slop值,根据经验设置为100。比如搜索电脑,如果document中存在电 脑,如果设置了positionIncrementGap就是设置了电和脑的最大间隔,只要在间隔内就可以搜索到。
在fieldtype定义的时候最要的是定义这个类型的数据在建立索引和进行查询时使用的分析器,包括分词和过滤,如:
上述
2. Field
Solr提供了大量的field域名,供用户使用。
在field标签中,定义域名name,域类型type(type就是在fieldtype中定义的各种类型),required是否必需,indexed是否被索引,stored是否被存储,multivalued是否存在多个值等。比如:
注意:在solr中添加域的时候,域名id域是必需的。Solr允许一个field名中存储多个值,需要设置multivalued=”ture”。
3. UniqueKey
唯一主键,solr中默认定义唯一主键key为id域,比如:
4. DynamicFiled
动态域,为了提供域名的扩展性,solr提供了动态域字段,动态字段不指定域的具体名称,只指定域名称的规则,比如:
在创建索引是凡是以_s结尾的域名都可以使用,比如在solr后台界面创建如下域名:{"id":"3","title_s":"动态域"}。
为了加强扩展性,用还可以自定义域,使用
5. Copefield
复制域。在solr中检索是通过get发送请求的,而get一次可以发送一个域,如果遇到要查询多个域的时候就不方便了,比如在一些电商网站,直接搜索电脑,一般在名称和描述这两个域中都有涉及到电脑这个值,那么在执行时需要发送两次域的查询,这样处理起来是麻烦的,所以,solr提供了复制域,可以将多个field复制到一个field中,就简化了代码和执行,比如上面的例子使用复制域可以写为:
先有定义的关于名称和描述的域
(二)安装中文解析器
步骤:
1. 把IKAnalyzer2012FF_u1.jar添加到tomcat的solr/WEB-INF/lib目录下。
2. 复制IKAnalyzer的配置文件和自定义词典和停用词词典到solr的classpath下。
在apache-tomcat-7.0.54\webapps\solr\WEB-INF目录下新建classes目录,将配置文件和词典放进去.
3. 在schema.xml中添加一个自定义的fieldType,使用中文分析器。定义field,指定field的type属性为text_ik。
例子:
注意:在使用编辑器编辑
4. 重启tomcat,测试。
打开solr后台,在collection的analysis中测试。
五、 设业务系统需要的field
一般,为了便于识别和管理,特别实在批量导入数据时,可以不使用solr提供的field,可以针对具体要求自定义一套同义命名规则的field,比如:
六、 solr索引维护
1. 添加/更新文档
添加和更新文档都使用/update,document type选择json,添加和修改是都需要填写id的值,如果这个id的值在索引库中不存在,则添加,如果存在,则修改。比如:
{"id":"1","title":"insert"}
如果再次输入id为1,修改title的值,那么就是更新,{"id":"1","title":"update"}。
可以使用query来查看效果。
2. 删除文档
删除时,选择document type为xml,比如删除id为1的文档,
1
必须要commit。
按照查询条件删除,比如:
title_s:动态域
这是使用query解析查询的语法来删除,那么删除所有的语法如下:
*:*
3. 批量导入数据
使用dataimport插件可以批量导入数据,简化操作。步骤:
1) 把dataimport插件依赖的jar包,dataimporthandler和dataimporthandler-extras添加到solrcore的collection1\lib中,没有lib目录就创建一个;
2) 使用的mysql数据库的资料,还需要mysql的驱动,放到上述同一个lib目录下;
3) 配置collection1的conf的solrconfig.xml文件,添加一个requestHandler,如下:
data-config.xml
4) 创建一个data-config.xml保存到collection1\conf\目录下,其中的内容如下:
其中
那么上述映射关系的前提就是在schema.xml中创建业务系统需求的自定义域,如下:
5) 重启tomcat,点击execute导入,执行时请对clean和commit两个单选框打勾,entity选择配置文件中entity标签配置的name的值。勾选auto-refresh status可以查看执行的状态情况。
注意:导入数据前,会先清空索引库,然后导入。
七、 后台界面查询索引
使用/select搜索索引,solr定制了一些参数完成不同需求的搜索。
1. q(query),查询字符串。
语法格式,域名:域值;如果查询所有使用*:*。可以使用AND OR NOT等复合查询的连词。语法同lucene查询解析的语法。
比如:product_name:幸福 AND product_catalog_name:幽默杂货;product_name:幸福;product_name:幸福 AND product_catalog_name:幽默杂货 NOT product_name:餐具;product_price:[1 TO 20];product_price:[1 TO 20}。
注意:范围表示中[表示包含,{表示不包含。
2. fq(filter query),过滤查询,在q查询的结果中同时是符合fq查询的。
通过界面的红色的-和绿色的+,可以加减查询条件。比如在q中查询product_name:幸福,然后在fq中查询product_catalog_name:幽默杂货,这就相当于上直接在q中查询product_name:幸福 AND product_catalog_name:幽默杂货。例如查询价格从1到20的记录,首先在q查询中product_name:幸福,然后在fq中查询product_price:[1 TO 20]。对于价格查询可以使用*表示无限,比如product_price:[* TO 20];product_price:[100 TO *]
注意:Fq查询必需建立在q查询的前提下,如果q查询为空语句,则fq查询结果也为空。
3. sort,排序,语法:fieldname desc|asc[,fieldname desc|asc][ ,fieldname desc|asc]…。
比如按照价格倒序:product_price desc;按照价格倒序,销量升序:product_price desc,product_sale asc。
4. start,rows,分页,开始的位置和每页显示的条数。比如:1 20;3 3。
5. fl,field list,指定返回那些内容的字段,比如选定product_name,product_price表示只返回这两个字段的内容。
6. df,default search field,指定一个搜索field,比如根据关键字搜索product_keywords。
Solr查询中只能有一个默认搜索域,这也是为什么要使用复制域将多个域复制到一个域来查询,如果不使用复制域,比较难以实现这个业务需求。
也可以在solrcore目录的/collection1/conf目录的solrconfig.xml文件中指定默认搜索field,指定后可以直接在q查询中输入关键字搜索。比如:
data-config.xml
keywords
7. wt,write type,指定输出格式,可以有xml,json,php,phps。
8. hl,highlighting,高亮单选框。
hl.fl,fields to highlinging on,要高亮的域。hl.simple.pre,高亮显示的内容的前缀,是一个html代码,比如:。前缀要配合后缀使用,hl.simple.post,比如上述的后缀:。高亮显示的文本结果使用的map结构,map里面嵌套map,最内层是list集合。
八、 使用solrJ管理索引库
Solrj是访问solr服务的java客户端,提供索引和搜索的请求方法,solrj通常嵌入在业务系统中,通过solrj的api接口操作solr服务。
SolrJ依赖的jar包也在solr的安装包里面,就是solrj-lib文件夹中所有的jar包和solr-solrj-x.x.x.jar这个jar包以及/example/lib/ext/下面所有的jar包。
(一)使用solrj的步骤:
1. 创建一个java项目,
2. 导入solrj的jar包,
3. 创建solrj管理类,
4. 创建用于添加域的方法
public voidaddField()throwsException{
//创建和solr服务器连接的solrServer对象
String baseUrl = "http://127.0.0.1:8080/solr/";
//关于这个url,如果不指定collection,默认就是collection1,相当于String baseUrl = "http://127.0.0.1:8080/solr/collection1";如果需要指定collection,可以在solr/后指定,如"http://127.0.0.1:8080/solr/collection2";
SolrServer solrServer = new HttpSolrServer(baseUrl);
//创建用于添加域的文档对象
SolrInputDocument solrInpuDocument = new SolrInputDocument();
//向文档中添加域
solrInpuDocument.setField("id", "no1");
solrInpuDocument.setField("name", "haha");
//将文档添加到solrserver
solrServer.add(solrInpuDocument);
//提交
solrServer.commit();
}
5. 打开tomcat,运行代码测试。
(二)删除域的方法
1. 根据id删除
@Test
public voiddeleteById()throws Exception{
//创建和solr服务器连接的solrServer对象
String baseUrl = "http://127.0.0.1:8080/solr/";
SolrServer solrServer = new HttpSolrServer(baseUrl);
//根据id删除
solrServer.deleteById("no1");
//提交删除
solrServer.commit();
}
2. 根据query删除
Query语法同lucene语法。
@Test
public voiddeleteByQuery()throws Exception{
//创建和solr服务器连接的solrServer对象
String baseUrl = "http://127.0.0.1:8080/solr/";
SolrServer solrServer = new HttpSolrServer(baseUrl);
//根据id删除
solrServer.deleteByQuery("name:haha",1000);
//提交删除
solrServer.commit();
}
(三)更新域
用的方法和添加一样,只要id存在就添加,id不存在就更新。添加和更新的这种设计在java开发中比较常见。
@Test
public voidupdateField()throws Exception{
//创建和solr服务器连接的solrServer对象
String baseUrl = "http://127.0.0.1:8080/solr/";
SolrServer solrServer = new HttpSolrServer(baseUrl);
//创建用于添加域的文档对象
SolrInputDocument solrInpuDocument = new SolrInputDocument();
//向文档中添加域
solrInpuDocument.setField("id", "no1");
solrInpuDocument.setField("name", "更新");
//将文档添加到solrserver
solrServer.add(solrInpuDocument);
solrServer.commit();
}
(四)查询域
1. 查询所有
@Test
public voidsearchField()throwsException{
//创建和solr服务器连接的solrServer对象
String baseUrl = "http://127.0.0.1:8080/solr/";
SolrServer solrServer = new HttpSolrServer(baseUrl);
//创建查询对象
SolrQuery solrQuery = new SolrQuery();
//设置查询条件
solrQuery.set("q","*:*");
//执行查询
QueryResponse response = solrServer.query(solrQuery);
//获得文档结果集
SolrDocumentList results = response.getResults();
//获得总记录数
long numFound = results.getNumFound();
System.out.println(numFound);
//输出文档的域
for(SolrDocument doc:results){
//默认显示前10条数据
System.out.println(doc.get("id"));
System.out.println(doc.get("product_name"));
System.out.println(doc.get("product_price"));
System.out.println(doc.get("product_catalog_name"));
System.out.println(doc.get("product_picture"));
}
}
2. 按复杂条件查询
例子1:
@Test
public voidsearchFieldByQueries()throws Exception{
//创建和solr服务器连接的solrServer对象
String baseUrl = "http://127.0.0.1:8080/solr/";
SolrServer solrServer = new HttpSolrServer(baseUrl);
//创建查询对象
SolrQuery solrQuery = new SolrQuery();
//设置复杂查询条件
//查询的范围
solrQuery.set("q","product_name:幸福");
//过滤条件
solrQuery.set("fq","product_catalog_name:幽默杂货");
solrQuery.set("fq","product_price:[* TO 20}");
//排序条件
solrQuery.addSort(newSortClause("product_price","desc"));
//分页
solrQuery.setStart(0);
solrQuery.setRows(5);
//设置默认域
solrQuery.set("df","product_name");
//设置指定查询域
solrQuery.set("fl","id,product_name,product_catalog_name");
//设置高亮
//打开高亮开关
solrQuery.setHighlight(true);
//指定高亮域
//solrQuery.addHighlightField("product_name,product_catalog_name");
solrQuery.addHighlightField("product_name");
//设置高亮前缀
solrQuery.setHighlightSimplePre("");
//设置高亮后缀
solrQuery.setHighlightSimplePost("");
//执行查询
QueryResponse response = solrServer.query(solrQuery);
//获得文档结果集
SolrDocumentList results = response.getResults();
/*获得高亮结果集,高亮的结果集是一个多层map,最外层是id:map,
进一层是指定高亮域和高亮显示内容的map,比如fieldname:电脑,
最里层是高亮显示的内容的list*/
Map>> highlighting = response.getHighlighting();
//获得总记录数
long numFound = results.getNumFound();
System.out.println(numFound);
//输出文档的域
for(SolrDocument doc:results){
//默认显示前10条数据
System.out.println(doc.get("id"));
System.out.println(doc.get("product_name"));
System.out.println(doc.get("product_price"));
System.out.println(doc.get("product_catalog_name"));
System.out.println(doc.get("product_picture"));
//每一个查询出的文档都有要高亮的内容,所有通过id获得要高亮的map
Map> map = highlighting.get(doc.get("id"));
//根据返回的map的指定域的键,获得要高亮的内容的集合
List list = map.get("product_name");
System.out.println("******************************");
System.out.println(list.get(0));
}
}
例子2:
设置多个高亮域
@Test
public voidsearchFieldByQueries()throws Exception{
//创建和solr服务器连接的solrServer对象
String baseUrl = "http://127.0.0.1:8080/solr/";
SolrServer solrServer = new HttpSolrServer(baseUrl);
//创建查询对象
SolrQuery solrQuery = new SolrQuery();
//设置复杂查询条件
//查询的范围
solrQuery.set("q","product_name:幸福 ANDproduct_catalog_name:幽默杂货");
// solrQuery.set("q","product_catalog_name:幽默杂货");
//过滤条件
//solrQuery.set("fq","product_catalog_name:幽默杂货");
solrQuery.set("fq","product_price:[* TO 20}");
//排序条件
solrQuery.addSort(newSortClause("product_price","desc"));
//分页
solrQuery.setStart(0);
solrQuery.setRows(5);
//设置默认域
solrQuery.set("df","product_name");
//设置指定查询域
solrQuery.set("fl","id,product_name,product_catalog_name");
//设置高亮
//打开高亮开关
solrQuery.setHighlight(true);
//指定高亮域
solrQuery.addHighlightField("product_catalog_name");
solrQuery.addHighlightField("product_name");
//设置高亮前缀
solrQuery.setHighlightSimplePre("");
//设置高亮后缀
solrQuery.setHighlightSimplePost("");
//执行查询
QueryResponse response = solrServer.query(solrQuery);
//获得文档结果集
SolrDocumentList results = response.getResults();
/*获得高亮结果集,高亮的结果集是一个多层map,最外层是id:map,
进一层是指定高亮域和高亮显示内容的map,比如fieldname:电脑,
最里层是高亮显示的内容的list*/
Map>> highlighting = response.getHighlighting();
//获得总记录数
long numFound = results.getNumFound();
System.out.println(numFound);
//输出文档的域
for(SolrDocument doc:results){
//默认显示前10条数据
System.out.println(doc.get("id"));
System.out.println(doc.get("product_name"));
System.out.println(doc.get("product_price"));
System.out.println(doc.get("product_catalog_name"));
System.out.println(doc.get("product_picture"));
//每一个查询出的文档都有要高亮的内容,所有通过id获得要高亮的map
Map> map = highlighting.get(doc.get("id"));
System.out.println(map);
//根据返回的map的指定域的键,获得要高亮的内容的集合
List list = map.get("product_name");
List list2 = map.get("product_catalog_name");
System.out.println("******************************");
System.out.println(list.get(0));
System.out.println(list2.get(0));
}
}