Solr的使用-独立服务器模式
一、Solr是什么?
- Solr是一个开源的、独立的、快速的、高性能、高可扩展的企业级搜索应用服务器,用于构建搜索应用程序。它是基于Lucene(全文搜索引擎)的Java搜索应用服务器(是一套war程序),是Lucene的更高一层的扩展封装,底层使用易于扩展和修改的Java 来实现。服务器通信使用标准的HTTP 和XML。同时也易于加入到Web应用程序中。
- Solr提供了层面搜索(即统计)、命中醒目显示并且支持多种输出格式(包括XML/XSLT、JSON、.CSV等格式)。它易于安装和配置,而且通过基于HTTP的webapp服务器实现可视化管理界面,方便使用人员配置、访问和调用。Solr已经在众多大型的网站中使用,较为成熟和稳定。
- Solr 包装并扩展了 Lucene的Java API(即内部集成了Lucene,apache提供的一些对搜索引擎做支持的jar包),所以Solr的基本上沿用了Lucene的相关术语。更重要的是,Solr 创建的索引与 Lucene 搜索引擎库完全兼容。
- 通过对Solr 进行适当的配置,某些情况下可能需要进行编码,Solr 可以阅读和使用构建到其他 Lucene 应用程序中的索引。
- 此外,很多 Lucene 工具(如Nutch、 Luke)也可以使用Solr 创建的索引。可以使用 Solr 的表现优异的基本搜索功能,也可以对它进行扩展从而满足企业的需要。
- 使用Solr构建的应用程序非常复杂,可提供高性能。
总之,Solr是一个可扩展、可部署、搜索/存储引擎,优化搜索大量以文本为中心的数据。
二、Solr的特性
- 高级、强大的全文搜索功能;
- 专为高通量的网络流量进行的优化;
- 基于开放接口(XML和HTTP)的标准;
- 综合的HTML管理界面;
- 可伸缩性 -- 能够有效地复制到另外一个Solr搜索服务器;
- 使用XML配置达到灵活性和适配性;
- 高可扩展的插件体系;
- 高亮显示检索结果
- 动态集群;
- 数据库接口和电子文档(Word ,PDF 等)的处理;
- 数据存储和处理;
三、Lucene是什么?
Lucene 是一个基于 Java 的全文信息检索工具包,它不是一个完整的搜索应用程序,而是为你的应用程序提供索引和搜索功能。Lucene 目前是 Apache Jakarta(雅加达) 家族中的一个开源项目。也是目前最为流行的基于 Java 开源全文检索工具包。目前已经有很多应用程序的搜索功能是基于 Lucene ,比如 Eclipse 帮助系统的搜索功能。Lucene 能够为文本类型的数据建立索引,所以你只要把你要索引的数据格式转化的文本格式,Lucene 就能对你的文档进行索引和搜索。
四、Solr vs Lucene
Solr与Lucene 并不是竞争对立关系,恰恰相反Solr 依存于Lucene,因为Solr底层的核心技术是使用 Apache Lucene 来实现的,简单的说Solr是Lucene的服务器化。
需要注意的是Solr 并不是简单的对Lucene 进行封装,它所提供的大部分功能都区别于Lucene 。
Solr和Lucene的本质区别有以下三点:搜索服务器,企业级和管理。Lucene本质上是搜索库,不是独立的应用程序,而Solr是。Lucene专注于搜索底层的建设,而Solr专注于企业应用。Lucene不负责支撑搜索服务所必须的管理,而Solr负责。所以说,一句话概括 Solr:
Solr是Lucene面向企业搜索应用的扩展。
Solr 和 Lucene的架构图:(此图是借用的别的博客上的,后面会写上链接)
Solr与Lucene的架构图.png
这个图很繁琐,看不懂,大家不要灰心,在后面的代码里你就能够了解了这个图所讲的。
不难看出,绿色的就是lucene的模块,而蓝色的就是solr扩展了lucene。从图上可以看出以下几点:
- 一个真正的拥有动态字段(Dynamic Field)和唯一键(Unique Key)的数据模式(Data Schema)
- 对Lucene查询语言的强大扩展!
- 支持对结果进行动态的分组和过滤
- 高级的,可配置的文本分析
- 高度可配置和可扩展的缓存机制
- 性能优化
- 支持通过XML进行外部配置
- 拥有一个管理界面
- 可监控的日志
- 支持高速增量式更新(Fast incremental Updates)和快照发布(Snapshot Distribution)
五、solr两种部署模式介绍
- Standalone Server 独立服务器模式(单机启动模式):适用于数据规模不大的场景;
- SolrCloud 分布式集群模式(集群启动模式):适用于数据规模大,高可靠、高可用、高并发的场景;
solr的简介就到此结束了,相信大家也对solr有了初步的了解,下面让我们开始安装、使用Solr吧,在使用过程中也顺便介绍一下solr的常用属性。
Solr软件的安装与使用
一、环境介绍
- 系统:Linux、Windows、MacOS;
- JDK需在JDK1.7+ 版本(运行标准Solr 服务只需要安装JRE 即可,但如果需要扩展功能或编译源码则需要下载JDK 来完成。从solr6开始只能使用jdk1.8+。);
- maven需在Maven3+ 版本;
- tomcat服务器建议在tomcat7+ 版本;
- mysql数据库需在mysql5+ 版本;
- solr需在solr4+ 版本(solr5.0以上建议tomcat服务器在tomcat8以上);
注意:
solr4.x版本的核心类及常用的属性,和solr5.0以上的差不多,但是还有细微的区别,请注意!
我这里使用的是MacOS10.14.5、JDK1.8.0_212、Maven3、tomcat9.0.20、mysql5.7、solr8.1.1。
二、安装Solr
- 通过brew安装
- 通过brew安装的,命令如下:
$ brew search solr # 搜索一下是否有可用的solr软件
$ brew install solr # 安装solr
安装solr.png
- 运行以下命令,启动Solr服务器。命令如下:
$ solr start # 开始运行Solr服务器,默认是8983端口
$ solr stop # 停止运行Solr服务器
启动solr服务.png
启动成功,说明Solr安装成功。
- 在浏览器中,输入“http://localhost:8983/solr/”访问solr服务。出现如下图页面,说明服务器已经成功启动了。
Solr Admin.png
- Solr官网下载Solr压缩包
- 在Solr官网 下载Solr 压缩包。
- 解压缩Solr压缩包到目标目录。
- 运行solr命令启动Solr服务器(默认是
8983
端口)。 - 在浏览器中,输入“http://localhost:8983/solr/”访问solr服务。
三、创建Core Admin/Collections
一个solr服务是可以有多个core的。
-
通过终端命令创建Core
通过终端命令的方式创建Core,会自动帮你创建基本的conf、data文件夹及相关文件、core.properties文件。创建命令如下:
$ solr create -c testdemo
创建Core命令.png
界面查看创建的Core.png
- 通过Solr可视化管理界面创建Core
- 我们先要在“server/solr”目录下创建必要的配置信息,那么让我们先进入到solr安装的目录下,找到server目录,然后进入到server下的solr 目录(solr目录是存放创建的Core的目录),在solr目录下创建一个名为
testdemo2
的文件夹,然后将server/solr/configsets/_default
目录下的conf
文件夹及文件拷贝到testdemo2文件夹中(conf文件夹及文件可以手动创建,我这里就简单粗暴一点了),然后再在testdemo2下创建一个名为data的空文件夹,到这里准备工作就完成了。文件结构如下:
从其他文件夹中拷贝conf文件夹及文件.png
- 在Solr可视化管理界面中,选择
Core Admin
选项,然后点击Add Core
按钮,添加Core,填写要求的基本信息。如下图:
Add Core.png
在添加的弹窗下方有个提示信息:instanceDir and dataDir need to exist before you can create the core
,意思就是在创建Core之前要保证instanceDir和dataDir这两个输入框中填写的文件夹一定是已经存在的,否则就会报错添加失败。
在实践中,dataDir输入框中填写的data(可自定义)文件夹可以不存在,它会自动帮你创建名为data
的文件夹及其下的文件,但是其他输入框中的文件是必须要存在的。否则报类似如下错误:
- 点击
✔️ Add Core
按钮,添加testdemo2核心。至此就完成了界面添加Core的过程,如下图:
添加核心testdemo2成功.png
四、配置中文分词器
solr虽然功能非常强大,但是solr还是存在一些不足的,下面我们就说说其中一个较大的问题那就是分词问题,特别是中英文的混合分词,处理起来非常棘手。在Solr中如果不配置中文分词器,则默认是不支持中文分词的。如图搜索解析:
没有添加中文分词器时的搜索解析信息.png
中文分词的算法:基于字符串配置,基于统计以及机器学习的分词方式。
这里我们使用两种方式解决。
-
使用Solr自带的Smartcn中文分词器
Smartcn是Lucene自带的一款基于统计规则来分词的中文分词器。
-
在
smartcn的jar包.pnglibexec/contrib/analysis-extras/lucene-libs
目录下找到Smartcn的jar包,如下图:
-
将Smartcn的jar包拷贝到
粘贴smartcn的jar到WEB-INF:lib目录下.pngserver/solr-webapp/webapp/WEB-INF/lib
目录下,如下图:
-
切换到创建的Core testdemo目录下,配置conf目录下的managed-schema文件(模式配置文件),在文件中添加如下信息,并保存。
<fieldType name="text_cn" class="solr.TextField" positionIncrementGap="100">
<analyzer type="index">
<tokenizer class="org.apache.lucene.analysis.cn.smart.HMMChineseTokenizerFactory" />
</analyzer>
<analyzer type="query">
<tokenizer class="org.apache.lucene.analysis.cn.smart.HMMChineseTokenizerFactory" />
</analyzer>
</fieldType>
- 添加完成,在终端运行
$ solr restart -force
命令重启solr服务。 -
回到Solr管理界面,重新加载testdemo Core。然后再次进行搜索解析,如下图:
重新加载testdemo Core.png
-
使用IK-Analyzer中文分词器
虽然solr自带了支持中文分词的Smartcn,但是其效果不是很好,扩展性比较差不能自定义扩展中文词库,所以推荐使用IK-Analyzer进行分词,IK-Analyzer支持屏蔽关键词、新词汇的配置。
- 下载IK-Analyzer分词器的jar包。然后将jar包拷贝到
server/solr-webapp/webapp/WEB-INF/lib
目录下(如果使用了tomcat,那么该jar包应该拷贝到apache-tomcat-9.0.20/webapps/solr/WEB-INF/lib/
目录下)。 - 配置
server/solr/testdemo/conf
目录下的managed-schema
文件,在文件中添加如下内容:
<fieldType name="text_ik" class="solr.TextField">
<!-- 生成索引时使用的分词器 -->
<analyzer type="index">
<tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" useSmart="false" conf="ik.conf" />
<filter class="solr.LowerCaseFilterFactory" />
</analyzer>
<!-- 查询时使用的分词器 -->
<analyzer type="index">
<tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" useSmart="true" conf="ik.conf" />
<filter class="solr.LowerCaseFilterFactory" />
</analyzer>
</fieldType>
- 添加完成,在终端运行
$ solr restart -force
命令重启solr服务。 -
回到Solr管理界面,重新加载testdemo Core。然后再次进行搜索解析
IKAnalyzer搜索解析结果.png
五、DIH导入索引数据并创建索引文件
DIH简介:
DIH全称是Data Import Handler 数据导入处理器,顾名思义这是向solr中导入数据的,我们的solr目的就是为了能让我们的应用程序更快的查询出用户想要的数据,而数据存储在应用中的各种地方入xml、pdf、关系数据库中,那么solr首先就要能够获取这些数据并在这些数据中建立索引来达成快速搜索的目的,这里就列举我们最常用的从关系型数据库中向solr导入索引数据。
- 使用Solr可视化管理界面到如数据
-
将
libexec/dist
目录下的solr-dataimporthandler-8.1.1.jar
和solr-dataimporthandler-extras-8.1.1.jar
两个jar包拷贝到server/solr-webapp/webapp/WEB-INF/lib
目录下。 -
进入testdemo Core目录下的conf目录,然后在
solrconfig.xml
文件中配置DIH,配置信息如下:
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
<lst name="defaults">
<str name="config">db-dataconfig.xml</str> <!-- 数据库配置文件的路径,根据你的配置文件的位置填写路径,可是写相对路径,也可以写绝对路径 -->
</lst>
</requestHandler>
- 创建
db-dataconfig.xml
数据库配置文件,然后配置数据库信息,
<dataConfig>
<!-- 配置数据库,参数之间需要用 & 连接 -->
<dataSource type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/artbase_30?useUnicode=true&characterEncoding=utf8&useSSL=false" user="xxx" password="xxx" />
<document>
<!-- 配置entity( entity的name可以随便起),可以把它当作与数据库中一个表对应,在query中书写查询sql -->
<entity name="art_course_info" query="select Member_ID,Course_Name,Add_Time from art_course_info where Member_ID=0099">
<field column="Course_ID" name="id"/>
<field column="Member_ID" name="memberID"/>
<field column="Course_Name" name="courseName"/>
<field column="Synopsis" name="synopsis"/>
<field column="Course_Photo" name="coursePhoto"/>
</entity>
</document>
</dataConfig>
注意:
这里容易与schema中的配置混淆,我的理解是schema中配置的是创建索引的配置,而索引的创建需要有数据基础,而现在讲的数据导入文件就是建立索引的数据基础,他是创建索引的元数据。现在配置文件完成后可以用DIH命令执行了。
- 配置
managed-schema
文件,此文件中需要配置我们的业务域(即需要存储的数据结构),配置信息如下:
<!-- 配置字段域 -->
<field name="id" type="string" indexed="false" stored="true" required="true" multiValued="false" />
<field name="memberID" type="string" indexed="true" stored="true" />
<field name="courseName" type="string" indexed="true" stored="true" />
<field name="synopsis" type="string" indexed="true" stored="true" />
<field name="coursePhoto" type="string" indexed="true" stored="true" />
<!-- 关键词 定义复制域字段,将课程名称和课程描述都复制到 course_keywords这一个字段上 -->
<field name="course_keywords" type="text" indexed="false" stored="false" multiValued="true" />
<copyField source="courseName" dest="course_keywords" />
<copyField source="synopsis" dest="course_keywords" />
注意:
配置的name的值必须与db-dataconfig.xml文件中的name一致。
- 以上文件配置完成,
重启solr服务
。然后访问Solr可视化管理后台,在界面左侧的下拉框中选择testdemo
Core,然后选择Dataimport
选项,然后选择你的 Entity(即在db-dataconfig.xml文件中定义的entity的name) ,点击execute
按钮,在右侧查看信息。具体如下图:
导入数据.png
如果导如失败,会在Raw Status-Output
下的statusMessages
参数下提示"Full Import Failed": 2019-07-05 09:11:52
。
执行导入操作后,会自动生成索引文件、日志文件和dataimport属性文件,如下图:
dataimport属性文件.png 生成的索引文件.png 日志文件.png
- 选择
Query
选项,查询导入的数据,操作如下图:
查询所有的数据.png
六、solrJ的集成和使用
solrJ是一个用来访问solr的java客户端,提供了索引和搜索的方法(将一些常用的命令封装进去了),通过solrJ提供的API 接口来操作solr服务。
在solr5系之后跟solr4最大的区别是被发布成了一个独立的应用。而不再需要tomcat等容器。在其内部集成了jetty服务器,他可以通过bin目录的脚本直接运行启动。solr5有两种运行模式,独立模式和云模式,独立模式是以core来管理,云模式是以collection来管理。
-
构建solrJ应用程序
我这里使用Maven集成的solrJ 8.1.1,具体代码如下:
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
<version>8.1.1</version>
</dependency>
如果需要连接数据库、注解等jar包,请自行添加依赖,这里只对solrJ做简单的使用。
- solrJ的使用
- 创建一个名为
CourseInfo
的Entity,CourseInfo
中声明的变量是和managed-schema
中定义的field(字段域)的name是一致的。代码如下:
public class CourseInfo {
// 课程id
@Field("id") // 如果field中的name和entity中的变量名不一致,则需要建立映射
private String courseID;
// 会员id
@Field
private String memberID;
// 课程名称
@Field
private String courseName;
// 课程简介
@Field
private String synopsis;
// 课程logo
@Field
private String coursePhoto;
/**
* set / get 方法
*/
public void setCourseID(String courseID) {
this.courseID = courseID;
}
public String getCourseID() {
return courseID;
}
public void setMemberID(String memberID) {
this.memberID = memberID;
}
public String getMemberID() {
return memberID;
}
public void setCourseName(String courseName) {
this.courseName = courseName;
}
public String getCourseName() {
return courseName;
}
public void setSynopsis(String synopsis) {
this.synopsis = synopsis;
}
public String getSynopsis() {
return synopsis;
}
public void setCoursePhoto(String coursePhoto) {
this.coursePhoto = coursePhoto;
}
public String getCoursePhoto() {
return coursePhoto;
}
/**
* 空构造函数
*/
public CourseInfo() {}
/**
* 有参构造函数
* @param courseID
* @param memberID
* @param courseName
* @param synopsis
* @param coursePhoto
*/
public CourseInfo(String courseID, String memberID, String courseName, String synopsis,
String coursePhoto) {
super();
this.courseID = courseID;
this.memberID = memberID;
this.courseName = courseName;
this.synopsis = synopsis;
this.coursePhoto = coursePhoto;
}
}
- 创建
CourseController
,并使用solrJ。
2.1 声明需要的常量和变量
// Solr的基链接
private static final String SOLR_URL = "http://localhost:8983/solr";
private static final String SOLR_CORE = "testdemo";
// 声明solrJ客户端
private HttpSolrClient solrClient;
2.3 创建solr客户端的方式
// 不同solr版本solrj 的创建方式有所不同
// solr4创建方式
SolrServer solrServer = new HttpSolrServer("http://127.0.0.1:8080/solr");
// solr5创建方式,在url中指定core名称:core1
HttpSolrClient solrServer=new HttpSolrClient("http://127.0.0.1:8080/solr/core1");
// solr7/8创建方式,在url中指定core名称:core1
HttpSolrClient solrServer= new HttpSolrClient.Builder("http://127.0.0.1:8080/solr/core1").build();
2.2 添加/创建索引
/**
* 添加索引(创建索引)
* @param courseInfo
* @throws Exception
*/
public void addDocument(CourseInfo courseInfo) throws Exception {
// 创建solrJ客户端实例,并指定与solr通信的连接和读取的超时时间,不指定走默认配置
solrClient = new HttpSolrClient.Builder(SOLR_URL).withConnectionTimeout(10000).withSocketTimeout(60000).build();
// 创建SolrInputDocument,并添加
SolrInputDocument solrInputDocument = new SolrInputDocument();
// 添加内容
solrInputDocument.addField("id", "2342131");
solrInputDocument.addField("Member_ID", "10");
solrInputDocument.addField("Course_Name", "dfadf");
solrInputDocument.addField("synopsis", "简介的方框拉风的静安寺附近为饿哦就我if抗菌素到哪里去卡");
solrInputDocument.addField("coursePhoto", "http://www.dsdf.com/1234.png");
// 添加到client
solrClient.add(SOLR_CORE, solrInputDocument);
// 索引文档必须commit
solrClient.commit(SOLR_CORE);
// 关闭资源
solrClient.close();
}
2.4 查询信息
/**
* solrJ之查询
* @param condition
* @throws Exception
*/
public void query() throws Exception {
// 创建solrJ客户端实例,并指定与solr通信的连接和读取的超时时间,不指定走默认配置
solrClient = new HttpSolrClient.Builder(SOLR_URL).withConnectionTimeout(10000).withSocketTimeout(60000).build();
// 封装查询参数
SolrQuery solrQuery = new SolrQuery("*:*");
// 添加需要回显得内容
solrQuery.addField("id");
solrQuery.addField("courseName");
solrQuery.addField("synopsis");
// 设置每页显示多少条
solrQuery.setStart(0);
solrQuery.setRows(20);
// 执行查询返回QueryResponse
QueryResponse queryResponse = solrClient.query(SOLR_CORE, solrQuery);
// 获取doc文档
SolrDocumentList documentList = queryResponse.getResults();
// 内容遍历
for (SolrDocument document: documentList) {
System.out.println("courseID: " + document.get("id")
+ "\t memberID: " + document.get("memberID")
+ "\t courseName: " + document.get("courseName")
+ "\t synopsis: " + document.get("synopsis")
+ "\t coursePhoto: " + document.get("coursePhoto"));
}
// 关闭资源
solrClient.close();
}
2.5 删除数据
/**
* solrJ之单个id 的删除索引(一)
* @param id
* @throws Exception
*/
public void deleteById(String id) throws Exception {
// 创建solrJ客户端实例,并指定与solr通信的连接和读取的超时时间,不指定走默认配置
solrClient = new HttpSolrClient.Builder(SOLR_URL).withConnectionTimeout(10000).withSocketTimeout(60000).build();
// 通过id删除
solrClient.deleteById("1002");
// 提交
solrClient.commit(SOLR_CORE);
// 关闭资源
solrClient.close();
}
/**
* solrJ之多个id 的list集合 删除索引(二)
* @param id
* @throws Exception
*/
public void deleteById2(String id) throws Exception {
// 创建solrJ客户端实例,并指定与solr通信的连接和读取的超时时间,不指定走默认配置
solrClient = new HttpSolrClient.Builder(SOLR_URL).withConnectionTimeout(10000).withSocketTimeout(60000).build();
// 通过多个id删除
ArrayList<String> ids = new ArrayList<String>();
ids.add("1002");
ids.add("1003");
solrClient.deleteById(SOLR_CORE, ids);
// 提交
solrClient.commit(SOLR_CORE);
// 关闭资源
solrClient.close();
}
/**
* solrJ之通过deleteByQuery删除索引(三)
* @param query
* @throws Exception
*/
public void deleteByQuery(String query) throws Exception {
// 创建solrJ客户端实例,并指定与solr通信的连接和读取的超时时间,不指定走默认配置
solrClient = new HttpSolrClient.Builder(SOLR_URL).withConnectionTimeout(10000).withSocketTimeout(60000).build();
// 执行删除
solrClient.deleteByQuery(SOLR_CORE, "id:1002");
// 提交操作
solrClient.commit(SOLR_CORE);
// 关闭资源
solrClient.close();
}
参考文章:
solr教程
Solr Apache Solr 初级教程(介绍、安装部署、Java接口、中文分词)
项目中如何使用solr
solr-部署详解(solr两种部署模式介绍、独立服务器模式详解、SolrCloud分布式集群模式详解)
Centos7安装配置单机solr-8.1.1+ik-analyzer8.1.0+mysql中文分词