Lucene文档

一.数据概论

  

我们生活中的数据总体分为两种:结构化数据和非结构化数据。

结构化数据:指具有固定格式或有限长度的数据,如数据库,元数据等。

非结构化数据:指不定长或无固定格式的数据,如邮件,word文档等。非结构化数据又一种叫法叫全文数据。

当然有的地方还会提到第三种,半结构化数据,如XMLHTML等,当根据需要可按结构化数据来处理,也可抽取出纯文本按非结构化数据来处理。



1.1.1.   数据的搜索

对结构化数据的搜索:如对数据库的搜索,用SQL语句。再如对元数据的搜索,如利用windows搜索对文件名,类型,修改时间进行搜索等。

对非结构化数据的搜索:如利用windows的搜索也可以搜索文件内容,Linux下的grep命令,再如用Google和百度可以搜索大量内容数据。

对非结构化数据也即对全文数据的搜索主要有两种方法:

一种是顺序扫描法 (Serial Scanning) 所谓顺序扫描,比如要找内容包含某一个字符串的文件,就是一个文档一个文档的看,对于每一个文档,从头看到尾,如果此文档包含此字符串,则此文档为我们要找的文件,接着看下一个文件,直到扫描完所有的文件。如利用windows的搜索也可以搜索文件内容,只是相当的慢。如果你有一个80G硬盘,如果想在上面找到一个内容包含某字符串的文件,不花他几个小时,怕是做不到。Linux下的grep命令也是这一种方式。大家可能觉得这种方法比较原始,但对于小数据量的文件,这种方法还是最直接,最方便的。但是对于大量的文件,这种方法就很慢了。

另一种是全文检索的基本思路,也即将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此有一定结构的数据进行搜索,从而达到搜索相对较快的目的。这部分从非结构化数据中提取出的然后重新组织的信息,我们称之索引 

这种先建立索引,再对索引进行搜索的过程就叫全文检索(Full-text Search) 


一.    全文检索

全文检索理论部

全文检索大体分两个过程,索引创建 (Indexing) 和搜索索引 (Search) 

索引创建:将现实世界中所有的结构化和非结构化数据提取信息,创建索引的过程。

搜索索引:就是得到用户的查询请求,搜索创建的索引,然后返回结果的过程。

为什么顺序扫描的速度慢:

其实是由于我们想要搜索的信息和非结构化数据中所存储的信息不一致造成的。

非结构化数据中所存储的信息是每个文件包含哪些字符串,也即已知文件,欲求字符串相对容易,也即是从文件到字符串的映射。而我们想搜索的信息是哪些文件包含此字符串,也即已知字符串,欲求文件,也即从字符串到文件的映射。两者恰恰相反。于是如果索引总能够保存从字符串到文件的映射,则会大大提高搜索速度。

由于从字符串到文件的映射是文件到字符串映射的反向过程,于是保存这种信息的索引称为反向索引 

技术分享

这也是全文搜索相对于顺序扫描的优势之一:一次索引,多次使用。

 

三、如何创建索引

全文检索的索引创建过程一般有以下几步:

第一步:一些要索引的原文档(Document)

为了方便说明索引创建过程,这里特意用两个文件为例:

文件一:Studentsshould be allowed to go out with their friends, but not allowed to drink beer.

文件二:My friendJerry went to school to see his students but found them drunk which is notallowed.

 

第二步:将原文档传给分词器(Tokenizer)

分词器(Tokenizer)会做以下几件事情此过程称为Tokenize) 

1. 将文档分成一个一个单独的单词。

2. 去除标点符号。

3. 去除停词(Stop word) 

所谓停词(Stopword)就是一种语言中最普通的一些单词,由于没有特别的意义,因而大多数情况下不能成为搜索的关键词,因而创建索引时,这种词会被去掉而减少索引的大小。

英语中停词(Stopword)如:“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)

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

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

1. 变为小写(Lowercase) 

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

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

Stemming lemmatization的异同:

相同之处:Stemminglemmatization都要使词汇成为词根形式。

两者的方式不同:

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”的映射,做转变时,只要查字典就可以了。

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

语言处理组件(linguistic processor)的结果称为词(Term) 

三、如何对索引进行搜索?

搜索主要分为以下几步:

第一步:用户输入查询语句。

查询语句的语法根据全文检索系统的实现而不同。最基本的有比如:AND, OR, NOT等。

举个例子,用户输入语句:luceneAND learned NOT hadoop

说明用户想找一个包含lucenelearned然而不包括hadoop的文档。

 

第二步:对查询语句进行词法分析,语法分析,及语言处理。

由于查询语句有语法,因而也要进行语法分析,语法分析及语言处理。

1. 词法分析主要用来识别单词和关键字。

如上述例子中,经过词法分析,得到单词有lucenelearnedhadoop, 关键字有AND, NOT

2. 语法分析主要是根据查询语句的语法规则来形成一棵语法树。

如果发现查询语句不满足语法规则,则会报错。如lucene NOT AND learned,则会出错。

3. 语言处理同索引过程中的语言处理几乎相同。

learned变成learn等。

第三步:搜索索引,得到符合语法树的文档。

此步骤有分几小步:

首先,在反向索引表中,分别找出包含lucenelearnhadoop的文档链表。

1.  其次,对包含lucene,learn的链表进行合并操作,得到既包含lucene又包含learn的文档链表。

