📄 1658.html
字号:
匹配度 有匹配度算法,将匹配程度(相似度)比较高的结果排在前面。 没有匹配程度的控制:比如有记录中net出现5词和出现1次的,结果是一样的。<br>
结果输出 通过特别的算法,将最匹配度最高的头100条结果输出,结果集是缓冲式的小批量读取的。 返回所有的结果集,在匹配条目非常多的时候(比如上万条)需要大量的内存存放这些临时结果集。<br>
可定制性 通过不同的语言分析接口实现,可以方便的定制出符合应用需要的索引规则(包括对中文的支持) 没有接口或接口复杂,无法定制<br>
结论 高负载的模糊查询应用,需要负责的模糊查询的规则,索引的资料量比较大 使用率低,模糊匹配规则简单或者需要模糊查询的资料量少<br>
<br>
Lucene的创新之处:<br>
<br>
大部分的搜索(数据库)引擎都是用B树结构来维护索引,索引的更新会导致大量的IO操作,Lucene在实现中,对此稍微有所改进:不是维护一个索引文件,而是在扩展索引的时候不断创建新的索引文件,然后定期的把这些新的小索引文件合并到原先的大索引中(针对不同的更新策略,批次的大小可以调整),这样在不影响检索的效率的前提下,提高了索引的效率。<br>
<br>
Lucene和其他一些全文检索系统/应用的比较:<br>
Lucene 其他开源全文检索系统<br>
增量索引和批量索引 可以进行增量的索引(Append),可以对于大量数据进行批量索引,并且接口设计用于优化批量索引和小批量的增量索引。 很多系统只支持批量的索引,有时数据源有一点增加也需要重建索引。<br>
数据源 Lucene没有定义具体的数据源,而是一个文档的结构,因此可以非常灵活的适应各种应用(只要前端有合适的转换器把数据源转换成相应结构), 很多系统只针对网页,缺乏其他格式文档的灵活性。<br>
内容分割 Lucene的文档是由多个字段组成的,甚至可以控制那些字段需要索引,<br>
那些字段不需要索引,近一步索引的字段也分:<br>
需要进行分词的索引,比如:标题,文章内容字段<br>
不需要进行分词的索引,比如:作者/日期字段 缺乏通用性,往往将文档整个索引了<br>
语言分析 通过语言分析器的不同扩展实现:<br>
可以过滤掉不需要的词:an the of 等,<br>
西文语法分析:将jumps jumped jumper都归结成jump进行索引/检索<br>
非英文支持:对亚洲语言,阿拉伯语言的索引支持 缺乏通用接口实现<br>
查询分析 通过查询分析接口的实现,可以定制自己的查询语法规则:<br>
比如: 多个关键词之间的 + - and or关系等 <br>
并发访问 能够支持多用户的使用 <br>
<br>
<br>
<br>
关于亚洲语言的的切分词问题(Word Segment)<br>
<br>
对于中文来说,全文索引首先还要解决一个语言分析的问题,对于英文来说,语句中单词之间是天然通过空格分开的,但亚洲语言的中日韩文语句中的字是一个字挨一个,所有,首先要把语句中按“词”进行索引的话,这个词如何切分出来就是一个很大的问题。<br>
<br>
首先,肯定不能用单个字符作(si-gram)为索引单元,否则查“上海”时,不能让含有“海上”也匹配。<br>
<br>
但一句话:“北京天安门”,计算机如何按照中文的语言习惯进行切分呢?<br>
“北京 天安门” 还是“北 京 天安 门”?让计算机能够按照语言习惯进行切分,往往需要机器有一个比较丰富的词库才能够比较准确的识别出语句中的单词。<br>
<br>
另外一个解决的办法是采用自动切分算法:将单词按照2元语法(bigram)方式切分出来,比如:<br>
"北京天安门" ==> "北京 京天 天安 安门"。<br>
<br>
这样,在查询的时候,无论是查询"北京" 还是查询"天安门",将查询词组按同样的规则进行切分:"北京","天安 安门",多个关键词之间按与"and"的关系组合,同样能够正确地映射到相应的索引中。这种方式对于其他亚洲语言:韩文,日文都是通用的。<br>
<br>
基于自动切分的最大优点是没有词表维护成本,实现简单,缺点是索引效率低,但对于中小型应用来说,基于2元语法的切分还是够用的。<br>
自动切分 词表切分<br>
实现 实现非常简单 实现复杂<br>
查询 增加了查询分析的复杂程度, 适于实现比较复杂的查询语法规则<br>
存储效率 索引冗余大,索引几乎和原文一样大 索引效率高,为原文大小的30%左右<br>
维护成本 无词表维护成本 词表维护成本非常高:中日韩等语言需要分别维护。<br>
还需要包括词频统计等内容<br>
适用领域 嵌入式系统:运行环境资源有限<br>
分布式系统:无词表同步问题<br>
多语言环境:无词表维护成本 对查询和存储效率要求高的专业搜索引擎<br>
<br>
目前比较大的搜索引擎的语言分析算法一般是基于以上2个机制的结合。关于中文的语言分析算法,大家可以在GOOGLE查关键词"word segment search"能找到更多相关的资料。<br>
<br>
安装和使用<br>
<br>
下载:http://jakarta.apache.org/lucene/<br>
<br>
注意:Lucene中的一些比较复杂的词法分析是用JavaCC生成的(JavaCC:Java Compiler Compiler,纯JAVA的词法分析生成器),所以如果从源代码编译或需要修改其中的QueryParser、定制自己的词法分析器,还需要从http://www.webgain.com/products/java_cc/下载javacc。<br>
<br>
lucene的组成结构:对于外部应用来说索引模块(index)和检索模块(search)是主要的外部应用入口<br>
org.apache.Lucene.search/ 搜索入口<br>
org.apache.Lucene.index/ 索引入口<br>
org.apache.Lucene.analysis/ 语言分析器<br>
org.apache.Lucene.queryParser/ 查询分析器<br>
org.apache.Lucene.document/ 存储结构<br>
org.apache.Lucene.store/ 底层IO/存储结构<br>
org.apache.Lucene.util/ 一些公用的数据结构<br>
<br>
简单的例子演示一下Lucene的使用方法:<br>
索引过程:从命令行读取文件名(多个),将文件分路径(path字段)和内容(body字段)2个字段进行存储,并对内容进行全文索引:索引的单位是Document对象,每个Document对象包含多个字段Field对象,针对不同的字段属性和数据输出的需求,对字段还可以选择不同的索引/存储字段规则,列表如下:<br>
方法 切词 索引 存储 用途<br>
Field.Text(String name, String value) Yes Yes Yes 切分词索引并存储,比如:标题,内容字段<br>
Field.Text(String name, Reader value) Yes Yes No 切分词索引不存储,比如:META信息,<br>
不用于返回显示,但需要进行检索内容<br>
Field.Keyword(String name, String value) No Yes Yes 不切分索引并存储,比如:日期字段<br>
Field.UnIndexed(String name, String value) No No Yes 不索引,只存储,比如:文件路径<br>
Field.UnStored(String name, String value) Yes Yes No 只全文索引,不存储<br>
<br>
public class IndexFiles { <br>
//使用方法:: IndexFiles [索引输出目录] [索引的文件列表] ... <br>
public static void main(String[] args) throws Exception {<br>
String indexPath = args[0];<br>
IndexWriter writer;<br>
//用指定的语言分析器构造一个新的写索引器(第3个参数表示是否为追加索引)<br>
writer = new IndexWriter(indexPath, new SimpleAnalyzer(), false);<br>
<br>
for (int i=1; i<args.length; i++) {<br>
System.out.println("Indexing file " + args[i]);<br>
InputStream is = new FileInputStream(args[i]);<br>
<br>
//构造包含2个字段Field的Document对象<br>
//一个是路径path字段,不索引,只存储<br>
//一个是内容body字段,进行全文索引,并存储<br>
Document doc = new Document();<br>
doc.add(Field.UnIndexed("path", args[i]));<br>
doc.add(Field.Text("body", (Reader) new InputStreamReader(is)));<br>
//将文档写入索引<br>
writer.addDocument(doc);<br>
is.close();<br>
};<br>
//关闭写索引器<br>
writer.close();<br>
}<br>
}<br>
<br>
<br>
索引过程中可以看到:<br>
<br>
* 语言分析器提供了抽象的接口,因此语言分析(Analyser)是可以定制的,虽然lucene缺省提供了2个比较通用的分析器SimpleAnalyser和StandardAnalyser,这2个分析器缺省都不支持中文,所以要加入对中文语言的切分规则,需要修改这2个分析器。<br>
* Lucene并没有规定数据源的格式,而只提供了一个通用的结构(Document对象)来接受索引的输入,因此输入的数据源可以是:数据库,WORD文档,PDF文档,HTML文档……只要能够设计相应的解析转换器将数据源构造成成Docuement对象即可进行索引。<br>
* 对于大批量的数据索引,还可以通过调整IndexerWrite的文件合并频率属性(mergeFactor)来提高批量索引的效率。<br>
<br>
检索过程和结果显示:<br>
<br>
搜索结果返回的是Hits对象,可以通过它再访问Document==>Field中的内容。<br>
<br>
假设根据body字段进行全文检索,可以将查询结果的path字段和相应查询的匹配度(score)打印出来,<br>
<br>
public class Search { <br>
public static void main(String[] args) throws Exception {<br>
String indexPath = args[0], queryString = args[1];<br>
//指向索引目录的搜索器<br>
Searcher searcher = new IndexSearcher(indexPath);<br>
//查询解析器:使用和索引同样的语言分析器<br>
Query query = QueryParser.parse(queryString, "body", <br>
new SimpleAnalyzer());<br>
//搜索结果使用Hits存储<br>
Hits hits = searcher.search(query);<br>
//通过hits可以访问到相应字段的数据和查询的匹配度<br>
for (int i=0; i<hits.length(); i++) {<br>
System.out.println(hits.doc(i).get("path") + "; Score: " + <br>
hits.score(i));<br>
};<br>
}<br>
}<br>
<br>
在整个检索过程中,语言分析器,查询分析器,甚至搜索器(Searcher)都是提供了抽象的接口,可以根据需要进行定制。<br>
<br>
Hacking Lucene<br>
<br>
简化的查询分析器<br>
<br>
个人感觉lucene成为JAKARTA项目后,画在了太多的时间用于调试日趋复杂QueryParser,而其中大部分是大多数用户并不很熟悉的,目前LUCENE支持的语法:<br>
<br>
Query ::= ( Clause )*<br>
Clause ::= ["+", "-"] [<TERM> ":"] ( <TERM> | "(" Query ")" )<br>
<br>
中间的逻辑包括:and or + - && ||等符号,而且还有"短语查询"和针对西文的前缀/模糊查询等,个人感觉对于一般应用来说,这些功能有一些华而不实,其实能够实现目前类似于GOOGLE的查询语句分析功能其实对于大多数用户来说已经够了。所以,Lucene早期版本的QueryParser仍是比较好的选择。<br>
<br>
添加修改删除指定记录(Document)<br>
<br>
Lucene提供了索引的扩展机制,因此索引的动态扩展应该是没有问题的,而指定记录的修改也似乎只能通过记录的删除,然后重新加入实现。如何删除指定的记录呢?删除的方法也很简单,只是需要在索引时根据数据源中的记录ID专门另建索引,然后利用IndexReader.delete(Term term)方法通过这个记录ID删除相应的Document。<br>
<br>
根据某个字段值的排序功能<br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -