Lucene创建,读取,优化 总结(一)

The goal of Apache Lucene and Solr is to provide world class search capabilities

上面这句话是apache在官网上对Lucene的介绍。本文对Lucene做一个入门级的介绍,其实每一部分都可以单独写一篇文章。

当数据量越来越大的时候,大数据量对于数据库还是有一定压力,一般采用分表,分表后读取数据也可能会比较慢,尤其是有很多搜索条件。如果数据量不大,并且读取写入频繁,可以采用Radis,实现master-slave(主从)同步。如果千万级别的数据量且对数据的读取速度有一定要求,就需要建索引,Lucene是不错的选择。

Lucene通过对存储内容建立索引,提高查询效率,索引文件存储在硬盘/内存上,索引类似字典上面的a-b-c-d顺序,a-b-c-d对应Lucene中的Field。

Lucene常用类介绍

  • **(1)IndexWriter : **

An IndexWriter creates and maintains an index. 用来创建并管理索引文件。该类负责创建新索引或者打开已有索引,以及向索引中添加、删除或更新索引文档的信息,但不能用于读取或搜索索引。IndexWriter需要开辟一定空间来存储索引,该功能可以由Directory完成。
IndexWriter有以下几种创建模式:
IndexWriterConfig.OpenMode

APPEND
Opens an existing index.

CREATE
Creates a new index or overwrites an existing one.

CREATE_OR_APPEND
Creates a new index if one does not exist, otherwise it opens the index and documents will be appended.
另外有几点需要注意:
1:如果在创建索引文件过程中出现OutOfMemoryError,随之调用commit()会出现IllegalStateException,Lucene内部会记录当前出现异常的位置,此时必须调用close()方法处理异常,close()方法内部会回滚状态到上一次提交的位置。当然也可以手动调用rollback()。所以在try,catch后一定加上finally,并在finally中调用close()方法。
2:Lucene方法都是线程安全的,内部涉及写入文件都是加锁的,可以多线程使用,所以在使用时不需要再加锁,尤其是IndexWriter实例不要加锁,不然有可能造成死锁。

  • (2)Directory :A Directory is a flat list of files. Files may be written once, when they are created. Once a file is created it may only be opened for read, or deleted. Random access is permitted both when reading and writing. 索引存放的位置,它是一个抽象类,它的子类负责具体制定索引的存储路径。支持读共享,写独占的方式来访问索引目录。lucene提供了两种索引存放的位置,一种是磁盘,一种是内存。一般情况将索引放在磁盘上;相应地lucene提供了FSDirectory(硬盘)和RAMDirectory(内存)两个类。Java的io操作Api都是通过这个类来操作。

  • (3)Analyzer TokenStream Token: 这三个都是analysis包下面的类,这里放在一起说了。
    Package org.apache.lucene.analysis
    API and code to convert text into indexable/searchable tokens. Covers Analyzer and related classes.
    Parsing? Tokenization? Analysis!
    Lucene, an indexing and search library, accepts only plain text input.所以analysis包就是用来处理文本的。

Analyzer :An Analyzer builds TokenStreams, which analyze text. It thus represents a policy for extracting index terms from text. 分析器或分词器,主要用于分析搜索引擎遇到的各种文本,Analyzer的工作是一个复杂的过程:把一个字符串按某种规则划分成一个个词语,并去除其中的无效词语,去掉有利于缩小索引文件、提高效率、提高命中率。分词的规则千变万化,在analysis包中含有多种分词器,当然也可以自己定义分词器。Lucene文档给出了以下几种实现好的分词器:

  • Common: Analyzers for indexing content in different languages and domains. 这里面有很多,常用的有:SimpleAnalyzer、CJKAnalyzer、IKAnalyzer、StandardAnalyzer。
  • ICU: Exposes functionality from ICU to Apache Lucene.
  • Kuromoji: Morphological analyzer for Japanese text.
  • Morfologik: Dictionary-driven lemmatization for the Polish language.
  • Phonetic: Analysis for indexing phonetic signatures (for sounds-alike search).
  • Smart Chinese: Analyzer for Simplified Chinese, which indexes words.
  • Stempel: Algorithmic Stemmer for the Polish Language.
  • UIMA: Analysis integration with Apache UIMA.

TokenStream 是用来代替Token的Api:从Lucene2.9以后不再推荐使用Token;

