【轉載】Lucene學習筆記(四)

Lucene技術拓展: Lucene與數據庫結合的建議:(引用自下邊的2.2標題中的內容)

比較好的一種方式是讓Lucene和數據庫結合使用,在索引中存入一些關鍵性的數據,

如數據庫表中數據的ID字段、路徑字段或者簡單文本。而真正的數據提取則從數據庫中得到

(就是根據據搜索出來的Document文檔中保存的表數據的類似ID字段的數據到數據庫中取出真正的數據),

這樣既可以發揮Lucene的作用,有分擔了服務器性能的壓力。



四、Lucene搜索:
大綱:
1. Lucene的搜索流程
2. Lucene搜索與結果的表示
3. Lucene的評分機制
4. 構建Lucene的各種Query
5. Lucene的QueryParser類

1. Lucene的搜索流程

1.1 初始化Lucene的檢索工具類----IndexSearch類:是Lucene中最基本的檢索工具,所有的檢索都會用到IndexSearch檢索工具,
但是使用它之前還要做一些準備工作,即對IndexReader進行初始化(需要傳入一個保存索引文件的目錄參數到構造方法,具體見後面)。

1.2 構建Query:就是值對於需要查詢的Field字段採用什麼樣的方式進行查詢(可以任務是創建一個具體的查詢條件對象),
如模糊查詢、語義查詢、短語查詢、範圍查詢、組合查詢等。正是因爲Query的存在,Lucene纔有了豐富的查詢語言。

1.2.1 使用:在使用Query之前,首先要創建一個Query的實例對象,
Lucene支持直接創建Query對象,也可以通過QueryParser.parse()方法返回一個Query對象。

1.3 搜索並處理返回結果:構建完成Query後,就可以將它作爲IndexSearch.search()方法的參數進行查詢了,將會返回結果集。
具體的操作見下邊的小節。

2. 搜索與結果:下邊介紹如何搜索和處理結果集。

2.1 檢索工具----IndexSearch類(搜索器):該類繼承子Search基類。該類的構造方法可以自己看Javadoc文檔。
我們會發現IndexSearch提供的四種構造方法的最終目的就是用一個IndexReader對象作爲一個索引目錄的讀取器,
還可以要傳入一個boolean值,用來判斷是否在關閉IndexSearch的時候也同時關閉傳入的IndexReader對象。
初始化工作完成之後,還要創建一個Query對象才能真正的進行搜索,這裏我們只關注IndexSearch對象的搜索功能,
至於Query我們後邊標題中會講到。

例子:
/*
構建5個Document文檔,每個含有兩個Field字段
其中title字段表示文檔名稱
其中name字段表示文字數據
*/
Document doc1 = new Document();
doc1.add(Field.Keyword("title", doc1));
doc1.add(Field.Text("name", "word1 word2 word3"));

Document doc2 = new Document();
doc2.add(Field.Keyword("title", doc2));
doc2.add(Field.Text("name", "word4 word5 word6"));

Document doc3 = new Document();
doc3.add(Field.Keyword("title", doc3));
doc3.add(Field.Text("name", "word1 word4"));

Document doc4 = new Document();
doc4.add(Field.Keyword("title", doc4));
doc4.add(Field.Text("name", "word2 word5"));

Document doc5 = new Document();
doc5.add(Field.Keyword("title", doc5));
doc5.add(Field.Text("name", "word3 word6"));

//生成一個索引寫入器
IndexWriter writer = new IndexWriter("C://lucene_index", new StandardAnalyzer(), true);
//依次將創建的Document文檔
writer.addDocument(doc1);
writer.addDocument(doc2);
writer.addDocument(doc3);
writer.addDocument(doc4);
writer.addDocument(doc5);
writer.close();

//生成Query對象
Query query = null;
//生成結果集
Hits hits = null;
//創建搜索關鍵字
String key1 = "word1";
String key2 = "word2";
String key3 = "word3";
String key4 = "word4";
String key5 = "word5";
String key6 = "word6";
//初始化IndexSearch搜索器對象
IndexSearch search = new IndexSearch("C://lucene_index");
//爲Query賦值:傳遞搜索關鍵字、要查詢的字段、分析器
query = QueryParser.parse(key1, "name", new StandardAnalyzer());
//得到結果集
hits = search.search(query);
//打印結果
printResult(hits, key1);

