在前面的系列我們一直在介紹有關(guān)索引建立的問題,現(xiàn)在是該利用這些索引來進(jìn)行搜索的時(shí)候了,Lucene良好的架構(gòu)使得我們只需要很少的幾行代碼就可以為我們的應(yīng)用加上搜索的功能,首先讓我們來認(rèn)識(shí)一下搜索時(shí)最常用的幾個(gè)類.
查詢特定的某個(gè)概念
當(dāng)我們搜索完成的時(shí)候會(huì)返回一個(gè)按Sorce排序的結(jié)果集Hits. 這里的Score就是接近度的意思,象Google那樣每個(gè)頁面都會(huì)有一個(gè)分值,搜索結(jié)果按分值排列. 如同你使用Google一樣,你不可能查看所有的結(jié)果, 你可能只查看第一個(gè)結(jié)果所以Hits返回的不是所有的匹配文檔本身, 而僅僅是實(shí)際文檔的引用. 通過這個(gè)引用你可以獲得實(shí)際的文檔.原因很好理解, 如果直接返回匹配文檔,數(shù)據(jù)量太大,而很多的結(jié)果你甚至不會(huì)去看, 想想你會(huì)去看Google 搜索結(jié)果10頁以后的內(nèi)容嗎?
下面用一個(gè)例子來簡(jiǎn)要介紹一下Search
先建立索引
namespace dotLucene.inAction.BasicSearch protected String[] unindexed = {"Java Development with Ant", "JUnit in Action"};
protected String[] unstored = {
"we have ant and junit",
"junit use a mock,ant is also",
};
protected String[] text1 = {
"ant junit",
"junit mock"
};
protected String[] text3 = {
"/Computers/Ant", "/Computers/JUnit"
};
[SetUp]
protected void Init()
{
string indexDir = "index";
dir = FSDirectory.GetDirectory(indexDir, true);
AddDocuments(dir);
}
for (int i = 0; i < keywords.Length; i++)
{
Document doc = new Document();
doc.Add(Field.Keyword("isbn", keywords[i]));
doc.Add(Field.UnIndexed("title", unindexed[i]));
doc.Add(Field.UnStored("contents", unstored[i]));
doc.Add(Field.Text("subject", text1[i]));
doc.Add(Field.Text("pubmonth", text2[i]));
doc.Add(Field.Text("category", text3[i]));
writer.AddDocument(doc);
}
writer.Optimize();
writer.Close();
}
protected virtual Analyzer GetAnalyzer()
{
PerFieldAnalyzerWrapper analyzer = new PerFieldAnalyzerWrapper(
new SimpleAnalyzer());
analyzer.AddAnalyzer("pubmonth", new WhitespaceAnalyzer());
analyzer.AddAnalyzer("category", new WhitespaceAnalyzer());
return analyzer;
}
}
}
這里用到了一些有關(guān)Analyzer的知識(shí),將放在以后的系列中介紹.
查詢特定的某個(gè)概念
然后利用利用TermQery來搜索一個(gè)Term(你可以把它理解為一個(gè)Word)
[Test]
public void Term()
{
IndexSearcher searcher = new IndexSearcher(directory);
Term t = new Term("subject", "ant");
Query query = new TermQuery(t);
Hits hits = searcher.Search(query);
Assert.AreEqual(1, hits.Length(), "JDwA");
t = new Term("subject", "junit");
hits = searcher.Search(new TermQuery(t));
Assert.AreEqual(2, hits.Length());
searcher.Close();
}
利用QueryParse簡(jiǎn)化查詢語句
顯然對(duì)于各種各樣的查詢(與或關(guān)系,等等各種復(fù)雜的查詢,在下面將介紹),你不希望一一對(duì)應(yīng)的為它們寫出相應(yīng)的XXXQuery. Lucene已經(jīng)為你考慮到了這點(diǎn), 通過使用QueryParse這個(gè)類, 你只需要寫出我們常見的搜索語句, Lucene會(huì)在內(nèi)部自動(dòng)做一個(gè)轉(zhuǎn)換.
這個(gè)過程有點(diǎn)類似于數(shù)據(jù)庫搜索, 我們已經(jīng)習(xí)慣于使用SQL查詢語句,其實(shí)在數(shù)據(jù)庫的內(nèi)部是要做一個(gè)轉(zhuǎn)換的, 因?yàn)閿?shù)據(jù)庫不認(rèn)得SQL語句,它只認(rèn)得查詢語法樹.
讓我們來看一個(gè)例子.
[Test]
public void TestQueryParser()
{
IndexSearcher searcher = new IndexSearcher(directory);
Query query = QueryParser.Parse("+JUNIT +ANT -MOCK",
"contents",
new SimpleAnalyzer());
Hits hits = searcher.Search(query);
Assert.AreEqual(1, hits.Length());
Document d = hits.Doc(0);
Assert.AreEqual("Java Development with Ant", d.Get("title"));
query = QueryParser.Parse("mock OR junit",
"contents",
new SimpleAnalyzer());
hits = searcher.Search(query);
Assert.AreEqual(2, hits.Length(), "JDwA and JIA");
}
由以上的代碼可以看出我們不需要為每種特定查詢而去設(shè)定XXXQuery 通過QueryParse類的靜態(tài)方法Parse就可以很方便的將可讀性好的查詢口語轉(zhuǎn)換成Lucene內(nèi)部所使用的各種復(fù)雜的查詢語句. 有一點(diǎn)需要注意:在Parse方法中我們使用了SimpleAnalyzer, 這時(shí)候會(huì)將查詢語句做一些變換,比如這里將JUNIT 等等大寫字母變成了小寫字母,所以才能搜索到(因?yàn)槲覀冊(cè)诮⑺饕臅r(shí)候使用的是小寫),如果你將StanderAnalyzer變成WhitespaceAnalyzer就會(huì)搜索不到.具體原理以后再說.
+A +B表示A和B要同時(shí)存在,-C表示C不存在,A OR B表示A或B二者有一個(gè)存在就可以..具體的查詢規(guī)則如下:
其中title等等的field表示你在建立索引時(shí)所采用的屬性名.
聯(lián)系客服