solr安装配置使用
搜索引擎之Solr
一、solr简介
Solr 是Apache下的一个顶级开源项目,采用Java开发,它是基于Lucene的全文搜索服务器。Solr提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展,并对索引、搜索性能进行了优化。
Solr是一个全文检索服务器,只需要进行配置就可以实现全文检索服务。
Solr的特性包括:
- 高级的全文搜索功能
- 专为高通量的网络流量进行的优化
- 基于开放接口(XML和HTTP)的标准
- 综合的HTML管理界面
- 可伸缩性-能够有效地复制到另外一个Solr搜索服务器
- 使用XML配置达到灵活性和适配性
- 可扩展的插件体系
Solr使用Lucene并且扩展了它!
- 一个真正的拥有动态域(Dynamic Field)和唯一键(Unique Key)的数据模式(Data Schema)
- 对Lucene查询语言的强大扩展!
- 支持对结果进行动态的分组和过滤
- 高级的,可配置的文本分析
- 高度可配置和可扩展的缓存机制
- 性能优化
- 支持通过XML进行外部配置
- 拥有一个管理界面
- 可监控的日志
- 支持高速增量式更新(Fast incremental Updates)和快照发布(Snapshot Distribution)
Schema(模式)
- 定义域类型和文档的域
- 能够驱动智能处理
- 声明式的Lucene分析器规范
- 动态域能够随时增加域
- 拷贝域功能允许对一个域进行多种方式的索引,或者将多个域联合成一个可搜索的域
- 显式类型能够减少对域类型的猜测
- 能够使用外部的基于文件的终止词列表,同义词列表和保护词列表的配置
查询
- 拥有可配置响应格式(XML/XSLT,JSON,Python,Ruby)的HTTP接口
- 高亮的上下文搜索结果
- 基于域值和显式查询的片段式搜索(Faceted Search)
- 对查询语言增加了排序规范
- 常量的打分范围(Constant scoring range)和前缀式查询-没有idf,coord,或者lengthNorm因子,对查询匹配的词没有数量限制
- 函数查询(Function Query)-通过关于一个域的数值或顺序的函数对打分进行影响
- 性能优化
核心
- 可插拔的查询句柄(Query Handler)和可扩展的XML数据格式
- 使用唯一键的域能够增强文档唯一性
- 能够高效地进行批量更新和删除
- 用户可配置的文档索引变化触发器(命令)
- 并发控制的搜索器
- 能够正确处理数字类型,从而能够进行排序和范围搜索
- 能够控制缺失排序域的文档
- 支持搜索结果的动态分组
缓存
- 可配置的查询结果,过滤器,和文档缓存实例
- 可插拔的缓存实现
- 后台缓存热启:当一个新的搜索器被打开时,可配置的搜索将它热启,避免第一个结果慢下来,当热启时,当前搜索器处理目前的请求(???)。
- 后台自动热启:当前搜索器缓存中最常访问的项目在新的搜索器中再次生成,能够在索引器和搜索器变化的时候高速缓存常查询的结果
- 快速和小的过滤器实现
- 支持自动热启的用户级别的缓存
二、Windows下安装 solr
https://mirrors.cnnic.cn/apache/lucene/solr/
3.安装配置Apache Solr
(1)解压
在
bin
目录下运行命令:
solr.cmd start –p 8983
默认是
8983
(注意依赖
java
环境变量)
Solr.cmd stop –p 8983
关闭
Solr.cmd restart –p 8983
重启
(
14
)运行
http://localhost:8983
(
15
)安装成功!
(16) 创建core1
第一次创建会提示创建失败但是会创建出对应路径文件夹
需要复制配置文件到创建的目录下,复制过程如下:
将D:\开发软件\solr-8.0.0\server\solr\configsets\_default\conf
conf目录复制到core1下
再次创建core1成功
三、核心配置文件介绍
1. managed-shema配置文件
默认加载的是managed-shema,可以在solrconfig.xml中,添加如下语句,然后使用自定义的schema.xml,然后就使用默认的也是可以的。
<schemaFactory class="ManagedIndexSchemaFactory">
<bool name="mutable">true</bool>
<str name="managedSchemaResourceName"> schema.xml </str>
</schemaFactory>
然后在同级目录下创建schema.xml
managed-shema是什么?
managed-shema 是用来定义索引数据中的域的,包括域名称,域类型,域是否索引,是否分词,是否存储等等。
managed-shema 配置文件的根元素就是 schema, 有个 name 属性, name 属性值可以随便配
schema 根元素:
<schema name="example" version="1.6">
schema 子元素:
<field> 表示字段域
<field name="name" type="text_general" indexed="true" stored="true"/>
<fieldType> 用来定义域的类型
<fieldType name="string" class="solr.StrField" sortMissingLast="true" />
< dynamicField > 动态的字段域,可以使用匹配符
<dynamicField name="*_s" type="string" indexed="true" stored="true" />
<copyField> 赋值另一个域的信息
<copyField source="author" dest="author_s"/>
1.1 field 元素
name: 表示域的名称,是强制必须有的属性
type: 域类型的名称,与 fieldType 元素的 name 属性值对应,也是强制必须有的属性,不可省略
indexed: true 即表示需要对该域进行索引,一般如果你需要在该域上进行查询或排序时,则需要配置为 true, 默认值为 false
stored: 表示是否需要把域值存储到硬盘上,方便你后续查询时能再次提取出来原样显示给用户
multiValued: 表示这个域是否可以存储多个值,若设置为 true, 即表示这是一个多值域
docValues: 表示此域是否需要添加一个 docValues 域,这对 facet 查询, group 分组,排序, function 查询有好处,尽管这个属性不是必须的,但他能加快索引数据加载,对 NRT 近实时搜索比较友好,且更节省内存,但它也有一些限制,比如当前docValues 域只支持 strField,UUIDField,Trie*Field 等域,且要求域的域值是单值不能是多值域
omitNorms: 此属性若设置为 true ,即表示将忽略域值的长度标准化,忽略在索引过程中对当前域的权重设置,且会节省内存。只有全文本域或者你需要在索引创建过程中设置域的权重时才需要把这个值设为 false, 对于基本数据类型且不分词的域如intFeild,longField,StrField 等默认此属性值就是 true, 否则默认就是 false.
termVectors: 设置为 true 即表示需要为该 field 存储项向量信息,当你需要MoreLikeThis 功能时,则需要将此属性值设为 true ,这样会带来一些性能提升。
termPositions: 是否存储 Term 的起始位置信息,这会增大索引的体积,但高亮功能需要依赖此项设置,否则无法高亮
termOffsets: 表示是否存储索引的位置偏移量,高亮功能需要此项配置,当你使用SpanQuery 时,此项配置会影响匹配的结果集
field 里还有两个比较难理解的域,是 Solr 扩展的,在 Lucene 中没有的概念,即dynamicField 动态域和 copyField 复制域:
1.2 dynamicField 动态域
动态域的属性配置跟普通的 field 差不多就不多说了,唯一有点区别就是 name 的属性值,可以用通配符,这样就可以模糊匹配多个域啦,这样设计的目的就是不用频繁的去修改我们的 managed-shema中的 field 配置去增加 field 域啦,比如之前有个 link_s域,某一天你想再增加一个 url_s 域,那你就需要去修改 managed-shema 配置文件,由于managed-shema修改过后需要重启 才能生效,重启即意味着程序的中断,这往往是不可接受的。所以引入动态域来避免频繁添加修改域,但前提是你的域需要符合你提前定义的动态域的域名称命名规则哦。
1.3 copyField赋值域
复制域即表示把某个域的值复制到一个目标域上面,那如果把多个域的值复制到一个目标域上面呢,你可以进行多次复制,体现到 XML 配置上就是类似这样的配置:
<copyField source="title" dest="text"/>
<copyField source="body" dest="text"/>
如上配置就表示把 title 和 body 这两个域的值全部复制到 text 这个新域上面,唯一要注意的是,如果你只是复制单个域,那么如果你被复制域本身就是多值域,那么目标域也是多值域,这毋庸置疑,那如果你复制的是多个域,只要其中有一个域是多值域,那么目标域就一定是多值域,这点一定要谨记。
四、solr后台管理功能介绍
1.目录
2.使用Documents功能导入数据
a. /update:表示:添加和修改。如果ID存在则修改,不存在则添加
b.可以指定需要添加的数据文件的类型
c.添加时候的json的key不能随便写。必须要在managed-shema配置文件中添加
d:该界面还有删除索引的功能,文档的类型选择为 xml类型。
如下:删除id为123的索引
<delete><query>id:123</query></delete>;
commit;
也可以删除所有的索引
<delete><query>*:*</query></delete>
<commit/>
3.使用query查询数据
a.根据设置的添加查询需要的内容,如果不指定则按照默认的参数查询
b.常用参数介绍
q - 查询字符串,必须的。
fl - 指定返回那些字段内容,用逗号或空格分隔多个。
start - 返回第一条记录在完整找到结果中的偏移位置,0开始。
rows - 指定返回结果最多有多少条记录,配合start来实现分页。
sort - 排序,示例:(inStock desc, price asc)表示先 “inStock”降序, 再 “price”升序,默认是相关性降序。
wt - (writer type)指定输出格式,可以有 xml, json, php, phps。
fq - (filter query )过虑查询,作用:在q查询符合结果中同时是fq查询符合的,例如:q=mm&fq=date_time:[20081001 TO 20091031],找关键字mm,并且date_time是20081001到20091031之间的。官方文档:http://wiki.apache.org/solr/CommonQueryParameters
price_d:[100 TO 3000] 价格在100到3000之间
c.不常用参数介绍
q.op - 覆盖managed-shema的defaultOperator(有空格时用“AND”还是用“OR”操作逻辑),一般默认指定 “OR”
df - 默认的查询字段,一般默认指定
qt – (query type)指定那个类型来处理查询请求,一般不用指定,默认是standard。
indent - 返回的结果是否缩进,默认关闭,用 indent=true|on 开启,一般调试json,php,phps,ruby输出才有必要用这个参数。
version - 查询语法的版本,建议不使用它,由服务器指定默认值。
4.使用DataImport导入数据
a.该选项默认是空的,需要安装插件才可以使用
b.插件安装步骤
步骤1:添加如下jar包到 \webapps\solr\WEB-INF\lib下面
solr-dataimporthandler-8.0.0.jar(solr-8.0.0\dist下面找)
solr-dataimporthandler-8.0.0.jar
mysql-connector-java-5.1.46.jar(自己下载)
步骤二:修改solr_home/core1/conf/managed-shema配置文件,添加对应的字段信息
PS:可以申明添加对应的字段,也可以使用动态域里面的信息 *_s 等
步骤三:修改solr_home/core1/conf/solrconfig.xml配置文件,在配置文件添加如下节点
<!-- import data-->
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
<lst name="defaults">
<str name="config">data-config.xml</str>
</lst>
</requestHandler>
步骤四:在同级目录下创建 data-config.xml配置文件,添加如下内容:
<?xml version="1.0" encoding="UTF-8"?>
<dataConfig>
<dataSource type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/ygtest" user="root" password="root"/>
<document>
<entity name="data" query="SELECT * from tb_item">
<field column="id" name=" item_id" />
<field column="title" name="item_title" />
<field column="sell_point" name="item_sell_point" />
<field column="price" name="item_price" />
<field column="num" name="item_num" />
<field column="status" name="item_status" />
</entity>
</document>
</dataConfig>
步骤五:重启solr服务器,重新访问。可以执行导入操作
测试查询:
五、安装中文分词器
需要的文件
安装步骤
第一步:配置IKAnalyzer的jar包
Ik分词器官网是谷歌的,
Ik-Analyzer 下载
GoogleCode 开源项目 :http://code.google.com/p/ik-analyzer/
开发包下载地址:https://code.google.com/archive/p/ik-analyzer/downloads------需要翻墙
这里提供一个别人提供的下载地址
https://files.cnblogs.com/files/ITDreamer/ikanalyzer-solr6.5.zip
然后将解压出来的两个jar包放到以下路径:
其它的三个文件放到以下路径:
如果没有classes文件夹就创建一个
第二步:IKAnalyzer的配置文件
编辑以下路径的managed-schema文件
第三步:修改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>
到这里ik分词器就配置好了,如果需要设置扩展字典和扩展停止字典,只需要编辑下列路径的ext(扩展字典)和stopword(扩展停止字典)文件就行了
第四步:设置业务系统Field
设置业务系统Field
<field name="item_id" type="pint" indexed="false" stored="false"/> <field name="item_title" type="text_ik" indexed="true" stored="true"/> <field name="item_sell_point" type="text_ik" indexed="true" stored="true"/> <field name="item_price" type="pint" indexed="false" stored="false" /> <field name="item_num" type="pint" indexed="false" stored="false" /> <field name="item_status" type="pint" indexed="false" stored="false" />
<field name="item_keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/> <copyField source="item_title" dest="item_keywords"/> <copyField source="item_sell_point" dest="item_keywords"/> |
安装分词器之后,需要把之前的数据重新添加
六、solrJ介绍
1.solrJ简介
使用SolrJ操作Solr会比利用httpClient来操作Solr要简单。SolrJ是封装了httpClient方法,来操作solr的API的。SolrJ底层还是通过使用httpClient中的方法来完成Solr的操作。
solrJ主要官方文档:http://lucene.apache.org/solr/5_5_4/solr-solrj/index.html
https://wiki.apache.org/solr/CommonQueryParameters
2.solrJ的使用
需求分析
http形式的服务。对外提供搜索服务是一个get形式的服务。调用此服务时需要查询条件,分页条件可以使用page(要显示第几页)、rows(每页显示的记录数)。返回一个json格式的数据。可以使用实体类包装一个商品列表转换成json。
请求的url: /getItems/ {查询条件}/ {page}/ {rows}
返回的结果:包装商品列表;并且查询条件需要高亮显示
a.创建springmvc项目导入相应的jar包
<properties>
<spring.version>4.0.5.RELEASE</spring.version>
<log4j.version>1.2.12</log4j.version>
</properties>
<dependencies>
<!-- 添加Spring依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring webmvc相关jar -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- alibaba fastjson 格式化对 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.41</version>
</dependency>
<!-- logback start -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- 添加servlet3.0核心包 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
<!--solr客户端solrj的依赖 -->
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
<version>4.10.1</version>
</dependency>
</dependencies>
b 创建solr配置文件solr.properties
solr.Url=http://127.0.0.1:8983/solr/myCore
solr.maxRetries=2
solr.connectionTimeout=5000
c创建spring核心配置文件applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
">
<!-- 1.配置solr文件 -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:solr.properties"/>
</bean>
<!--定义solr的server-->
<bean id="httpSolrServer" class="org.apache.solr.client.solrj.impl.HttpSolrServer">
<constructor-arg index="0" value="${solr.Url}"/>
<!-- 设置响应解析器 -->
<property name="parser">
<bean class="org.apache.solr.client.solrj.impl.XMLResponseParser"/>
</property>
<!-- 设置重试次数-->
<property name="maxRetries" value="${solr.maxRetries}"/>
<!-- 建立连接的最长时间 -->
<property name="connectionTimeout" value="${solr.connectionTimeout}"/>
</bean>
</beans>
d 创建springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
<!-- 扫描controller(controller层注入) -->
<context:component-scan base-package="com.sykj.control"
use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean
class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json" />
<property name="features">
<array>
<value>WriteMapNullValue</value>
<value>WriteDateUseDateFormat</value>
</array>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<mvc:default-servlet-handler />
</beans>
e 创建日志文件log4j.properties
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
f 修改web.xml添加spring监听器,配置springmvc
<!-- 读取spring配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<filter>
<filter-name>encode</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encode</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
g.创建实体类并映射到solr字段
创建实体类,注意字段上面需要使用@Field注解
public class Item {
@Field(value="id")
private String solrId ;
@Field(value="_item_id_i")
private Integer id ;
@Field(value="item_title")
private String title;//商品标题
@Field(value="item_sell_point")
private String sellPoint;//商品卖点
@Field(value="item_price_i")
private Integer price;//商品价格,单位为:分
@Field(value="item_num_i")
private Integer num;//库存数量
@Field(value="item_status_i")
private Integer status;//商品状态,1-正常,2-下架,3-删除
}
h.创建control
package com.sykj.control;
import java.util.List;
import java.util.Map;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.sykj.bean.Item;
@RestController
public class ItemControl {
@Autowired
private HttpSolrServer httpSolrServer;
/**
* 根据关键字查询商品
* @param keyWords
* @return
* @throws SolrServerException
*/
@RequestMapping(value="/getItems/{keyWords}/{page}/{rows}",method=RequestMethod.GET)
public List<Item> getItemByKeyWords(@PathVariable(value="keyWords") String keyWords,
@PathVariable(value="page") Integer page,
@PathVariable(value="rows") Integer rows
) throws SolrServerException{
//常规做法
//SELECT * FROM tb_item WHERE title LIKE "%AK47%"
//如果keyWords是联通AK47呢,我又想查出带AK47的商品也想查出有关联通的商品
//常规做法办不到
SolrQuery solrQuery = new SolrQuery();
solrQuery.setStart((page-1)*rows);
solrQuery.setRows(rows);
solrQuery.set("q", "item_keywords:"+keyWords);
solrQuery.setHighlight(true);
solrQuery.addHighlightField("item_title");
solrQuery.addHighlightField("item_sell_point");
solrQuery.setHighlightSimplePre("<span style=\"color:red\">");
solrQuery.setHighlightSimplePost("</span>");
// // 调用查询 返回结果
QueryResponse query = httpSolrServer.query(solrQuery);
long numFound = query.getResults().getNumFound();//获取总数
Map<String, Map<String, List<String>>> highlighting = query.getHighlighting();
List<Item> beans = query.getBeans(Item.class);
for (Item item : beans) {
List<String> list = highlighting.get(item.getSolrId()).get("item_title");
if(list!=null&&list.size()>0)
item.setTitle(list.get(0));
list = highlighting.get(item.getSolrId()).get("item_sell_point");
if(list!=null&&list.size()>0)
item.setSellPoint(list.get(0));
}
return beans;
}
}