query = QueryParser.parse(key2, "name", new StandardAnalyzer());
hits = search.search(query);
printResult(hits, key2);

query = QueryParser.parse(key3, "name", new StandardAnalyzer());
hits = search.search(query);
printResult(hits, key3);

query = QueryParser.parse(key4, "name", new StandardAnalyzer());
hits = search.search(query);
printResult(hits, key4);

query = QueryParser.parse(key5, "name", new StandardAnalyzer());
hits = search.search(query);
printResult(hits, key5);

query = QueryParser.parse(key6, "name", new StandardAnalyzer());
hits = search.search(query);
printResult(hits, key6);

//關閉IndexSearch對象
search.close();

//打印結果
public void printResult(Hits hits, String key){
System.out.println("查找關鍵字: " + key);
if(hits != null && hits.length() != 0){
System.out.println("找到文檔:");
for(int i = 0; i < hits.length(); i++){
//得到結果集中的文檔對象
Document doc = hits.doc(i);
String docName = doc.get("title");
System.out.println(docName);
}
}else{
System.out.println("沒有任何搜索結果!");
}
//加兩個換行
System.out.println();
System.out.println();
}

** 注意:search.close()方法其實關閉的並不是IndexSearch對象,
而關閉的是IndexSearch內部封裝的IndexReader流對象。

** 注意:IndexSearch類中有很多search()方法的重載,其中可以傳入一些輔助參數,可以對查詢結果進行處理,
主要是爲了對搜索結果的排序、過濾等功能而設置的,這些具體的參數講解將會在下一章高級部分講解。

2.2 檢索結果----Hits類:搜索完成之後,就要把搜索結果返回給用戶進行顯示。
在Lucene中的結果集是用Hits類的對象來表示的。我們可以查看Javadoc來查看Hits提供的方法。

** 注意:如果一個結果集含有100000條記錄,而hits對象一次性就把結果集返回,那麼此時hits結果集中的內容會有所不同。
即它不會將所有的結果都返回,而是採用一種類似Hibernate懶加載的懶惰(Lazy)的方式來返回結果,當用戶要訪問某個文檔結果時,
hits對象在內部會對Lucene的索引再一次進行檢索,返回一個對應的最新的索引文檔結果。《具體原理可以查看源代碼》

** 建議:比較好的一種方式是讓Lucene和數據庫結合使用,在索引中存入一些關鍵性的數據,
如數據庫表中數據的ID字段、路徑字段或者簡單文本。而真正的數據提取則從數據庫中得到
(就是根據據搜索出來的Document文檔中保存的表數據的類似ID字段的數據到數據庫中取出真正的數據),
這樣既可以發揮Lucene的作用,有分擔了服務器性能的壓力。

3. Lucene的評分機制:我們可以通過Hits的score(int)方法得到對應位置的Document的得分,下邊我們就來了解一下得分的意義吧!

3.1 理解評分的概念:通常情況下,當用戶輸入一個關鍵字,搜索引擎接收到關鍵字信息,然後即可分析最後進行搜索。
這裏主要要說的就是得到的搜索結果,如果結果數據比較多,那麼結果需要按照一定的順序返回給用戶。
因此Lucene引入了評分機制,對檢索結果中的每個Document文檔都按照某種Lucene中規定好的標準進行評估打分,
然後按照分值的高低來對結果進行排序並返回給用戶。

** 注意:對於一個商用的搜索引擎來說,評分機制是其收入來源的重要部分。例如某公司向搜索引擎繳納一定的費用,
要求該搜索引擎將搜索結果中有關它公司的部分分值加大 ,以便在用戶搜索有關信息的時候能使該公司的結果處於更靠前的位置。