TokenStream 介绍:A TokenStream enumerates the sequence of tokens, either from Field Document or from query text. 分词器做好处理之后得到的一个流,这个流中存储了分词的各种信息.可以通过TokenStream有效的获取到分词单元,从而获取各个分词的信息。

            //第一个参数只是标识性没有实际作用
            TokenStream stream = analyzer.tokenStream("", new StringReader(str));
            //获取词与词之间的位置增量
            PositionIncrementAttribute postiona = stream.addAttribute(PositionIncrementAttribute.class);
            //获取各个单词之间的偏移量
            OffsetAttribute offseta = stream.addAttribute(OffsetAttribute.class);
            //获取每个单词信息
            CharTermAttribute chara = stream.addAttribute(CharTermAttribute.class);
            //获取当前分词的类型
            TypeAttribute typea = stream.addAttribute(TypeAttribute.class);
            while(stream.incrementToken()){
                System.out.print("位置增量" +postiona.getPositionIncrement()+":\t");
                System.out.println(chara+"\t[" + offseta.startOffset()+" - " + offseta.endOffset() + "]\t<" + typea +">");
            }
            System.out.println();
  • ** (4) Document : **The logical representation of a Document for indexing and searching.文档 Document相当于一个要进行索引的单元,可以是文本文件、字符串或者数据库表的一条记录等等,一条记录经过索引之后,就是以一个Document的形式存储在索引文件,索引的文件都必须转化为Document对象才能进行索引。这个没什么可说的,下面会有一段代码简单描述索引创建过程看一下就知道了。
  • (5) Field : 一个Document可以包含多个信息域,比如一篇文章可以包含“标题”、“正文”等信息域,这些信息域就是通过Field在Document中存储的。一个Field包含内容,是否存储,是否分词等,这可由构造函数看出:
xxmcField = new Field("xxmc", "", Field.Store.YES, Field.Index.ANALYZED_NO_NORMS);
xxmc.setValue("laotie");

其中"xxmc"是键值中的键名,"laotie"是值,Field.Store.YES代表存储内容,如果使用索引查询后需要展示其内容,那么选择存储即可,如果不需要展示只是用来索引,那么Field.Store.NO,分词则表示是否用分词器对内容分词,便于搜索。

  • **(6) IndexReader : **
    IndexReader is an abstract class, providing an interface for accessing an index. 打开一个Directory读取索引类。同样值得注意的是,Lucene在内部已经保证了线程安全,不需外部对IndexReader的实例加锁,不然可能造成死锁。
  • (7) IndexSearcher:To perform a search, applications usually call IndexSearcher.search(Query,int) or IndexSearcher.search(Query,Filter,int). 是lucene中最基本的检索工具,所有的检索都会用到IndexSearcher工具。创建索引最终就是为了搜索,这部分涉及比较复杂的就是打分,也就是一个索引的分数即其重要程度。而其分值可以直接影响搜索结果的排名。关于评分机制可以参考

  • (8)Query 查询,抽象类,必须通过一系列子类来表述检索的具体需求,lucene中支持模糊查询,语义查询,短语查询,组合查询等等,如有 TermQuery,BooleanQuery,RangeQuery,WildcardQuery等一些类。
    创建索引的典型方式,重用Document与Field:

Document doc = new Document();
Field field1 = new TextField("field1", field1Value, Field.Store.YES);
doc.add(field1);
Field field2 = new StringField("field2", field2Value,Field.Store.YES);
doc.add(field2);
while ((line = br.readLine()) != null) {
    field1.setStringValue("field1Value");
    field2.setStringValue("field2Value");

    writer.addDocument(doc);
}
  • 重点介绍一下这几种Query方式

TermQuery

这是一种最简单的查询,类似键值对,使用键和值去索引里查询包含对应键值的数据。可以用来构成BooleanQuery.

BooleanQuery

A Query that matches documents matching boolean combinations of other queries
其实这个就是一种组合查询,其中组合方式可以选择MUST, NOT_MUST, SHOULD。其中SHOULD其实只是按打分影响排名,具体组合参考:

  • 1.MUST和MUST:取得连个查询子句的交集。
  • 2.MUST和MUST_NOT:表示查询结果中不能包含MUST_NOT所对应得查询子句的检索结果。
  • 3.SHOULD与MUST_NOT:连用时,功能同MUST和MUST_NOT。
  • 4.SHOULD与MUST连用时,结果为MUST子句的检索结果,但是SHOULD可影响排序。
  • 5.SHOULD与SHOULD:表示“或”关系,最终检索结果为所有检索子句的并集。
  • 6.MUST_NOT和MUST_NOT:无意义,检索无结果。

RangeQuery:NumericRangeQuery和TermRangeQuery两种

范围查询,一般NumericRangeQuery用的比较多,用来查询数字范围,比如价格,人数等等。TermRangeQuery也是范围查询但最终转为AscII码,文档上写的就是最终比较byte,Byte.compareTo(byte). 所以不如NumericRangeQuery查询使用的多。

WildcardQuery

这个是通配符查询,使用文档里的通配符查询每个item中的内容,比如"use*"可以查到"useful"、"user"。注意:这个查询比较慢,因为要查询每个item,,为了避免极慢的查询速度,请不要使用以星号开头的通配符进行查询。另外,WildcardQuery对于用户输入的查询关键字是大小写敏感的,请不要使用大写形式,因为索引中的Term都是小写形式的。

下一篇介绍:Lucene 建立及读取的例子,优化的几个方面

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容