文档章节

构建自己的搜索引擎之Lucene详解

潘少online
 潘少online
发布于 2015/04/17 23:32
字数 2104
阅读 139
收藏 3

    Lucene中的基本概念:

  • 索引(Index):文档的集合组成索引。和一般的数据库不一样,Lucene不支持定义主键,但Solr支持。

  • 为了方便索引大量的文档,Lucene中的一个索引分成若干个子索引,叫做段(segment)。段中包含了一些可搜索的文档。 

  • 文档(Document):代表索引库中的一条记录。一个文档可以包含多个列(Field)。和一般的数据库不一样,一个文档的一个列可以有多个值。例如一篇文档既可以属于互联网类,又可以属于科技类。

  • 列(Field):命名的词的集合。

  • 词(Term) :由两个值定义——词语和这个词语所出现的列。

  • 倒排索引是基于词(Term)的搜索。


    要学习搜索引擎,就需要了解倒排索引(倒排索引的定义请自行谷歌百度),首先我们来讲一下倒排索引的原理:

    文档1: When in Rome, do as the Romans do. 文档2: When do you come back from Rome?

    停用词:   in,  as,  the, from

    

    Lucene的整体结构:


    索引相关类:

  • 一个Document代表索引库中的一条记录。一个Document可以包含多个列。例如一篇文章可以包含“标题”、“正文”、“修改时间”等field,创建这些列对象以后,可以通过Document的add方法增加这些列到Document实例。


  • 一段有意义的文字通过Analyzer分割成一个个的词语后写入到索引库。

    

    创建索引:

//创建新的索引库
IndexWriter index = new IndexWriter(indexDirectory,//索引库存放的路径
                  new StandardAnalyzer(Version.LUCENE_CURRENT),
                  true,//新建索引库
                        IndexWriter.MaxFieldLength.UNLIMITED);//不限制列的长度
        
File dir = new File(sourceDir);
indexDir(dir); //索引sourceDir路径下的文件
 index.optimize();//索引优化
 index.close();//关闭索引库

    向索引增加文档:

    一个索引和一个数据库表类似,但是数据库中是先定义表结构后使用。但Lucene在放数据的时候定义字段结构。

Document doc = new Document();
//创建网址列
Field f = new Field(“url”, news.URL , //news.URL 存放url地址的值
                    Field.Store.YES, Field.Index. NOT_ANALYZED,//不分词
                    Field.TermVector.NO);
doc.add(f);
//创建标题列
f = new Field(“title”, news.title , //news.title 存放标题的值
                    Field.Store.YES, Field.Index.ANALYZED,//分词
                    Field.TermVector.WITH_POSITIONS_OFFSETS);//存Token位置信息
doc.add(f);
//创建内容列
f = new Field(“body”, news.body , //news.body 存放内容列的值
                    Field.Store.YES, Field.Index. ANALYZED, //分词
                    Field.TermVector.WITH_POSITIONS_OFFSETS); //存Token位置信息
doc.add(f);
index.addDocument(doc); //把一个文档加入索引

    

    搜索:

IndexSearcher isearcher = new IndexSearcher(directory,//索引路径
   true); //只读
//搜索标题列
QueryParser parser = new QueryParser(Version.LUCENE_CURRENT,"title", analyzer);
Query query = parser.parse(“NBA”); //搜索NBA这个词
//返回前1000条搜索结果
ScoreDoc[] hits = isearcher.search(query, 1000).scoreDocs;
//遍历结果
for (int i = 0; i < hits.length; i++) {
      Document hitDoc = isearcher.doc(hits[i].doc);
      System.out.println(hitDoc.get("title"));
}
isearcher.close();
directory.close();


    查询语法:

加权                solr^4 lucene

修饰符   +   -     NOT     

                   +solr lucene    

布尔操作符    OR AND

                   (solr OR lucene) AND user

按域查询 

title:NBA

    QueryParser:

  • QueryParser将输入查询字串解析为Lucene Query对象。

  • QueryParser是使用JavaCC(Java Compiler Compiler )工具生成的词法解析器。

  • QueryParser.jj中定义了查询语法。


  • 需要让QueryParser更好的支持中文,例如全角空格等?


    分析器(Analyzer):


分析公司名的流程
Analyzer analyzer = new CompanyAnalyzer(); 
TokenStream ts = analyzer.tokenStream(“title",
   new StringReader("北京xxx科技发展有限公司"));