3.2 Lucene的評分算法:下邊介紹Lucene到底是怎麼確定各個Document評分值!
Document文檔的得分是在用戶進行檢索時(或說成得到搜索結果的同時)實時計算出來的。而不是在建立索引時確定的Document的得分,
如果是建立索引時確定的得分,那麼不管輸入什麼關鍵字進行搜索,那麼建立索引時得分最高的永遠會排在第一位,所以明顯不合理。
因此,所有Document文檔的得分應當和用戶輸入的關鍵有關係,
根據關鍵字和Document文檔中查找到的匹配上的信息進行實時的運算得到的結果。
所謂的得分可以認爲成搜索關鍵字在Document文檔中出現的頻率,可認爲出現頻率越高得分越多。
《有一個得分計算公式,可以到Lucene書的260頁查看,不太重要,知道上邊的原理就行了》

3.3 改變Document文檔的得分:我們可以手動干預Document文檔的得分。通過Document.setBoost()方法來修改得分。
其實就是改變Document的boost屬性值,讓原文檔得分乘以boost屬性,所以最後得分會跟Document.setBoost()設置的值十分相近,
所以就可以認爲最終值就是setBoost()設置的值。

例如:
//創建一個手動指定分值的Document
Document doc = new Document();
doc.add(Field.Text("name", "word"));
//設置得分值,因爲接收的是float類型的所以要加"f"標識符
doc.setBoost(0.1f);

//創建索引寫入器,然後保存索引到指定的磁盤目錄
......
//進行搜索
......

** 結果:從結果可以看到分值的顯示

** 注意:上面這種手動設置評分值的方式仍然不是一種理想的方式,
因爲在建立索引時還需要人爲的指定每個Document文檔的boost值。《有關排序的更高級話題,後面會講到》

4. 構建各種Query查詢對象:搜索流程中第二步就是構建一個Query對象。
當用戶輸入一個關鍵字後,Lucene不能直接使用關鍵字字符串進行搜索,
而是要將字符串分析和處理之後將關鍵字轉換成Lucene指定的類型(Query)後才能真正的進行搜索,
要轉換成的格式就是Query類型的對象,使用Query對象的好處不單單提高了檢索的效率,同時能檢索出更加有效的結果。

** 注意:Query是一個抽象類,所以我們需要學習一下它的子類,下邊共介紹了八個子類的使用方法。

4.1 按分詞搜索----TermQuery類:使用TermQuery類搜索可以認爲是"分詞搜索"。
在搜索引擎中最基本的搜索就是在索引中搜索某一分詞
(就是建立索引是把Document文檔中的Field字段中保存的數據進行分詞後產生的詞條數據信息),
TermQuery就是可以完成這樣功能的一個類。

4.1.1 分詞Term概念:前邊我們已經提到了這個概念,這裏重複敘述一下。
在Lucene中分詞是最基本的搜索單位。從本質上來講一個分詞其實就是一個名/值對。
"名"是Field字段名,
"值"是對應"名"的字段中所有分詞(創建索引的時候字段中的數據將會被分詞器進行分詞)的某一個。

4.1.2 構造TermQuery對象:首先要構造Term對象,然後將該對象作爲構造TermQuery對象的參數。
如:
Term term = new Term("name", "word");
Query query = new TermQuery(term);
這樣如果使用該query對象進行搜索的話,那麼所有的以"name"爲字段名,
並且字段中包含"word"分詞的Document文檔,都將會作爲查詢結果返回給用戶。

** 注意:我們之前搜索的時候都要傳第一個和創建索引時所用的分詞器對象相同的對象,但是這裏沒有用到分詞器,
這是因爲之前我們通過QueryParser.parse("word", "name", new StandardAnalyzer());創建Query時,傳入的就是搜索關鍵字,
而不是把它作爲分詞傳遞過來進行搜索,所以要先對該關鍵字進行一下分詞。
但是現在我們構造Term對象的時候傳入的"值"是用來作爲分詞進行查詢的,雖然傳入的不一定是分詞,
但是我們在代碼中的意思就是把它作爲了分詞,所以不需要再用分詞器進行分詞處理了。

4.2 "與或"搜索----BooleanQuery類:它也是實際開發過程中經常使用的一個Query。
其實它是一個組合的Query類,一般不是單獨使用,
換句話說就是在使用時可以把各種Query的子類對象添加到該BooleanQuery對象中並標明添加到
該BooleanQuery對象中的這些Query條件之間的邏輯關係,就相當於sql中的and、or這樣的連接條件的邏輯關係表達式。

