java全文检索引擎,全文检索引擎Solr系列(上)

全文检索引擎Solr系列——入门篇

Solr采用Lucene搜索库为核心,提供全文索引和搜索开源企业平台,提供REST的HTTP/XML和JSON的API,如果你是Solr新手,那么就和我一起来入门吧!本教程以solr4.8作为测试环境,jdk版本需要1.7及以上版本。

准备

本文假设你对Java有初中级以上水平,因此不再介绍Java相关环境的配置。下载解压缩solr,在example目录有start.jar文件,启动:

java -jar start.jar

索引数据

服务启动后,目前你看到的界面没有任何数据,你可以通过POSTing命令向Solr中添加(更新)文档,删除文档,在exampledocs目录包含一些示例文件,运行命令:

java -jar post.jar solr.xml monitor.xml

上面的命令是向solr添加了两份文档,打开这两个文件看看里面是什么内容,solr.xml里面的内容是:

SOLR1000

Solr, the Enterprise Search Server

Apache Software Foundation

software

search

Advanced Full-Text Search Capabilities using Lucene

Optimized for High Volume Web Traffic

Standards Based Open Interfaces - XML and HTTP

Comprehensive HTML Administration Interfaces

Scalability - Efficient Replication to other Solr Search Servers

Flexible and Adaptable with XML configuration and Schema

Good unicode support: héllo (hello with an accent over the e)

0

10

true

2006-01-17T00:00:00.000Z

表示向索引中添加一个文档,文档就是用来搜索的数据源,现在就可以通过管理界面搜索关键字”solr”,具体步骤是:

a1a693ad547a48d17d5d26b0457d35e5.png

点击页面下的Execute Query按钮后右侧就会显示查询结果,这个结果就是刚才导入进去的solr.xml的json格式的展示结果。solr支持丰富的查询语法,比如:现在想搜索字段name里面的关键字”Search”就可以用语法name:search,当然如果你搜索name:xxx就没有返回结果了,因为文档中没有这样的内容。

数据导入

导入数据到Solr的方式也是多种多样的:

可以使用DIH(DataImportHandler)从数据库导入数据

支持CSV文件导入,因此Excel数据也能轻松导入

支持JSON格式文档

二进制文档比如:Word、PDF

还能以编程的方式来自定义导入

更新数据

如果同一份文档solr.xml重复导入会出现什么情况呢?实际上solr会根据文档的字段id来唯一标识文档,如果导入的文档的id已经存在solr中,那么这份文档就被最新导入的同id的文档自动替换。你可以自己尝试试验一下,观察替换前后管理界面的几个参数:Num

Docs,Max Doc,Deleted Docs的变化。

numDocs:当前系统中的文档数量,它有可能大于xml文件个数,因为一个xml文件可能有多个标签。

maxDoc:maxDoc有可能比numDocs的值要大,比如重复post同一份文件后,maxDoc值就增大了。

deletedDocs:重复post的文件会替换掉老的文档,同时deltedDocs的值也会加1,不过这只是逻辑上的删除,并没有真正从索引中移除掉

删除数据

通过id删除指定的文档,或者通过一个查询来删除匹配的文档

java -Ddata=args -jar post.jar "SOLR1000"

java -Ddata=args -jar post.jar "name:DDR"

此时solr.xml文档从索引中删除了,再次搜”solr”时不再返回结果。当然solr也有数据库中的事务,执行删除命令的时候事务自动提交了,文档就会立即从索引中删除。你也可以把commit设置为false,手动提交事务。

java -Ddata=args -Dcommit=false -jar post.jar "3007WFP"

执行完上面的命令时文档并没有真正删除,还是可以继续搜索相关结果,最后可以通过命令:

java -jar post.jar -

提交事务,文档就彻底删除了。现在把刚刚删除的文件重新导入Solr中来,继续我们的学习。

删除所有数据:

http://localhost:8983/solr/collection1/update?stream.body=*:*&commit=true

