admin | Lucene | 2012-01-31
用Lucene构建搜索引擎

Lucene不是一个完整的全文索引应用,而是是一个用Java写的全文索引引擎工具包,它可以方便的嵌入到各种应用中实现针对应用的全文索引/检索功能。
Lucene的作者:Lucene的贡献者Doug Cutting是一位资深全文索引/检索专家,曾经是V-Twin搜索引擎(Apple的Copland操作系统的成就之一)的主要开发者,后在 Excite担任高级系统架构设计师,目前从事于一些INTERNET底层架构的研究。他贡献出的Lucene的目标是为各种中小型应用程序加入全文检索功能。
Lucene的发展历程:早先发布在作者自己的www.lucene.com,后来发布在SourceForge,2001年年底成为APACHE基金会jakarta的一个子项目:http://jakarta.apache.org/lucene/
已经有很多Java项目都使用了Lucene作为其后台的全文索引引擎
一、开始
首先在Apache下载Lucene 2.3.0包,其中包含了核心jar和LuceneAPI文档,解压后,将 lucene-core-2.3.0.jar放在classpath中。
二、创建索引
     创建索引时需要指定存放索引的目录(将来检索时需要对这个目录中的索引进行检索),和文件的目录(如果是对文件进行索引的话)代码如下:
  
    public void crateIndex() throws Exception {
File indexDir = new File(“D://luceneIndex”);
       // 存储索引文件夹
       File dataDir = new File(“D://test”);
       // 需要检索文件夹
Analyzer luceneAnalyzer =

admin | Lucene |
lucene大数据量动态更新问题解决方式

问题:
目前索引里面已经有1000多万的数据了,现在需要每几分钟就增量得添加新的内容到索引中。
但是,我发现新加入索引后,整个索引结构都要重新调整。非常耗时(长达半个小时)。

不知道大家有没有什么比较好的办法,加快这个过程?

回答:

我觉得用lucene做,一个原则索引里面尽量少存储,索引文件小了,optimize要移动的数据块也小。
还有Lucene实在不适合做实时,有一个办法,将新索引建在内存中,新建在内存上的searcher与硬盘索引searcher合并为MutliSearcher提供给前端搜索,内存到达一定量时再后台合并到主索引上,合并完成用新的Searcher替换MutliSearcher。
Lucene2.3已经放出来了,添加了很多新功能,可以去看看…

admin | Lucene |
lucene自定义SORT

Lucene的搜索结果默认按相关度排序,这个相关度排序是基于内部的Score和DocID,Score又基于关键词的内部评分和做索引时的boost。默认Score高的排前面,如果Score一样,再按索引顺序,先索引的排前面。那么有人问了,如果我要先索引的排后面怎么办呢?隐士研究了源码后发现这是相当简单的事情。以下代码基于Lucene 2.0。

看Sort的默认构造函数,相关度就是SortField.FIELD_SCORE和SortField.FIELD_DOC的组合。

java 代码
/**
* Sorts by computed relevance. This is the same sort criteria as calling
* {@link Searcher#search(Query) Searcher#search()}without a sort criteria,
* only with slightly more overhead.
*/
public Sort() {
this(new SortField[] { SortField.FIELD_SCORE,

admin | Lucene |
限制lucene的遍历结果

一、缘起
Lucene在索引文件上G之后的搜索性能下降很严重,随便跑个搜索就要上0.x秒。如果是单线程搜索那么性能尚可,总可以在0.x秒返回结果,如果是Web式的多线程访问,由于Lucene的内部机制导致数据被大量载入内存,用完后立即丢弃,随之引起JVM频繁GC,性能极其低下,1-10秒的长连接比比皆是。这也是世人为之诟病的Lucene应用瓶颈问题,那么是否有解决方法呢?

二、思路
我们来观察Google, Baidu的搜索,有一个总体的感觉就是搜索结果多的关键词耗时比较少,结果少的关键词耗时反而多,且结果多的时候会说“约******个结果”。隐士猜测Google, Baidu的算法是找到前n个结果后停止扫描索引,根据前n个结果来推断总共有多少个结果,此猜想可由Google, Baidu翻页限制而得到部分验证。
再看Lucene,其Hits.length()返回的总是精确的结果,如果可以让Lucene也返回模糊的结果,那么索引文件就算是10G也可以轻松应对了。

三、探索
隐士带着这个问题访名山、觅高人,可惜没有找到前人的成果,可能是隐士走的路不够勤,如有类似的解决方案,隐士不吝赐教。
无奈之下,隐士详细研究了Lucene 2.1.0源码,准备重新发明轮子。
一般来说大多数搜索应用中的Query都会落在BooleanQuery上,隐士就拿它开刀。一路看来,BooleanScorer2里的一个method吸引了隐士,代码如下:

代码
public void score(HitCollector hc) throws IOException {
if (countingSumScorer == null) {
initCountingSumScorer();
}
while (countingSumScorer.next()) {
hc.collect(countingSumScorer.doc(), score());
}
}

<script>render_code();</script>

在while循环里嵌入写日志代码可证结果集有多大,此处就循环了多少次。countingSumScorer.next()的意思是找到下一个符合boolean规则的document,找到后放入HitCollector,这HitCollector后面会换个马甲放在大家熟悉的Hits里面。
如果可以在这个while循环里嵌一个break,到一定数量就break出来,性能提升将相当明显。这个代码相当简单,果然大幅提高了性能,带来的副作用是结果不太准,这个可以通过调整业务模型、逻辑来修正。毕竟这是一条提升Lucene性能的有效方法。
细细想来,正是由于这个break会导致结果集大的关键词提前出来,搜索时间少,结果集小的关键词不可避免会走完整个索引,相应的搜索时间会长一点。

四、效果…

admin | Lucene |
lucene结果内容显示的文本数量

1, highlighter.setTextFragmenter(new SimpleFragmenter(15))
这个控制显示的结果数量多少.
结果展现:
String result= highlighter.getBestFragment(tokenStream, hits.doc(i).get(“key”));

admin | Lucene.net |
Lucene.Net用法

1. 基本应用

using System;
using System.Collections.Generic;
using System.Text;
using Lucene.Net;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.QueryParsers;
using Lucene.Net.Search;
using Lucene.Net.Store;
using Lucene.Net.Util;

namespace ConsoleApplication1.Lucene
{
public class LuceneTest
{
private const string FieldName = “name”;
private

admin | Lucene,Lucene.net |
Lucene.net实现全文搜索

Lucene.net实现全文搜索:

本文介绍一下Lucene.Net 是什么?Lucene.Net 能作什么?以及怎么做的问题?最后给出 Lucene.Net 实现全文搜索的一个示例

1、Lucene.Net 是什么?

Lucene.net 起初是一个开源项目然后转向商业化,也在Lucene.net 2.0已经发布,不过是要money D ,Lucene.net的命运有点类似于FreeTextBox ,它在 1.6.5 版本之后发布的 2.0 开始了商业路线,2.0 提供了 DLL 方式的免费版本,源代码版本则必须购买商业的许可 licence;不过它留下了 1.6.5 版本的源代码,还是可以看到大部分的内部细节,但 2.0 版本中添加的对 Mozilla 浏览器的支持部分只有通过它生成的 HTML 和 JavaScript 脚本去窥测。

Lucene 是 Java 世界中常用的索引 API,使用它提供的方法可以为文本资料创建索引,并提供检索。(参考:NLucene 和 Lucene .NET)NLucene

admin | Lucene |
Lucene FuzzyQuery与WildCardQuery(通配符)

FuzzyQuery:

创建索引:

 

IndexWriter writer = new IndexWriter(path, new StandardAnalyzer(),
false);
writer.setUseCompoundFile(false);

Document doc1 = new Document();
Document doc2 = new Document();
Document doc3 = new Document();
Document doc4 = new Document();
Document doc5 = new Document();
Document doc6 = new Document();

Field f1 = new Field(“content”, ”word”, Field.Store.YES,
Field.Index.TOKENIZED);
Field f2 = new Field(“content”, ”work”, Field.Store.YES,
Field.Index.TOKENIZED);
Field f3 = new Field(“content”, ”seed”, Field.Store.YES,
Field.Index.TOKENIZED);
Field f4 = new Field(“content”, ”sword”, Field.Store.YES,
Field.Index.TOKENIZED);
Field f5 = new Field(“content”, ”world”, Field.Store.YES,
Field.Index.TOKENIZED);
Field

admin | Lucene |
Lucene MultiPhraseQuery多短语的搜索

前缀搜索:

 

IndexSearcher searcher=new IndexSearcher(INDEX_STORE_PATH);

MultiPhraseQuery query=new MultiPhraseQuery();
query.add(new Term(“bookname”,”钢”));
Term t1=new Term(“bookname”,”铁”);
Term t2=new Term(“bookname”,”和”);
Term t3=new Term(“bookname”,”要”);
query.add(new Term[]{t1,t2,t3});
Hits hits=searcher.search(query);
for(int i=0;i<hits.length();i++)
{
System.out.println(hits.doc(i));
}

后缀搜索:

 

IndexSearcher searcher=new IndexSearcher(INDEX_STORE_PATH);

MultiPhraseQuery query=new MultiPhraseQuery();

Term t1=new Term(“bookname”,”钢”);
Term t2=new Term(“bookname”,”和”);
query.add(new Term[]{t1,t2});

query.add(new Term(“bookname”,”铁”));
Hits hits=searcher.search(query);
for(int i=0;i<hits.length();i++)
{
System.out.println(hits.doc(i));
}

混合搜索:

 …

admin | Lucene |
Lucene评分(score)机制

通过Searcher.explain(Query query, int doc)方法可以查看某个文档的得分的具体构成。

在Lucene中score简单说是由 tf * idf * boost * lengthNorm计算得出的。

tf:是查询的词在文档中出现的次数的平方根
idf:表示反转文档频率,观察了一下所有的文档都一样,所以那就没什么用处,不会起什么决定作用。
boost:激励因子,可以通过setBoost方法设置,需要说明的通过field和doc都可以设置,所设置的值会同时起作用
lengthNorm:是由搜索的field的长度决定了,越长文档的分值越低。

所以我们编程能够控制score的就是设置boost值。

还有个问题,为什么一次查询后最大的分值总是1.0呢?
因为Lucene会把计算后,最大分值超过1.0的分值作为分母,其他的文档的分值都除以这个最大值,计算出最终的得分。

下面用代码和运行结果来进行说明:

Java代码 复制代码
  1. public class ScoreSortTest {
  2.     public final static String INDEX_STORE_PATH = “index”;
  3.     public