** 注意:BooleanQuery是可以嵌套的,一個BooleanQuery可以成爲另一個BooleanQuery的條件子句。
例子:
//創建三個Document
Document doc1 = new Document();
doc1.add(Field.Text("name", "word1 word2 word3"));
doc1.add(Field.Keyword("title", "doc1"));

Document doc2 = new Document();
doc2.add(Field.Text("name", "word1 word4 word5"));
doc2.add(Field.Keyword("title", "doc2"));

Document doc3 = new Document();
doc3.add(Field.Text("name", "word1 word2 word6"));
doc3.add(Field.Keyword("title", "doc3"));
//創建索引寫入器
IndexWriter writer = new IndexWriter("C://lucene_index", new StandardAnalyzer(), true);
//添加Document文檔並創建索引
writer.addDocument(doc1);
writer.addDocument(doc2);
writer.addDocument(doc3);
writer.close();

//開始搜索,創建搜索器
IndexSearch search = new IndexSearch("C://lucene_index");
//創建兩個Query對象
Query query1 = new TermQuery(new Term("name", "word1"));
Query query2 = new TermQuery(new Term("name", "word2"));
//創建複合條件Query對象:BooleanQuery對象,產生"與"關係效果
Query query = new BooleanQuery();
query.add(query1, true, false);
query.add(query2, true, false);
//得到結果
Hits hits = search.search(query);
//打印結果,使用上邊的打印方法
......

** 結果:會查到"name"字段中同時包含分詞"word1"和"word2"的Document文檔對象。

** 注意:在調用BooleanQuery.add(Query, boolean, boolean)方法時除了第一個參數外還有兩個參數。
第二個參數意思是當前加入的查詢子句Query對象是否必須滿足。
第三個參數意思是當前加入的查詢子句Query對象是否不需要滿足。

這樣的話那麼可能產生的關係組合方式有四種:
<1>true和false:表明當前加入的子句是必須要滿足的
<2>false和true:表明當前加入的子句是不可以被滿足的
<3>false和false:表明當前加入的子句是可選的
<4>true和true:這種組合是錯誤的,執行的時候會有錯。
上邊代碼中表示的是<1>,即兩個query子查詢都要滿足,即"and"的關係
如果換成:
query.add(query1, false, false);
query.add(query2, false, false);
那就是<3>,表示"or"的關係

** 注意:由於BooleanQuery這樣的查詢是可以嵌套的,索引可以表示多種條件下的組合查詢。但是如果子句比較多的話,
無疑又會導致查找效率降低。
爲此,Lucene給出了一個默認的限制,就是BooleanQuery的子句數目不能超過1024個

4.3 在某一範圍內搜索----RangeQuery類:有的時候用戶會需要一種在一個範圍內查找某個文檔,
比如查找某一時間段內的所有文檔,爲此Lucene提供了RangeQuery類來實現類似這樣的功能。

** 原理:RangeQuery表示在某範圍內的搜索條件,實現從一個開始分詞到一個結束分詞的搜索功能,
即構造RangeQuery對象的時候要指定一個開始分詞和一個結束分詞,還可以指定是否包含開始、結束分詞在內一起查找。

例子:
//創建Document文檔對象
Document doc1 = new Document();
doc1.add(Field.Text("time","200001"));
Document doc2 = new Document();
doc2.add(Field.Text("time","200002"));
Document doc3 = new Document();
doc3.add(Field.Text("time","200003"));
Document doc4 = new Document();
doc4.add(Field.Text("time","200004"));
Document doc5 = new Document();
doc5.add(Field.Text("time","200005"));
//創建索引寫入器
IndexWriter writer = new IndexWriter("C://lucene_index", new StandardAnalyzer(), true);

//設置使用複合索引格式
writer.setUseCompoundFile(true);

//添加Document文檔並創建索引
writer.addDocument(doc1);
writer.addDocument(doc2);
writer.addDocument(doc3);
writer.addDocument(doc4);
writer.addDocument(doc5);
writer.close();

//開始搜索,創建搜索器
IndexSearch search = new IndexSearch("C://lucene_index");
//準備搜索範圍:即創建兩個Term分詞對象
Term beginTime = new Term("time", "200001");
Term endTime = new Term("time", "200005");
/*
創建RangeQuery對象:
之所以沒有使用Query是因爲我們下邊要調用RangeQuery中特有的方法,並指定不包含邊界值在內進行搜索
*/
RangeQuery query = new RangeQuery(beginTime, endTime, false);
//得到結果
Hits hits = search.search(query);
//打印結果
......