while (ts.incrementToken()) {
  System.out.println("token: "+ts));
}

    最基本的词条查询-TermQuery:

    一般用于查询不切分的字段或者基本词

IndexSearcher isearcher = new IndexSearcher(directory, true);
//查询url地址列
Termterm = new Term("url","http://www.lietu.com");
TermQuery query = new TermQuery(term);
//返回前1000条结果
ScoreDoc[] hits = isearcher.search(query, 1000).scoreDocs;

    布尔逻辑查询-BooleanQuery:

    举例:同时查询标题列和内容列

QueryParser parser = new QueryParser(Version.LUCENE_CURRENT, "body", analyzer);
QuerybodyQuery =  parser.parse("NBA");//查询内容列
parser
= new QueryParser(Version.LUCENE_CURRENT, "title", analyzer);
QuerytitleQuery = parser.parse("NBA");//查询标题列
BooleanQuery bodyOrTitleQuery = new BooleanQuery();
//用OR条件合并两个查询
bodyOrTitleQuery.add(bodyQuery, BooleanClause.Occur.SHOULD);
bodyOrTitleQuery.add(titleQuery, BooleanClause.Occur.SHOULD);
//返回前1000条结果
ScoreDoc[] hits = isearcher.search(bodyOrTitleQuery, 1000).scoreDocs;

布尔查询的实现原理:


RangeQuery-区间查找:

 例如日期列time按区间查询的语法:

time:[2007-08-13T00:00:00Z TO 2008-08-13T00:00:00Z] 

后台实现代码:

ConstantScoreRangeQuery dateQuery = new   ConstantScoreRangeQuery("time", t1, t2, true,
true);


Lucene2.9以前版本区间查询的问题

RangeQuery采用扩展成TermQuery来实现,如果查询区间范围太大,RangeQuery会导致TooManyClausesException

ConstantScoreRangeQuery 内部采用Filter来实现,当索引很大的时候,查询速度会很慢

Trie结构实现的区间查询

    在Lucene2.9以后的版本中,用Trie结构索引日期和数字等类型。例如:把521 这个整数索引成为:百位是5、十位是52、个位是521。这样重复索引的好处是可以用最低的精度搜索匹配区域的中心地带,用较高的精度匹配边界。这样减少了要搜索的Term数量。


Trie结构区间查询

例如:TrieRange:[423 TO 642] 分解为5个子条件来执行:

handreds:5 OR

tens:[43 TO 49] OR

ones:[423 TO 429] OR

tens:[60 TO 63] OR

ones:[640 TO 642]


