一 。solr简介
solr是以lucene为内核开发的企业级搜索应用 应用程序可以通过http请求方式来提交索引,查询索引,提供了比lucene更丰富的查询语言,是一个高性能,高可用环境全文搜索引擎。
文章表
TYPE | ASCII | HTML |
文章id | 文章标题 | 文章内容 |
1 | 我爱中国 | 中国地大物博 |
2 | 香港是中国一部分 | 香港的英文是hongkong |
倒排索引
索引 | 文章id |
我 | 1 |
我 | 1 |
中国 | 1,2 |
香港 | 2 |
是 | 2 |
一部分 | 2 |
二。分词器
将中文拆分成有意义的词
常用的IK分词器,庖丁解牛分词器。
三。lucene
lucene是一个将text数据类型,分词建立索引的一个库,不适合企业级使用。
企业级考虑高可用问题。
solr是一个企业级应用的搜索引擎。
支持使用json格式提交数据。
json格式:
[] 代表数组
{} 对象(文档 document)
键值对 属性
{
id:1
hobby:[“篮球”,“上厕所”]
tt:{
}
}
数据库表结构:
文章id 文章标题 文章内容
1 我爱中国 中国地大物博
2 香港是中国一部分 香港的英文是hongkong
模拟json
[
{
id:1,
title:‘我爱中国’,
content:‘中国地大物博’
},
{
id:2,
title:‘香港是中国一部分’,
content:‘香港的英文是hongkong’
}
]
四 。solr安装
核(core):是用于存储json格式的数据,等价于mysql中数据库的概念
文档:一个json对象就是一个文档 相同属性的json数组集合就是一个表
docker安装solr:
docker run --name my_solr -id --net host -t solr:5.5.5
检测端口:
netstat -aon | grep 8983
yum -y install net-tools telnet
创建完成后提示:
[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":2137},
"core":"mycore"}
五。solr搜索
假设提交:
{“id”:“change.me”,“title”:“change.me”}
q表示安装什么字段来搜索
字段名:值 (where 列名=值)
支持or 和and语法
比如 i:1 and j:2
比如 i[1 to 10]
模拟数据:
{
"id":"1",
"title":"我爱中国",
"content":"中国地大物博"
}
六。solr中文分词器配置
生成ik支持5.5.5的ikjar包
jar拷贝到 /opt/solr/server/solr-webapp/webapp/WEB-INF/lib
进入core对应的目录
/opt/solr/server/solr/mycore
类型定义文件目录
/opt/solr/server/solr/mycore/conf/managed-schema
修改文件 managed-schema
定义分词器数据类型:
<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>
定义动态字段:
<dynamicField name="*_ik" type="text_ik" indexed="true" stored="true"/>
插入json:
{
"id":"3",
"title_ik":"我爱中国",
"content_ik":"中国地大物博"
}
七。数据库数据迁移solr
拷贝支持导入的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
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/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.1.3:3306/test" user="root" password="123456" batchSize="-1" />
<document>
<entity name="book" pk="newid" dataSource="source1"
query="select * from mynew" >
<field column="newid" name=""/>
<field column="newtitle" name="title_ik"/>
</entity>
</document>
</dataConfig>
docker cp ./data-c.xml my_solr:/opt/solr/server/solr/mycore/conf
修改solrconfig.xml 指定data-c.xml文件
docker cp my_solr:/opt/solr/server/solr/mycore/conf/solrconfig.xml .
内容:
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
<lst name="defaults">
<str name="config">data-c.xml</str>
</lst>
</requestHandler>
docker cp ./solrconfig.xml my_solr:/opt/solr/server/solr/mycore/conf
所有实际步骤如下:
》配置中文分词器
默认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 引用lucene5 和ik
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>com.janeluo</groupId>
<artifactId>ikanalyzer</artifactId>
<version>2012_u6</version>
<exclusions>
<exclusion>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
</exclusion>
</exclusions>
</dependency>
在项目中 添加完整的包名和类名 和 ik中一致 拷贝源代码
代码修改对应的方法即可
IKAnalyzer
package org.wltea.analyzer.lucene;
import java.io.Reader;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.Tokenizer;
public final class IKAnalyzer extends Analyzer {
private boolean useSmart;
public boolean useSmart() {
return useSmart;
}
public void setUseSmart(boolean useSmart) {
this.useSmart = useSmart;
}
public IKAnalyzer() {
this(false);
}
public IKAnalyzer(boolean useSmart) {
super();
this.useSmart = useSmart;
}
/**这里就去掉了 Reader的一个参数
*/
@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 java.io.Reader;
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;
public final class IKTokenizer extends Tokenizer {
private IKSegmenter _IKImplement;
private final CharTermAttribute termAtt;
private final OffsetAttribute offsetAtt;
private final TypeAttribute typeAtt;
private int endPosition;
//去掉了其中Reader的第一个构造参数
public IKTokenizer(boolean useSmart) {
super();//去掉super中的构造参数
offsetAtt = addAttribute(OffsetAttribute.class);
termAtt = addAttribute(CharTermAttribute.class);
typeAtt = addAttribute(TypeAttribute.class);
_IKImplement = new IKSegmenter(input, useSmart);
}
@Override
public boolean incrementToken() throws IOException {
clearAttributes();
Lexeme nextLexeme = _IKImplement.next();
if (nextLexeme != null) {
termAtt.append(nextLexeme.getLexemeText());
termAtt.setLength(nextLexeme.getLength());
offsetAtt.setOffset(nextLexeme.getBeginPosition(), nextLexeme.getEndPosition());
endPosition = nextLexeme.getEndPosition();
typeAtt.setType(nextLexeme.getLexemeTypeString());
return true;
}
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);
}
}
将编译好的class文件替换原始jar包即可
将solrhome下 配置文件managed-schema 添加一个字段类型 使用ik分词器
<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>
不能修改 StrField 不支持自定义分词器
<fieldType name="string" class="solr.StrField" sortMissingLast="true" >
</fieldType>
然后将对应需要进行中文分词的字段使用 text_ik该字段类型 比如
<dynamicField name="*_s" type="text_ik" indexed="true" stored="true" />
重启 或者 cloud环境下重新生成collection 插入数据即可实现中文词通过某些中文关键字搜索
》初始全量导入数据库数据
假设存在表 news表示 其中 有以下数据:
进入solr所在服务器 搜索 dataimport相关jar包
solr@localhost:/opt/solr$ find / -name *import*.jar
/opt/solr/dist/solr-dataimporthandler-5.5.5.jar
/opt/solr/dist/solr-dataimporthandler-extras-5.5.5.jar
将这两个jar包拷贝到 solr启动应用 webapp/lib目录下
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
同时将mysql的驱动包 丢到该目录下
编辑core的conf/solrconfig.xml 添加
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
<lst name="defaults">
<str name="config">data-config.xml</str>
</lst>
</requestHandler>
在solrconfig.xml同一目录下 添加 data-config.xml(配置连接的数据库以及查询的sql语句 )
<?xml version="1.0" encoding="UTF-8"?>
<dataConfig>
<dataSource name="source1" type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://192.168.1.3:3306/test" user="root" password="123456" batchSize="-1" />
<document>
<entity name="book" pk="newid" dataSource="source1"
query="select * from mynew" >
<field column="newid" name=""/>
<field column="newtitle" name="title_ik"/>
</entity>
</document>
</dataConfig>
访问solrweb管理界面 http://ip:
solr客户端 实例演示
solr提供的solrj java客户端可以使用java来添加和查询索引
使用maven引入solrj的依赖库
<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>
SolrMain
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 {
public static void main(String[] args) {
SpringApplication.run(SolrMain.class, args);
}
}
Emp
import org.apache.solr.client.solrj.beans.Field;
import org.springframework.data.solr.core.mapping.SolrDocument;
@SolrDocument(solrCoreName="mycore")
public class Emp {
private String id;
@Field("ename_ik")
private String ename;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
}
SolrController
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.Emp;
@RestController
public class SolrController {
@Autowired
private SolrTemplate st;
@GetMapping("/queryEmp")
public List<Emp> queryEmp(String keyword){
SimpleQuery sq=new SimpleQuery("ename_ik:"+keyword);
Page<Emp> query = st.query(sq, Emp.class);
return query.getContent();
}
}
application.yml配置文件
spring:
data:
solr:
host: http://192.168.67.128:8983/solr
server:
port: 8888
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js">
</script>
<script>
function query(){
$.ajax({
url:'queryEmp',
dataType:'json',
data:'keyword='+$("#myKeyWord").val(),
type:'get',
success:function(r){
$("#myEmp").html(JSON.stringify(r));
}
});
}
</script>
</head>
<body>
新闻:<input id="myKeyWord" type="text" name="keyword"><button onclick="query()">搜索</button>
<div id="myEmp">
</div>
</body>
</html>