** 注意:這裏調用了writer.setUseCompoundFile()方法設置了使用複合索引創建索引文件。

4.4 使用前綴搜索----PrefixQuery類:通常情況下定義一個Term,該分詞包含要查找的字段名和對應字段中分詞的前綴,
用這個Term對象構造一個PrefixQuery對象,就可以使用前綴搜索了。

例子:
//創建四個文檔Document,其中每個中都保存Field.Text("name", "David/Darwen/Smith/Smart");
//創建寫入器,同上
......
//創建搜索器,然後構造Term對象
Term pre1 = new Term("name", "Da");//大寫D和小寫a
Term pre2 = new Term("name", "da");//純小寫
Term pre3 = new Term("name", "sm");//純小寫
//分別構造PrefixQuery對象
PrefixQuery query = null;
query = new PrefixQuery(pre1);
hits = search.search(query);
//打印:但是結果卻沒有找到,這是因爲標準的分詞器對數據進行分詞的時候會把所有大寫字母轉換成小寫所以"Da"找不到記錄

//查詢第二個"da"前綴,因爲正好標準分詞器分詞時將數據改成小寫所以"da"才能找到結果
query = new PrefixQuery(pre1);
hits = search.search(query);
//查找第三個前綴"sm",打印後可以找到對應的結果
query = new PrefixQuery(pre1);
hits = search.search(query);
//打印
......

** 注意:上面通過前綴"Da"不能找到結果,而通過"da"卻能找到結果,
這是因爲在創建索引的時候標準分詞器對文檔數據分析切詞的時候,將所有的大寫字母都會轉變成小寫字母,
所以根據大寫字母的前綴,不能找到對應的分詞;而相反如果換成小寫的前綴"da"正好能找到對應的分詞。

4.5 多關鍵字的搜索----PhraseQuery類:用搜索引擎搜索時,常常查找的並非是一個簡單的單詞,很有可能是幾個不同的關鍵字。
這些關鍵字要麼是緊密相聯,構成一個精確的短語;要麼可能這些關鍵字之間還有些未知的無關緊要的內容。
此時用戶希望能夠通過查找找到對應的內容,但是我們知道多個關鍵字之間存在了一些其他未知內容,
當我們用普通方法搜索的時候,我們會知道,被搜索的索引文件中雖然有我們搜索的這些關鍵字,
但是對於索引文件中的內容他們是不連貫的,顯然這將導致搜索結果的得分較低,所以達不到我們的要求。
Lucene爲此提供了PhraseQuery類來解決上面的問題。

** 功能:它的add方法讓用戶往其內部添加關鍵字,
在添加完畢後,用戶還可以通過setSlop()方法來設定一個稱之爲"坡度"的變量來確定關鍵字之間
是否允許和允許多少個無關詞彙的存在。

例子:
Document doc1 = new Document()
doc1.add(Field.Text("content", "david mary smith robert"))
//創建索引寫入器,設爲複合索引格式,最後創建索引,之後創建搜索器
......
//構建關鍵字詞條
Term word1 = new Term("content", "david");
Term word2 = new Term("content", "smith");
//創建Query
PhraseQuery query = new PhraseQuery();
query.add(word1);
query.add(word2);
//設置坡度:坡度值大於等於實際分詞之間擁有的分詞個數就行,也就是坡度值必須大於等於無關緊要的數據被分詞後的個數
query.setSlop(1);//即大於等於1的數值都可以讓搜索結果找到,但是如果小於1的話就找不到了。
//結果集
Hits hits = search.search(query);
//打印
......

** 注意:坡度值可以大於等於無關緊要的數據被分詞後的個數,但是不能小於這個個數。
如"david mary smith robert",查找的時候傳入的分詞是"david"和"smith",那麼坡度可以">=1"但是不能"<1"

