Solr
Solr概述
Solr是一个独立的企业级搜索应用服务器,它对外提供类似于的API接口。用户可以通过http请求,向搜索引擎服务器提交一定格式的文件,生成索引;也可以通过Http Get操作提出查找请求,并得到指定格式的返回结果。
Solr安装部署
下载Solr
官网:https://lucene.apache.org/solr/
说明:Solr与Lucene是同步更新的,课程中使用4.10.3版本solr官网下载地址:http://archive.apache.org/dist/lucene/solr/
解压Solr
目录结构说明:
bin:solr运行脚本
contrib:solr的一些扩展jar包,用于增强solr功能
dist:build过程中生成的war和jar文件,以及一些依赖文件
docs:solr的API文档
example:solr工程的例子目录
example/solr:标准的SolrHome,包含一个默认的SolrCore
example/webapps:包含了一个solr.war,该war可作为solr的运行示例工程,我们部署的solr服务就是用的它
licenes:solr相关的许可信息
安装环境
Solr本身集成了Jetty服务器,可以直接启动运行。 Tomcat、jetty、Jboss、underTow
打开cmd命令行窗口,进入solr解压目录中的example目录。
进行cmd执行:java -jar start.jar
启动成功,访问:http://localhost:8983/solr
虽然solr内置了Jetty服务器,但是在企业中一般使用Tomcat部署
需要环境:
Solr:4.10.3
JDK环境:1.8(solr4.10 要求jdk1.7以上版本)
服务器:Tomcat 8
配置SolrHome
SolrHome目录是solr服务运行的主目录。一个SolrHome目录包含有多个SolrCore。SolrCore目录中包含了运行solr实例的配置文件和数据文件(日志和索引文件)。每一个SolrCore提供独立的索引和搜索服务。
拷贝【资料\solr-4.10.3\example\solr】目录到【D:\solr】,重命名solr为solrhome
SolrHome目录:
SolrCore目录:
说明:
conf:SolrCore运行配置信息
data:SolrCore存放日志和索引文件的目录
core.properties:SolrCore的信息,比如:名称
配置SolrCore
配置SolrCore目录下的conf/solrconfig.xml文件:
说明:solrconfig.xml文件是配置SolrCore实例的相关信息。默认情况下可以不做修改。在企业项目中需要修改三个常用的标签:lib标签、datadir标签、requestHandler标签。
lib标签
在solrconfig.xml中可以加载一些扩展的jar,如果需要使用。(一般不需要使用)
solrconfig.xml文件75-85行,修改前:
datadir标签
配置SolrCore的data目录,data目录用来存放SolrCore的索引文件和tlog日志文件。solr.data.dir表示${SolrCore}目录,等价于${solr.install.dir}
说明:一般不需要修改。requestHandler标签
requestHandler请求处理器,定义了索引和搜索的访问方式。
通过/select搜索索引,完成检索操作。
通过/update维护索引,可以完成索引的添加、修改、删除操作。
设置搜索参数完成搜索,搜索参数也可以设置一些默认值,如下:
<requestHandler name="/select" class="solr.SearchHandler">
<!-- default values for query parameters can be specified, these
will be overridden by parameters in the request
-->
<lst name="defaults">
<str name="echoParams">explicit</str>
<int name="rows">10</int> <!-- 每页大小 -->
<str name="df">text</str> <!-- 默认域 -->
</lst>
</requestHandler>
Solr部署
准备Tomcat
修改Tomcat访问端口
部署solr.war
第1步
拷贝solr-4.10.3\example\webapps目录中的solr.war到tomcat的webapps目录中
复制到:
第2步
解压solr.war包:
第3步
【删除solr.war包】
说明:删除为了防止tomcat启动的时候,再进行解压,覆盖已经解压配置好的solr。
第4步
【加入solr服务扩展jar包】
solr-4.10.3\example\lib\ext目录下的所有jar包copy到部署到Tomcat中的solr的/WEB-INF/lib
把solr解压包下solr-4.10.3\example\lib\ext目录下的所有jar包拷贝到Tomcat部署的solr的WEB-INF/lib文件夹
复制到:
第5步
【准备log4j.properties日志文件】
把solr解压包下solr-4.10.3\example\resources\log4j.properties文件,复制到Tomcat的webapps\solr\WEB-INF\classes目录下(如果没有classes目录,创建一个):
第6步
【配置web.xml】
修改web.xml,配置SolrHome
打开env-entry注释,修改env-entry-value为solrHome目录。
第7步
【启动tomcat服务】
运行tomcat安装bin目录中的startup.bat文件:
Solr管理界面操作
Solr管理管理界面介绍
SolrCore介绍
选择一个SolrCore进行详细操作:
Analysis
通过此界面可以测试索引分析器和搜索分析器的执行情况
Dataimport
数据导入,从关系数据库将数据导入到Solr索引库中。默认没有配置,需要手工配置。
Documents
通过/update表示更新索引,solr默认根据id(唯一约束)域来更新Document的内容,如果根据id值搜索不到id域则会执行添加操作,如果找到则更新。
删除文档:
<delete>
<id>1</id>
</delete>
<commit/>
Solr使用
schema.xml
schema.xml文件在SolrCore/conf目录。主要用于配置域的类型(FieldType)和配置域(Field)。在solr中域要先配置再使用。
Field
作用:配置域。
<field name="id" type="string" indexed="true" stored="true"
required="true" multiValued="false"/>
name:域的名称
type:域的类型
indexed:是否索引
stored:是否存储
required:是否必须(如果为true添加索引的时候,文档对象中必须要包含该域)
mulitValued:是否多值(用于复制域)
DynamicField
作用:配置动态域
<dynamicField name="*_ik" type="text_ik" indexed="true" stored="true"/>
name:动态域的名称
UniqueKey
作用:指定唯一约束域,相当于关系数据库中的主键
<uniqueKey>id</uniqueKey>
说明:在solr中,唯一约束域是必须的。
CopyField
作用:配置复制域
<copyField source="name" dest="keywords"/>
<copyField source="title" dest="keywords"/>
source:源域的名称
dest:目标域的名称
说明:在solr中,允许将多个域的值复制给一个域,这样的目的是方便执行搜索。solr在创建索引的时候,会把源域的内容复制给目标域。(目标域的定义必须要包含一个属性:multiValued="true")
FieldType
作用:配置域的类型
<fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">
<analyzer type="index">
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
<!-- in this example, we will only use synonyms at query time
<filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>
-->
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
<analyzer type="query">
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
<filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
</fieldType>
name:域的类型名称
class:域的类型,solr中的类型
analyzer:指定分词器,index是索引流程;query是检索流程
tokenizer:具体使用的分词器
filter:具体使用的过滤器
配置IK分词器
说明:配置ik中文分词器。
拷贝IK分词器jar包
拷贝Maven仓库中的jar包:
拷贝到solr/WEB-INF/lib目录:
拷贝IK分词器配置文件
拷贝IK解压目录中的配置文件
拷贝到solr/WEB-INF/classes目录:
配置schema.xml
通过FieldType标签,配置使用IK分词器的域类型
<!-- 配置使用IK分词器的域类型 -->
<fieldType name="aaa" class="solr.TextField" >
<analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/>
</fieldType>
通过Field标签,配置使用IK分词器的域
<!-- 配置使用IK分词器的域 -->
<field name="content_ik" type="aaa" indexed="false"
stored="true" multiValued="false"/>
重启tomcat
5.3配置业务域
说明:使用solr实现商品数据的搜索,需要把保存在关系数据库中的商品数据,导入solr索引库。
5.3.1准备商品数据
导入goods.sql
商品编号(商品编号是主键,作为唯一约束域uniqueKey):
<field name="id" type="string" indexed="true" stored="true"
required="true"/>
商品名称:
<field name="name" type="text_ik" indexed="true" stored="true"/>
商品标题:
<field name="title" type="text_ik" indexed="true" stored="true"/>
商品价格:
<field name="price" type="double" indexed="true" stored="true"/>
商品图片:
<field name="pic" type="string" indexed="false" stored="true"/>
定义商品复制域(在实际项目中复制域不需要存储,这里存储是为了给大家看到数据效果):
<field name="keywords" type="text_ik" indexed="true" stored="true" multiValued="true" />
把商品名称和商品标题添加到复制域
<copyField source="name" dest="keywords"/>
<copyField source="title" dest="keywords"/>
5.3.3配置schema.xml
<?xml version="1.0" encoding="UTF-8" ?>
<schema name="example" version="1.5">
<!-- 版本号(必须) -->
<field name="_version_" type="long" indexed="true" stored="true"/>
<!-- 普通域 -->
<field name="id" type="string" indexed="true" stored="true" required="true"/>
<field name="name" type="text_ik" indexed="true" stored="true"/>
<field name="title" type="text_ik" indexed="true" stored="true"/>
<field name="pic" type="string" indexed="false" stored="true"/>
<field name="price" type="double" indexed="true" stored="true"/>
<field name="keywords" type="text_ik" indexed="true" stored="true"
multiValued="true"/>
<!-- 复制域 -->
<copyField source="name" dest="keywords"/>
<copyField source="title" dest="keywords"/>
<!-- 唯一约束域 -->
<uniqueKey>id</uniqueKey>
<!-- 配置动态域 -->
<dynamicField name="*_ik" type="text_ik" indexed="true" stored="true"/>
<!-- 配置域类型 -->
<fieldType name="string" class="solr.StrField"/>
<fieldType name="boolean" class="solr.BoolField"/>
<fieldType name="int" class="solr.TrieIntField"/>
<fieldType name="float" class="solr.TrieFloatField"/>
<fieldType name="long" class="solr.TrieLongField"/>
<fieldType name="double" class="solr.TrieDoubleField"/>
<fieldType name="date" class="solr.TrieDateField"/>
<!-- 配置使用IK分词器的域类型 -->
<fieldType name="text_ik" class="solr.TextField" >
<analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/>
</fieldType>
</schema>
5.3.4配置solrconfig.xml 默认域
更改默认域名称,改成我们的域名称:
5.4 配置数据导入
5.4.1拷贝数据导入jar包
拷贝solr解压目录dist目录中的jar包:
拷贝到solr/solrHome/lib(lib手动创建):
5.4.2拷贝数据库驱动jar包
拷贝到solr/solrhome/lib:
5.4.3配置solrconfig.xml
第1步
配置lib标签,加载jar包
<lib dir="${solr.install.dir:../}/lib" regex=".*\.jar" />
第2步
通过requestHandler标签,配置数据导入的请求处理器
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
<lst name="defaults">
<str name="config">import-config.xml</str>
</lst>
</requestHandler>
第3步
创建import-config.xml
<?xmlversion="1.0" encoding="UTF-8"?>
<dataConfig>
<!-- 配置连接数据库信息 -->
<dataSourcetype="JdbcDataSource"
driver="com.mysql.jdbc.Driver"
url="jdbc:mysql://127.0.0.1:3306/lucene_db"
user="root"
password="admin"/>
<document>
<!-- 配置sql语句中的字段,与solr索引库的域的对应关系 -->
<entityname="goods"
query="SELECT id,name,title,price,pic FROM goods">
<fieldcolumn="id"name="id"/>
<fieldcolumn="name"name="name"/>
<fieldcolumn="title"name="title"/>
<fieldcolumn="price"name="price"/>
<fieldcolumn="pic"name="pic"/>
<!-- 测试动态域 -->
<fieldcolumn="pic"name="pic_ik"/>
<fieldcolumn="name"name="name_ik"/>
<fieldcolumn="title"name="title_ik"/>
</entity>
</document>
</dataConfig>
5.4.4重启tomcat,执行导入
查询数据
Solr管理界面搜索
q: 指定查询表达式
`*:*`:表示搜索全部
name:商务,表示搜索name域中包含有“商务”
fq:指定搜索的过滤条件
price:[2000 TO 8000],表示搜索2000 至8000之间的商品,包含2000 ,包含8000
price:{2000 TO 8000},表示搜索2000 至8000之间的商品,不包含2000 ,不包含8000
sort:指定搜索结果排序
price asc,表示按照商品价格升序排序
price desc,表示按照商品价格降序排序
start,rows:指定分页
start:分页开始记录数
rows:每页显示记录数
fl:指定搜索结果显示的域列表
pid,name,price,显示商品id、商品名称、商品价格
df:指定默认搜索域
注意事项:默认搜索域只能指定一个。
wt:指定搜索结果的响应格式
常用格式:json/xml/csv。
hl:指定高亮显示
hl.fl:指定高亮显示的域名称
hl.simple.pre:指定高亮显示的html标签的开始部分
hl.simple.post:指定高亮显示的html标签的结束部分
注意:高亮结果集的数据结构为:`Map<String,Map<String,List<String>>`
Solrj
Solrj是什么
SolrJ是一个使Java应用程序可以轻松与Solr对话的API。SolrJ隐藏了许多连接到Solr的细节,并允许您的应用程序通过简单的高级方法与Solr进行交互。
SolrJ的中心是org.apache.solr.client.solrj包,它只包含五个主要的类。首先创建一个SolrClient代表你想要使用的Solr实例。然后发送SolrRequests或SolrQuerys找回SolrResponses。
需求
使用solrj访问solr服务,完成索引的增删改查操作。
6.3 需求实现
6.3.1 创建Maven项目
配置依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.dfbz</groupId>
<artifactId>Solr</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--solr核心包-->
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-core</artifactId>
<version>4.10.3</version>
</dependency>
<!--solr依赖日志包-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
代码实现
添加(更新)索引
说明:solr是根据pid域执行索引的更新。先根据pid域执行搜索,搜索到执行更新;搜索不到执行添加。
创建HttpSolrServer对象,连接solr服务
创建域相关的实体对象(Product)
使用HttpSolrServer对象,执行添加(更新)
提交事务
第一步:创建域相关的实体类
package com.dfbz.entity;
import org.apache.solr.client.solrj.beans.Field;
public class Goods {
@Field("id")
private String id;
@Field
private String name;
@Field
private String title;
@Field
private Double price;
@Field
private String pic;
}
package com.dfbz.solr;
import com.dfbz.entity.Goods;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.junit.Test;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public class Demo1 {
private HttpSolrServer solrServer = new HttpSolrServer("http://localhost:8983/solr/collection1");
@Test
public void saveOrUpdate() throws IOException, SolrServerException {
Goods goods = new Goods();
goods.setId("888");
goods.setName("东标方准牌手机");
goods.setTitle("东标方准牌手机,好吃不上火!");
goods.setPrice(9999.99);
//如果没有则添加,如果有则修改
solrServer.addBean(goods);
//提交事务
solrServer.commit();
}
}
注意:在solr中,添加和修改是同一个方法,如果id存在则修改,如果id不存在则添加
删除索引
根据id删除:
创建HttpSolrServer对象,连接solr服务
使用HttpSolrServer对象执行删除
提交事务
/** 根据id删除索引 */
@Test
public void deleteById() throws Exception{
solrServer.deleteById("888");
// 提交事务
solrServer.commit();
}
根据条件删除
创建HttpSolrServer对象,连接solr服务
使用HttpSolrServer对象,执行删除
提交事务
//根据条件删除
@Test
public void deleteByQuery() throws IOException, SolrServerException {
solrServer.deleteByQuery("title:手机");
solrServer.commit();
}
查询索引
创建HttpSolrServer对象,连接solr服务
创建查询对象(SolrQuery),封装查询条件
使用HttpSolrServer对象执行搜索,返回QueryResposne对象
通过QueryResponse对象中获取查询的结果
处理结果
//条件查询
@Test
public void query() throws IOException, SolrServerException {
SolrQuery solrQuery = new SolrQuery("title:商务");
QueryResponse response = solrServer.query(solrQuery);
System.out.println("数量: " + response.getResults().getNumFound());
List<Goods> goodsList = response.getBeans(Goods.class);
for (Goods goods : goodsList) {
System.out.println(goods);
System.out.println("-------------------");
}
}
查询步骤:
创建HttpSolrServer对象,连接solr服务
创建查询对象(SolrQuery),封装查询条件
使用HttpSolrServer对象执行搜索,返回QueryResponse
通过QueryResponse获取搜索结果
通过QueryResponse获取高亮内容
处理搜索结果及高亮内容
//复杂条件查询
@Test
public void query2() throws IOException, SolrServerException {
SolrQuery solrQuery = new SolrQuery("title:手机");
//添加过滤条件
solrQuery.addFilterQuery("price:[1000 TO 9000]");
//排序
solrQuery.addSort("price", SolrQuery.ORDER.desc);
//分页
solrQuery.setStart(0);
solrQuery.setRows(10);
//设置查询域
solrQuery.setFields("id,price,name,title");
//开启高亮
solrQuery.setHighlight(true);
//高亮域
solrQuery.addHighlightField("title");
solrQuery.setHighlightSimplePre("<font color='red'>");
solrQuery.setHighlightSimplePost("</font>");
QueryResponse response = solrServer.query(solrQuery);
System.out.println("查询到的条数: " + response.getResults().getNumFound());
//获取查询数据
List<Goods> goodsList = response.getBeans(Goods.class);
//获取高亮数据
Map<String, Map<String, List<String>>> high = response.getHighlighting();
for (Goods goods : goodsList) {
//根据id查询到对应高亮数据(map);查询title的值
System.out.println("id: " + goods.getId() + " title: " + high.get(goods.getId()).get("title"));
;
}
}
set复杂查询
//set查询
@Test
public void query3() throws IOException, SolrServerException {
SolrQuery solrQuery = new SolrQuery();
//设置全局查询
solrQuery.set("q","小米");
//设置FilterQuery
solrQuery.set("fq", "price:[0 TO 8000]");
//设置起始页数
solrQuery.set("start", 0);
//设置页大小
solrQuery.set("rows", 5);
//设置排序规则
solrQuery.set("sort", "price desc");
//设置FieldList
solrQuery.set("fl", "id,name,price");
//设置默认域
solrQuery.set("df", "keywords");
//开启高亮
solrQuery.setHighlight(true);
//设置高亮域
solrQuery.addHighlightField("title");
//设置高亮域
solrQuery.addHighlightField("name");
//高亮前缀
solrQuery.setHighlightSimplePre("<font color='red'>");
//高亮后缀
solrQuery.setHighlightSimplePost("</font>");
//执行查询
QueryResponse response = solrServer.query(solrQuery);
System.out.println("记录数: " + response.getResults().getNumFound());
List<Goods> goodsList = response.getBeans(Goods.class);
System.out.println("查询数据: ");
for (Goods goods : goodsList) {
System.out.println(goods);
}
System.out.println("----------------------");
System.out.println("高亮数据: ");
Map<String, Map<String, List<String>>> high = response.getHighlighting();
for (Goods goods : goodsList) {
String name = high.get(goods.getId()).get("title").get(0);
String title = high.get(goods.getId()).get("name").get(0);
System.out.println("name: " + name + ";title: " + title);
}
}