删除指定数据

http://localhost:8983/solr/collection1/update?stream.body=title:abc&commit=true

多条件删除

http://localhost:8983/solr/collection1/update?stream.body=

title:abc AND name:zhang&commit=true

查询数据

查询数据都是通过HTTP的GET请求获取的,搜索关键字用参数q指定,另外还可以指定很多可选的参数来控制信息的返回,例如:用fl指定返回的字段,比如f1=name,那么返回的数据就只包括name字段的内容

http://localhost:8983/solr/collection1/select?q=solr&fl=name&wt=json&indent=true

排序

Solr提供排序的功能,通过参数sort来指定,它支持正序、倒序,或者多个字段排序

q=video&sort=price desc

q=video&sort=price asc

q=video&sort=inStock asc, price desc

默认条件下,Solr根据socre 倒序排列,socre是一条搜索记录根据相关度计算出来的一个分数。

高亮

网页搜索中,为了突出搜索结果,可能会对匹配的关键字高亮出来,Solr提供了很好的支持,只要指定参数:

hl=true #开启高亮功能

hl.fl=name #指定需要高亮的字段

http://localhost:8983/solr/collection1/select?q=Search&wt=json&indent=true&hl=true&hl.fl=features

返回的内容中包含:

"highlighting":{

"SOLR1000":{

"features":["Advanced Full-Text Search Capabilities using Lucene"]

}

}

文本分析

文本字段通过把文本分割成单词以及运用各种转换方法(如:小写转换、复数移除、词干提取)后被索引,schema.xml文件中定义了字段在索引中,这些字段将作用于其中.

默认情况下搜索”power-shot”是不能匹配”powershot”的,通过修改schema.xml文件(solr/example/solr/collection1/conf目录),把features和text字段替换成”text_en_splitting”类型,就能索引到了。

...

修改完后重启solr,然后重新导入文档

java -jar post.jar *.xml

现在就可以匹配了

power-shot—>Powershot

features:recharing—>Rechargeable

1 gigabyte –> 1G

总结

作为入门文章,本文没有引入太多概念。安装到部署,文档更新,对solr有了初步感性的认识,下一篇将介绍全文检索的基本原理。

全文检索引擎Solr系列—–全文检索基本原理

场景:小时候我们都使用过新华字典,妈妈叫你翻开第38页,找到“坑爹”所在的位置,此时你会怎么查呢?毫无疑问,你的眼睛会从38页的第一个字开始从头至尾地扫描,直到找到“坑爹”二字为止。这种搜索方法叫做顺序扫描法。对于少量的数据,使用顺序扫描是够用的。但是妈妈叫你查出坑爹的“坑”字在哪一页时,你要是从第一页的第一个字逐个的扫描下去,那你真的是被坑了。此时你就需要用到索引。索引记录了“坑”字在哪一页,你只需在索引中找到“坑”字,然后找到对应的页码,答案就出来了。因为在索引中查找“坑”字是非常快的,因为你知道它的偏旁,因此也就可迅速定位到这个字。

那么新华字典的目录(索引表)是怎么编写而成的呢?首先对于新华字典这本书来说,除去目录后,这本书就是一堆没有结构的数据集。但是聪明的人类善于思考总结,发现每个字都会对应到一个页码,比如“坑”字就在第38页,“爹”字在第90页。于是他们就从中提取这些信息,构造成一个有结构的数据。类似数据库中的表结构:

word page_no

---------------

坑 38

爹 90

... ...

这样就形成了一个完整的目录(索引库),查找的时候就非常方便了。对于全文检索也是类似的原理,它可以归结为两个过程:1.索引创建(Indexing)2.

搜索索引(Search)。那么索引到底是如何创建的呢?索引里面存放的又是什么东西呢?搜索的的时候又是如何去查找索引的呢?带着这一系列问题继续往下看。

340db2c360d9da4940985d35348a2dc8.png

