solr搜索应用

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条

遇到这个:

  1. 先去查一下数据有没有进去
  2. 查看状态报告。(会告诉你哪错了)
    在这里插入图片描述
    表示:连数据库都没有连上
  3. 试一下可不可以连上 telnet 192.168.0.197 3306
  4. 去数据库去查看 用户是否可以远程访问。如果不能访问,就找一个可以的把用户名改掉。(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();
	}
}
1 概述 4 1.1 企业搜索引擎方案选型 4 1.2 Solr的特性 4 1.2.1 Solr使用Lucene并且进行了扩展 4 1.2.2 Schema(模式) 5 1.2.3 查询 5 1.2.4 核心 5 1.2.5 缓存 5 1.2.6 复制 6 1.2.7 管理接口 6 1.3 Solr服务原理 6 1.3.1 索引 6 1.3.2 搜索 7 1.4 源码结构 8 1.4.1 目录结构说明 8 1.4.2 Solr home说明 9 1.4.3 solr的各包的说明 10 1.5 版本说明 11 1.5.1 1.3版本 11 1.5.2 1.4版本 12 1.6 分布式和复制 Solr 架构 13 2 Solr的安装与配置 13 2.1 在Tomcat下Solr安装 13 2.1.1 安装准备 13 2.1.2 安装过程 14 2.1.3 验证安装 15 2.2 中文分词配置 15 2.2.1 mmseg4j 15 2.2.2 paoding 19 2.3 多核(MultiCore)配置 22 2.3.1 MultiCore的配置方法 22 2.3.2 为何使用多core ? 23 2.4 配置文件说明 23 2.4.1 schema.xml 24 2.4.2 solrconfig.xml 25 3 Solr应用 29 3.1 SOLR应用概述 29 3.1.1 Solr应用模式 29 3.1.2 SOLR的使用过程说明 30 3.2 一个简单的例子 30 3.2.1 Solr Schema 设计 30 3.2.2 构建索引 30 3.2.3 搜索测试 31 3.3 搜索引擎的规划设计 32 3.3.1 定义业务模型 32 3.3.2 定制索引服务 34 3.3.3 定制搜索服务 34 3.4 搜索引擎配置 34 3.4.1 Solr Schema 设计(如何定制索引的结构?) 34 3.5 如何进行索引操作? 36 3.5.1 基本索引操作 36 3.5.2 批量索引操作 37 3.6 如何进行搜索 39 3.6.1 搜索语法 39 3.6.2 排序 42 3.6.3 字段增加权重 42 3.6.4 Solr分词器、过滤器、分析器 42 3.6.5 Solr高亮使用 46 4 SolrJ的用法 46 4.1 搜索接口的调用实例 46 4.2 Solrj的使用说明 47 4.2.1 Adding Data to Solr 47 4.2.2 Directly adding POJOs to Solr 49 4.2.3 Reading Data from Solr 51 4.3 创建查询 51 4.4 使用 SolrJ 创建索引 52 4.5 Solrj包的结构说明 53 4.5.1 CommonsHttpSolrServer 53 4.5.2 Setting XMLResponseParser 53 4.5.3 Changing other Connection Settings 53 4.5.4 EmbeddedSolrServer 54 5 Solr的实际应用测试报告 54 5.1 线下压力测试报告 54 5.2 线上环境运行报告 54 6 solr性能调优 55 6.1 Schema Design Considerations 55 6.1.1 indexed fields 55 6.1.2 stored fields 55 6.2 Configuration Considerations 55 6.2.1 mergeFactor 55 6.2.2 mergeFactor Tradeoffs 56 6.3 Cache autoWarm Count Considerations 56 6.4 Cache hit rate(缓存命中率) 56 6.5 Explicit Warming of Sort Fields 56 6.6 Optimization Considerations 56 6.7 Updates and Commit Frequency Tradeoffs 56 6.8 Query Response Compression 57 6.9 Embedded vs HTTP Post 57 6.10 RAM Usage Considerations(内存方面的考虑) 57 6.10.1 OutOfMemoryErrors 57 6.10.2 Memory allocated to the Java VM 57 7 FAQ 58 7.1 出现乱码或者查不到结果的排查方法: 58
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值