solr简介
solr 是以 licene 为内核开发的企业级搜索应用,应用程序可以通过http请求方式来提交索引,查询索引,提供了比 lucene更丰富的查询语言,是一个高性能,高可用环境全文搜索引擎
。
solr高可用版本:
https://blog.youkuaiyun.com/liaomin416100569/article/details/77301756
一。倒排索引的原理
文章表:
文章id | 文章标题 | 文章内容 |
---|---|---|
1 | 我爱中国 | 中国文化博大精深 |
2 | 我爱自己 | 爱自己 最踏实 |
倒排索引:
可以进入:
ikanalyzer在线分词(分词工具): https://www.sojson.com/analyzer
索引 | 文章id |
---|---|
我 | 1 ,2 |
爱 | 1,2 |
中国 | 1 |
自己 | 2 |
二。分词器
将中文拆分成有意义的词。
常用的有:IK分词器,庖丁解牛分词器
三。lucene
lucene 是一个将text 数据类型,分词建立索引的一个库。不适合企业级使用,企业级考虑高可用问题。
solr 是一个企业级应用的搜索引擎,支持使用json格式提交数据
json格式:
[ ] 代表数组
{ } 对象(文档)
键值对 属性
{
id:1,
hobby:["篮球","杨幂"],
tt:{
}
}
文章id | 文章标题 | 文章内容 |
---|---|---|
1 | 我爱中国 | 中国文化博大精深 |
2 | 我爱自己 | 爱自己 最踏实 |
模拟json:
[
{
id:1,
title:"我爱中国,
content:"中国文化博大精深"
},
{
id:2,
title:"我爱自己",
content:"爱自己 最踏实"
}
]
四。solr安装
网址: https://hub.docker.com/_/solr/
核(core): 是用于存储json格式的数据,等价于mysql中数据库的概念。
文档: 一个json对象就是一个文档,相同属性的json数组集合就是一个表。
安装solr:
下载solr安装包 solr所有版本 :http://archive.apache.org/dist/lucene/solr/
这里下载 solr-5.5.4
docker run --name my_solr -itd --net host solr:5.5.5 //会开启一个8983端口
检测端口:
netstat -aon | grep 8983
yum -y install net-tools
telnet localhost 端口号
(telnet是用来检查端口的)
如果开了端口用java代码调不了,首先就要到cmd上 telnet 一下,看一下通不通,连不通的情况下有可能是防火墙没关等其他可能。
创建core:
docker exec -it --user=solr my_solr bin/solr create_core -c mycore
–user表示是用solr这个账号去执行这个命令,默认用户名是solr,当启动这个容器的时候会自动创建一个用户叫solr,用solr账号去创建,必须要用solr执行这个命令,不加–user=solr执行不了。
创建完成后提示:
[root@localhost ~]# docker exec -it --user=solr my_solr bin/solr create_core -c mycore
Copying configuration to new core instance directory:
/opt/solr/server/solr/mycore //表示将来插入的数据存放在哪个磁盘哪个位置.
Creating new core 'mycore' using command:
http://localhost:8983/solr/admin/cores?action=CREATE&name=mycore&instanceDir=mycore
{
//表示存入后的状态.
"responseHeader":{
"status":0, //代表成功.
"QTime":3375}, //表示用了多少时间才创建成功.(毫秒)
"core":"mycore"}
就是调8983 执行http://localhost:8983/solr/admin/cores?action=CREATE&name=mycore&instanceDir=mycore 创建,取个名字叫mycore
直接执行(跟命令是一样的,slor支持http):
http://localhost:8983/solr/admin/cores?action=CREATE&name=mycore&instanceDir=mycore
也可以创建,但是要换成 本机ip 换个名字.
建完之后就可以用浏览器打开8983端口了:http://192.168.126.131:8983/solr
五。solr搜索
假设提交
{"id":"change.me","title":"change.me"}
q
表示按照什么字段来搜索
字段名:值 (where 列名=值)
支持or 和and语法
比如 i:1 and j:2
比如 i[1 to 10]
** 模拟数据**
{
id:2,
title:"我爱自己",
content:"爱自己 最踏实"
}
成功插入:
搜索结果:
注:现在这种情况要全文字匹配(完全一样)才可以搜索到。
六。solr中文分词器配置
默认solr 没有使用中文分词器 所有搜索的词 都是整个句子就是一个词 搜索时 将单词全部写入才能搜索或者使用* 需要配置中文分词器
目前比较好用的分词器 是IK 2012年停更 只支持到 Lucene4.7 所有 solr5.5 需要lucene5支持 需要修改部分源码来支持solr5.5
找到 IKAnalyzer类 需要重写 protected TokenStreamComponents createComponents(String fieldName) 方法
找到 IKTokenizer类 需要重写构造方法 public IKTokenizer(Reader in, boolean useSmart) 为 public IKTokenizer(boolean useSmart) {
参考:https://blog.youkuaiyun.com/liaomin416100569/article/details/77301756
html 替换:(把包后面加上版本号进行替换)
<dependencies>
<dependency>
<groupId>com.janeluo</groupId>
<artifactId>ikanalyzer</artifactId>
<version>2012_u6</version>
<!-- <exclusions> 去除(把下列包去除,好重新下载)
<exclusion>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
</exclusion>
</exclusions> -->
</dependency>
<dependency> <!--在后面加版本号就不用在去除之前版本的包了,直接替换-->
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>5.5.5</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>5.5.5</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>5.5.5</version>
</dependency>
</dependencies>
创建一样的文件:(重新创建一个更包里面的文件一模一样的包进行修改。)
IKTokenizer 文件
package org.wltea.analyzer.lucene;
import java.io.IOException;
import org.apache.lucene.analysis.Tokenizer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
import org.apache.lucene.analysis.tokenattributes.TypeAttribute;
import org.wltea.analyzer.core.IKSegmenter;
import org.wltea.analyzer.core.Lexeme;
/**
* IK分词器 Lucene Tokenizer适配器类
* 兼容Lucene 4.0版本
*/
public final class IKTokenizer extends Tokenizer {
// IK分词器实现
private IKSegmenter _IKImplement;
// 词元文本属性
private final CharTermAttribute termAtt;
// 词元位移属性
private final OffsetAttribute offsetAtt;
// 词元分类属性(该属性分类参考org.wltea.analyzer.core.Lexeme中的分类常量)
private final TypeAttribute typeAtt;
// 记录最后一个词元的结束位置
private int endPosition;
/**
* Lucene 4.0 Tokenizer适配器类构造函数
* @param in
* @param useSmart
*/
public IKTokenizer(boolean useSmart) { //把 super(in); 删除.
offsetAtt = addAttribute(OffsetAttribute.class);
termAtt = addAttribute(CharTermAttribute.class);
typeAtt = addAttribute(TypeAttribute.class);
_IKImplement = new IKSegmenter(input, useSmart);
}
/*
* (non-Javadoc)
* @see org.apache.lucene.analysis.TokenStream#incrementToken()
*/
@Override
public boolean incrementToken() throws IOException {
// 清除所有的词元属性
clearAttributes();
Lexeme nextLexeme = _IKImplement.next();
if (nextLexeme != null) {
// 将Lexeme转成Attributes
// 设置词元文本
termAtt.append(nextLexeme.getLexemeText());
// 设置词元长度
termAtt.setLength(nextLexeme.getLength());
// 设置词元位移
offsetAtt.setOffset(nextLexeme.getBeginPosition(), nextLexeme.getEndPosition());
// 记录分词的最后位置
endPosition = nextLexeme.getEndPosition();
// 记录词元分类
typeAtt.setType(nextLexeme.getLexemeTypeString());
// 返会true告知还有下个词元
return true;
}
// 返会false告知词元输出完毕
return false;
}
/*
* (non-Javadoc)
* @see org.apache.lucene.analysis.Tokenizer#reset(java.io.Reader)
*/
@Override
public void reset() throws IOException {
super.reset();
_IKImplement.reset(input);
}
@Override
public final void end() {
// set final offset
int finalOffset = correctOffset(this.endPosition);
offsetAtt.setOffset(finalOffset, finalOffset);
}
}
IKAnalyzer 文件
package org.wltea.analyzer.lucene;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.Tokenizer;
/**
* IK分词器,Lucene Analyzer接口实现
* 兼容Lucene 4.0版本
*/
public final class IKAnalyzer extends Analyzer {
private boolean useSmart;
public boolean useSmart() {
return useSmart;
}
public void setUseSmart(boolean useSmart) {
this.useSmart = useSmart;
}
/**
* IK分词器Lucene Analyzer接口实现类
*
* 默认细粒度切分算法
*/
public IKAnalyzer() {
this(false);
}
/**
* IK分词器Lucene Analyzer接口实现类
*
* @param useSmart 当为true时,分词器进行智能切分
*/
public IKAnalyzer(boolean useSmart) {
super();
this.useSmart = useSmart;
}
/**
* 重载Analyzer接口,构造分词组件
*/
@Override
protected TokenStreamComponents createComponents(String fieldName) { //删除 , final Reader in
Tokenizer _IKTokenizer = new IKTokenizer(this.useSmart()); //删除 in,
return new TokenStreamComponents(_IKTokenizer);
}
}
修改完成后找到 ikanalyzer-2012_u6.jar 所在位置,替换里面的IKTokenizer 和 IKAnalyzer 文件
在 opt 中创建一个文件 (mkdir ika) 放入 ikanalyzer-2012_u6.jar 文件
进入容器:docker exec -it my_solr bash
find -name lib
server/solr-webapp/webapp/WEB-INF/lib 里面装着所有solr启动的jar包
退到 进入 index.html 里面就是Solr 页面
退出容器.
拷贝:docker cp ./ikanalyzer-2012_u6.jar my_solr:/opt/solr/server/solr-webapp/webapp/WEB-INF/lib
关闭:docker stop my_solr
启动:docker start my_solr
只是丢了一个架包进去,还是没有用,要做配置:类型
进入 :
docker exec -it my_solr bash
进入core对应的目录:cd /opt/solr/server/solr/mycore
进入:cd conf
(/opt/solr/server/solr/mycore/conf/managed-schema)
里面的managed-schema
用来定义 分词器 和 数据类型的
修改:docker cp my_solr:/opt/solr/server/solr/mycore/conf/managed-schema ./ //先拷贝,然后去notepad++进行修改
<!--定义数据类型-->
<!--定义了一个text_ik的数据类型-->
<fieldType name="text_ik" class="solr.TextField" >
<!--在建索引的时候使用IK分词器.-->
<analyzer type="index" isMaxWordLength="false" class="org.wltea.analyzer.lucene.IKAnalyzer"/>
<!--在搜索的时候也使用IK分词器-->
<analyzer type="query" isMaxWordLength="true" class="org.wltea.analyzer.lucene.IKAnalyzer"/>
</fieldType>
<!--定义动态字段-->
<!--将来要是有字段的名称有ik结尾,就调用text_ik这个数据类型
indexed="true"表示是否建反向索引,如果是false就不会创建反向索引了,不创建怎么调用ik分词器.
-->
<dynamicField name="*_ik" type="text_ik" indexed="true" stored="true"/>
修改完后先停掉容器 docker stop my_solr
,再进行拷贝: docker cp ./managed-schema my_solr:/opt/solr/server/solr/mycore/conf/
启动容器:docker start my_solr
进入:http://192.168.126.131:8983/solr/#/mycore/documents 进行测试。
提交:
{
"id":"3",
"title_ik":"我爱中国",
"content_ik":"中国地大物博"
}
搜索:
再比如搜索: title_ik:我喜欢中国 也是可以搜索出来的.
但是 title_ik:中 就不行了(中是没有意义的,所有不会有结果的).
数据库数据迁移solr
进入容器:docker exec -it my_solr bash
拷贝支持导入的jar包:
cp /opt/solr/dist/solr-dataimporthandler-5.5.5.jar /opt/solr/server/solr-webapp/webapp/WEB-INF/lib
cp /opt/solr/dist/solr-dataimporthandler-extras-5.5.5.jar /opt/solr/server/solr-webapp/webapp/WEB-INF/lib
退出 用 rz 命令把 mysql-connector-java-5.1.24.jar 包传入
https://mvnrepository.com/artifact/mysql/mysql-connector-java/5.1.24
拷贝:docker cp ./mysql-connector-java-5.1.24.jar my_solr:/opt/solr/server/solr-webapp/webapp/WEB-INF/lib
进入 mycore/conf 目录 新建连接数据的四要素:/opt/solr/server/solr/mycore/conf
<?xml version="1.0" encoding="UTF-8"?>
<dataConfig>
<dataSource name="source1" type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://192.168.0.197:3306/unit02" user="root" password="ps123456" batchSize="-1"/><!--batchSize="一次抓取多少行数据"-->
<document>
<entity name="class_t" pk="name" dataSource="source1"
query="select * from class_t" >
<field column="id" name="id"/>
<field column="name" name="name_ik"/>
</entity>
</document>
</dataConfig>
拷贝:docker cp ./data-config.xml my_solr:/opt/solr/server/solr/mycore/conf
修改solrconfig.xml 指定data-config.xml 文件
拷出:docker cp my_solr:/opt/solr/server/solr/mycore/conf/solrconfig.xml .
在solrconfig里面加入:
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
<lst name="defaults">
<str name="config">data-config.xml</str> <!--要指定的文件名字-->
</lst>
</requestHandler>
修改完成 再拷回:docker cp ./solrconfig.xml my_solr:/opt/solr/server/solr/mycore/conf
重启:docker restart my_solr
配置好了以后在访问核的时候 dataimport 就可以点开了 ,Entity就会有你创建的那个文件(data-config.xml)的名字(name=“meps”),自动调用select * from meps 把数据查出来
注意: 里面的 Clean 如果勾上会把之前增加的内容全部删掉(小心使用)。
Commit:自动提交。
如果遇到:
解决方法 :将 data-config.xml 里面的 encoding=“UTF-8” 里面的杠去掉 (encoding=“UTF8”).
请求了一次,抓取到了0条,跳过了0条,处理了0条
遇到这个:
- 先去查一下数据有没有进去
- 查看状态报告。(会告诉你哪错了)
表示:连数据库都没有连上 - 试一下可不可以连上 telnet 192.168.0.197 3306
- 去数据库去查看 用户是否可以远程访问。如果不能访问,就找一个可以的把用户名改掉。(Configuration Debug模式可以直接进行修改)
注意: Debug只对当前一次生效,只用来调试。
排序:sort 【id desc】。
六。SpringBoot集成Solr
添加Springboot 的 maven 依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-solr</artifactId>
</dependency>
</dependencies>
application.yml
spring:
data:
solr:
host: http://192.168.126.132:8983/solr
server:
port: 5050
**Main **
@SpringBootApplication
public class SolrMain {
@Bean
public SolrTemplate solrTemplate(SolrClient client) {
return new SolrTemplate(client);
}
public static void main(String[] args) {
SpringApplication.run(SolrMain.class, args);
}
}
控制层:
@RestController
public class SolrController {
@Autowired
private SolrTemplate solrTemplate;
@GetMapping("/queryNews")
public List<News> queryNews(String keyword) {
SimpleQuery sq = new SimpleQuery("name_ik:" + keyword);
Page<News> query = solrTemplate.query(sq, News.class);
return query.getContent();
}
}