索引

Solr/Lucene采用的是一种反向索引,所谓反向索引:就是从关键字到文档的映射过程,保存这种映射这种信息的索引称为反向索引

5b3ec5cec5441439ead049ecf43a633a.png

左边保存的是字符串序列

右边是字符串的文档(Document)编号链表,称为倒排表(Posting

List)

字段串列表和文档编号链表两者构成了一个字典。现在想搜索”lucene”,那么索引直接告诉我们,包含有”lucene”的文档有:2,3,10,35,92,而无需在整个文档库中逐个查找。如果是想搜既包含”lucene”又包含”solr”的文档,那么与之对应的两个倒排表去交集即可获得:3、10、35、92。

索引创建

假设有如下两个原始文档:

文档一:Students should be allowed to go

out with their friends, but not allowed to drink beer.

文档二:My friend Jerry went to school to

see his students but found them drunk which is not allowed.

创建过程大概分为如下步骤:

44c3f28a6f0101af2eae40520e8f827d.png

一:把原始文档交给分词组件(Tokenizer)

分词组件(Tokenizer)会做以下几件事情(这个过程称为:Tokenize),处理得到的结果是词汇单元(Token)

将文档分成一个一个单独的单词

去除标点符号

去除停词(stop word)

所谓停词(Stop word)就是一种语言中没有具体含义,因而大多数情况下不会作为搜索的关键词,

这样一来创建索引时能减少索引的大小。英语中停词(Stop word)如:

”the”、”a”、”this”,中文有:”的,得”等。

不同语种的分词组件(Tokenizer),都有自己的停词(stop word)集合。

经过分词(Tokenizer)后得到的结果称为词汇单元(Token)。上例子中,便得到以下词汇单元(Token):

"Students","allowed","go","their","friends","allowed",

"drink","beer","My","friend","Jerry","went","school",

"see","his","students","found","them","drunk","allowed"

二:词汇单元(Token)传给语言处理组件(Linguistic Processor)

语言处理组件(linguistic processor)主要是对得到的词元(Token)做一些语言相关的处理。

对于英语,语言处理组件(Linguistic Processor)一般做以下几点:

变为小写(Lowercase)。

将单词缩减为词根形式,如”cars”到”car”等。这种操作称为:stemming。

将单词转变为词根形式,如”drove”到”drive”等。这种操作称为:lemmatization。

语言处理组件(linguistic processor)处理得到的结果称为词(Term),例子中经过语言处理后得到的词(Term)如下:

"student","allow","go","their","friend","allow","drink","beer","my","friend",

"jerry","go","school","see","his","student","find","them","drink","allow"。

经过语言处理后,搜索drive时drove也能被搜索出来。Stemming

和 lemmatization的异同:

相同之处:

Stemming和lemmatization都要使词汇成为词根形式。

两者的方式不同:

Stemming采用的是”缩减”的方式:”cars”到”car”,”driving”到”drive”。

Lemmatization采用的是”转变”的方式:”drove”到”drove”,”driving”到”drive”。

两者的算法不同:

Stemming主要是采取某种固定的算法来做这种缩减,如去除”s”,

去除”ing”加”e”,将”ational”变为”ate”,将”tional”变为”tion”。

Lemmatization主要是采用事先约定的格式保存某种字典中。

比如字典中有”driving”到”drive”,”drove”到”drive”,”am,

is, are”到”be”的映射,做转变时,按照字典中约定的方式转换就可以了。

Stemming和lemmatization不是互斥关系,是有交集的,有的词利用这两种方式都能达到相同的转换。

三:得到的词(Term)传递给索引组件(Indexer)

利用得到的词(Term)创建一个字典

Term Document ID

student 1

allow 1

go 1

their 1

friend 1

allow 1

drink 1

beer 1

my 2

friend 2

jerry 2

go 2

school 2

see 2

his 2

student 2

find 2

them 2

