solr简介
solr是基于Lucene开发的一个开元的高性能企业级搜索平台,高可靠,可拓展,可容错.提供分布式索引,索引备份,查询负载均衡等等
linux安装solr到tomcat
下载solr和tomcat压缩包,解压
tar zxf solr-4.10.3.tgz.tgz
tar zxf apache-tomcat-7.0.47.tar.gz
新建solr文件夹,将tomcat复制到solr下重命名为tomcat
mkdir solr
cp -r apache-tomcat-7.0.47 solr/tomcat
复制solr-4.10.3.war文件到solr/tomcat/webapps下重命名
cp solr-4.10.3/dist/solr-4.10.3.war solr/tomcat/webapps/solr.war
启动tomcat解压缩solr.war完成后关闭tomcat
solr/tomcat/bin/startup.sh
solr/tomcat/bin/shutdown.sh
将solr下ext文件夹复制到solr下
cp -r solr-4.10.3/example/lib/ext/* solr/tomcat/webapps/solr/WEB-INF/lib/
复制example/solr到solr下重命名,创建一个名字叫做collection1的core在solrhome下面
cp -r solr-4.10.3/example/solr solr/solrhome
修改/opt/solr/tomcat/webapps/solr/WEB-INF,填写solrhome位置
注意要把注释去掉
<env-entry>
<env-entry-name>solr/home</env-entry-name>
<env-entry-value>/opt/solr/solrhome</env-entry-value>
<env-entry-type>java.lang.String</env-entry-type>
</env-entry>
给solrhome文件夹赋权
chmod -R 777 solrhome
启动solr
solr/tomcat/bin/startup.sh
查看web服务
http://localhost:8080/solr/#/
solr测试
插入数据
image.png
查询数据
image.png
solr基础
solr core
solr中的core指的是一个单一的索引数据,索引由多个document组成,core就是多个document的打包集合.同一个core下的不同document的索引域可以不同,在solrcloud模式下,不同core可能分布在不同的机器节点上
core的目的和问题
1.solr设计多core来存放索引是为了把不相干的对象分离独立管理,这样每个对象的索引都有自己的一套Schema.xml,solrconfig.xml,彼此之间互不影响
2.分成多个core之后可以减小单个core的索引大小
3.分成多个core后跨core联合查询会影响性能
设计solr的core时需要考虑的问题
1.业务上是否有明确的划分需要独立管理
2.是否需要跨core查询,影响性能是否能够接受
3.每个索引类型的更新频率.如果频率差异大,不适合放在同一个core
4.数据是否有权限划分要求,如果有最好分core
5.每个索引类型的数据的数据量要大致在一个数量级上,如果差异大需要分core
solr设计多core的解决的问题
1.重建索引,可以单独重建某个core的索引数据,提高效率
2.配置变更影响最小化,修改某个core不影响其他core
3.索引合并和分裂,多个core可以合并和拆分
4.core热交换,方便上线
core的基本原理
core有两个元素组成, Schema.xml和solrconfig.xml,由这两个配置文件衍生出其他需要的配置文件
core的目录结构要求,Schema.xml和solrconfig.xml,必须放置在solrhome/core_name/conf下
solrhome是Solr Core的宿主目录,所有的core都要存放在solrhome下面solrhome可以通过web.xml配置
<env-entry>
<env-entry-name>solr/home</env-entry-name>
<env-entry-value>/opt/solr/solrhome</env-entry-value>
<env-entry-type>java.lang.String</env-entry-type>
</env-entry>
conf目录
currency.xml: 各种货币之间的汇率常量配置,因为schema.xml中提供了货币域currencyField,如果在schema.xml中注释掉货币域就不需要以来currency.xml了
protwords.txt: 用来配置不需要进行词还原的英文单词,如果不需要text_en这个域可以不依赖protwords.txt
schema.xml:用来定义索引数据的索引域和域类型,即用来声明需要用到哪些域和域的类型,域类型决定域中的值如何被索引,被分词,被存储
solrconfig.xml: 是solr必须的一个配置文件
stopwords.txt: 停用词
synonyms.txt: 同义词词典
data目录
用来存放索引数据和Solr日志文件,data目录不需要手动创建,可以在core创建成功后自动生成
索引数据全部在data/index下
日志数据全部在data/tlog下
创建一个新的core
cd /opt/solr/solrhome
新建一个core叫test
mkdir test
将solr自带的example下的collections下的conf模板移动到test core下
cp -r /opt/solr/solrhome/collection1/conf /opt/solr/solrhome/test
chmod -R 777 test
在web后台新建core
点击Core Admin下的Add Core
name: core的名称
instanceDir: core的根目录
dataDir: 相对于instanceDir的data目录的相对目录
config: solrconfig.xml的相对位置,相对于instanceDir下conf目录
schema: schema.xml存放路径,相对于instanceDir下conf目录
或者直接在磁盘目录新建test目录,conf目录和core.properties文件,重启solr
image.png
创建成功后会在core目录下生成core.properties , data两个目录,其中core.properties用来记录创建core时的基本信息
core的管理操作包括
unload: 卸载,将core从solr中取消注册,但是目录下会保留文件,可以随意删除
rename: 只会影响访问的url,不会修改磁盘目录上的文件名
swap: 热切换,可以将线上活跃的core替换为候补的core
reload: 重新加载,在修改了core的配置文件之后需要重新加载才能生效,这个需要一点时间,在还没加载完成时,还是使用旧的配置文件
添加数据到core
在WEB后台的Documents下实现插入数据
image.png
- Requests-Handler: 请求的url,省略了前缀,实际上是 http://localhost:8080/solr/collection1/update
- Document Type: Document类型,支持csv,xml,json
- commit within: 索引数据必须在限定的时间内提交
- Overwrite: 覆盖,是否根据uniquekey进行覆盖,如果uniquekey出现一致,就覆盖,需要在schema.xml中上设置,否则就算Overwrite设置了true也不行
- Boost: 当前添加的索引数据的权重值,默认是1
- Document(s): 要添加的索引数据,如果域值是数值或者bool,可以不用添加引号
{"id":"change.me","title":"change.me"}
一个花括号是一个document对象,id之类是索引域,后面是域值
索引域需要在schema.xml中定义好
<field name="title" type="text_general" indexed="true" stored="true" multiValued="true"/>
solr插入删除数据
DIH是Data Import Handler,是一种可配置的方式向Solr中导入数据,可以全量导入,也可以增量导入
通过WEB后台的Documents添加数据只能一条一条插入,效率太低,
json数据的批量插入
选择Document type为Solr Command
image.png
reload一下即可查到新的数据
删除core下的所有数据
image.png
solr查看数据
带引号精确查询
image.png
不带引号模糊查询,将query分词,组合条件根据schema.xml中的<solrQueryParser defaultOperator="OR"/>决定
image.png
带有特征字符的需要转义
image.png
多个query使用OR AND连接,格式需要带有空格
image.png
start和rows: 设置分页和每页数据量,默认是第0页,每页展示10条
image.png
fl: 设置field list,只展示指定的域,用空格或者逗号隔开
image.png
image.png
显示所有列和score
image.png
sort: 指定根据域进行排序,参数可选asc,desc,以空格隔开
image.png
fq: 定义一个filter query,用于对结果的进一步过滤
限制某一字段值
image.png
取反
image.png
设置某一列的限定范围
image.png
筛选某个字段不为空的数据
image.png
筛选某个字段为空, 某个字段不为空的字段
image.png
使用pysolr导入,查询,删除数据
查询数据
# 获得solr对象
solr = pysolr.Solr('http://localhost:8080/solr/collection1', timeout=10)
查询数据
hits = solr.search('*:*').hits # 总条数
res = solr.search('*:*')
res.docs
#[{'id': '10', 'title': ['"武汉奇致激光技术股份有限公司"'], '_version_': 1666200793041076224},
#{'id': '11', 'title': ['"广东佳景科技股份有限公司"'], '_version_': 1666200793042124800},
#{'id': '12', 'title': ['"武汉江通动画传媒股份有限公司"'], '_version_': 1666200793043173376}]
# 指定域
res = solr.search("ent:曙光信息产业股份有限公司")
solr.search("ent:曙光信息产业股份有限公司").hits
# 指定分页,默认一页展示10条
res = solr.search("ent:曙光信息产业股份有限公司", **{"start": 2})
res.docs
# 指定rows
res = solr.search("ent:曙光信息产业股份有限公司", **{"start": 2, "rows": 20})
res.docs
# 指定fq条件
res = solr.search('"中国银行"').hits # 3287021
# 只选择rstatus:T
res = solr.search('"中国银行"', **{"fq": "rstatus:T"}).hits # 676311
# 只选择category不为新闻
res = solr.search('"中国银行"', **{"fq": "rstatus:T AND -category:新闻"}).hits #162976, 需要加AND条件
# 对某一列进行限制范围查询
res = solr.search('*:*', **{'fq': 'batchno:[47754 TO 47755]'}) # 包含两边
res = solr.search('*:*', **{'fq': 'batchno:[47754 TO *]'}) # 查询47754之后的
# 多个组合条件的fq
# ID OR条件, 再组合category AND条件
solr.search('*:*', **{"fq": "(ID:277051303 OR ID:277051309 OR ID:277051310) AND category:公告"}).hits
增加数据
solr.add([
{
"id": "doc_2",
"title": "fantasy",
}
])
solr.commit() # commit才能生效
solr.add([{"id": "doc_3", "title": "fantasy"}])
solr.commit()
# 或者直接在add函数中指定commit=True
solr.add([{"id": "doc_10", "title": "fantasy"}], commit=True)
# 批量插入数据
word = set()
with open("data/corpus6.txt", "r", encoding="utf8") as f:
for line in f.readlines():
items = line.strip().split(" ")
for item in items:
word.add(item)
for i, j in enumerate(word):
solr.add([{"id": i, "title": j}])
if i % 100 == 0:
print(i, i / 84704)
solr.commit()
solr.commot()
删除数据
#删除id为doc_1的数据
solr.delete(id='doc_1')
solr.commit()
#删除所有数据
solr.delete(q='*:*')
solr.commit()
使用http接口查询数据
import requests
# 必须指定wt是json,默认是xml
res = requests.get("http://localhost:8080/solr/collection1/select?q=id:28364&wt=json")
res.json()
# {'responseHeader': {'status': 0,
# 'QTime': 0,
# 'params': {'q': 'id:28364', 'wt': 'json'}},
# 'response': {'numFound': 1,
# 'start': 0,
# 'docs': [{'id': '28364',
# 'ent': '上海润达医疗科技股份',
# '_version_': 1666363955258327040}]}}
# 模糊查询
res = requests.get("http://localhost:8080/solr/collection1/select?q=ent:上海润达医疗科技股份有限公司&wt=json&start=0&rows=3")
res.json()
res.json()["response"]["docs"][0]
# {'id': '50911', 'ent': '上海润达医疗科技股份有限公司', '_version_': 1666364111486713856}
# 精确查询
res = requests.get('http://localhost:8080/solr/collection1/select?q=ent:"上海润达医疗科技股份有限公司"&wt=json&start=0&rows=3')
res.json()
# {'responseHeader': {'status': 0,
# 'QTime': 1,
# 'params': {'q': 'ent:"上海润达医疗科技股份有限公司"',
# 'start': '0',
# 'rows': '3',
# 'wt': 'json'}},
# 'response': {'numFound': 1,
# 'start': 0,
# 'docs': [{'id': '50911',
# 'ent': '上海润达医疗科技股份有限公司',
# '_version_': 1666364111486713856}]}}
solr索引
Lucene索引原理
Lucene索引结构的几个概念
索引index:是多个文档的集合,同一个索引文件下的所有文件构成一个Lucene索引
文档Document:是构成索引的基本单位,新添加的文档保存在一个新生成的段文件中
域Field:一个文档是多个域的集合,一个域好比是文档的一个字段,不同的域可以有不同的存储方式
段Segment:新增文档就会生成一个新段,也会出发段的合并,一个索引可以包含多个段文件,合并段有助于提升索引性能,索引可以由多个子索引组成,子索引就是段.
词Term:每个域值经过分词之后的每一个词项就是词Term,他是索引中的最小单位,两个不同域中的相同词被认为是不同的词
Lucene倒排索引结构
book -> 0, 1, [5, 8], [0.9]
整个信息称为postinglist,0, 1, [5, 8], [0.9]分别代表文档id,该文档id下词频,出现的索引位置,payload元数据,payload允许用户添加自定义信息,这个自定义信息用于影响文档的最终得分.
Lucene对索引采用了压缩技术,最终的目的是节省空间.
Lucene常见术语
Indexed:表示是否需要创建索引,即是否需要添加到倒排索引中,用来修饰Field域,如果不对某域创建索引,则不能根据这个域进行全文检索
Stored:表示是否需要存储某个域的域值,即是否需要将某个域的域值写入磁盘持久化
Tokenized:表示是否对域的域值进行分词,如果不分词,则整个域值都会被当成一个Term
Norms: 即Normalization的缩写,标准化的意思,是对排序的评分进行标准化修正的
Lucene的评分机制
image.png
coord(q, d): 查询query在所有文档中的频率
queryNorm(q): 计算query的权重,会影响最后的得分,但是不影响排序,因为每一个文档都会应用这个权重
tf(t, d): query分词在文档中的出现频率
idf(t): query分词的逆文档词频
t.getBoot(): 分词的权重,就是最后的元数据payload
norm(t, d): 分词对分数的修正,比如分词个数,以及分词自己的boot
solr配置
solr.xml
solr.xml主要用于配置Core相关的管理参数配置
solr的core自动发现机制
solr启动时,会在solr_home目录下递归查找名称为core.properties的配置文件,然后根据core.properities去加载Core,一个Core对应一个core.properities.
solr启动后,solr不再监视Core根目录的任何变化,如果某个Core目录下没有core.properities,则这个Core不能被自动发现
solrconfig.xml
lib配置告诉solr如何加载在solrconfig.xml和schema.xml中定义的插件依赖的jar包,其中dir属性值是一个相对于Core根目录的相对路径,该目录下的所有jar包都将被加载
schema.xml
solr的schema决定了索引是如何创建并存储的,solr的schema是相对宽松的,因为solr中配置的每个域不是必须的,非必须域可以缺失,但是对索引创建和查询没有影响.
<?xml version="1.0" encoding="UTF-8" ?>
<schema name="jsb" version="1.5">
<fields>
<!--field属性说明:
filed字段用于定义数据源字段所使用的搜索类型与相关设置.
name:数据源字段名,搜索使用到.
type:搜索类型对应于fieldType中的name.不需要分词的字符串类型,string即可,如果需要分词,types中配置好的分词type。
indexed:是否被索引,只有设置为true的字段才能进行搜索排序分片
stored:是否存储内容,如果不需要存储字段值,尽量设置为false以提高效率。
multiValued:是否为多值类型
字段名由字母数字下划线组成,且不能以数字开头。两端为下划线的字段为保留字段,如(_version_)。保留字段,不能删除,否则报错
-->
<field name="_version_" type="long" indexed="true" stored="true"/>
<field name="id" type="string" stored="true" indexed="true"/>
<field name="ent" type="text_zh" stored="true" indexed="true"/>
<field name="search_text" stored="false" type="text_zh" multiValued="true" indexed="true"/>
</fields>
<!--uniqueKey节点
设置主键,solr必须有一个主键,一般为id也可以自行定义.
这个字段决定和增强文档的唯一性
-->
<uniqueKey>id</uniqueKey>
<!--defaultSearchField节点
默认搜索的字段,默认值为text,
如果我们已经将需要搜索的字段拷贝至search_text字段了,在这里设为search_text即可-->
<defaultSearchField>search_text</defaultSearchField>
<!--copyField节点
如果我们的搜索需要搜索多个字段,将所有的中文分词字段全部拷贝至search_text中,只用搜索search_text字段就OK了.
source是要合并的字段,search_text是合并到的字段,search_text必须multiValued="true"
-->
<copyField source="ent" dest="search_text"/>
<!--solrQueryParser节点
默认搜索操作符参数,及搜索短语间的逻辑,用AND增加准确率,用OR增加覆盖面
<solrQueryParser defaultOperator="OR"/>
<!--定义字段处理类型 -->
<types>
<fieldType name="long" class="solr.TrieLongField" precisionStep="0" positionIncrementGap="0"/>
<fieldType name="string" class="solr.StrField" sortMissingLast="true" />
<fieldType name="text_zh" class="solr.TextField">
<!--分词器配置
analyzer的type有两个可选值,index表示建立索引时进行分词,query表示查询时对用户输入的query进行分词
isMaxWordLength 表示使用粗颗粒度搜索,是IK分词的一个参数
isMaxWordLength,这个参数是标识IK是否采用最大词长分词,还是采用最细粒度切分两种分词算法。实际两种算法的实现,最大词长切分是对最细粒度切分的一种后续处理,是对最细粒度切分结果的过滤,选择出最长的分词结果。
-->
<analyzer type="index" isMaxWordLength="true" class="org.wltea.analyzer.lucene.IKAnalyzer"/>
<analyzer type="query" isMaxWordLength="true" class="org.wltea.analyzer.lucene.IKAnalyzer"/>
</fieldType>
</types>
</schema>
solr的域类型定义
一个域类型定义包含4种类型信息
name: 域类型的名称,强制性必须指定,保证唯一性
class: 域类型对应的java类,强制性必须指定,如果是solr内置的域来下,使用solr.开头,如果是自定的域类型,必须指定完整包路径
如果域类型为TextField,那么还需要配置<analyzer>元素来指定分词器
solr的域
创建一个域名为ent,域类型为text_zh,默认值是"0.0",需要存储域值到磁盘,需要为该域建立索引的域
<field name="ent" type="text_zh" default="0.0" stored="true" indexed="true"/>
field和fieldtype可能会出现相同的参数配置,如果出现了相同的参数,field会覆盖fieldtype
solr的复制域
source表示复制的源域名称,dest表示目标域,maxChars表示最多从source复制多少个字符到dest
常用的场景是创建一个单一的搜索域来满足用户的各种查询需求,比如搜索一本书,将书的作者,标题,描述,关键词定义到一个copyField,设置默认搜索都对copyField进行查询,不过这样会增大索引体积
<copyField source="ent" dest="search_text" maxChars="30000" />
<copyField source="title" dest="search_text" maxChars="30000" />
solr的动态域
动态域的name支持模糊匹配,需要为name指定一个通配符表达式,当索引文档时,如果一个Field找不到,那么就会根据通配符去匹配动态域
<dynamicField name="*t" type="text_zh" stored="true" indexed="true" />
如果ant没有在Field中找到,则会启动"*t"动态域
solr配置中文分词器IK
IK是一个开源java编写的轻量级中文分词工具
1.需要将IK的jar包放在 /opt/solr/tomcat/webapps/solr/WEB-INF/lib下
2.将ext.dic IKAnalyzer.cfg.xml stopword.dic 放在 /opt/solr/tomcat/webapps/solr/WEB-INF/classes下
3.IKAnalyzer.cfg.xml用于配置扩展词和停用词的位置,ext.dic是拓展词,相当于自定义词典, stopword.dic是停用词
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 扩展配置</comment>
<!--用户可以在这里配置自己的扩展字典,多个用;隔开 -->
<entry key="ext_dict">ext.dic;</entry>
<!--用户可以在这里配置自己的扩展停止词字典,多个用;隔开-->
<entry key="ext_stopwords">stopword.dic;</entry>
</properties>
在schema.xml中的配置
<fieldType name="text_zh" class="solr.TextField">
<analyzer type="index" isMaxWordLength="true" class="org.wltea.analyzer.lucene.IKAnalyzer"/>
<analyzer type="query" isMaxWordLength="true" class="org.wltea.analyzer.lucene.IKAnalyzer"/>
</fieldType>












网友评论