??xml version="1.0" encoding="utf-8" standalone="yes"?>
]]>
Query有很多子c, 各种不同的子cM表了不同的查询条?下文详述
QueryParser是一个非帔R用的帮助类Q他的作用是把用戯入的文本转换为内|的Query对象Q大多数web搜烦引擎都提供一个查询输入框来让用户输入查询条gQ。QueryParser内置提供了很多语法来使用可以输入各U高U条件的Query。比? "Hello AND world"会被解析Z个AND关系的BooleanQueryQ他包含两个TermQuery(Hell和world)。这些语法虽然强大,但都针对英文设计Q对我们需要中文搜索来说都不需要了解太多的QuerycdQ一般几个简单的够用了。QueryParser的用如?br />QueryParser.parse(String query, String field, Analyzer analyzer) throws ParseException
其中Qquery是用戯入的内容,field是搜索默认的fieldQ其他field需要显式指定)Qanalyzer是用来将用户输入的内容也作分析处理(分词Q,一般情况下q里的anaylyzer是index的时候采用的同一analyzer?br />另外我们也可以自己构造一个QueryParser: new QueryParser(String field, Analyzer a)(含义同上),q样做的好处是可以自己定义调整一些参?
搜烦l果的处?Hits对象
Hits对象是搜索结果的集合 主要有下面几个方?
length() ,q个Ҏ记录有多条l果q回(lazy loading)
doc(n) q回Wn个记?
id(in) q回Wn个记录的Document ID
score(n) Wn个记录的相关?U分)
׃搜烦的结果一般比较大Q从性能上考虑QHits对象q不会真正把所有的l果全部取回Q默认情况下是保留前100个记?对于一般的搜烦引擎,100个记录够了).
分页的处?br />100条记录还是太多,我们多半会每|C?0条记录,然后分ؓ若干|C,对于分页Q一般有两个办法
在session中保留indexreader对象和hit对象Q翻늚时候提取内?
不用sessionQ每ơ都单处理ؓ重新查询
lucene推荐先用第二个办法Q即每次都重新查询,q样做的好处是简单方便,不需要考虑session的问题,lucene的查询效率也能保证每ơ查询时间不长,除非真正有了性能问题Q否则不用考虑W一个办法?br />~存QRAMDirectory的用?br />RAMDirectory对象很好用,通过它,我们可以把一个普通的index完全d到内存中,用法如下Q?br />RAMDirectory ramDir = new RAMDirectory(dir);
q样的ramdir效率自然比真正的文gpȝ快很?br />Lucene的scoring法
lucence查询的纪录默认按照相兛_排序Q这个相兛_是score,scoring的算法是比较复杂?对于我们做应用的Z乎没有什么帮助,Q先说一下Term: 我的理解是TermZ个独立的查询?用户输入的的查询通过各种分词Q大写处理(正规?,消除stopwords{)以后Q会已Term为基本单位)Q几个关键参数稍微留意一下即可?br />Term在文章中出现的频率量
包含同一个Term的文章的频率
field中的boosting参数
term的长?
term在文章中的数?br />一般来?q些参数我们都不可能去调? 如果你想了解更多,IndexSearcherq提供了一个explainҎ, 通过传入一个Query和document ID,你可以得C个Explaination对象,他是对内部算法信息的单封?toString()一下就可以看到详细的说?
?创徏Query:各种query介绍
最普通的TermQuery
TermQuery最普? 用Term t=new Term("contents","cap"); new TermQuery(t)可以构?br />TermQuery把查询条件视Z个key, 要求和查询内容完全匹?比如Field.Keywordcd可以用TermQuery
RangeQuery
RangeQuery表示一个范围的搜烦条g,RangeQuery query = new RangeQuery(begin, end, included);
最后一个booleanDC是否包含边界条件本w? 用字W表CZؓ"[begin TO end]" 或?{begin TO end}"
PrefixQuery
思义,是表示以某某开头的查询, 字符表示?something*"
BooleanQuery
q个是一个组合的Query,你可以把各种Querydq去q标明他们的逻辑关系,d条g?br />public void add(Query query, boolean required, boolean prohibited)
Ҏ, 后两个boolean变量是标CAND or NOT三种关系 字符表示? AND or NOT" ?"+ -" ,一个BooleanQuery中可以添加多个Query, 如果过setMaxClauseCount(int)的?默认1024?的话,会抛出TooManyClauses错误.
PhraseQuery
表示不严D句的查询,比如"red pig"要匹?red fat pig","red fat big pig"{?PhraseQuery所以提供了一个setSlop()参数,在查询中,lucene会尝试调整单词的距离和位|?q个参数表示可以接受调整ơ数限制,如果实际的内容可以在q么多步内调整ؓ完全匚w,那么p视ؓ匚w.在默认情况下slop的值是0, 所以默认是不支持非严格匚w? 通过讄slop参数(比如"red pig"匚w"red fat pig"需?个slop来把pig后移??,我们可以让lucene来模p查? 值得注意的是,PhraseQuery不保证前后单词的ơ序,在上面的例子?"pig red"需?个slop,也就是如果slop如果大于{于2,那么"pig red"也会被认为是匚w?
WildcardQuery
使用??来表CZ个或多个字母比如wil*可以匚w wild ,wila ,wilxaaaa...,值得注意的是,在wildcard?只要是匹配上的纪?他们的相兛_都是一L,比如wilxaaaa和wild的对于wil*的相兛_是一L.
FuzzyQuery
q个Query对中文没有什么用?他能模糊匚w英文单词(前面的都是词l?,比如fuzzy和wuzzy他们可以看成cM, 对于英文的各U时态变化和复数形式,q个FuzzyQueryq算有用,匚wl果的相兛_是不一L.字符表示?"fuzzy~"
?QueryParser使用
对于搜烦引擎, 很多情况下用户只需要一个输入框p输入所有的查询条g(比如google), q时,QueryParser派上用Z,他的作用是把各U用戯入{为Query或者Queryl? 他把上面提到的Query的字W表C?Query.toString)转化为实际的Query对象,比如"wuzzy~"׃转换为FuzzyQuery, 不过QueryParser用到了Analyzer,所以QueryParser parseq后的Query再toString未必和原来的一?Query额外的语法有:
分组:Groupping
比如"(a AND b) or C",是括号分组,很容易理?br />FieldSelectiong
QueryParser的查询条件是寚w认的Fieldq行? 它在QueryParser解析的时候编码指? 如果用户需要在查询条g中选用另外的Field, 可以使用如下语法: fieldname:fielda, 如果是多个分l?可以用fieldname:(fielda fieldb fieldc)表示.
*号问?br />QueryParse默认不允?号出现在开始部分,q样做的目的主要是ؓ了防止用戯输入*来头D严重的性能问题Q会把所有记录读出)
boosting
通过hello^2.0 可以对helloq个termq行boostingQ?我想不到什么用户会q样么bt)
QueryParser是一个准备好?立即可以工作的帮助类,不过他还是提供了很多参数供程序员调整Q首?我们需要自己构造一个新的QueryParser,然后对他的各U参数来定制?
]]>
通过本文你将了解Lucene的基本查询语?q可以学习所有的试代码已加Z?
具体的查询语?/strong>
在了解了SQL? 你是否想了解一下查询语法树?在这里简要介l一些能被Lucene直接使用的查询语?
1.
TermQuery
查询某个特定的词,在文章开始的例子中已有介l?常用于查询关键字.
[Test]
public void Keyword()
{
IndexSearcher searcher = new IndexSearcher(directory);
Term t = new Term("isbn", "1930110995");
Query query = new TermQuery(t);
Hits hits = searcher.Search(query);
Assert.AreEqual(1, hits.Length(), "JUnit in Action");
}
注意Lucene中的关键?是需要用户去保证唯一性的.
TermQuery和QueryParse
只要在QueryParse的ParseҎ中只有一个word,׃自动转换成TermQuery.
2.
RangeQuery
用于查询范围,通常用于旉,q是来看例子:
namespace dotLucene.inAction.BasicSearch
{
public class RangeQueryTest : LiaTestCase
{
private Term begin, end;
[SetUp]
protected override void Init()
{
begin = new Term("pubmonth", "200004");
end = new Term("pubmonth", "200206");
base.Init();
}
[Test]
public void Inclusive()
{
RangeQuery query = new RangeQuery(begin, end, true);
IndexSearcher searcher = new IndexSearcher(directory);
Hits hits = searcher.Search(query);
Assert.AreEqual(1, hits.Length());
}
[Test]
public void Exclusive()
{
RangeQuery query = new RangeQuery(begin, end, false);
IndexSearcher searcher = new IndexSearcher(directory);
Hits hits = searcher.Search(query);
Assert.AreEqual(0, hits.Length());
}
}
}
RangeQuery的第三个参数用于表示是否包含该v止日?
RangeQuery ?/strong> QueryParse
[Test]
public void TestQueryParser()
{
Query query = QueryParser.Parse("pubmonth:[200004 TO 200206]", "subject", new SimpleAnalyzer());
Assert.IsTrue(query is RangeQuery);
IndexSearcher searcher = new IndexSearcher(directory);
Hits hits = searcher.Search(query);
query = QueryParser.Parse("{200004 TO 200206}", "pubmonth", new SimpleAnalyzer());
hits = searcher.Search(query);
Assert.AreEqual(0, hits.Length(), "JDwA in 200206");
}
Lucene用[] 和{}分别表示包含和不包含.
3. PrefixQuery
用于搜烦是否包含某个特定前缀,常用于Catalog的检?
[Test]
public void TestPrefixQuery()
{
PrefixQuery query = new PrefixQuery(new Term("category", "/Computers"));
IndexSearcher searcher = new IndexSearcher(directory);
Hits hits = searcher.Search(query);
Assert.AreEqual(2, hits.Length());
query = new PrefixQuery(new Term("category", "/Computers/JUnit"));
hits = searcher.Search(query);
Assert.AreEqual(1, hits.Length(), "JUnit in Action");
}
PrefixQuery和QueryParse
[Test]
public void TestQueryParser()
{
QueryParser qp = new QueryParser("category", new SimpleAnalyzer());
qp.SetLowercaseWildcardTerms(false);
Query query =qp.Parse("/Computers*");
Console.Out.WriteLine("query = {0}", query.ToString());
IndexSearcher searcher = new IndexSearcher(directory);
Hits hits = searcher.Search(query);
Assert.AreEqual(2, hits.Length());
query =qp.Parse("/Computers/JUnit*");
hits = searcher.Search(query);
Assert.AreEqual(1, hits.Length(), "JUnit in Action");
}
q里需要注意的是我们用了QueryParser对象,而不是QueryParserc? 原因在于使用对象可以对QueryParser的一些默认属性进行修?比如在上面的例子中我们的category是大写的,而QueryParser默认会把所有的?的查询字W串变成写/computer*. q样我们׃查不到原文中?Computers* ,所以我们需要通过讄QueryParser的默认属性来改变q一默认选项.即qp.SetLowercaseWildcardTerms(false)所做的工作.
4.
BooleanQuery
用于试满多个条g.
下面两个例子用于分别试了满与条g和或条g的情?
[Test]
public void And()
{
TermQuery searchingBooks =
new TermQuery(new Term("subject", "junit"));
RangeQuery currentBooks =
new RangeQuery(new Term("pubmonth", "200301"),
new Term("pubmonth", "200312"),
true);
BooleanQuery currentSearchingBooks = new BooleanQuery();
currentSearchingBooks.Add(searchingBooks, true, false);
currentSearchingBooks.Add(currentBooks, true, false);
IndexSearcher searcher = new IndexSearcher(directory);
Hits hits = searcher.Search(currentSearchingBooks);
AssertHitsIncludeTitle(hits, "JUnit in Action");
}
[Test]
public void Or()
{
TermQuery methodologyBooks = new TermQuery(
new Term("category",
"/Computers/JUnit"));
TermQuery easternPhilosophyBooks = new TermQuery(
new Term("category",
"/Computers/Ant"));
BooleanQuery enlightenmentBooks = new BooleanQuery();
enlightenmentBooks.Add(methodologyBooks, false, false);
enlightenmentBooks.Add(easternPhilosophyBooks, false, false);
IndexSearcher searcher = new IndexSearcher(directory);
Hits hits = searcher.Search(enlightenmentBooks);
Console.Out.WriteLine("or = " + enlightenmentBooks);
AssertHitsIncludeTitle(hits, "Java Development with Ant");
AssertHitsIncludeTitle(hits, "JUnit in Action");
}
什么时候是与什么时候又是或? 关键在于BooleanQuery对象的AddҎ的参?
参数一是待d的查询条?
参数二Required表示q个条g必须满? True表示必须满, False表示可以不满条g.
参数三Prohibited表示q个条g必须拒绝? True表示q么满q个条g的结果要排除, False表示可以满该条?
q样会有三种l合情况,如下表所C?
BooleanQuery ?/strong> QueryParse
[Test]
public void TestQueryParser()
{
Query query = QueryParser.Parse("pubmonth:[200301 TO 200312] AND junit", "subject", new SimpleAnalyzer());
IndexSearcher searcher = new IndexSearcher(directory);
Hits hits = searcher.Search(query);
Assert.AreEqual(1, hits.Length());
query = QueryParser.Parse("/Computers/JUnit OR /Computers/Ant", "category", new WhitespaceAnalyzer());
hits = searcher.Search(query);
Assert.AreEqual(2, hits.Length());
}
注意AND和OR的大?如果惌Q与非B q A AND –B 表示, +A –B也可?
默认的情况下QueryParser会把I格认ؓ是或关系,pgoogle一?但是你可以通过QueryParser对象修改q一属?
[Test]
public void TestQueryParserDefaultAND()
{
QueryParser qp = new QueryParser("subject", new SimpleAnalyzer());
qp.SetOperator(QueryParser.DEFAULT_OPERATOR_AND );
Query query = qp.Parse("pubmonth:[200301 TO 200312] junit");
IndexSearcher searcher = new IndexSearcher(directory);
Hits hits = searcher.Search(query);
Assert.AreEqual(1, hits.Length());
}
5. PhraseQuery
查询短语,q里面主要有一个slop的概? 也就是各个词之间的位Ud? q个g影响到结果的评分.如果slop?,当然最匚w.看看下面的例子就比较Ҏ明白?有关slop的计用户就不需要理解了,不过slop太大的时候对查询效率是有影响?所以在实际使用中要把该D一? PhraseQuery对于短语的顺序是不管?q点在查询时除了提高命中率外,也会Ҏ能产生很大的媄? 利用SpanNearQuery可以对短语的序q行控制,提高性能.
[SetUp]
protected void Init()
{
// set up sample document
RAMDirectory directory = new RAMDirectory();
IndexWriter writer = new IndexWriter(directory,
new WhitespaceAnalyzer(), true);
Document doc = new Document();
doc.Add(Field.Text("field",
"the quick brown fox jumped over the lazy dog"));
writer.AddDocument(doc);
writer.Close();
searcher = new IndexSearcher(directory);
}
private bool matched(String[] phrase, int slop)
{
PhraseQuery query = new PhraseQuery();
query.SetSlop(slop);
for (int i = 0; i < phrase.Length; i++)
{
query.Add(new Term("field", phrase[i]));
}
Hits hits = searcher.Search(query);
return hits.Length() > 0;
}
[Test]
public void SlopComparison()
{
String[] phrase = new String[]{"quick", "fox"};
Assert.IsFalse(matched(phrase, 0), "exact phrase not found");
Assert.IsTrue(matched(phrase, 1), "close enough");
}
[Test]
public void Reverse()
{
String[] phrase = new String[] {"fox", "quick"};
Assert.IsFalse(matched(phrase, 2), "exact phrase not found");
Assert.IsTrue(matched(phrase, 3), "close enough");
}
[Test]
public void Multiple()-
{
Assert.IsFalse(matched(new String[] {"quick", "jumped", "lazy"}, 3), "not close enough");
Assert.IsTrue(matched(new String[] {"quick", "jumped", "lazy"}, 4), "just enough");
Assert.IsFalse(matched(new String[] {"lazy", "jumped", "quick"}, 7), "almost but not quite");
Assert.IsTrue(matched(new String[] {"lazy", "jumped", "quick"}, 8), "bingo");
}
PhraseQuery和QueryParse
利用QueryParseq行短语查询的时候要先设定slop的?有两U方式如下所C?/p>
[Test]
public void TestQueryParser()
{
Query q1 = QueryParser.Parse(""quick fox"",
"field", new SimpleAnalyzer());
Hits hits1 = searcher.Search(q1);
Assert.AreEqual(hits1.Length(), 0);
Query q2 = QueryParser.Parse(""quick fox"~1", //W一U方?br /> "field", new SimpleAnalyzer());
Hits hits2 = searcher.Search(q2);
Assert.AreEqual(hits2.Length(), 1);
QueryParser qp = new QueryParser("field", new SimpleAnalyzer());
qp.SetPhraseSlop(1); //W二U方?br /> Query q3=qp.Parse(""quick fox"");
Assert.AreEqual(""quick fox"~1", q3.ToString("field"),"sloppy, implicitly");
Hits hits3 = searcher.Search(q2);
Assert.AreEqual(hits3.Length(), 1);
}
6. WildcardQuery
通配W搜?需要注意的是child, mildew的分值是一L.
[Test]
public void Wildcard()
{
IndexSingleFieldDocs(new Field[]
{
Field.Text("contents", "wild"),
Field.Text("contents", "child"),
Field.Text("contents", "mild"),
Field.Text("contents", "mildew")
});
IndexSearcher searcher = new IndexSearcher(directory);
Query query = new WildcardQuery(
new Term("contents", "?ild*"));
Hits hits = searcher.Search(query);
Assert.AreEqual(3, hits.Length(), "child no match");
Assert.AreEqual(hits.Score(0), hits.Score(1), 0.0, "score the same");
Assert.AreEqual(hits.Score(1), hits.Score(2), 0.0, "score the same");
}
WildcardQuery和QueryParse
需要注意的是出于性能的考虑使用QueryParse的时?不允许在开头就使用׃用通配W?
同样处于性能考虑会将只在末尾含有*的查询词转换为PrefixQuery.
[Test, ExpectedException(typeof (ParseException))]
public void TestQueryParserException()
{
Query query = QueryParser.Parse("?ild*", "contents", new WhitespaceAnalyzer());
}
[Test]
public void TestQueryParserTailAsterrisk()
{
Query query = QueryParser.Parse("mild*", "contents", new WhitespaceAnalyzer());
Assert.IsTrue(query is PrefixQuery);
Assert.IsFalse(query is WildcardQuery);
}
[Test]
public void TestQueryParser()
{
Query query = QueryParser.Parse("mi?d*", "contents", new WhitespaceAnalyzer());
Hits hits = searcher.Search(query);
Assert.AreEqual(2, hits.Length());
}
7. FuzzyQuery
模糊查询, 需要注意的是两个匹配项的分值是不同?q点和WildcardQuery是不同的
[Test]
public void Fuzzy()
{
Query query = new FuzzyQuery(new Term("contents", "wuzza"));
Hits hits = searcher.Search(query);
Assert.AreEqual( 2, hits.Length(),"both close enough");
Assert.IsTrue(hits.Score(0) != hits.Score(1),"wuzzy closer than fuzzy");
Assert.AreEqual("wuzzy", hits.Doc(0).Get("contents"),"wuzza bear");
}
FuzzyQuery和QueryParse
注意和PhraseQuery中表Cslop的区?前者~后要跟数?
[Test]
public void TestQueryParser()
{
Query query =QueryParser.Parse("wuzza~","contents",new SimpleAnalyzer());
Hits hits = searcher.Search(query);
Assert.AreEqual( 2, hits.Length(),"both close enough");
}
Lucene 是一个基?Java 的全文信息检索工具包Q它不是一个完整的搜烦应用E序Q而是Z的应用程序提供烦引和搜烦功能。Lucene 目前?Apache Jakarta 家族中的一个开源项目。也是目前最为流行的Z Java 开源全文检索工具包?/p>
目前已经有很多应用程序的搜烦功能是基?Lucene 的,比如 Eclipse 的帮助系l的搜烦功能。Lucene 能够为文本类型的数据建立索引Q所以你只要能把你要索引的数据格式{化的文本的,Lucene p对你的文档进行烦引和搜烦。比如你要对一?HTML 文档QPDF 文档q行索引的话你就首先需要把 HTML 文档?PDF 文档转化成文本格式的Q然后将转化后的内容交给 Lucene q行索引Q然后把创徏好的索引文g保存到磁盘或者内存中Q最后根据用戯入的查询条g在烦引文件上q行查询。不指定要烦引的文档的格式也?Lucene 能够几乎适用于所有的搜烦应用E序?/p>
?1 表示了搜索应用程序和 Lucene 之间的关p,也反映了利用 Lucene 构徏搜烦应用E序的流E:
索引是现代搜索引擎的核心Q徏立烦引的q程是把源数据处理成非常方便查询的索引文g的过E。ؓ什么烦引这么重要呢Q试想你现在要在大量的文档中搜烦含有某个关键词的文档Q那么如果不建立索引的话你就需要把q些文档序的读入内存,然后查这个文章中是不是含有要查找的关键词Q这L话就会耗费非常多的旉Q想x索引擎可是在毫秒U的旉内查扑և要搜索的l果的。这是׃建立了烦引的原因Q你可以把烦引想象成q样一U数据结构,他能够你快速的随机讉K存储在烦引中的关键词Q进而找到该关键词所兌的文档。Lucene 采用的是一U称为反向烦引(inverted indexQ的机制。反向烦引就是说我们l护了一个词/短语表,对于q个表中的每个词/短语Q都有一个链表描qC有哪些文档包含了q个?短语。这样在用户输入查询条g的时候,p非常快的得到搜烦l果。我们将在本pd文章的第二部分详l介l?Lucene 的烦引机Ӟ׃ Lucene 提供了简单易用的 APIQ所以即使读者刚开始对全文本进行烦引的机制q不太了解,也可以非常容易的使用 Lucene 对你的文档实现烦引?/p>
Ҏ档徏立好索引后,可以在q些索引上面q行搜烦了。搜索引擎首先会Ҏ索的关键词进行解析,然后再在建立好的索引上面q行查找Q最l返回和用户输入的关键词相关联的文档?/p>
Lucene 软g包的发布形式是一?JAR 文gQ下面我们分析一下这?JAR 文g里面的主要的 JAVA 包,使读者对之有个初步的了解?/p>
Package: org.apache.lucene.document
q个包提供了一些ؓ装要烦引的文档所需要的c,比如 Document, Field。这P每一个文档最l被装成了一?Document 对象?/p>
Package: org.apache.lucene.analysis
q个包主要功能是Ҏ档进行分词,因ؓ文档在徏立烦引之前必要q行分词Q所以这个包的作用可以看成是为徏立烦引做准备工作?/p>
Package: org.apache.lucene.index
q个包提供了一些类来协助创建烦引以及对创徏好的索引q行更新。这里面有两个基的类QIndexWriter ?IndexReaderQ其?IndexWriter 是用来创建烦引ƈd文档到烦引中的,IndexReader 是用来删除烦引中的文档的?/p>
Package: org.apache.lucene.search
q个包提供了对在建立好的索引上进行搜索所需要的cR比?IndexSearcher ?Hits, IndexSearcher 定义了在指定的烦引上q行搜烦的方法,Hits 用来保存搜烦得到的结果?/p>
假设我们的电脑的目录中含有很多文本文档,我们需要查扑֓些文档含有某个关键词。ؓ了实现这U功能,我们首先利用 Lucene 对这个目录中的文档徏立烦引,然后在徏立好的烦引中搜烦我们所要查扄文档。通过q个例子读者会对如何利?Lucene 构徏自己的搜索应用程序有个比较清楚的认识?/p>
ZҎ档进行烦引,Lucene 提供了五个基的类Q他们分别是 Document, Field, IndexWriter, Analyzer, Directory。下面我们分别介l一下这五个cȝ用途:
Document
Document 是用来描q文档的Q这里的文档可以指一?HTML 面Q一电子邮Ӟ或者是一个文本文件。一?Document 对象由多?Field 对象l成的。可以把一?Document 对象惌成数据库中的一个记录,而每?Field 对象是记录的一个字Dc?/p>
Field
Field 对象是用来描qC个文档的某个属性的Q比如一电子邮件的标题和内容可以用两个 Field 对象分别描述?/p>
Analyzer
在一个文档被索引之前Q首先需要对文档内容q行分词处理Q这部分工作是?Analyzer 来做的。Analyzer cL一个抽象类Q它有多个实现。针对不同的语言和应用需要选择适合?Analyzer。Analyzer 把分词后的内容交l?IndexWriter 来徏立烦引?/p>
IndexWriter
IndexWriter ?Lucene 用来创徏索引的一个核心的c,他的作用是把一个个?Document 对象加到索引中来?/p>
Directory
q个cM表了 Lucene 的烦引的存储的位|,q是一个抽象类Q它目前有两个实玎ͼW一个是 FSDirectoryQ它表示一个存储在文gpȝ中的索引的位|。第二个?RAMDirectoryQ它表示一个存储在内存当中的烦引的位置?/p>
熟悉了徏立烦引所需要的q些cdQ我们就开始对某个目录下面的文本文件徏立烦引了Q清?l出了对某个目录下的文本文g建立索引的源代码?/p>
清单 1. Ҏ本文件徏立烦?/b>
package TestLucene; import java.io.File; import java.io.FileReader; import java.io.Reader; import java.util.Date; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.index.IndexWriter; /** * This class demonstrate the process of creating index with Lucene * for text files */ public class TxtFileIndexer { public static void main(String[] args) throws Exception{ //indexDir is the directory that hosts Lucene's index files File indexDir = new File("D:\\luceneIndex"); //dataDir is the directory that hosts the text files that to be indexed File dataDir = new File("D:\\luceneData"); Analyzer luceneAnalyzer = new StandardAnalyzer(); File[] dataFiles = dataDir.listFiles(); IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true); long startTime = new Date().getTime(); for(int i = 0; i < dataFiles.length; i++){ if(dataFiles[i].isFile() && dataFiles[i].getName().endsWith(".txt")){ System.out.println("Indexing file " + dataFiles[i].getCanonicalPath()); Document document = new Document(); Reader txtReader = new FileReader(dataFiles[i]); document.add(Field.Text("path",dataFiles[i].getCanonicalPath())); document.add(Field.Text("contents",txtReader)); indexWriter.addDocument(document); } } indexWriter.optimize(); indexWriter.close(); long endTime = new Date().getTime(); System.out.println("It takes " + (endTime - startTime) + " milliseconds to create index for the files in directory " + dataDir.getPath()); } } |
在清?中,我们注意到类 IndexWriter 的构造函数需要三个参敎ͼW一个参数指定了所创徏的烦引要存放的位|,他可以是一?File 对象Q也可以是一?FSDirectory 对象或?RAMDirectory 对象。第二个参数指定?Analyzer cȝ一个实玎ͼ也就是指定这个烦引是用哪个分词器Ҏ挡内容进行分词。第三个参数是一个布型的变量,如果?true 的话׃表创Z个新的烦引,?false 的话׃表在原来索引的基上进行操作。接着E序遍历了目录下面的所有文本文档,qؓ每一个文本文档创Z一?Document 对象。然后把文本文档的两个属性:路径和内容加入到了两?Field 对象中,接着在把q两?Field 对象加入?Document 对象中,最后把q个文档?IndexWriter cȝ add Ҏ加入到烦引中厅R这h们便完成了烦引的创徏。接下来我们q入在徏立好的烦引上q行搜烦的部分?/p>
利用Luceneq行搜烦像建立索引一样也是非常方便的。在上面一部分中,我们已经Z个目录下的文本文档徏立好了烦引,现在我们p在这个烦引上q行搜烦以找到包含某个关键词或短语的文档。Lucene提供了几个基的类来完成这个过E,它们分别是呢IndexSearcher, Term, Query, TermQuery, Hits. 下面我们分别介绍q几个类的功能?/p>
Query
q是一个抽象类Q他有多个实玎ͼ比如TermQuery, BooleanQuery, PrefixQuery. q个cȝ目的是把用户输入的查询字W串装成Lucene能够识别的Query?/p>
Term
Term是搜索的基本单位Q一个Term对象有两个Stringcd的域l成。生成一个Term对象可以有如下一条语句来完成QTerm term = new Term(“fieldName?”queryWord?; 其中W一个参C表了要在文档的哪一个Field上进行查找,W二个参C表了要查询的关键词?/p>
TermQuery
TermQuery是抽象类Query的一个子c,它同时也是Lucene支持的最为基本的一个查询类。生成一个TermQuery对象由如下语句完成: TermQuery termQuery = new TermQuery(new Term(“fieldName?”queryWord?); 它的构造函数只接受一个参敎ͼ那就是一个Term对象?/p>
IndexSearcher
IndexSearcher是用来在建立好的索引上进行搜索的。它只能以只ȝ方式打开一个烦引,所以可以有多个IndexSearcher的实例在一个烦引上q行操作?/p>
Hits
Hits是用来保存搜索的l果的?/p>
介绍完这些搜索所必须的类之后Q我们就开始在之前所建立的烦引上q行搜烦了,清单2l出了完成搜索功能所需要的代码?/p>
清单2 Q在建立好的索引上进行搜?/b>
package TestLucene; import java.io.File; import org.apache.lucene.document.Document; import org.apache.lucene.index.Term; import org.apache.lucene.search.Hits; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.TermQuery; import org.apache.lucene.store.FSDirectory; /** * This class is used to demonstrate the * process of searching on an existing * Lucene index * */ public class TxtFileSearcher { public static void main(String[] args) throws Exception{ String queryStr = "lucene"; //This is the directory that hosts the Lucene index File indexDir = new File("D:\\luceneIndex"); FSDirectory directory = FSDirectory.getDirectory(indexDir,false); IndexSearcher searcher = new IndexSearcher(directory); if(!indexDir.exists()){ System.out.println("The Lucene index is not exist"); return; } Term term = new Term("contents",queryStr.toLowerCase()); TermQuery luceneQuery = new TermQuery(term); Hits hits = searcher.search(luceneQuery); for(int i = 0; i < hits.length(); i++){ Document document = hits.doc(i); System.out.println("File: " + document.get("path")); } } } |
在清?中,cIndexSearcher的构造函数接受一个类型ؓDirectory的对象,Directory是一个抽象类Q它目前有两个子c:FSDirctory和RAMDirectory. 我们的程序中传入了一个FSDirctory对象作ؓ其参敎ͼ代表了一个存储在盘上的索引的位|。构造函数执行完成后Q代表了q个IndexSearcher以只ȝ方式打开了一个烦引。然后我们程序构造了一个Term对象Q通过q个Term对象Q我们指定了要在文档的内容中搜烦包含关键词”lucene”的文档。接着利用q个Term对象构造出TermQuery对象q把q个TermQuery对象传入到IndexSearcher的searchҎ中进行查询,q回的结果保存在Hits对象中。最后我们用了一个@环语句把搜烦到的文档的\径都打印了出来。好了,我们的搜索应用程序已l开发完毕,怎么P利用Lucene开发搜索应用程序是不是很简单?br />
本文首先介绍?Lucene 的一些基本概念,然后开发了一个应用程序演CZ利用 Lucene 建立索引q在该烦引上q行搜烦的过E。希望本文能够ؓ学习 Lucene 的读者提供帮助?/p>