1.solr简介
solr是以lucene为内核开发的企业级搜索应用 应用程序可以通过http请求方式来提交索引,查询索引,提供了比lucene更丰富的查询语言,是一个高性能,高可用环境全文搜索引擎
2.倒排索引原理
- 文章表
id | 标题 | 内容 |
---|---|---|
1 | 我爱中国 | 中国地大物博 |
2 | 中国永存 | 遍地都是精英 |
- 倒排索引
关键词 文章号
我 1
爱 1
中国 1,2
永存 2
3.分词器
- 将中文拆分成有意义的词
- 常用的IK分词器,庖丁解牛分词器。
4.lucene
- lucene是一个将text数据类型,分词建立索引的一个库,不适合企业级使用。
企业级考虑高可用问题。 - solr是一个企业级应用的搜索引擎,支持使用json格式提交数据。
- json格式:
- [] 代表数组
- {} 对象(文档 document)
- 键值对 属性
- 模拟json格式:
[
{id:1,title:'我爱中国',content:'中国地大物博'},
{id:2,title:'中国永存',content:'遍地都是精英'}
]
5.solr高可用版本参考
https://blog.csdn.net/liaomin416100569/article/details/77301756
6.solr安装
- 核(core)的概念
是用于存储json格式的数据,等价于mysql中数据库的概念
- 文档(document)
一个json对象就是一个文档 相同属性的json数组集合就是一个表
- 下载solr:5.5.5版本
docker pull solr:5.5.5
- 启动solr:5.5.5
docker run --name my_solr -id --net host -t solr:5.5.5
- 检查端口是否被占用
netstat -aon | grep 8983
下载netstat:yum -y install net-tools telnet
- 创建core
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":2137},
"core":"mycore"}
- 访问linux的ip地址:8983
例:192.168.153.133:8983
7.solr搜索
- 假设提交
{"id":"change.me","title":"change.me"}
- q表示按照什么字段来搜索
字段名:值 (where 列名=值)
支持or 和and语法
比如 i:1 and j:2
-
模拟数据
-
数据查询,现在还没做分词,必须去全词比配才有值
8.配置分词器
- 默认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)
- 创建一个maven项目
- pom.xml添加
下载ikanalyzer后默认是2.4.7版本,要替换成5.5.5版本
<!-- https://mvnrepository.com/artifact/com.janeluo/ikanalyzer -->
<dependency>
<groupId>com.janeluo</groupId>
<artifactId>ikanalyzer</artifactId>
<version>2012_u6</version>
</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>
- 重写IKAnalyzer类和IKTokenizer类
- 先创建包
org.wltea.analyzer.lucene
- 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) {
Tokenizer _IKTokenizer = new IKTokenizer(this.useSmart());
return new TokenStreamComponents(_IKTokenizer);
}
}
- 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) {
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里的IKAnalyzerClass文件和IKTokenizerClass文件替换成自己写的Class文件
替换到
-
将替换好的jar包放到linux里面
9.solr中文分词器配置
- 进入my_solr容器
docker exec -it my_solr bash
2.在当前目录下搜索lib
find . -name lib
- 查看my_solr容器jar包所在位置
cd server/solr-webapp/webapp/WEB-INF/lib
- 将linux拷贝过来的jar包拷进server/solr-webapp/webapp/WEB-INF/lib目录下
docker cp ./ikanalyzer-2012_u6.jar my_solr:/opt/solr/server/solr-webapp/webapp/WEB-INF/lib/
- 重启docker
关闭:docker stop my_solr
开启:docker start my_solr
- 拷贝my_solr容器里的managed-schema文件到linux /opt/ika/目录下去修改,配置文分词
docker cp my_solr:/opt/solr/server/solr/mycore/conf/managed-schema /opt/ika/
- 打开 managed-schema文件
<uniqueKey>标签表示主键
<fieldType name="stirng">设置数据类型
<dynamicField>动态字段
- 配置搜索引擎
<fieldType name="text_ik" class="solr.TextField" >
<analyzer type="index" isMaxWordLength="false" class="org.wltea.analyzer.lucene.IKAnalyzer"/>
<analyzer type="query" isMaxWordLength="true" class="org.wltea.analyzer.lucene.IKAnalyzer"/>
</fieldType>
indexed代表倒排索引
stored代表存储数据
- 关掉my_solr容器
docker stop my_solr
- 将修改好的managed-schema文件拷贝my_solr:/opt/solr/server/solr/mycore/conf/目录下
docker cp ./managed-schema my_solr:/opt/solr/server/solr/mycore/conf/
- 重启my_solr容器
docker start my_solr
- 进行引擎搜索测试
-
图1
-
图2
10.数据库数据迁移solr
- 拷贝支持导入的jar三个
- 进入my_solr容器拷贝2个jar
docker exec -it my_solr bash
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
- 第3个数据库驱动类,我使用的5.1.14版本
下载:https://mvnrepository.com/artifact/mysql/mysql-connector-java
将下载好的jar包放到/opt/ika目录下在拷到my_solr的/opt/solr/server/solr-webapp/webapp/WEB-INF/lib目录下
docker cp ./mysql-connector-java-5.1.14.jar my_solr:/opt/solr/server/solr-webapp/webapp/WEB-INF/lib
- 在opt/ika目录下创建data_c.xml文件配置如下
<?xml version="1.0" encoding="UTF-8"?>
<dataConfig>
<dataSource name="source1" type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://192.168.0.198:3306/unit02" user="root" password="ps123456" batchSize="-1" />
<document>
<entity name="user_t" pk="id" dataSource="source1"
query="select * from user_t" >
<field column="id" name="id"/>
<field column="names" name="names_ik"/>
</entity>
</document>
</dataConfig>
- 拷贝到my_solr:/opt/solr/server/solr/mycore/conf目录下
docker cp ./data_c.xml my_solr:/opt/solr/server/solr/mycore/conf
5.修改solrconfig.xml 指定data-c.xml文件先把solrconfig.xml文件从my_solr容器中拷贝出来,.代表当前目录
docker cp my_solr:/opt/solr/server/solr/mycore/conf/solrconfig.xml .
6.先solrconfig.xml增加内容
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
<lst name="defaults">
<str name="config">data_c.xml</str>
</lst>
</requestHandler>
- 再将solrconfig.xml拷贝到my_solr:/opt/solr/server/solr/mycore/conf目录下
docker cp ./solrconfig.xml my_solr:/opt/solr/server/solr/mycore/conf
- 重启my_solr容器
docker restart my_solr
访问页面是出现
说明:这个两个jar包没考到/opt/solr/server/solr-webapp/webapp/WEB-INF/lib目录下
-
solr-dataimporthandler-5.5.5.jar
-
solr-dataimporthandler-extras-5.5.5.jar
-
访问solrweb管理界面 http://ip:
11.使用Windows实现
- 创建SOLR Maven项目
- 引入依赖pom.xml
<!-- 集成SpringBoot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath />
</parent>
<dependencies>
<!-- 引入SpringMvc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入solr -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-solr</artifactId>
</dependency>
</dependencies>
- 配置文件application.yml
spring:
data:
solr:
#指定solr的ip端口
host: http://192.168.153.133:8983/solr
server:
#端口号
port: 8888
- 程序入口SolrMain
package cn.ps;
import org.apache.solr.client.solrj.SolrClient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.solr.core.SolrTemplate;
@SpringBootApplication
public class SolrMain {
/**
* 初始化SolrTemplate对象
* @param client
* @return
*/
@Bean
public SolrTemplate solrTemplate(SolrClient client) {
return new SolrTemplate(client);
}
public static void main(String [] args) {
SpringApplication.run(SolrMain.class, args);
}
}
- 控制层SolrController
package cn.ps.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.solr.core.SolrTemplate;
import org.springframework.data.solr.core.query.SimpleQuery;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.ps.entity.User;
@RestController
public class SolrController {
/**
* 用来操作Solr
*/
@Autowired
SolrTemplate st;
@GetMapping("user")
public List<User> queryUser(String keyWord){
//将浏览器传过来的数据赋给names_ik引擎搜索的键值
SimpleQuery sq = new SimpleQuery("names_ik:"+keyWord);
//去Solr里查询数据
Page<User> query = st.query(sq, User.class);
//返回查询出来的数据
List<User> content = query.getContent();
return content;
}
}
- 实体类User
package cn.ps.entity;
import org.apache.solr.client.solrj.beans.Field;
import org.springframework.data.solr.core.mapping.SolrDocument;
//指定Solr的核心
@SolrDocument(solrCoreName="mycore")
public class User {
private String id;
//设置引擎搜索变量names_ik
@Field("names_ik")
private String names;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getNames() {
return names;
}
public void setNames(String names) {
this.names = names;
}
}
- 页面index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="jquery-3.3.1.js"></script>
<script type="text/javascript">
function query(){
$.ajax({
url:'user',
dataType:'json',
type:'get',
data:'keyWord='+ $("#key").val(),
success:function(data){
$("#myUser").text(JSON.stringify(data))
}
})
}
</script>
</head>
<body>
故事:<input id="key" type="text" name="keyWord"><button onclick="query()" value="查询">查询</button>
<div id="myUser"></div>
</body>
</html>