Solr
1. Solr介绍
1.1. 什么是solr
Solr 是Apache下的一个顶级开源项目,采用Java开发,它是基于Lucene的全文搜索服务。Solr可以独立运行在Jetty、Tomcat等这些Servlet容器中。都是Web服务器 Servlet容器
使用Solr 进行创建索引和搜索索引的实现方法很简单,如下:
l 创建索引:客户端(可以是浏览器可以是Java程序)用 POST 方法向 Solr 服务器发送一个描述 Field 及其内容的 XML 文档,Solr服务器根据xml文档添加、删除、更新索引 。
l 搜索索引:客户端(可以是浏览器可以是Java程序)用 GET方法向 Solr 服务器发送请求,然后对 Solr服务器返回Xml、json等格式的查询结果进行解析。Solr不提供构建页面UI的功能。Solr提供了一个管理界面,通过管理界面可以查询Solr的配置和运行情况。
Solr是一个可以独立运行的搜索服务器,使用solr进行全文检索服务的话,只需要通过http请求访问该服务器即可。
1.2. Solr和Lucene的区别
Lucene是一个开放源代码的全文检索引擎工具包,它不是一个完整的全文检索应用。Lucene仅提供了完整的查询引擎和索引引擎,目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者以Lucene为基础构建全文检索应用。
Solr的目标是打造一款企业级的搜索引擎系统,它是基于Lucene一个搜索引擎服务,可以独立运行,通过Solr可以非常快速的构建企业的搜索引擎,通过Solr也可以高效的完成站内搜索功能。
2. Solr安装配置
2.1. 下载solr
Solr和lucene的版本是同步更新的,本人使用的版本:4.10.3
下载地址:http://archive.apache.org/dist/lucene/solr/
Linux下需要solr-4.10.3.tgz,windows下需要solr-4.10.3.zip。
解压solr-4.10.3.zip:
bin:solr的运行脚本
contrib:solr的一些扩展jar包,用于增强solr的功能。
dist:该目录包含build过程中产生的war和jar文件,以及相关的依赖文件。
docs:solr的API文档
example:solr工程的例子目录:
l example/solr:
该目录是一个标准的SolrHome,它包含一个默认的SolrCore
l example/multicore:
该目录包含了在Solr的multicore中设置的多个Core目录。
l example/webapps:
该目录中包括一个solr.war,该war可作为solr的运行实例工程。
licenses:solr相关的一些许可信息
2.2. 运行环境
solr 需要运行在一个Servlet容器中,Solr4.10.3要求jdk使用1.7以上,Solr默认提供Jetty(java写的Servlet容器),
使用jetty启动:
使用cmd命令行,进入example文件夹启动
启动命令java -jar start.jar
启动后访问地址:http://127.0.0.1:8983/solr
但是企业中一般使用Tomcat作为服务器,本课程也是一样,
相关环境如下:
l Solr:4.10.3
l Jdk环境:1.7(solr4.10 不能使用jdk1.7以下)
l 服务器:Tomcat 7
2.3. SolrCore配置
2.3.1. SolrHome和SolrCore
SolrHome是Solr服务运行的主目录,该目录中包括了多个SolrCore目录。SolrCore目录中包含了运行Solr实例所有的配置文件和数据文件,Solr实例就是SolrCore。
每个SolrCore提供单独的搜索和索引服务。
2.3.1.1. 目录结构
SolrHome目录:
SolrCore目录:
2.3.2. 创建SolrCore
创建SolrCore先要创建SolrHome。在solr解压包下solr-4.10.3\example\solr文件夹就是一个标准的SolrHome,只需要将它复制到指定的目录下即可。
拷贝solr解压包下solr-4.10.3\example\solr文件夹。
复制该文件夹到本地的一个目录,把文件名称改为solrhome。
改名不是必须的,只是为了便于理解
打开solrhome目录确认solrcore
2.3.3. 配置SolrCore
其实就是配置SolrCore目录下的conf/solrconfig.xml。
这个文件是来配置SolrCore实例的相关信息。如果使用默认配置可以不用做任何修改。它里面包含了不少标签,但是我们经常使用的标签为:lib标签、datadir标签、requestHandler标签。
2.3.3.1. lib 标签
在solrconfig.xml中可以加扩展载一些的jar,如果需要使用,则首先要把这些jar复制到指定的目录,我们复制到SolrHome同级目录
复制之前解压的文件夹中的contrib和dist文件夹
粘贴到SolrHome同级目录下。
修改solrconfig.xml配置文件加载扩展的jar。
configsolr.install.dir表示${SolrCore}的目录位置,需要如下修改:
./ 表示当前目录 ../表示上一级目录
2.3.3.2. datadir标签
配置SolrCore的data目录。
data目录用来存放SolrCore的索引文件和tlog日志文件
solr.data.dir表示${SolrCore}/data的目录位置
如果不想使用默认的目录也可以通过solrconfig.xml更改索引目录 ,
例如:
(建议不修改,否则配置多个SolrCore会报错)
2.3.3.3. requestHandler标签
requestHandler请求处理器,定义了索引和搜索的访问方式。
通过/update维护索引,可以完成索引的添加、修改、删除操作。
通过/select搜索索引。
设置搜索参数完成搜索,搜索参数也可以设置一些默认值,如下:
1 <requestHandler name="/select" class="solr.SearchHandler"> 2 3 <!-- 设置默认的参数值,可以在请求地址中修改这些参数--> 4 5 <lst name="defaults"> 6 7 <str name="echoParams">explicit</str> 8 9 <int name="rows">10</int><!--显示数量--> 10 11 <str name="wt">json</str><!--显示格式--> 12 13 <str name="df">text</str><!--默认搜索字段--> 14 15 </lst> 16 17 </requestHandler>
2.4. Solr工程部署
由于在项目中用到的web服务器大多数是用的Tomcat,所以就进行solr和Tomcat的整合。
2.4.1. 安装Tomcat-略
2.4.2. 部署solr.war到Tomcat中
1、 从solr解压包下的solr-4.10.3\example\webapps目录中拷贝solr.war
复制solr.war
粘贴到自己Tomcat的webapps里
在Tomcat的webapps里,把war解压到当前路径,并删除solr.war
效果:
2.4.3. 添加solr服务的扩展jar包(日志包)
把solr解压包下solr-4.10.3\example\lib\ext目录下的所有jar包拷贝到Tomcat部署的solr的WEB-INF/lib文件夹
复制扩展jar包
粘贴到Tomcat的webapps的solr工程的WEB-INF\lib目录
2.4.4. 配置solr应用的web.xml
需要修改web.xml,让Tomcat使用JNDI的方式告诉solr服务器SolrHome在哪。
修改内容:
第42行的Solr/home名称必须是固定的,修改第43行,如下图
2.4.5. 启动Tomcat进行访问
访问
http://localhost:8081/solr/
出现以下界面则说明solr安装成功!!!
2.5. 管理界面功能介绍
2.5.1. Dashboard
仪表盘,显示了该Solr实例开始启动运行的时间、版本、系统资源、jvm等信息。
2.5.2. Logging
Solr运行日志信息
2.5.3. Cloud
Cloud即SolrCloud,即Solr云(集群),当使用Solr Cloud模式运行时会显示此菜单,该部分功能在第二个项目,即电商项目会演示。
2.5.4. Core Admin
Solr Core的管理界面。在这里可以添加SolrCore实例(有bug,不推荐使用浏览器界面添加SolrCore)。
2.5.5. java properties
Solr在JVM 运行环境中的属性信息,包括类路径、文件编码、jvm内存设置等信息。
2.5.6. Tread Dump
显示Solr Server中当前活跃线程信息,同时也可以跟踪线程运行栈信息。
2.5.7. Core selector
选择一个SolrCore进行详细操作,如下:
2.5.7.1. Analysis
通过此界面可以测试索引分析器和搜索分析器的执行情况
2.5.7.2. dataimport
可以定义数据导入处理器,从关系数据库将数据导入到Solr索引库中。
默认没有配置,需要手工配置。
2.5.7.3. Document
通过/update表示更新索引,solr默认根据id(唯一约束)域来更新Document的内容,如果根据id值搜索不到id域则会执行添加操作,如果找到则更新。
通过此菜单可以创建索引、更新索引、删除索引等操作,界面如下:
l overwrite="true" : solr在做索引的时候,如果文档已经存在,就用xml中的文档进行替换
l commitWithin="1000" : solr 在做索引的时候,每隔1000(1秒)毫秒,做一次文档提交。为了方便测试也可以在Document中立即提交,</doc>后添加“<commit/>”
2.5.7.4. Query
通过/select执行搜索索引,必须指定“q”查询条件方可搜索。
3. Solrj的使用
3.1. 什么是solrj
solrj是访问Solr服务的java客户端,提供索引和搜索的请求方法,如下图:
通过SolrJ请求Solr服务 |
最终Solr完在索引和搜索 | |
javaEE应用程序 |
Solr服务 |
Index索引库
|
SolrJ程序客户端 |
Tomcat |
Solrj和图形界面操作的区别就类似于数据库中使用jdbc和mysql客户端的区别一样。
3.2. 需求
使用solrj调用solr服务实现对索引库的增删改查操作。
3.3. 环境准备
l Solr:4.10.3
l Jdk环境:1.7
l IDE环境:Eclipse Mars2
3.4. 工程搭建
3.4.1. 创建java工程
3.4.2. 添加jar
Solrj的包,\solr-4.10.3\dist\目录下
solrj依赖包,\solr-4.10.3\dist\solrj-lib
Solr服务的依赖包,\solr\example\lib\ext
3.5. 代码实现
3.5.1. 添加&修改索引
3.5.1.1. 步骤
1、 创建HttpSolrServer对象,通过它和Solr服务器建立连接。
2、 创建SolrInputDocument对象,然后通过它来添加域。
3、 通过HttpSolrServer对象将SolrInputDocument添加到索引库。
4、 提交。
3.5.1.2. 代码(如下)
说明:根据id(唯一约束)域来更新Document的内容,如果根据id值搜索不到id域则会执行添加操作,如果找到则更新。
3.5.2. 删除索引
3.5.2.1. 代码
抽取HttpSolrServer 的创建代码
private HttpSolrServer httpSolrServer;
// 提取HttpSolrServer创建
@Before
public void init() {
// 1. 创建HttpSolrServer对象
// 设置solr服务接口,浏览器客户端地址http://127.0.0.1:8081/solr/#/
String baseURL = "http://127.0.0.1:8081/solr/";
this.httpSolrServer = new HttpSolrServer(baseURL);
}
删除索引逻辑,两种:
根据id删除
根据条件删除,根据条件删除
可以使用*:*作为条件,就是删除所有数据(慎用)
1 public class SolrJDemo { 2 private static HttpSolrServer httpSolrServer; 3 static { 4 //1、创建HttpSolrServer对象,通过它和Solr服务器建立连接。 5 String baseURL = "http://localhost:8080/solr/"; 6 httpSolrServer = new HttpSolrServer(baseURL); 7 } 8 //添加 修改 9 @Test 10 public void addTest() throws Exception { 11 //1、创建HttpSolrServer对象,通过它和Solr服务器建立连接。 12 /*String baseURL = "http://localhost:8080/solr/"; 13 SolrServer httpSolrServer = new HttpSolrServer(baseURL);*/ 14 //2、创建SolrInputDocument对象,然后通过它来添加域。 15 SolrInputDocument solrInputDocument = new SolrInputDocument(); 16 SolrInputDocument solrInputDocument1 = new SolrInputDocument(); 17 solrInputDocument.addField("id", "c1002"); 18 solrInputDocument.addField("content", "柳先生!"); 19 solrInputDocument1.addField("id", "c1004"); 20 solrInputDocument1.addField("content", "赵先生!"); 21 //3、通过HttpSolrServer对象将SolrInputDocument添加到索引库。 22 httpSolrServer.add(solrInputDocument); 23 httpSolrServer.add(solrInputDocument1); 24 //4、提交。 25 httpSolrServer.commit(); 26 } 27 //删除索引 28 @Test 29 public void deleteTest() throws Exception { 30 httpSolrServer.deleteById("c1001"); 31 httpSolrServer.commit(); 32 } 33 //删除索引 全部 34 @Test 35 public void deleteAllTest() throws Exception { 36 httpSolrServer.deleteByQuery("*:*"); 37 httpSolrServer.commit(); 38 } 39 //简单查询 40 @Test 41 public void testSearchIndex() throws Exception { 42 // 创建搜索对象 43 SolrQuery solrQuery = new SolrQuery(); 44 // 设置搜索条件 45 solrQuery.setQuery("*:*"); 46 // 发起搜索请求 47 QueryResponse query = httpSolrServer.query(solrQuery); 48 // 处理搜索结果 49 SolrDocumentList results = query.getResults(); 50 System.out.println("共"+results.getNumFound()+"条"); 51 // 遍历搜索结果 52 for (SolrDocument solrDocument : results) { 53 System.out.println("----------------------------------------------------"); 54 System.out.println("id:" + solrDocument.get("id")); 55 System.out.println("content" + solrDocument.get("content")); 56 } 57 } 58 }
3.5.3. 查询索引
3.5.3.1. 简单查询(代码如上)
4. solr基本使用
4.1. schema.xml
schema.xml文件在SolrCore的conf目录下,在此配置文件中定义了域以及域的类型等一些配置。在solr中域必须先定义后使用。
4.1.1. field
1 <field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" />
l Name:域的名称
l Type:域的类型
l Indexed:是否索引
l Stored:是否存储
l Required:是否必须
l multiValued:是否是多值,存储多个值时设置为true,solr允许一个Field存储多个值,比如存储一个用户的好友id(多个),商品的图片(多个,大图和小图)
4.1.2. dynamicField(动态域)
<dynamicField name="*_i" type="int" indexed="true" stored="true"/>
<dynamicField name="*_is" type="int" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*_s" type="string" indexed="true" stored="true" />
<dynamicField name="*_ss" type="string" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*_l" type="long" indexed="true" stored="true"/>
<dynamicField name="*_ls" type="long" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*_t" type="text_general" indexed="true" stored="true"/>
<dynamicField name="*_txt" type="text_general" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*_en" type="text_en" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*_b" type="boolean" indexed="true" stored="true"/>
<dynamicField name="*_bs" type="boolean" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*_f" type="float" indexed="true" stored="true"/>
<dynamicField name="*_fs" type="float" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*_d" type="double" indexed="true" stored="true"/>
<dynamicField name="*_ds" type="double" indexed="true" stored="true" multiValued="true"/>
Name:动态域的名称,是一个表达式,*匹配任意字符,只要域的名称和表达式的规则能够匹配就可以使用。
例如:搜索时查询条件[product_i:钻石]就可以匹配这个动态域,可以直接使用,不用单独再定义一个product_i域。
4.1.3. uniqueKey
1 <uniqueKey>id</uniqueKey>
相当于主键,每个文档中必须有一个id域。
4.1.4. copyField(复制域)
1 <copyField source="cat" dest="text"/>
可以将多个Field复制到一个Field中,以便进行统一的检索。当创建索引时,solr服务器会自动的将源域的内容复制到目标域中。
l source:源域
l dest:目标域,搜索时,指定目标域为默认搜索域,可以提高查询效率。
定义目标域:
1 <field name="text" type="text_general" indexed="true" stored="false" multiValued="true"/>
目标域必须要使用:multiValued="true"
4.1.5. fieldType(域类型)
1 <fieldType name="text_general" class="solr.TextField" positionIncrementGap="100"> 2 3 <analyzer type="index"> 4 5 <tokenizer class="solr.StandardTokenizerFactory"/> 6 7 <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" /> 8 9 <filter class="solr.LowerCaseFilterFactory"/> 10 11 </analyzer> 12 13 <analyzer type="query"> 14 15 <tokenizer class="solr.StandardTokenizerFactory"/> 16 17 <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" /> 18 19 <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/> 20 21 <filter class="solr.LowerCaseFilterFactory"/> 22 23 </analyzer> 24 25 </fieldType>
l name:域类型的名称
l class:指定域类型的solr类型。
l analyzer:指定分词器。在FieldType定义的时候最重要的就是定义这个类型的数据在建立索引和进行查询的时候要使用的分析器analyzer,包括分词和过滤。
l type:index和query。Index 是创建索引,query是查询索引。
l tokenizer:指定分词器
l filter:指定过滤器
4.2. 配置中文分析器
使用IKAnalyzer中文分析器
第一步:把IKAnalyzer2012FF_u1.jar添加到solr/WEB-INF/lib目录下。
第二步:复制IKAnalyzer的配置文件和自定义词典和停用词词典到solr的solr/WEB-INF/classes目录下。
复制IK分词器配置文件、自定义词典、停用词词典
粘贴到Tomcat的solr的/WEB-INF/classes目录下
第三步:在schema.xml中添加一个自定义的fieldType,使用中文分析器。
1 <!-- IKAnalyzer--> 2 3 <fieldType name="text_ik" class="solr.TextField"> 4 5 <analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/> 6 7 </fieldType>
第四步:在schema.xml中添加field,指定field的type属性为text_ik
1 <!--IKAnalyzer Field--> 2 3 <field name="content_ik" type="text_ik" indexed="true" stored="true" />
第五步:重启tomcat
效果:
4.3. 配置业务Field
4.3.1. 需求
要使用solr实现网站中商品搜索,需要将mysql数据库中数据在solr中创建索引。
- 需要在solr的schema.xml文件定义要存储的商品Field。
- 需要把MySQL的数据导入到solr索引库中
- 开发搜索功能
4.3.2. 数据库添加数据
4.3.3. 定义Field
先确定定义的商品document的Field域有哪些?---用自己的表举例
可以根据mysql数据库中商品表的字段来确定:
products商品表:
Schema.xml中配置业务域
1 <!--product--> 2 3 <field name="product_name" type="text_ik" indexed="true" stored="true"/> 4 5 <field name="product_price" type="float" indexed="true" stored="true"/> 6 7 <field name="product_description" type="text_ik" indexed="true" stored="false" /> 8 9 <field name="product_picture" type="string" indexed="false" stored="true" /> 10 11 <field name="product_catalog_name" type="string" indexed="true" stored="true" /> 12 13 14 15 <field name="product_keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/> 16 17 <copyField source="product_name" dest="product_keywords"/> 18 19 <copyField source="product_description" dest="product_keywords"/>
4.4. dataimportHandler插件
使用dataimport插件批量导入数据。
第一步:把dataimport插件依赖的jar包添加到solrcore(collection1\lib)中, 还需要mysql的数据库驱动。
第二步:配置solrconfig.xml文件,添加一个requestHandler。
1 <requestHandler name="/dataimport" 2 3 class="org.apache.solr.handler.dataimport.DataImportHandler"> 4 5 <lst name="defaults"> 6 7 <str name="config">data-config.xml</str> 8 9 </lst> 10 11 </requestHandler>
第三步:创建一个data-config.xml,保存到collection1\conf\目录下
1 <?xml version="1.0" encoding="UTF-8" ?> 2 3 <dataConfig> 4 5 <dataSource type="JdbcDataSource" 6 7 driver="com.mysql.jdbc.Driver" 8 9 url="jdbc:mysql://localhost:3306/lucene" 10 11 user="root" 12 13 password="root"/> 14 15 <document> 16 17 <entity name="product" query="SELECT pid,name,catalog_name,price,description,picture FROM products "> 18 19 <field column="pid" name="id"/> 20 21 <field column="name" name="product_name"/> 22 23 <field column="catalog_name" name="product_catalog_name"/> 24 25 <field column="price" name="product_price"/> 26 27 <field column="description" name="product_description"/> 28 29 <field column="picture" name="product_picture"/> 30 31 </entity> 32 33 </document> 34 35 </dataConfig>
第四步:重启tomcat
第五步:点击“execute”按钮导入数据
注意:导入数据前会先清空索引库,然后再导入。
5. solrj的复杂查询
5.1. solr的查询语法
- q: 查询关键字,必须的。
请求的q是字符串,如果查询所有使用*:*
- fq: (filter query)过滤查询
作用:在q查询符合结果中同时是fq查询符合的
请求fq是一个数组(多个值)
过滤查询价格从1到20的记录。
也可以使用“*”表示无限,例如:
20以上:product_price:[20 TO *]
20以下:product_price:[* TO 20]
也可以在“q”查询条件中使用product_price:[1 TO 20],
如下效果和上面一样:
- sort: 排序,desc代表降序,asc代表升序
按照价格升序排
- start: 分页显示使用,开始记录下标,从0开始
rows: 指定返回结果最多有多少条记录,配合start来实现分页。
- fl: (Field List)指定返回那些字段内容,用逗号或空格分隔多个。
显示商品id、商品名称、商品分类名称
- df: 指定默认搜索Field
- wt: (writer type)指定输出格式,可以有 xml, json, php, phps
- hl: 是否高亮 ,设置高亮Field,设置格式前缀和后缀。
5.2. solrj的复杂查询
页面的查询条件,复杂查询条件和页面的查询条件一致
代码实现:(httpSolrServer由上面代码抽取,这里用的是静态变量)
1 //复杂查询 2 @Test 3 public void testSearchIndex2() throws Exception { 4 // 创建搜索对象 5 SolrQuery solrQuery = new SolrQuery(); 6 //设置查询条件 7 solrQuery.setQuery("钻石"); 8 //过滤条件 9 solrQuery.setFilterQueries("product_catalog_name:幽默杂货"); 10 //排序条件 11 solrQuery.setSort("product_price", ORDER.asc); 12 //分页处理 13 solrQuery.setStart(0); 14 solrQuery.setRows(10); 15 //结果中域的列表 16 solrQuery.setFields("id","product_name","product_price","product_catalog_name","product_picture"); 17 //设置默认搜索域 18 solrQuery.set("df", "product_keywords"); 19 //高亮显示 20 solrQuery.setHighlight(true); 21 //高亮显示的域 22 solrQuery.addHighlightField("product_name"); 23 //高亮显示的前缀 24 solrQuery.setHighlightSimplePre("<font color=\"red\">"); 25 //高亮显示的后缀 26 solrQuery.setHighlightSimplePost("</font>"); 27 //执行查询 28 QueryResponse queryResponse = httpSolrServer.query(solrQuery); 29 //取查询结果 30 SolrDocumentList solrDocumentList = queryResponse.getResults(); 31 //共查询到商品数量 32 System.out.println("共查询到商品数量:" + solrDocumentList.getNumFound()); 33 //遍历查询的结果 34 for (SolrDocument solrDocument : solrDocumentList) { 35 System.out.println(solrDocument.get("id")); 36 //取高亮显示 37 String productName = ""; 38 Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting(); 39 List<String> list = highlighting.get(solrDocument.get("id")).get("product_name"); 40 //判断是否有高亮内容 41 if (null != list) { 42 productName = list.get(0); 43 } else { 44 productName = (String) solrDocument.get("product_name"); 45 } 46 System.out.println(productName); 47 System.out.println(solrDocument.get("product_price")); 48 System.out.println(solrDocument.get("product_catalog_name")); 49 System.out.println(solrDocument.get("product_picture")); 50 } 51 }