然后,将此链表与hadoop的文档链表进行差操作,去除包含hadoop的文档,从而得到既包含lucene又包含learn而且不包含hadoop的文档链表。

此文档链表就是我们要找的文档。

第四步:根据得到的文档和查询语句的相关性,对结果进行排序。

虽然在上一步,我们得到了想要的文档,然而对于查询结果应该按照与查询语句的相关性进行排序,越相关者越靠前。

如何计算文档和查询语句的相关性呢?

如何判断文档之间的关系:

首先,一个文档有很多词(Term)组成 ,如search, lucene, full-text, this,a, what等。

其次对于文档之间的关系,不同的Term重要性不同。

找出词(Term) 对文档的重要性的过程称为计算词的权重(Termweight) 的过程。

计算词的权重(termweight)有两个参数,第一个是词(Term),第二个是文档(Document)

词的权重(Termweight)表示此词(Term)在此文档中的重要程度,越重要的词(Term)有越大的权重(Term weight),因而在计算文档之间的相关性中将发挥更大的作用。

1. 计算权重(Term weight)的过程。

影响一个词(Term)在一篇文档中的重要性主要有两个因素:

Term Frequency (tf):即此Term在此文档中出现了多少次。tf 越大说明越重要。

Document Frequency (df):即有多少文档包含次Termdf 越大说明越不重要。

判断Term之间的关系从而得到文档相关性的过程,也即向量空间模型的算法(VSM)

在进入Lucene之前,对上述索引创建和搜索过程所一个总结,如图:

技术分享

1. 索引过程:

1) 有一系列被索引文件

2) 被索引文件经过语法分析和语言处理形成一系列词(Term) 

3) 经过索引创建形成词典和反向索引表。

4) 通过索引存储将索引写入硬盘。

2. 搜索过程:

a) 用户输入查询语句。

b) 对查询语句经过语法分析和语言分析得到一系列词(Term) 

c) 通过语法分析得到一个查询树。

d) 通过索引存储将索引读入到内存。

e) 利用查询树搜索索引,从而得到每个词(Term) 的文档链表,对文档链表进行交,差,并得到结果文档。

f) 将搜索到的结果文档对查询的相关性进行排序。

g) 返回查询结果给用户。

 

 

 

Lucene 是有索引和搜索的两个过程,包含索引创建,索引,搜索三个要点。

让我们更细一些看Lucene的各组件:

技术分享

·     被索引的文档用Document对象 表示。

·     IndexWriter 通过函数addDocument 将文档添加到索引中,实现创建索引的过程。

·     Lucene 的索引是应用反向索引。

·     当用户有请求时,Query 代表用户的查询语句。

·     IndexSearcher 通过函数search 搜索LuceneIndex 

·     IndexSearcher 计算termweight score 并且将结果返回给用户。

·     返回给用户的文档集合用TopDocsCollector 表示。

 

那么如何应用这些组件呢?

让我们再详细到对Lucene API 的调用实现索引和搜索过程。

技术分享

 

索引过程如下:

创建一个IndexWriter 用来写索引文件,它有几个参数,INDEX_DIR 就是索引文件所存放的位置,Analyzer 便是用来对文档进行词法分析和语言处理的。

创建一个Document 代表我们要索引的文档。

将不同的Field 加入到文档中。我们知道,一篇文档有多种信息,如题目,作者,修改时间,内容等。不同类型的信息用不同的Field 来表示,在本例子中,一共有两类信息进行了索引,一个是文件路径,一个是文件内容。其中FileReader SRC_FILE 就表示要索引的源文件。

IndexWriter 调用函数addDocument 将索引写到索引文件夹中。

搜索过程如下:

IndexReader 将磁盘上的索引信息读入到内存,INDEX_DIR 就是索引文件存放的位置。

创建IndexSearcher 准备进行搜索。

创建Analyer 用来对查询语句进行词法分析和语言处理。

创建QueryParser 用来对查询语句进行语法分析。

QueryParser 调用parser 进行语法分析,形成查询语法树,放到Query 中。

IndexSearcher 调用search 对查询语法树Query 进行搜索,得到结果TopScoreDocCollector 

以上便是Lucene API函数的简单调用。

 

 

全文检索的流程对应的Lucene实现的包结构

技术分享

Lucene analysis 模块主要负责词法分析及语言处理而形成Term 

Lucene index 模块主要负责索引的创建,里面有IndexWriter 

Lucene store 模块主要负责索引的读写。

Lucene QueryParser 主要负责语法分析。

Lucene search 模块主要负责对索引的搜索。

Lucene similarity 模块主要负责对相关性打分的实现。

 

技术分享

 

技术分享

 

 

QueryParse支持的特殊语法格式:

技术分享

 

分析器:AnalyzerAnalyzer 定义了从文本中抽取词的一组规范。 首先要实现一个Tokenizer,这个类会把输入流中的字符串切分成原始的词元。然后多个TokenFilter    就能够将这些词元规范化得到分词的结果

分词器:Tokenzier

过滤器:TokenFilter,主要可以用来对分词结果进行标准化。比如去停用词、转换大小写、英文的词干化(stemming)和词类归并 (lemmatization)等等

 

技术分享

 

高亮:

技术分享


本文出自 “Magic” 博客,转载请与作者联系!

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。