字符串匹配算法:lucene笔记十一: 建索引优化 复杂排序HitCollector 匹配算法

  1, 提高建索引速度

  /**

  * 在IndexWriter中有个MERGE_FACTOR参数可以帮助你在构造索引器后根据应用环境情况充分利用内存减少文件操作根据我使用经验:缺省Indexer是每20条记录索引后写入每将MERGE_FACTOR增加50倍索引速度可以提高1倍左右

  */

  indexWriter.MergeFactor(1000);

  2, 排序

  « 从 汉化 到 国际化 | (回到Blog入口)|(回到首页) | Resin学习笔记 »

  Lucene:基于Java全文检索引擎介绍

  作者:车东 发表于:2002-08-06 18:08 最后更新于:2007-04-12 11:04

  版权声明:可以任意转载转载时请务必以超链接形式标明文章原始出处和作者信息及本声明

  http://www.chedong.com/tech/lucene.html

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

  Lucene是个基于Java全文索引工具包

  基于Java全文索引引擎Lucene介绍:有关作者和Lucene历史

  全文检索实现:Luene全文索引和数据库索引比较

  中文切分词机制介绍:基于词库和自动切分词算法比较

  具体安装和使用介绍:系统结构介绍和演示

  Hacking Lucene:简化查询分析器删除实现定制排序应用接口扩展

  从Lucene我们还可以学到什么

  基于Java全文索引/检索引擎——Lucene

  Lucene不是个完整全文索引应用而是是个用Java写全文索引引擎工具包它可以方便嵌入到各种应用中实现针对应用全文索引/检索功能

  Lucene作者:Lucene贡献者Doug Cutting是位资深全文索引/检索专家曾经是V-Twin搜索引擎(AppleCopland操作系统成就的)主要开发者后在Excite担任高级系统架构设计师目前从事于些INTERNET底层架构研究他贡献出Lucene目标是为各种中小型应用加入全文检索功能

  Lucene发展历程:早先发布在作者自己www.lucene.com后来发布在SourceForge2001年年底成为APACHE基金会jakarta个子项目:http://jakarta.apache.org/lucene/

  已经有很多Java项目都使用了Lucene作为其后台全文索引引擎比较著名有:

  Jive:WEB论坛系统;

  Eyebrows:邮件列表HTML归档/浏览/查询系统本文主要参考文档“TheLucene search engine: Powerful, flexible, and free”作者就是EyeBrows系统主要开发者的而EyeBrows已经成为目前APACHE项目主要邮件列表归档系统

  Cocoon:基于XMLweb发布框架全文检索部分使用了Lucene

  Eclipse:基于Java开放开发平台帮助部分全文索引使用了Lucene

  对于中文用户来说最关心问题是其是否支持中文全文检索但通过后面对于Lucene结构介绍你会了解到由于Lucene良好架构设计对中文支持只需对其语言词法分析接口进行扩展就能实现对中文检索支持

  全文检索实现机制

  LuceneAPI接口设计比较通用输入输出结构都很像数据库>记录>字段所以很多传统应用文件、数据库等都可以比较方便映射到Lucene存储结构/接口中总体上看:可以先把Lucene当成个支持全文索引数据库系统

  比较下Lucene和数据库:

  Lucene 数据库

  索引数据源:doc(field1,field2...) doc(field1,field2...) indexer / _____________ | Lucene Index| -------------- / searcher 结果输出:Hits(doc(field1,field2) doc(field1...)) 索引数据源:record(field1,field2...) record(field1..) SQL: insert/ _____________ | DB Index | ------------- / SQL: select 结果输出:results(record(field1,field2..) record(field1...))

  Document:个需要进行索引“单元”

  个Document由多个字段组成 Record:记录包含多个字段

  Field:字段 Field:字段

  Hits:查询结果集由匹配Document组成 RecordSet:查询结果集由多个Record组成

  全文检索 ≠ like "%keyword%"

  通常比较厚书籍后面常常附关键词索引表(比如:北京:12, 34页上海:3,77页……)它能够帮助读者比较快地找到相关内容页码而数据库索引能够大大提高查询速度原理也是想像下通过书后面索引查找速度要比页地翻内容高多少倍……而索引的所以效率高另外个原因是它是排好序对于检索系统来说核心是个排序问题

  由于数据库索引不是为全文索引设计因此使用like "%keyword%"时数据库索引是不起作用在使用like查询时搜索过程又变成类似于页页翻书遍历过程了所以对于含有模糊查询数据库服务来说LIKE对性能危害是极大如果是需要对多个关键词进行模糊匹配:like"%keyword1%" and like "%keyword2%" ...其效率也就可想而知了

  所以建立个高效检索系统关键是建立个类似于科技索引反向索引机制将数据源(比如多篇文章)排序顺序存储同时有另外个排好序关键词列表用于存储关键词>文章映射关系利用这样映射关系索引:[关键词>出现关键词文章编号出现次数(甚至包括位置:起始偏移量结束偏移量)出现频率]检索过程就是把模糊查询变成多个可以利用索引精确查询逻辑组合过程从而大大提高了多关键词查询效率所以全文检索问题归结到最后是个排序问题

  由此可以看出模糊查询相对数据库精确查询是个非常不确定问题这也是大部分数据库对全文检索支持有限原因Lucene最核心特征是通过特殊索引结构实现了传统数据库不擅长全文索引机制并提供了扩展接口以方便针对区别应用定制

  可以通过下表格对比下数据库模糊查询:

  Lucene全文索引引擎 数据库

  索引 将数据源中数据都通过全文索引建立反向索引 对于LIKE查询来说数据传统索引是根本用不上数据需要逐个便利记录进行GREP式模糊匹配比有索引搜索速度要有多个数量级下降

  匹配效果 通过词元(term)进行匹配通过语言分析接口实现可以实现对中文等非英语支持 使用:like "%net%" 会把netherlands也匹配出来

  多个关键词模糊匹配:使用like "%com%net%":就不能匹配词序颠倒xxx.net..xxx.com

  匹配度 有匹配度算法将匹配程度(相似度)比较高结果排在前面 没有匹配程度控制:比如有记录中net出现5词和出现1次结果是

  结果输出 通过特别算法将最匹配度最高头100条结果输出结果集是缓冲式小批量读取 返回所有结果集在匹配条目非常多时候(比如上万条)需要大量内存存放这些临时结果集

  可定制性 通过区别语言分析接口实现可以方便定制出符合应用需要索引规则(包括对中文支持) 没有接口或接口复杂无法定制

  结论 高负载模糊查询应用需要负责模糊查询规则索引资料量比较大 使用率低模糊匹配规则简单或者需要模糊查询资料量少

  全文检索和数据库应用最大区别在于:让最相关头100条结果满足98%以上用户需求

  Lucene创新的处:

  大部分搜索(数据库)引擎都是用B树结构来维护索引索引更新会导致大量IO操作Lucene在实现中对此稍微有所改进:不是维护个索引文件而是在扩展索引时候不断创建新索引文件然后定期把这些新小索引文件合并到原先大索引中(针对区别更新策略批次大小可以调整)这样在不影响检索效率前提下提高了索引效率

  Lucene和其他些全文检索系统/应用比较:

  Lucene 其他开源全文检索系统

  增量索引和批量索引 可以进行增量索引(Append)可以对于大量数据进行批量索引并且接口设计用于优化批量索引和小批量增量索引 很多系统只支持批量索引有时数据源有点增加也需要重建索引

  数据源 Lucene没有定义具体数据源而是个文档结构因此可以非常灵活适应各种应用(只要前端有合适转换器把数据源转换成相应结构) 很多系统只针对网页缺乏其他格式文档灵活性

  索引内容抓取 Lucene文档是由多个字段组成甚至可以控制那些字段需要进行索引那些字段不需要索引步索引字段也分为需要分词和不需要分词类型:

  需要进行分词索引比如:标题文章内容字段

  不需要进行分词索引比如:作者/日期字段 缺乏通用性往往将文档整个索引了

  语言分析 通过语言分析器区别扩展实现:

  可以过滤掉不需要词:an the of 等

  西文语法分析:将jumps jumped jumper都归结成jump进行索引/检索

  非英文支持:对亚洲语言阿拉伯语言索引支持 缺乏通用接口实现

  查询分析 通过查询分析接口实现可以定制自己查询语法规则:

  比如: 多个关键词的间 + - and or关系等  

  并发访问 能够支持多用户使用  

  有关亚洲语言切分词问题(Word Segment)

  对于中文来说全文索引首先还要解决个语言分析问题对于英文来说语句中单词的间是天然通过空格分开但亚洲语言中日韩文语句中字是个字挨所有首先要把语句中按“词”进行索引这个词如何切分出来就是个很大问题

  首先肯定不能用单个作(si-gram)为索引单元否则查“上海”时不能让含有“海上”也匹配

  但句话:“北京天安门”计算机如何按照中文语言习惯进行切分呢?

  “北京 天安门” 还是“北 京 天安门”?让计算机能够按照语言习惯进行切分往往需要机器有个比较丰富词库才能够比较准确识别出语句中单词

  另外个解决办法是采用自动切分算法:将单词按照2元语法(bigram)方式切分出来比如:

  "北京天安门" > "北京 京天 天安 安门"

  这样在查询时候无论是查询"北京" 还是查询"天安门"将查询词组按同样规则进行切分:"北京""天安安门"多个关键词的间按和"and"关系组合同样能够正确地映射到相应索引中这种方式对于其他亚洲语言:韩文日文都是通用

  基于自动切分最大优点是没有词表维护成本实现简单缺点是索引效率低但对于中小型应用来说基于2元语法切分还是够用基于2元切分后索引般大小和源文件差不多而对于英文索引文件般只有原文件30%-40%区别

  自动切分 词表切分

  实现 实现非常简单 实现复杂

  查询 增加了查询分析复杂程度 适于实现比较复杂查询语法规则

  存储效率 索引冗余大索引几乎和原文样大 索引效率高为原文大小30%左右

  维护成本 无词表维护成本 词表维护成本非常高:中日韩等语言需要分别维护

  还需要包括词频统计等内容

  适用领域 嵌入式系统:运行环境资源有限

  分布式系统:无词表同步问题

  多语言环境:无词表维护成本 对查询和存储效率要求高专业搜索引擎

  目前比较大搜索引擎语言分析算法般是基于以上2个机制结合有关中文语言分析算法大家可以在Google查关键词"wordsegment search"能找到更多相关资料

  安装和使用

  下载:http://jakarta.apache.org/lucene/

  注意:Lucene中些比较复杂词法分析是用JavaCC生成(JavaCC:JavaCompilerCompiler纯Java词法分析生成器)所以如果从源代码编译或需要修改其中QueryParser、定制自己词法分析器还需要从http://javacc.dev.java.net/下载javacc

  lucene组成结构:对于外部应用来说索引模块(index)和检索模块(search)是主要外部应用入口

  org.apache.Lucene.search/ 搜索入口

  org.apache.Lucene.index/ 索引入口

  org.apache.Lucene.analysis/ 语言分析器

  org.apache.Lucene.queryParser/ 查询分析器

  org.apache.Lucene.document/ 存储结构

  org.apache.Lucene.store/ 底层IO/存储结构

  org.apache.Lucene.util/ 些公用数据结构

  简单例子演示下Lucene使用思路方法:

  索引过程:从命令行读取文件名(多个)将文件分路径(path字段)和内容(body字段)2个字段进行存储并对内容进行全文索引:索引单位是Document对象每个Document对象包含多个字段Field对象针对区别字段属性和数据输出需求对字段还可以选择区别索引/存储字段规则列表如下: 思路方法 切词 索引 存储 用途

  Field.Text(String name, String value) Yes Yes Yes 切分词索引并存储比如:标题内容字段

  Field.Text(String name, Reader value) Yes Yes No 切分词索引不存储比如:META信息

  不用于返回显示但需要进行检索内容

  Field.Keyword(String name, String value) No Yes Yes 不切分索引并存储比如:日期字段

  Field.UnIndexed(String name, String value) No No Yes 不索引只存储比如:文件路径

  Field.UnStored(String name, String value) Yes Yes No 只全文索引不存储

  public IndexFiles { //使用思路方法:: IndexFiles [索引输出目录] [索引文件列表] ... public void (String args) throws Exception { String indexPath = args[0]; IndexWriter writer; //用指定语言分析器构造个新写索引器(第3个参数表示是否为追加索引) writer = IndexWriter(indexPath, SimpleAnalyzer, false); for ( i=1; i<args.length; i) { .out.prln("Indexing file " + args); InputStream is = FileInputStream(args); //构造包含2个字段FieldDocument对象 //个是路径path字段不索引只存储 //个是内容body字段进行全文索引并存储 Document doc = Document; doc.add(Field.UnIndexed("path", args)); doc.add(Field.Text("body", (Reader) InputStreamReader(is))); //将文档写入索引 writer.addDocument(doc); is.close; }; //关闭写索引器 writer.close; }} 索引过程中可以看到:

  语言分析器提供了抽象接口因此语言分析(Analyser)是可以定制虽然lucene缺省提供了2个比较通用分析器SimpleAnalyser和StandardAnalyser这2个分析器缺省都不支持中文所以要加入对中文语言切分规则需要修改这2个分析器

  Lucene并没有规定数据源格式而只提供了个通用结构(Document对象)来接受索引输入因此输入数据源可以是:数据库WORD文档PDF文档HTML文档……只要能够设计相应解析转换器将数据源构造成成Docuement对象即可进行索引

  对于大批量数据索引还可以通过调整IndexerWrite文件合并频率属性(mergeFactor)来提高批量索引效率

  检索过程和结果显示:

  搜索结果返回是Hits对象可以通过它再访问Document>Field中内容

  假设根据body字段进行全文检索可以将查询结果path字段和相应查询匹配度(score)打印出来

  public Search { public void (String args) throws Exception { String indexPath = args[0], queryString = args[1]; //指向索引目录搜索器 Searcher searcher = IndexSearcher(indexPath); //查询解析器:使用和索引同样语言分析器 Query query = QueryParser.parse(queryString, "body", SimpleAnalyzer); //搜索结果使用Hits存储 Hits hits = searcher.search(query); //通过hits可以访问到相应字段数据和查询匹配度 for ( i=0; i<hits.length; i) { .out.prln(hits.doc(i).get("path") + "; Score: " + hits.score(i)); }; }}在整个检索过程中语言分析器查询分析器甚至搜索器(Searcher)都是提供了抽象接口可以根据需要进行定制

  Hacking Lucene

  简化查询分析器

  个人感觉lucene成为JAKARTA项目后画在了太多时间用于调试日趋复杂QueryParser而其中大部分是大多数用户并不很熟悉目前LUCENE支持语法:

  Query ::= ( Clause )*

  Clause ::= ["+", "-"] [<TERM> ":"] ( <TERM> | "(" Query ")")

  中间逻辑包括:and or + - &&||等符号而且还有"短语查询"和针对西文前缀/模糊查询等个人感觉对于般应用来说这些功能有些华而不实其实能够实现目前类似于Google查询语句分析功能其实对于大多数用户来说已经够了所以Lucene早期版本QueryParser仍是比较好选择

  添加修改删除指定记录(Document)

  Lucene提供了索引扩展机制因此索引动态扩展应该是没有问题而指定记录修改也似乎只能通过记录删除然后重新加入实现如何删除指定记录呢?删除思路方法也很简单只是需要在索引时根据数据源中记录ID专门另建索引然后利用IndexReader.delete(Termterm)思路方法通过这个记录ID删除相应Document

  根据某个字段值排序功能

  lucene缺省是按照自己相关度算法(score)进行结果排序但能够根据其他字段进行结果排序是个在LUCENE开发邮件列表中经常提到问题很多原先基于数据库应用都需要除了基于匹配度(score)以外排序功能而从全文检索原理我们可以了解到任何不基于索引搜索过程效率都会导致效率非常如果基于其他字段排序需要在搜索过程中访问存储字段速度回大大降低因此非常是不可取

  但这里也有个折中解决思路方法:在搜索过程中能够影响排序结果只有索引中已经存储docID和score这2个参数所以基于score以外排序其实可以通过将数据源预先排好序然后根据docID进行排序来实现这样就避免了在LUCENE搜索结果外对结果再次进行排序和在搜索过程中访问不在索引中某个字段值

Tags:  图像匹配算法 模式匹配算法 匹配算法 字符串匹配算法

延伸阅读

最新评论

发表评论