lucene
全文检索
将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此有一定结构的数据进行搜索,从而达到搜索相对较快的目的。这部分从非结构化数据中提取出的然后重新组织的信息,我们称之索引。
以字典为例:
字典的拼音表和部首检字表就相当于字典的索引,对每一个字的解释是非结构化的,如果字典没有音节表和部首检字表,在茫茫辞海中找一个字只能顺序扫描。然而字的某些信息可以提取出来进行结构化处理,比如读音和偏旁部首,它就比较结构化,于是将读音和偏旁部首拿出来按一定的顺序排列,每一项都指向此字的详细解释的页数。我们搜索时按结构化的拼音搜到读音,然后按其指向的页数,便可找到我们的非结构化数据(也即对字的解释)。
这种先建立索引,再对索引进行搜索的过程就叫全文检索(Full-text Search) 。虽然创建索引的过程是非常耗时的(类比字典制作出来那可是相当费时的),但是索引一旦创建就可以多次使用,全文检索主要处理的是查询,所以耗时间创建索引是值得的。
怎么实现全文检索呢?
我们可以使用Lucene来实现全文检索。Lucene是apache下的一个开放源代码的全文检索引擎工具包(它提供了许多的jar包,即实现全文检索的类库)。它提供了完整的查询引擎和索引引擎,部分文本分析引擎。Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便地在目标系统中实现全文检索的功能。
索引创建和查询流程
lucene的全文检索过程分为(创建)索引、(索引)搜索两个过程。 具体流程可以看下图:
以一个案例来说明: 现在有这样一个需求:实现一个文件的搜索功能,通过关键字搜索文件,凡是文件名或文件内容包括关键字的文件都需要找出来,除此之外,还可以根据中文词语进行查询,并且需要支持多个条件查询。假设本案例中的原始内容就是磁盘上的文件,如下图所示。
- 获取文档
从互联网上、数据库、文件系统中等数据源处获取需要搜索的原始信息。
- 构建文档对象
获取原始内容的目的是为了索引,在索引前需要将原始内容创建成文档(Document),文档中包括一个一个的域(Field),域中存储内容。文档类似数据库中的一条数据,域类似数据库中的字段。
因为我们需要找磁盘中的某个文件,所以可以将磁盘上的一个文件当作成一个Document,Document中包括一些Field,例如file_name(文件名称)、file_path(文件路径)、file_size(文件大小)、file_content(文件内容)等,如下图所示。
注意:每个Document可以有多个Field,不同的Document可以有不同的Field,同一个Document可以有相同的Field(域名和域值都相同)。而且,每个文档都有一个唯一的编号,就是文档id。
分析文档
将原始内容创建为包含域(Field)的文档(Document),需要再对域中的内容进行分析,分析的过程是经过对原始文档提取单词、将字母转为小写、去除标点符号、去除停用词(没有意义的单词) 等过程生成最终的语汇单元,我们可以将语汇单元理解为一个一个的单词。例如,假设原始文档内容如下:
我们可以使用Lucene来实现全文检索,Lucene是apache下的一个开放源代码的全文检索引擎工具包。
上边的文档经过分析得出的语汇单元为:
lucene、全文检索、apache、引擎、工具包…
每个单词叫做一个Term,不同的域中拆分出来的相同的单词是不同的Term(同一个域中拆分出来的相同的单词是同一个Term),例如,文件名中包含的apache和文件内容中包含的apache是不同的Term。Term中包含两部分内容,一部分是文档的域名,另一部分是单词的内容。Term有点类似于Map<K, V>,你可以将文档的域名看作是Term中的键,单词的内容看作是Term中的值。<file_name: apache, file_content: apache...>,关键词搜索其实就是对语汇单元term进行搜索。
- 创建索引
将文档分析得到的语汇单元和文档本身的内容存储到索引库,所以索引库实际上包含了索引和文档内容两部分。
创建索引是为了对语汇单元进行查询,通过词语找文档,这种索引的结构叫倒排索引结构。传统方法是根据文件找到该文件的内容,在文件内容中匹配搜索关键字,这种方法是顺序扫描方法,数据量大、搜索慢。
倒排索引结构是根据内容(词语)找文档,如下图所示。
索引创建和搜索的整个流程如下图:
solr
Solr是Apache下的一个顶级开源项目,采用Java开发,它是基于Lucene的全文搜索服务器。Solr提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展,并对索引、搜索性能进行了优化。
与lucene的区别
lucene是一个开源的用于实现全文检索的工具包,不能独立运行。solr是基于lucene开发的全文检索的服务,作为一个搜索的服务独立运行。
solr是一个分布式的搜索引擎,但是这里介绍的是单机版的solr的架构图
solr的安装
docker安装
简单点就用docker-compose来安装
version: '3.9'
services:
solr:
image: solr:8.11.1
container_name: solr
ports:
- '8983:8983'
restart: always
privileged: true
volumes:
- '/home/ubuntu/solr/data:/var/solr/data/'
- '/home/ubuntu/solr/server/solr-8.11.1:/opt/solr-8.11.1'
注意一下数据卷的挂载:
1.先不挂载数据卷启动,进入容器找到solr的data和solr服务的路径。
2.在本地创建一个solr文件夹,在solr下创建data和server用来挂载solr的data和服务。
启动后访问:http://81.70.245.149:8983/ 可以进入solr的管理后台
创建core
docker exec -it solr bin/solr create_core -c ik_core
core可以类比pg的数据库来理解。
分词器配置
使用的是IK分词器:github.com/magese/ik-a…
下载jar
将jar包放入 solr 服务的 Jetty 或 Tomcat 的 webapp/WEB-INF/lib/ 目录下
docker cp ik-analyzer-8.5.0.jar solr:/opt/solr-8.11.1/server/solr-webapp/webapp/WEB-INF/lib/
shell
复制配置文件
# 使用root用户(超级管理)进入 solr 的容器里创建文件夹
docker exec -it --user=root solr /bin/bash
# 创建文件夹(classes)
cd /opt/solr-8.4.0/server/solr-webapp/webapp/WEB-INF
mkdir classes
# 退出容器
exit
# 将下面的5个配置文件放入solr服务的Jetty或Tomcat的webapp/WEB-INF/classes/目录下
① IKAnalyzer.cfg.xml
② ext.dic
③ stopword.dic
④ ik.conf
⑤ dynamicdic.txt
docker cp IKAnalyzer.cfg.xml solr:/opt/solr-8.4.0/server/solr-webapp/webapp/WEB-INF/classes/
...
配置 solr 的 managed-schema,添加ik分词器
docker exec -it --user=root solr /bin/bash
cd /var/solr/data/ik_core/conf
vi managed-schema
添加如下:
<!-- ik分词器 -->
<fieldType name="text_ik" class="solr.TextField">
<analyzer type="index">
<tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" useSmart="false" conf="ik.conf"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
<analyzer type="query">
<tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" useSmart="true" conf="ik.conf"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
</fieldType>
重启 solr 查看分词效果
docker restart solr
ruby中使用solr
rsolr
rsolr 是一个 Ruby 的库,它提供了与 Solr 搜索引擎的交互能力。封装了很多操作方法使得ruby开发者能够轻松地与 Solr 实例进行交互,包括索引文档、查询以及管理索引。
require 'rsolr'
# 创建一个 Solr 实例
solr = RSolr::Client.new("http://localhost:8983/solr")
# 创建一个文档
doc = RSolr::Document.new
doc.add_field("id", "12345")
doc.add_field("title", "The Great Gatsby")
doc.add_field("author", "F. Scott Fitzgerald")
# 索引文档
solr.add(doc)
# 查询
params = RSolr::Params.new
params.add("q", "Gatsby")
params.add("fl", "id,title")
# 执行查询
response = solr.get(params)
puts response.status
puts response.body
# 删除文档
solr.delete_by_id("12345")
# 更新文档
doc = RSolr::Document.new
doc.add_field("id", "12345")
doc.add_field("title", "The Great Gatsby")
doc.add_field("author", "F. Scott Fitzgerald")
# 更新文档
solr.update(doc)
sunspot
Sunspot 用 Ruby 的方式实现了与 Solr 搜索引擎的交互。底层是基于 Rsolr,而且提供了很方便的 DSL 接口实现建立索引和搜索。 sunspot的基本用法:
1、 在 Gemfile 中添加gem,bundle安装
gem "sunspot_rails", "~> 2.5"
gem "sunspot_solr", "~> 2.5" # 开发环境使用,本地的solr环境
2、 接着生成配置文件config/sunspot.yml:
rails generate sunspot_rails:install
3、配置config/sunspot.yml
production:
solr:
hostname: localhost
port: 8983
log_level: WARNING
path: /solr/production
# read_timeout: 2
# open_timeout: 0.5
development:
solr:
hostname: localhost
port: 8983
log_level: INFO
path: /solr/ik_core # solr的core的名称
test:
solr:
hostname: localhost
port: 8983
log_level: WARNING
path: /solr/ik_core
4、对象的设置
class Article < ActiveRecord::Base
searchable do
text :title
text :slug
integer :view_counts
end
end
5、对象搜索
# fulltext会在所有的text类型字段中搜索
Article.search do
fulltext '教程'
with(:view_count).more_than 100
end.results
常用的语法:
fulltext: 用于执行全文搜索。可以指定要搜索的关键词或短语。
Model.search do
fulltext 'keyword'
end
keywords: 用于执行关键词搜索,类似于fulltext,但更适合处理多个关键词。
Model.search do
keywords 'keyword1 keyword2'
end
with: 用于添加过滤条件,限定搜索结果。
Model.search do
with(:field_name).equal_to(value)
end
any_of: 用于指定多个条件之间的逻辑关系,表示其中任意一个条件满足即可。all_of表示满足所有条件。
Model.search do
any_of do
with(:field_name1).equal_to(value1)
with(:field_name2).equal_to(value2)
end
end
order_by: 用于指定搜索结果的排序方式。
Model.search do
order_by(:field_name, :asc)
end
paginate: 用于分页查询结果。
Model.search do
paginate(:page => 1, :per_page => 10)
end
参考资料: explorexd.com/articles/34 github.com/sunspot/sun… www.w3cschool.cn/solr_doc/