1.全文索引相对于顺序扫描的优势:一次索引,多次使用
2.创建索引的步骤:
(1)要索引的原文档
(2)将原文档传给分词组件(Tokenizer)
分词组件会做如下事情:(此过程称为Tokenize)
a.将文档分成一个一个的单词
b.去除标点符号
c.去除停词(Stop Word) **停词就是语句中无意义的词汇,英语中比如 the is of
每一种分词组件(Tokenize)都有一个停词集合
经过分词组件分词后得到的结果称为(词元)Token
(3).将得到的词元传给语言处理组件(Linguistic Processor)
语言处理组件主要对词元进行一些同语言相关的操作
对于英语,语言处理组件主要做如下处理:
a.变为小写(Lowercase)
b.将单词缩减为词根形式 如cars -> car ,这种操作叫做 stemming
c.将单词转变为词根形式 如drove -> drive 这种操作为lemmatization
stemming与lemmatization的异同:
相同:都使单词变成词根形式
不同:两者的方式不同,一个是缩减,一个是转变
两者的算法不同 ,前者主要某种固定的算法来做这种缩减
后者主要采用保存某种字典的方式做这种转变
两者不是互斥关系,是有交集的,有的单词可以用两种方法达到同一种效果,比如 driving
语言处理组件处理后得到的是词(term).
(4).将得到的词(term)传给索引组件(Indexer)
a.利用得到的词创建一个字典 (term,documentId)
b.对字典按字母排序进行排序
c.合并相同的词(Term)成为文档倒排(Posting List)链表
document frequency 即文档频次,表示总共有多少文件包含此词
frequency 即词频率,表示文档中包含了几个此词
3.对索引进行搜索
(1)用户输入查询语句 and or not 查询语句有很多语法。举例 lucene AND learned NOT hadoop
(2)对查询语句进行词法分析,语法分析,语言处理。
a.词法分析主要用来识别单词和关键字,如果关键字错误,则视为正常单词 如lucene AMD .. 则视为AMD 这个单词
b.语法分析主要是根据查询语句的语法规则形成一颗语法树
c.语言处理与建立索引的语言处理基本相同
(3)搜索索引,得到满足语法树的文档
a.首先,在反向索引表中,分别找出包含lucene , learn , hadoop 的文档列表
b.其次,对包含lucene , learn 的链表进行合并操作,得到既包含lucene 又包含learn的文档链表
c.然后,将此链表与hadoop的文档链表进行差操作,去除包含hadoop的文档,从而得到既包含lucene ,又包含learn ,但是不包含hadoop的文档链表
d.得到的链表就是我们需要的文档
(4)根据得到的文档和查询语句的相关性,对结果进行排序。
判断文档之间的关系:
首先要找出哪些词(term)对文档之间的关系最重要,然后判断这些词之间的关系
(4.1)找出词对文档的重要性的过程称为计算词的权重(term weight)问题
(A)影响计算权重的两个因素:
(I)term frequency 表示term在文档中出现多少次,tf越大说明越重要
(II)document frequency 表示有多少文档包含此term df越大说明越重要 但是次数太多,比如this ,该元素会有所调整
(B)判断term之间的关系从而得到文档相关性的的过程,也即向量空间模型的算法。
第三章 : lucene的索引文件格式
lucene索引结构是有层次结构的,具体如下:
(1)索引 : 索引全部是是放在同一个文件夹中,这些内容构成了一个完整的lucene索引
(2)段(segment):一个索引可以包括很多段,段与段之间是独立的,新添加新文档可以生成新的段,段与段可以合并
具有相同前缀文件的属于同一段,如图中共两个段"_0","_1";
segments.gen 和 segment_5是段的数据文件,也即它们保存了段的属性信息
(3)文档(document):文档是建立索引的基本单位,不同的文档保存在不同的段中,一个段可以包括多篇文档
新添加的文档是单独保存在新生成的段中,随着段的合并,不同的文档合并到同一个段中。
(4)域(field):一篇文档包含不同类型的信息,可以分开索引,比如正题,时间,正文等,都可以保存在不同的域中,
不同域的索引方式可以不同
(5)词(term):词是索引的最小单位,是经过词法分析和语言处理后的字符串
lucene的索引结构中,既包括了正向信息,也包括了反向信息
正向信息:按层次保存了从索引,一直到词的包含关系,索引(Index)-->段(segment)-->文档(document)-->域(field)-->词(term);
每个层次都包含了本层次的信息以及下层次的元信息
包含正向信息的文件有:
segment_N保存了此索引包含多少段,每段包含了多少个文档
XXX.fnm保存了此段包含了多少域,每个域的名称以及索引方式
XXX.fdx XXX.fdt 保存了此段包含的所有文档,每个文档包含了多少域,每个域放了那些信息
XXX.tvx XXX.tvd XXX.tvf 保存了此段包含的所有文档,每个文档包含了多少域,每个域包含了那些词,每个词的字符串位置等信息
反向信息:保存了字典到倒排表的映射,词(term)--> 文档(document)
XXX.tis XXX.tii 保存了词典,也即此段包含的所有的词按字典顺序的排序
XXX.frq 保存了倒排表,也即包含每个词的文档Id列表
XXX.prx 保存了倒排表中每个词在包含此词的文档中的位置
lucene 如何表示String :首先是一个Vint来表示此字符串包含的字数的个数,接着便是utf-8 编码的字符序列的chars
lucene为了使信息的存储空间更小,访问速度更快,采用了一些技巧:
1.前缀后缀原则:当某一个词和前面一个词有共同的前缀的时候,后面的词仅仅保留前缀在词中的偏移,以及出前缀外的字符串
2.差值原则 :先后保存两个整数的时候,后面的整数仅仅保存和前面整数的差值即可。
3.或然跟随原则: lucene索引结构用一个标志位来表示某个值A后面可能存放某个值B 这样会浪费一个byte的空间,实际上一个bit就够了
4.跳跃表规则:元素按顺序排列,按跳跃间隔和跳跃层次提交查找速度
标准化因子在索引过程中总的计算:包括三个参数,
(1)document boost 此值越大,说明此文档越重要
(2)field boost 此值越大,说明此域越重要
(3)一个域中包含的term越多,也即文档越长,此值越小,文档越短,此值越大
文档的删除主要有三种方式:
indexWriter.deleteDocuments(Term ,term)所有包含此词的文档都会被删除
indexWriter.deleteDocuments(Query , query) 所有能满足此查询的文档都会被删除
indexReader.deleteDocuments(int docNum) 删除此id的文档
归根结底还是按照文档号删除
删除文档既可以用reader,也可以用writer
不同点:reader进行删除后,可以马上生效
而用writer删除后,会被缓存在deleteinRAM以及deleteFlushed中,只要写入到索引文件,当indexReader再次打开时,才能够看到
deleteinRAM与deleteFlushed
有些版本的lucene对文档的删除是支持多线程的,当用indexWriter删除文档的时候,都是缓存在deleteinRAM中的,
直到flush,才将删除的文档写入到索引文件中,如果flush的过程中,另一个线程又有文档删除的话,流程怎么样呢?
一般这样进行,在flush的时候,首先在同步的方法pushDeletes中,将deleteinRAM全部加到deleteFlushed中,然后将deleteinRAM
清空,退出同步方法,于是在flush的线程就向索引文件写入deleteFlushed中的删除文档的同时,其他线程新删除的文档则 添加到新的
deleteinRAM中去,直到下次flush才写入索引文件。
关闭indexwriter 会进行两个操作:
1.将索引信息由内存写入硬盘
2.进行段合并
indexReader打开一个索引,就好像对此索引照了一张像,无论背后索引如何改变,此indexReader在被重新打开之前,看到的信息总是相同的
lucene的文档号仅仅对打开的某个reader有效,当再打开一个reader的时候,不同的reader的同一个文档号
就可能不同,因此文档号要在reader关闭之前应用。
BoostingQuery 包括三个成员变量:
1.Query match:结果集必须满足的查询条件
2.Query context:此查询对象不对结果集产生任何影响,仅在当文档包括context查询的时候,将文档打分乘以boost
3.float boost
CustomScoreQuery 包括两个成员变量:
1.Query subQuery 子查询
2.ValueSourceQuery[] valSrcQueries :其他信息源
举例:doc.add("context", .......);
doc.add("scorefield","10","Filed.Store.NO","Filed.Index.NOT_ANALYZED");
将来进行查询的时候,普通查询 TermQuery termQuery = new TermQuery(new Term("context","apple"));
得到的结果为包含apple多的打分就高.
如果按CustomScoreQuery进行查询:TermQuery subQuery = new TermQuery(new Term("context","apple"));
FiledScoreQuery scorefield = new FieldScoreQuery("scorefield",FiledScoreQuery.Type.BYTE);
CustomScoreQuery query = new CustomScoreQuery(subQuery,scorefield);
则文档因为设置数据源为10而变得最高
不同Analyzer的就是 组合不同的tokenizer 和tokenfilter得到最后的TokenStream
1.ChineseAnalyzer 按字分词,并过滤停词,标点,英文
This year ,persidetn Hu 科学发展观 --> year president hu 科 学 发 展 观
2.CJKAnalyzer 每两个字组成一个词,并过滤停词,标点,英文
--> year president hu 科学 学发 发展 展观
3.PorterStemAnalyzer 将转为小写的token ,利用porter算法进行stemming
4.SmartChineseAnalyzer 分句子,句子中分词组,用porter算法进行stemming,去停词
5.SnowBallAnalyzer 标准分词器,标准过滤器,转换为小写,去停词,利用porter算法进行stemming