使用Trie结构实现的区间查询

  • 索引时,增加一个浮点数列到索引:

    document.add(new NumericField("weight").setFloatValue(value));

  • 搜索时,使用NumericRangeQuery来查询这样的数字列。例如:

    Query q = NumericRangeQuery.newFloatRange(“weight”, //列名称 

                 new Float(0.3f), //最小值从它开始   

                 new Float(0.10f),//最大值到它结束   

                 true,  //是否包含最小值

用压缩来改进搜索性能

压缩的原理

因为存在冗余,所以可以压缩。例如生活中的压缩写法:同上。压缩的原理:使用预测编码,对前后相似的内容压缩。


压缩的对象

  • 字符串数组(Term List)

  • 整数数组(DocId)



字符串数组排序后使用前缀压缩

整数数组排序后使用差分编码压缩

    

    压缩算法的两个过程:编码(压缩)过程和解码(解压缩)过程。编码过程可以时间稍长,解码过程需要速度快。类似ADSL上网机制:下载速度快,而上传速度慢。因为在索引数据阶段执行编码过程,而在搜索阶段执行解码过程。索引数据速度可以稍慢,但是搜索速度不能慢。


前缀编码(Front Encoding)

    因为索引词是排序后写入索引的,所以前后两个索引词词形差别往往不大。前缀压缩算法省略存储相邻两个单词的共同前缀。每个词的存储格式是:

<相同前缀的字符长度,不同的字符长度,不同的字符>

例如:顺序存储如下三个词:term、termagancy、termagant

不用压缩算法的存储方式是<词长,词>,例如:

<4,term> <10,termagancy> <9,termagant>

应用前缀压缩算法后,实际存储的内容如下:

<4,term> <4,6, agancy> <8,1,t>


差分编码(Differential Encoding)

变长压缩算法对于较小的数字有较好的压缩比。差分编码可以把数组中较大的数值用较小的数来表示,所以可以和变长压缩算法联合使用来实现数组的压缩。

编码过程

解码过程

例如,排好序的DocId序列:

编码前:345, 777, 11437, …

                   

编码后:345, 432, 10660, …


变长压缩(Variable byte encoding)  


VInt是一个变长的正整数表示格式,是一种整数的压缩格式表示方法。每字节的最高位表明是否有更多的字节在后面,如果是0表示这个字节是尾字节,1表示还有后续字节。按如下的规则编码正整数x:

if (x < 128),则使用一个字节(最高位置0,低7位表示数值);

if (x< 128*128),则使用2个字节(第一个字节最高位置1,低7位表示低位数值,第二个字节最高位置0 ,低7位表示高位数值);依次类推。 

把VInt看成是128进制的表示方法,低位优先,随着数值的增大,向后面的字节进位。

Lucene源码结构

    这是Lucene的用法和原理,构建自己的搜索引擎可以使用Lucene这个强大的工具包,将大大缩减开发周期,实现一个高性能的个人搜索引擎。


© 著作权归作者所有

共有 人打赏支持
潘少online
粉丝 11
博文 58
码字总数 107019
作品 2
深圳
程序员
【重出江湖】开发自己的搜索引擎Lucene+Heritrix(第二版)

【重出江湖】开发自己的搜索引擎Lucene+Heritrix(第二版) 搜索引擎技术经典图书《开发自己的搜索引擎Lucene+Heritrix(第二版)》再次推出 本书是一本介绍搜索引擎开发的书籍,通过本书,读...

youlechang
2009/12/24
4.4K
9
推荐9个Java的搜索引擎框架

在这个信息相当繁杂的互联网时代,我们已经学会了如何利用搜索引擎这个强大的利器来找寻目标信息,比如你会在Google上搜索情人节如何讨女朋友欢心,你也会在百度上寻找正规的整容医疗机构(尽...

孟飞阳
2016/06/19
108
0
9个基于Java的搜索引擎框架

在这个信息相当繁杂的互联网时代,我们已经学会了如何利用搜索引擎这个强大的利器来找寻目标信息,比如你会在Google上搜索情人节如何讨女朋友欢心,你也会在百度上寻找正规的整容医疗机构(尽...

zh119893
2014/09/04
488
0
9个基于Java的搜索引擎框架

9个基于Java的搜索引擎框架 [导读] Lucene是目前最受欢迎的Java全文搜索框架,准确地说,它是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎。Lucene为开发人员...

引鸩怼孑
2015/07/30
0
0
Lucene教程详解

注明:本文是由本人在开发有关基于lucene资源检索系统时的一点总结,其中一部分是自己根据开发过程自己总结的,也有部分是摘自网络,因无法获取当时摘文的地址,所以在此没有写源地址。 转载...

长平狐
2012/11/12
1K
2

没有更多内容

加载失败,请刷新页面

加载更多

学习设计模式——命令模式

参考博客 1. 认识命令模式 1. 定义:将一个请求封装成为一个对象,从而可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,并支持可撤销操作。 2. 组织结构: Commond:定义命令的...

江左煤郎
11分钟前
0
0
字典树收集(非线程安全,后续做线程安全改进)

将500W个单词放进一个数据结构进行存储,然后进行快速比对,判断一个单词是不是这个500W单词之中的;来了一个单词前缀,给出500w个单词中有多少个单词是该前缀. 1、这个需求首先需要设计好数据结...

算法之名
昨天
6
0
GRASP设计模式

此文参考了这篇博客,建议读者阅读原文。 面向对象(Object-Oriented,OO)是当下软件开发的主流方法。在OO分析与设计中,我们首先从问题领域中抽象出领域模型,在领域模型中以适当的粒度归纳...

克虏伯
昨天
0
0
Coding and Paper Letter(四十)

资源整理。 1 Coding: 1.Tomislav Hengl撰写的非官方作者指南:Michael Gould•Wouter Gerritsma。 UnofficialGuide4Authors 2.R语言包rwrfhydro,社区贡献的工具箱,用于管理,分析和可视化...

胖胖雕
昨天
0
0
JAVA 内存回收

参考:https://www.cnblogs.com/leesf456/p/5218594.html 1,JMV 中哪些可以作为 GC Root? 1. 虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。 2. 方法区中的类静态属性引...

Carlyle_Lee
昨天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部