lucene.net 应用资料
本文介紹了什么是Lucene,Lucene能做什么.
如何從一個(gè)文件夾下的所有txt文件中查找特定的詞?
本文將圍繞該個(gè)實(shí)例介紹了lucene.net的索引的建立以及如何針對索引進(jìn)行搜索.最后還將給出源代碼供大家學(xué)習(xí).
源代碼下載
What’s Lucene
Lucene是一個(gè)信息檢索的函數(shù)庫(Library),利用它你可以為你的應(yīng)用加上索引和搜索的功能.
Lucene的使用者不需要深入了解有關(guān)全文檢索的知識(shí),僅僅學(xué)會(huì)使用庫中的一個(gè)類,你就為你的應(yīng)用實(shí)現(xiàn)全文檢索的功能.
不過千萬別以為Lucene是一個(gè)象google那樣的搜索引擎,Lucene甚至不是一個(gè)應(yīng)用程序,它僅僅是一個(gè)工具, 一個(gè)Library.你也可以把它理解為一個(gè)將索引,搜索功能封裝的很好的一套簡單易用的API.利用這套API你可以做很多有關(guān)搜索的事情,而且很方 便.
What Can Lucene Do
Lucene可以對任何的數(shù)據(jù)做索引和搜索. Lucene不管數(shù)據(jù)源是什么格式,只要它能被轉(zhuǎn)化為文字的形式,就可以被Lucene所分析利用.也就是說不管是MS word, Html ,pdf還是其他什么形式的文件只要你可以從中抽取出文字形式的內(nèi)容就可以被Lucene所用.你就可以用Lucene對它們進(jìn)行索引以及搜索.
How To Use Lucene --- A Simple Example
示例介紹:
為作為輸入?yún)?shù)的文件夾下的所有txt類型的文件做索引,做好的索引文件放入index文件夾.
然后在索引的基礎(chǔ)上對文件進(jìn)行全文搜索.
1.?????? 建立索引
IndexWriter writer = new IndexWriter("index", new StandardAnalyzer(), true);
IndexDocs(writer, new System.IO.FileInfo(args[0]));??????????????
writer.Optimize();
writer.Close();
IndexWriter是對索引進(jìn)行寫操作的一個(gè)類,利用它可以創(chuàng)建一個(gè)索引對象然后往其中添加文件.需要注意它并不是唯一可以修改索引的類.在索引建好后利用其他類還可以對其進(jìn)行修改.
構(gòu)造函數(shù)第一個(gè)參數(shù)是建立的索引所要放的文件夾的名字.第二個(gè)參數(shù)是一個(gè)分析對象,主要用于從文本中抽取那些需要建立索引 的內(nèi)容,把不需要參與建索引的文本內(nèi)容去掉.比如去掉一些a the之類的常用詞,還有決定是否大小寫敏感.不同的選項(xiàng)通過指定不同的分析對象控制.第三個(gè)參數(shù)用于確定是否覆蓋原有索引的.
第二步就是利用這個(gè)writer往索引中添加文件.具體后面再說.
第三步進(jìn)行優(yōu)化.
第四步關(guān)閉writer.
?
下面具體看看第二步:
???public static void IndexDirectory(IndexWriter writer, FileInfo file)
???????? {
????????????? if (Directory.Exists(file.FullName))
????????????? {
?????????????????? String[] files = Directory.GetFileSystemEntries(file.FullName);
?????????????????? // an IO error could occur
?????????????????? if (files != null)
?????????????????? {
?????????????????????? for (int i = 0; i < files.Length; i++)
?????????????????????? {
??????????????????????????? IndexDirectory(writer, new FileInfo(files[i]));? //這里是一個(gè)遞歸
?????????????????????? }
?????????????????? }
????????????? }
????????????? else if (file.Extension == ".txt")
????????????? {
?????????????????? IndexFile(file, writer);
????????????? }
???????? }
?
???????? private static void IndexFile(FileInfo file, IndexWriter writer)
???????? {
????????????? Console.Out.WriteLine("adding " + file);
????????????? try
????????????? {
?????????????????? Document doc = new Document();???????????????????
?????????????????? doc.Add(Field.Keyword("filename", file.FullName));
?????????????????? doc.Add(Field.Text("contents", new StreamReader(file.FullName)));
?????????????????? writer.AddDocument(doc);
????????????? }
?????????????
????????????? catch (FileNotFoundException fnfe)
????????????? {
??????????????????
????????????? }
???? }
主要就是兩個(gè)函數(shù)一個(gè)用于處理文件夾(不是為文件夾建立索引),一個(gè)用于真正為文件建立索引.
因此主要集中看一下IndexFile這個(gè)方法.首先建立Document對象,然后為Document對象添加一些屬性Field.你可以把Document對象看成是虛擬文件,將來將從此獲取信息.而Field則看成是描述此虛擬文件的元數(shù)據(jù)(metadata).
其中Field包括四個(gè)類型:
| Keywork | 該類型的數(shù)據(jù)將不被分析,而會(huì)被索引并保存保存在索引中. |
| UnIndexed | 該類型的數(shù)據(jù)不會(huì)被分析也不會(huì)被索引,但是會(huì)保存在索引. |
| UnStored | 和UnIndexed剛好相反,被分析被索引,但是不被保存. |
| Text | 和UnStrored類似.如果值的類型為string還會(huì)被保存.如果值的類型Reader就不會(huì)被保存和UnStored一樣. |
?
最后將每一個(gè)Document添加到索引當(dāng)中.
需要注意的是索引不僅可以建立在文件系統(tǒng)上,也可以建立在內(nèi)存中.
例如
IndexWriter writer = new IndexWriter("index", new StandardAnalyzer(), true);
在第一個(gè)參數(shù)不是指定文件夾的名字而是使用Directory對象,并使用它的子類RAMDirectory,就可以將索引建立在內(nèi)存當(dāng)中.
?
2.?????? 對索引進(jìn)行搜索
IndexSearcher indexSearcher= new IndexSearcher(indexDir);
Query query = QueryParser.Parse(queryString, "contents",new StandardAnalyzer());
Hits hits = indexSearcher.Search(query);
?第一步利用IndexSearcher打開索引文件用于后面搜索,其中的參數(shù)是索引文件的路徑.
第二步使用QueryParser將可讀性較好的查詢語句(比如查詢的詞lucene ,以及一些高級方式lucene AND .net)轉(zhuǎn)化為Lucene內(nèi)部使用的查詢對象.
第三步執(zhí)行搜索.并將結(jié)果返回到hits集合.需要注意的是Lucene并不是一次將所有的結(jié)果放入hits中而是采取一次放一部分的方式.出于空間考慮.
| Lucene.net 系列二 --- index 上 |
| 作者 idior ?? | |
| 2005-03-17 23:39 | |
| 本文繼續(xù)系列一詳細(xì)介紹了有關(guān)Lucene.net索引添加刪除更新的詳細(xì)內(nèi)容.并給出了所有的TestCase供學(xué)習(xí)參考. Lucene建立Index的過程: 1.?????? 抽取文本. ?? 比如將PDF以及Word中的內(nèi)容以純文本的形式提取出來.Lucene所支持的類型主要為String,為了方便同時(shí)也支持Date 以及Reader.其實(shí)如果使用這兩個(gè)類型lucene會(huì)自動(dòng)進(jìn)行類型轉(zhuǎn)換. 2.?????? 文本分析. ?? Lucene將針對所給的文本進(jìn)行一些最基本的分析,并從中去除一些不必要的信息,比如一些常用字a ,an, the 等等,如果搜索的時(shí)候不在乎字母的大小寫, 又可以去掉一些不必要的信息.總而言之你可以把這個(gè)過程想象成一個(gè)文本的過濾器,所有的文本內(nèi)容通過分析, 將過濾掉一些內(nèi)容,剩下最有用的信息. 3.?????? 寫入index. 和google等常用的索引技術(shù)一樣lucene在寫index的時(shí)候都是采用的倒排索引技術(shù)(inverted index.) 簡而言之,就是通過某種方法(類似hash表?)將常見的”一篇文檔中含有哪些詞”這個(gè)問題轉(zhuǎn)成”哪篇文檔中有這些詞”. 而各個(gè)搜索引擎的索引機(jī)制的不同主要在于如何為這張倒排表添加更準(zhǔn)確的描述.比如google有名的PageRank因素.Lucene當(dāng)然也有自己的技 術(shù),希望在以后的文章中能為大家加以介紹. 在上一篇文章中,使用了最基本的建立索引的方法.在這里將對某些問題加以詳細(xì)的討論. 1. 添加Document至索引 doc.Add(Field.Keyword("filename", file.FullName)); 在Lucene中對每個(gè)文檔的描述是可以不同的,比如,兩份文檔都是描述一個(gè)人,其中一個(gè)添加的是name, age 另一個(gè)添加的是id, sex ,這種不規(guī)則的文檔描述在Lucene中是允許的. string baseWord = "fast"; 這點(diǎn)純粹是為了方便用戶的使用.在內(nèi)部Lucene自動(dòng)做了轉(zhuǎn)化,效果和將它們拼接好再存是一樣. 2. 刪除索引中的文檔 ??? 這一點(diǎn)Lucene所采取的方式比較怪,它使用IndexReader來對要?jiǎng)h除的項(xiàng)進(jìn)行標(biāo)記,然后在Reader Close的時(shí)候一起刪除. [TestFixture] ??????? Assert.AreEqual(2, reader.MaxDoc());?? //文檔從0開始計(jì)數(shù),MaxDoc表示下一個(gè)文檔的序號 ??????? Assert.AreEqual(2, reader.NumDocs());? //NumDocs表示當(dāng)前索引中文檔的個(gè)數(shù)? ??? [Test]
IndexReader reader = IndexReader.Open(dir); 你還可以通過reader.UndeleteAll()這個(gè)方法取消前面所做的標(biāo)記,即在read.Close()調(diào)用之前取消所有的刪除工作 3. 更新索引中的文檔 ?? 這個(gè)功能Lucene沒有支持, 只有通過刪除后在添加來實(shí)現(xiàn). 看看代碼,很好理解的. [TestFixture] ? ??????? doc.Add(Field.UnIndexed("country", "Netherlands"));??????? doc.Add(Field.UnStored("contents","Amsterdam has lots of bridges")); ??????? doc.Add(Field.Text("city", "Haag")); ??????? writer.AddDocument(doc); ??????? writer.Optimize(); ??????? writer.Close(); ??????? Assert.AreEqual(1, GetHitCount("city", "Haag")); ??? } ? ??? protected override Analyzer GetAnalyzer() ??? } ??? private int GetHitCount(String fieldName, String searchString) ??? 需要注意的是以上所有有關(guān)索引的操作,為了避免頻繁的打開和關(guān)閉Writer和Reader.又由于添加和刪除是不同的連接(Writer, Reader)做的.所以應(yīng)該盡可能的將添加文檔的操作放在一起批量執(zhí)行,然后將刪除文檔的操作也放在一起批量執(zhí)行.避免添加刪除交替進(jìn)行. |
| Lucene.net 系列三 --- index 中 |
| 作者 idior ?? | |||||||||||||
| 2005-03-18 23:48 | |||||||||||||
| 本文將進(jìn)一步討論有關(guān)Lucene.net建立索引的問題: 主要包含以下主題: 源代碼下載 本文將進(jìn)一步討論有關(guān)Lucene.net建立索引的問題: 索引的權(quán)重 比如對公司內(nèi)很多的郵件做了索引,你當(dāng)然希望主要查看和公司有關(guān)的郵件,而不是員工的個(gè)人郵件.這點(diǎn)根據(jù)郵件的地址就可以做出判斷比如包含@alphatom.com的就是公司郵件,而@gmail.com等等就是私人郵件.如何提高相應(yīng)郵件的權(quán)重? 代碼如下: ???? public static? String COMPANY_DOMAIN = "alphatom.com";???? Document doc = new Document(); ???? String senderEmail = GetSenderEmail(); ???? String senderName = getSenderName(); ???? String subject = GetSubject(); ???? String body = GetBody(); ???? doc.Add(Field.Keyword("senderEmail”, senderEmail)); ???? doc.Add(Field.Text("senderName", senderName)); ???? doc.Add(Field.Text("subject", subject)); ???? doc.Add(Field.UnStored("body", body)); ???? if (GetSenderDomain().EndsWith(COMPANY_DOMAIN))? ????//如果是公司郵件,提高權(quán)重,默認(rèn)權(quán)重是1.0 ???? writer.AddDocument(doc); 不僅如此你還可以對Field也設(shè)置權(quán)重.比如你對郵件的主題更感興趣.就可以提高它的權(quán)重.???????Field senderNameField = Field.Text("senderName", senderName); ???? subjectField.SetBoost(1.2); lucene搜索的時(shí)候會(huì)對符合條件的文檔按匹配的程度打分,這點(diǎn)就和google的PageRank有點(diǎn)類似, 而SetBoost中的Boost就是其中的一個(gè)因素,當(dāng)然還有其他的因素.這要放到搜索里再說. 利用IndexWriter 變量對建立索引進(jìn)行高級管理
? Lucene默認(rèn)情況是每加入10份文檔就從內(nèi)存往index文件寫入并生成一個(gè)segement,然后每10個(gè)segment就合并成一個(gè)segment.通過MergeFactory這個(gè)變量就可以對此進(jìn)行控制. MaxMergeDocs用于控制一個(gè)segment文件中最多包含的Document數(shù).比如限制為100的話,即使當(dāng)前有10個(gè)segment也不會(huì)合并,因?yàn)楹喜⒑蟮膕egmnet將包含1000個(gè)文檔,超過了限制. MinMergeDocs用于確定一個(gè)當(dāng)內(nèi)存中文檔達(dá)到多少的時(shí)候才寫入文件,該項(xiàng)對segment的數(shù)量和大小不會(huì)有什么影響,它僅僅影響內(nèi)存的使用,進(jìn)一步影響寫索引的效率. 為了生動(dòng)的體現(xiàn)這些變量對性能的影響,用一個(gè)小程序?qū)Υ俗隽苏f明. 這里有點(diǎn)不可思議.Lucene in Action書上的結(jié)果比我用dotLucene做的結(jié)果快了近千倍.這里給出書中用Lucene的數(shù)據(jù),希望大家比較一下看看是不是我的問題. Lucene in Action書中的數(shù)據(jù): % java lia.indexing.IndexTuningDemo 100000 10 9999999 10 利用RAMDirectory充分發(fā)揮內(nèi)存的優(yōu)勢 從上面來看充分利用內(nèi)存的空間,減少讀寫文件(寫入index)的次數(shù)是優(yōu)化建立索引的重要方法.其實(shí)在Lucene中提 供了更強(qiáng)大的方法來利用內(nèi)存建立索引.使用RAMDirectory來替代FSDirectory. 這時(shí)所有的索引都將建立在內(nèi)存當(dāng)中,這種方法對于數(shù)據(jù)量小的搜索業(yè)務(wù)很有幫助,同時(shí)可以使用它來進(jìn)行一些小的測試,避免在測試時(shí)頻繁建立刪除索引文件. 在實(shí)際應(yīng)用中RAMDirectory和FSDirectory協(xié)作可以更好的利用內(nèi)存來優(yōu)化建立索引的時(shí)間. 具體方法如下: 1.建立一個(gè)使用FSDirectory的IndexWriter 2 .建立一個(gè)使用RAMDirectory的IndexWriter 3 把Document添加到RAMDirectory中 4 當(dāng)達(dá)到某種條件將RAMDirectory 中的Document寫入FSDirectory. 5 重復(fù)第三步 示意代碼: ? ??? ?private RAMDirectory ramDir = new RAMDirectory(); ?????? private IndexWriter fsWriter = IndexWriter(fsDir,new SimpleAnalyzer(), true); 這里的條件完全由用戶控制,而不是FSDirectory采用對Document計(jì)數(shù)的方式控制何時(shí)寫入文件.相比之下有更大的自由性,更能提升性能. 利用RAMDirectory并行建立索引 RAMDirectory還提供了使用多線程來建立索引的可能性.下面這副圖很好的說明了這一點(diǎn). ? ?甚至你可以在一個(gè)高速的網(wǎng)絡(luò)里使用多臺(tái)計(jì)算機(jī)來同時(shí)建立索引.就像下面這種圖所示. ? 雖然有關(guān)并行同步的問題需要你自己進(jìn)行處理,不過通過這種方式可以大大提高對大量數(shù)據(jù)建立索引的能力. ? 控制索引內(nèi)容的長度.在我的一篇速遞介紹過Google Desktop Search只能搜索到文本中第5000個(gè)字的.也就是google在建立索引的時(shí)候只考慮前5000個(gè)字,在Lucene中同樣也有這個(gè)配置功能. Lucene對一份文本建立索引時(shí)默認(rèn)的索引長度是10,000. 你可以通過IndexWriter 的MaxFieldLength屬性對此加以修改.還是用一個(gè)例子說明問題.? ???? [Test] ???對索引內(nèi)容限長往往是處于效率和空間大小的考慮.能夠?qū)Υ诉M(jìn)行配置是建立索引必備的一個(gè)功能. Optimize 優(yōu)化的是什么? 在以前的例子里,你可能已經(jīng)多次見過writer.Optimize()這段代碼.Optimize到底做了什么? 讓你吃驚的是這里的優(yōu)化對于建立索引不僅沒有起到加速的作用,反而是延長了建立索引的時(shí)間.為什么? 因?yàn)檫@里的優(yōu)化不是為建立索引做的,而是為搜索做的.之前我們提到Lucene默認(rèn)每遇到10個(gè)Segment就合并一次,盡管如此在索引完成后仍然會(huì)留下幾個(gè)segmnets,比如6,7. 而Optimize的過程就是要減少剩下的Segment的數(shù)量,盡量讓它們處于一個(gè)文件中. 它的過程很簡單,就是新建一個(gè)空的Segmnet,然后把原來的幾個(gè)segmnet全合并到這 一個(gè)segmnet中,在此過程中,你的硬盤空間會(huì)變大,因?yàn)橥瑫r(shí)存在兩份一樣大小的索引.不過在優(yōu)化完成后,Lucene會(huì)自動(dòng)將原來的多份 Segments刪除,只保留最后生成的一份包含原來所有索引的segment. 盡量減少segments的個(gè)數(shù)主要是為了增加查詢的效率.假設(shè)你有一個(gè)Server,同時(shí)有很多的Client建立了各自不同的索引,如果此時(shí)搜索,那么必然要同時(shí)打開很多的索引文件,這樣顯然會(huì)受到很大的限制,對性能產(chǎn)生影響. 當(dāng)然也不是隨時(shí)做Optimize就好,如前所述做優(yōu)化時(shí)要花費(fèi)更多的時(shí)間和空間,而且在做優(yōu)化的時(shí)候是不能進(jìn)行查詢的.所以索引建立的后期,并且索引的內(nèi)容不會(huì)再發(fā)生太多的變化的時(shí)候做優(yōu)化是一個(gè)比較好的時(shí)段. | |||||||||||||
| Lucene.net 系列四 --- index 下 |
| 作者 idior ?? | |
| 2005-03-21 21:33 | |
| 本文將介紹有關(guān)索引并發(fā)控制的問題,以結(jié)束對Lucene.net建立索引問題的討論. 1. 允許任意多的讀操作并發(fā).即可以有任意多的用戶在同一時(shí)間對同一份索引做查詢工作. 2.?允許任意多的讀操作在索引被正在被修改的時(shí)候進(jìn)行.即哪怕索引正在被優(yōu)化,添加刪除文檔,這時(shí)也是允許用戶對索引進(jìn)行查詢工作. (it’s so cool.) 3.?同一時(shí)間只允許一個(gè)對索引修改的操作.即同一時(shí)間只允許IndexWriter或IndexReader打開同一份索引.不能允許兩個(gè)同時(shí)打開一份索引. Lucene提供了幾種對索引進(jìn)行讀寫的操作.添加文檔到索引,從索引中刪除文檔,優(yōu)化索引,合并Segments.這些都是對索引進(jìn)行寫操作的方法. 查詢的時(shí)候就會(huì)讀取索引的內(nèi)容. 有關(guān)索引并發(fā)的問題是一個(gè)比較重要的問題,而且是Lucene的初學(xué)者容易忽略的問題,當(dāng)索引被破壞,或者程序突然出現(xiàn)異常的時(shí)候初學(xué)者往往不知道是自己的誤操作造成的. 下面讓我們看看Lucene是如何處理索引文件的并發(fā)控制的. 首先記住一下三點(diǎn)準(zhǔn)則: 1. 允許任意多的讀操作并發(fā).即可以有任意多的用戶在同一時(shí)間對同一份索引做查詢工作. 2.?允許任意多的讀操作在索引被正在被修改的時(shí)候進(jìn)行.即哪怕索引正在被優(yōu)化,添加刪除文檔,這時(shí)也是允許用戶對索引進(jìn)行查詢工作. (it’s so cool.) 3.?同一時(shí)間只允許一個(gè)對索引修改的操作.即同一時(shí)間只允許IndexWriter或IndexReader打開同一份索引.不能允許兩個(gè)同時(shí)打開一份索引. 第一個(gè)準(zhǔn)則很容易理解,第二個(gè)準(zhǔn)則說明Lucene對并發(fā)的操作支持還是不錯(cuò)的.第三個(gè)準(zhǔn)則也很正常,不過需要注意的是第 三個(gè)準(zhǔn)則只是表明IndexWriter和IndexReader不能并存,而沒有反對在多線程中利用同一個(gè)IndexWriter對索引進(jìn)行修改.這個(gè) 功能可是經(jīng)常用到的,所以不要以為它是不允許的.不過這個(gè)時(shí)候的并發(fā)就需要你自己加以控制,以免出現(xiàn)沖突. (注: 在前面的系列中已說過IndexReader不是對Index進(jìn)行讀操作,而是從索引中刪除docuemnt時(shí)使用的對象) 有關(guān)這三個(gè)原則在實(shí)際使用Lucene API時(shí)候的體現(xiàn),讓我們先看看下面這張表:
而X處表明X軸的操作和Y軸的操作不允許同時(shí)進(jìn)行. 比如Add document到索引的時(shí)候不允許同時(shí)從索引中刪除document. 其實(shí)以上這張表就是前面三個(gè)準(zhǔn)則的體現(xiàn).Add Optimize Merge操作都是由IndexWriter來做的.而Delete則是通過IndexReader完成.所以表中空白處正是第一條和第二條準(zhǔn)則的體現(xiàn),而X(沖突)處正是第三個(gè)原則的具體表現(xiàn). ? 為了在不了解并發(fā)控制的情況下對Lucene API的亂用. Lucene提供了基于文件的鎖機(jī)制以確保索引文件不會(huì)被破壞. 當(dāng)你對index 進(jìn)行修改的時(shí)候, 比如添加刪除文檔的時(shí)候就會(huì)產(chǎn)生 ***write.lock文件,而當(dāng)你從segment進(jìn)行讀取信息或者合并segments的時(shí)候就會(huì)產(chǎn)生***commit.lock文件.在默認(rèn) 情況下,這些文件是放在系統(tǒng)臨時(shí)文件夾下的. 簡而言之, write.lock文件存在的時(shí)間比較長,也就是對index進(jìn)行修改的鎖時(shí)間比較長,而commit.lock存在的時(shí)間往往很短.具體情況見下表. 如果索引存在于server, 很多clients想訪問的時(shí)候,自然希望能看到其他用戶的鎖文件,這時(shí)把鎖文件放到系統(tǒng)臨時(shí)文件夾就不好了.此時(shí)可以通過配置文件來改變鎖文件存放的位置. 比如在一個(gè)asp.net的應(yīng)用下,你就可以象下面這樣利用web.config文件來實(shí)現(xiàn)你的目的. <configuration> 不僅如此,在某些情況下比如你的索引文件存放在一個(gè)CD-ROM中,這時(shí)根本就無法對索引進(jìn)行修改,也就不存在所謂的并發(fā)沖突,這種情況下你甚至可以講鎖文件的機(jī)制取消掉.同樣通過配置文件. <configuration> 不過請注意不要亂用此功能,不然你的索引文件將不再受到安全的保護(hù). 下面用一個(gè)例子說明鎖機(jī)制的體現(xiàn). using System;
?[SetUp] ?[Test] ?[Test] 不過很令人失望的是在Lucene(Java)中應(yīng)該收到的異常在dotLucene(1.4.3)我卻沒有捕獲到.隨后我在dotLucene的論壇上問了一下,至今尚未有解答.這也是開源項(xiàng)目的無奈了吧. |
| Lucene.net 系列五 --- search 上 |
| 作者 idior ?? | |
| 2005-03-23 19:24 | |
| 在前面的系列我們一直在介紹有關(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è)例子來簡要介紹一下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 = { ???????? protected String[] text1 = { ????????????? "200206", ????????????? "200309" ???????? }; ???????? protected String[] text3 = { ???????? [SetUp] ???????? { ????????????? IndexWriter writer=new IndexWriter(dir, GetAnalyzer(), true); ????????????? for (int i = 0; i < keywords.Length; i++) ?????????????????? Document doc = new Document(); ????????????? } ???????? }
查詢特定的某個(gè)概念 然后利用利用TermQery來搜索一個(gè)Term(你可以把它理解為一個(gè)Word) ???????? [Test] ????????????? t = new Term("subject", "junit"); ????????????? searcher.Close();
顯然對于各種各樣的查詢(與或關(guān)系,等等各種復(fù)雜的查詢,在下面將介紹),你不希望一一對應(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] ????????????? Query query = QueryParser.Parse("+JUNIT +ANT -MOCK", ????????????? query = QueryParser.Parse("mock OR junit", 由以上的代碼可以看出我們不需要為每種特定查詢而去設(shè)定XXXQuery 通過QueryParse類的靜態(tài)方法Parse就可以很方便的將可讀性好的查詢口語轉(zhuǎn)換成Lucene內(nèi)部所使用的各種復(fù)雜的查詢語句. 有一點(diǎn)需要注意:在Parse方法中我們使用了SimpleAnalyzer, 這時(shí)候會(huì)將查詢語句做一些變換,比如這里將JUNIT 等等大寫字母變成了小寫字母,所以才能搜索到(因?yàn)槲覀冊诮⑺饕臅r(shí)候使用的是小寫),如果你將StanderAnalyzer變成WhitespaceAnalyzer就會(huì)搜索不到.具體原理以后再說. +A +B表示A和B要同時(shí)存在,-C表示C不存在,A OR B表示A或B二者有一個(gè)存在就可以..具體的查詢規(guī)則如下: ? 其中title等等的field表示你在建立索引時(shí)所采用的屬性名. |
| Lucene.net系列六 -- search 下 |
| 作者 idior ?? | |
| 2005-03-23 19:36 | |
| 本文主要結(jié)合測試案例介紹了Lucene下的各種查詢語句以及它們的簡化方法. 通過本文你將了解Lucene的基本查詢語句,并可以學(xué)習(xí)所有的測試代碼已加強(qiáng)了解. 源代碼下載 具體的查詢語句 在了解了SQL后, 你是否想了解一下查詢語法樹?在這里簡要介紹一些能被Lucene直接使用的查詢語句. 1.???????? TermQuery ???????? 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中的關(guān)鍵字,是需要用戶去保證唯一性的. ?TermQuery和QueryParse只要在QueryParse的Parse方法中只有一個(gè)word,就會(huì)自動(dòng)轉(zhuǎn)換成TermQuery. 2.???????? RangeQuery { ???? public class RangeQueryTest : LiaTestCase ???? { ???????? private Term begin, end; ???????? [SetUp] ????????????? end = new Term("pubmonth", "200206"); ???????? [Test] ????????????? Assert.AreEqual(1, hits.Length()); ???????? } ???????? [Test] ????????????? Hits hits = searcher.Search(query); ???? } RangeQuery的第三個(gè)參數(shù)用于表示是否包含該起止日期. RangeQuery和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()); Lucene用[] 和{}分別表示包含和不包含. 3.???PrefixQuery 用于搜索是否包含某個(gè)特定前綴,常用于Catalog的檢索. ???????????[Test]???????? public? void? TestPrefixQuery() ???????? { ????????????? PrefixQuery query = new PrefixQuery(new Term("category", "/Computers")); ???????????? ?IndexSearcher searcher = new IndexSearcher(directory); PrefixQuery和QueryParse ??????????? ? [Test] ????????????? QueryParser qp = new QueryParser("category", new SimpleAnalyzer()); 這里需要注意的是我們使用了QueryParser對象,而不是QueryParser類. 原因在于使用對象可以對QueryParser的一些默認(rèn)屬性進(jìn)行修改.比如在上面的例子中我們的category是大寫的,而QueryParser默 認(rèn)會(huì)把所有的含*的查詢字符串變成小寫/computer*. 這樣我們就會(huì)查不到原文中的/Computers* ,所以我們需要通過設(shè)置QueryParser的默認(rèn)屬性來改變這一默認(rèn)選項(xiàng).即 qp.SetLowercaseWildcardTerms(false)所做的工作. 4.????BooleanQuery 用于測試滿足多個(gè)條件. 下面兩個(gè)例子用于分別測試了滿足與條件和或條件的情況. ???????? [Test]???????? public void And() ???????? { ????????????? TermQuery searchingBooks = ?????????????????? new TermQuery(new Term("subject", "junit")); ????????????? RangeQuery currentBooks = ????????????? AssertHitsIncludeTitle(hits, "JUnit in Action"); ???????? } 什么時(shí)候是與什么時(shí)候又是或? 關(guān)鍵在于BooleanQuery對象的Add方法的參數(shù). 參數(shù)一是待添加的查詢條件. 參數(shù)二Required表示這個(gè)條件必須滿足嗎? True表示必須滿足, False表示可以不滿足該條件. 參數(shù)三Prohibited表示這個(gè)條件必須拒絕嗎? True表示這么滿足這個(gè)條件的結(jié)果要排除, False表示可以滿足該條件. 這樣會(huì)有三種組合情況,如下表所示: BooleanQuery和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的大小 如果想要A與非B 就用 A AND –B 表示, +A –B也可以. 默認(rèn)的情況下QueryParser會(huì)把空格認(rèn)為是或關(guān)系,就象google一樣.但是你可以通過QueryParser對象修改這一屬性. [Test] ???????? } ???????? searcher = new IndexSearcher(directory); ???????? for (int i = 0; i < phrase.Length; i++) ???????? Hits hits = searcher.Search(query); ???? [Test] ???????? Assert.IsFalse(matched(phrase, 0), "exact phrase not found"); ???????? Assert.IsTrue(matched(phrase, 1), "close enough"); ?????[Test] ???????? Assert.IsFalse(matched(phrase, 2), "exact phrase not found"); ???????? Assert.IsTrue(matched(phrase, 3), "close enough"); ???? [Test] PhraseQuery和QueryParse 利用QueryParse進(jìn)行短語查詢的時(shí)候要先設(shè)定slop的值,有兩種方式如下所示 [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",????????? //第一種方式 ???????? qp.SetPhraseSlop(1);??????????????????????????????????? //第二種方式 ???????? 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 ???????? [Test] ???????? } ???????? [Test] ???????? [Test]
注意和PhraseQuery中表示slop的區(qū)別,前者~后要跟數(shù)字. ???????? [Test] |
【出自博客園深藍(lán)居,轉(zhuǎn)載請注明作者出處】
總結(jié)
以上是生活随笔為你收集整理的lucene.net 应用资料的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 离开网易的转型之路3:热爱测试之路-路上
- 下一篇: 怎么解除文件的权限 去除文件访问限制的方