在前面的系列我們一直在介紹有關索引建立的問題,現在是該利用這些索引來進行搜索的時候了,Lucene良好的架構使得我們只需要很少的幾行代碼就可以爲我們的應用加上搜索的功能,首先讓我們來認識一下搜索時最常用的幾個類.
查詢特定的某個概念
當我們搜索完成的時候會返回一個按Sorce排序的結果集Hits. 這裏的Score就是接近度的意思,象Google那樣每個頁面都會有一個分值,搜索結果按分值排列. 如同你使用Google一樣,你不可能查看所有的結果, 你可能只查看第一個結果所以Hits返回的不是所有的匹配文檔本身, 而僅僅是實際文檔的引用. 通過這個引用你可以獲得實際的文檔.原因很好理解, 如果直接返回匹配文檔,數據量太大,而很多的結果你甚至不會去看, 想想你會去看Google 搜索結果10頁以後的內容嗎?
下面用一個例子來簡要介紹一下Search
先建立索引
namespace dotLucene.inAction.BasicSearch{
[TestFixture]
public class BaseIndexingTestCase
{
protected String[] keywords = {"1930110994", "1930110995"};
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",
};
"we have ant and junit",
"junit use a mock,ant is also",
};
protected String[] text1 = {
"ant junit",
"junit mock"
};
protected String[] text2 = {"ant junit",
"junit mock"
};
"200206",
"200309"
};
protected String[] text3 = {
"/Computers/Ant", "/Computers/JUnit"
};
protected Directory dir;
"/Computers/Ant", "/Computers/JUnit"
};
[SetUp]
protected void Init()
{
string indexDir = "index";
dir = FSDirectory.GetDirectory(indexDir, true);
AddDocuments(dir);
}
protected void AddDocuments(Directory dir)protected void Init()
{
string indexDir = "index";
dir = FSDirectory.GetDirectory(indexDir, true);
AddDocuments(dir);
}
{
IndexWriter writer=new IndexWriter(dir, GetAnalyzer(), true);
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);
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();
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;
}
}
}
這裏用到了一些有關Analyzer的知識,將放在以後的系列中介紹.
查詢特定的某個概念
然後利用利用TermQery來搜索一個Term(你可以把它理解爲一個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");
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());
hits = searcher.Search(new TermQuery(t));
Assert.AreEqual(2, hits.Length());
searcher.Close();
}
}
利用QueryParse簡化查詢語句
顯然對於各種各樣的查詢(與或關係,等等各種複雜的查詢,在下面將介紹),你不希望一一對應的爲它們寫出相應的XXXQuery. Lucene已經爲你考慮到了這點, 通過使用QueryParse這個類, 你只需要寫出我們常見的搜索語句, Lucene會在內部自動做一個轉換.
這個過程有點類似於數據庫搜索, 我們已經習慣於使用SQL查詢語句,其實在數據庫的內部是要做一個轉換的, 因爲數據庫不認得SQL語句,它只認得查詢語法樹.
讓我們來看一個例子.
[Test]
public void TestQueryParser()
{
IndexSearcher searcher = new IndexSearcher(directory);
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"));
"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");
}
"contents",
new SimpleAnalyzer());
hits = searcher.Search(query);
Assert.AreEqual(2, hits.Length(), "JDwA and JIA");
}
由以上的代碼可以看出我們不需要爲每種特定查詢而去設定XXXQuery 通過QueryParse類的靜態方法Parse就可以很方便的將可讀性好的查詢口語轉換成Lucene內部所使用的各種複雜的查詢語句. 有一點需要注意:在Parse方法中我們使用了SimpleAnalyzer, 這時候會將查詢語句做一些變換,比如這裏將JUNIT 等等大寫字母變成了小寫字母,所以才能搜索到(因爲我們在建立索引的時候使用的是小寫),如果你將StanderAnalyzer變成WhitespaceAnalyzer就會搜索不到.具體原理以後再說.
+A +B表示A和B要同時存在,-C表示C不存在,A OR B表示A或B二者有一個存在就可以..具體的查詢規則如下:
其中title等等的field表示你在建立索引時所採用的屬性名.