drink 2

allow 2

对字典按字母顺序排序:

Term Document ID

allow 1

allow 1

allow 2

beer 1

drink 1

drink 2

find 2

friend 1

friend 2

go 1

go 2

his 2

jerry 2

my 2

school 2

see 2

student 1

student 2

their 1

them 2

合并相同的词(Term)成为文档倒排(Posting List)链表postlist

3fc76649cfe87bbbac086eeae1db8124.png

Document Frequency:文档频次,表示多少文档出现过此词(Term)

Frequency:词频,表示某个文档中该词(Term)出现过几次

对词(Term) “allow”来讲,总共有两篇文档包含此词(Term),词(Term)后面的文档链表总共有两个,第一个表示包含”allow”的第一篇文档,即1号文档,此文档中,”allow”出现了2次,第二个表示包含”allow”的第二个文档,是2号文档,此文档中,”allow”出现了1次

至此索引创建完成,搜索”drive”时,”driving”,”drove”,”driven”也能够被搜到。因为在索引中,”driving”,”drove”,”driven”都会经过语言处理而变成”drive”,在搜索时,如果您输入”driving”,输入的查询语句同样经过分词组件和语言处理组件处理的步骤,变为查询”drive”,从而可以搜索到想要的文档。

搜索步骤

搜索”microsoft job”,用户的目的是希望在微软找一份工作,如果搜出来的结果是:”Microsoft

does a good job at software industry…”,这就与用户的期望偏离太远了。如何进行合理有效的搜索,搜索出用户最想要得结果呢?搜索主要有如下步骤:

一:对查询内容进行词法分析、语法分析、语言处理

词法分析:区分查询内容中单词和关键字,比如:english and janpan,”and”就是关键字,”english”和”janpan”是普通单词。

根据查询语法的语法规则形成一棵树

cea6eee44d60ae740a53abfbb6b21d8d.png

语言处理,和创建索引时处理方式是一样的。比如:leaned–>lean,driven–>drive

二:搜索索引,得到符合语法树的文档集合

三:根据查询语句与文档的相关性,对结果进行排序

我们把查询语句也看作是一个文档,对文档与文档之间的相关性(relevance)进行打分(scoring),分数高比较越相关,排名就越靠前。当然还可以人工影响打分,比如百度搜索,就不一定完全按照相关性来排名的。

如何评判文档之间的相关性?一个文档由多个(或者一个)词(Term)组成,比如:”solr”, “toturial”,不同的词可能重要性不一样,比如solr就比toturial重要,如果一个文档出现了10次toturial,但只出现了一次solr,而另一文档solr出现了4次,toturial出现一次,那么后者很有可能就是我们想要的搜的结果。这就引申出权重(Term

weight)的概念。

权重表示该词在文档中的重要程度,越重要的词当然权重越高,因此在计算文档相关性时影响力就更大。通过词之间的权重得到文档相关性的过程叫做空间向量模型算法(Vector

Space Model)

影响一个词在文档中的重要性主要有两个方面:

Term Frequencey(tf),Term在此文档中出现的频率,ft越大表示越重要

Document Frequency(df),表示有多少文档中出现过这个Trem,df越大表示越不重要

物以希为贵,大家都有的东西,自然就不那么贵重了,只有你专有的东西表示这个东西很珍贵,权重的公式:

62bb9b4e0a30d492cc675959385d86d9.png

空间向量模型

文档中词的权重看作一个向量

Document = {term1, term2, …… ,term N}

Document Vector = {weight1, weight2,

…… ,weight N}

把欲要查询的语句看作一个简单的文档,也用向量表示:

Query = {term1, term 2, …… , term N}

Query Vector = {weight1, weight2, ……

, weight N}

把搜索出的文档向量及查询向量放入N维度的空间中,每个词表示一维:

62bb9b4e0a30d492cc675959385d86d9.png

夹角越小,表示越相似,相关性越大

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值