4.6 使用短語綴搜索----PhrasePrefixQuery類:PhrasePrefixQuery與PhraseQuery類似。
在PhraseQuery中,如果用戶想查找及存在短語"david robert"又存在"mary robert"的文檔的話,
那麼只能使用兩個PhraseQuery對象,一個用來查找存在關鍵字"david robert"的文檔,另一個用來查找存在關鍵字"mary robert"的文檔,
最後使用BooleanQuery創建兩個query的關係爲"或",然後再進行查找。
爲此Lucene提供了PhrasePrefixQuery類可以很方便的爲我們完成上面的需求。

例子:
//創建文檔,並保存到索引,同上
......
//創建搜索器,並且創建分詞Term對象
Term word1 = new Term("content", "david");
Term word2 = new Term("content", "mary");
Term word3 = new Term("content", "robert");
//創建Query對象
PhrasePrefixQuery query = new PhrasePrefixQuery();
//添加短語中可能出現的第一個分詞
query.add(new Term[]{word1, word2});
//加入短語中出現的最後一個分詞
query.add(word3);
//設置坡度,這裏的坡度設置原則和上邊設定PhraseQuery的坡度的原則相同
query.setSlop(2);
//開始搜索,返回結果集
Hits hits = search.search(query);
//打印
......

** 注意:上邊查找的目的是查找存在"david robert"或"mary robert"短語的Document文檔。
其中我們使用了add(Term[]);這裏接收的參數是一個Term數組表示這是要搜索的分詞句中第一個出現的分詞,可以設置多個。
而add(Term)方法的意思就是設置要搜索的分詞句中最後一個出現的分詞。然後還是通過設置坡度來說明無關緊要的分詞的個數。

4.7 相近詞語的搜索----FuzzyQuery類:是一種模糊查詢,它可以簡單的識別兩個相近的詞語。=

例子:
Document doc1 = new Document();
doc1.add(Field.Text("name", "david"));

Document doc2 = new Document();
doc2.add(Field.Text("name", "sdavid"));

Document doc3 = new Document();
doc3.add(Field.Text("name", "davie"));
//創建索引寫入器,然後保存索引,在創建搜索器,同上
......
//創建用戶模糊查詢的分詞Term對象
Term word = new Term("name", "david");
//創建Query對象:該FuzzyQuery沒有提供add方法,只能通過構造函數傳入搜索分詞對象
FuzzyQuery query = new FuzzyQuery(word);
//搜索
Hits hits = search.search(query);
//打印
......

** 注意:搜索結果是三個Document文檔都搜索到了,這就是模糊查詢的效果,
可以把個包含要搜索的分詞相似的分詞對應的Document文檔對象查找到。

4.8 使用通配符搜索----WildcardQuery類:可以使用通配符進行查詢。
Lucene中通配符供提供兩個:"?","*".
"?"表示一個字符
"*"表示0個至多個字符,即任何多個字符

例子:
/*
創建三個Document文檔,分別加入一個Field字段:名爲"content",
值分別爲"whatever/whoever/however/everest"中的一個。
然後創建索引,並創建搜索器
*/
......
//構造通配符式的分詞Term對象
Term word1 = new Term("content", "*ever"); //查詢將找到含有"whatever"、"whoever"和"however"分詞的Document
Term word1 = new Term("content", "wh?ever"); //查詢將找到含有"whoever"分詞的Document
Term word1 = new Term("content", "h??ever"); //查詢將找到含有"however"分詞的Document
Term word1 = new Term("content", "ever*"); //查詢將找到含有"everest'分詞的Document
//創建Query對象
WildcardQuery query = new WildcardQuery(word1); //這裏只做了一個演示
//搜索
Hits hits = search.search(query);
//打印
......

5. 查詢字符串的解析器----QueryParser類:對於搜索引擎(如百度和Google)來講,
很多情況下只需要用戶在輸入框內輸入所需查詢的內容,然後單擊"搜索"就可以了。
對此因爲用戶輸入的不一定就是索引中生成好的一個分詞,所以在Lucene中,這項工作交由QueryParser類來完成對用戶輸入信息的分析,
然後創建一個合適的Query對象或一個Query組。雖然Lucene提供的API允許使用者創建各種各樣的Query子類對象,
但它同時也允許通過QueryParser分析器類生成各種各樣的Query子對象,這使的Lucene的查詢功能更加靈活和強大。

5.1 QueryParser的簡單用法:它實際上就是一個解析用戶輸入的工具,可以通過掃描用戶輸入的字符串,生成Query對象。

例子:Query query = QueryParser.parse(keywords,fieldName,new StandarcAnalyzer());

** 注意:我們要傳入用戶輸入的關鍵字,還要告訴QueryParser默認將到指定的fieldName字段內查找關鍵字被切詞後的分詞。
當然這裏說是指定QueryParser默認要查詢的fieldName,那麼就還可以指定fielName,
當然,我們可以在輸入搜索關鍵字的時候通過Lucene特有的方法指定fieldName,
即:"content:david",如果輸入的是這樣形式的搜索關鍵字,
那麼生成的Query對象中綁定的搜索字段將不是QueryParser.parse(keywords,fieldName,new StandarcAnalyzer());中指定的fieldName,
而是keywords中指定的代表字段名稱的字段,即"content:david",也就是到字段content中搜索。

** 用戶輸入的關鍵字和QueryParser理解的含義:

5.1.1 輸入"David"------------- QueryParser解析成:在默認的字段中檢索"David"被切詞後的分詞
5.1.2 輸入"content: David"------------- QueryParser解析成:在"content"字段中檢索"David"被切詞後的分詞
5.1.3 輸入"David Mary"或輸入"David OR Mary"---- QueryParser解析成:在默認的字段中
檢索"David"和"Mary"被切詞後的分詞,它們是"或"的關係
5.1.4 輸入"+David +Mary"或輸入"David AND Mary"---- QueryParser解析成:在默認的字段中
檢索"David"和"Mary"被切詞後的分詞,它們是"與"的關係
5.1.5 輸入"content:David -title:Manager"或輸入"content:David AND NOT title:Manager"---- QueryParser解析成:
在"content"字段中檢索包括關鍵字"David"被切詞後的分詞,
但是在"title"字段中不能包含關鍵字"Manager"被切詞後的分詞的Document文檔
5.1.6 輸入"(David OR Mary) AND Robert"---- QueryParser解析成:在默認字段中檢索包含"David"或"Mary"被切詞後的分詞,
但是一定要包含"Robert"被切詞後的分詞的Document文檔
5.1.7 輸入"Davi*"------------QueryParser解析成:在默認字段中檢索前綴爲"David"被切詞後的分詞作爲分詞前綴的Document文檔
5.1.8 輸入"content: "David is a manager"" ---- QueryParser解析成:在"content"字段中包含短語"David is a manager"被切詞後的分詞短語的Document文檔

** 注意:在使用QueryParser對用戶輸入的關鍵字進行掃描時,還要傳一個分詞器對象(後面詳講)。
不過,當對用戶輸入的關鍵字進行分詞時,分詞器應當與建立索引時使用的分詞器是一樣的,
這樣才保證搜索關鍵字的分詞原則和建立索引時的分詞原則是一樣的,這樣才能保證分詞成功。

5.2 QueryParser的 "與" 和 "或" :這裏所說的與和或主要針對用戶輸入的搜索關鍵字的形式爲"David Mary"的時候,
QueryParser解析成Query的時候把這種情況是按照把這兩個關鍵詞的分詞作爲了"或"的關係,也就是生成的Query對象查詢的時候,
查找的目標是對應的字段中出現分詞"david"或"mary"(注意這裏被分詞器分析切詞後都是小寫),而不是"與"的關係。

** 要解決的問題:就是我們想讓QueryParser把用戶輸入的"David Mary"這種形式按照"與"的關係進行處理,怎麼實現呢?

** 解決方案:以下兩行代碼可以參看Javadoc文檔做進一步理解。
QueryParser parser = new QueryParser(fieldName, new StandardAnalyzer());
parser.setOperator(QueryParser.DEFAULT_OPERATOR_AND);

** 說明:加上這兩行代碼就是將默認的用"或"關係處理改成了用"與"的關係進行處理,
這樣設置之後,在進行對用戶輸入的"David Mary"這種形式的搜索關鍵字,
那麼不會再按照"或"的關係進行處理,而是按照"與"的關係處理。

發佈了101 篇原創文章 · 獲贊 26 · 訪問量 29萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章