3.8 高级检索方式(二)
上節已經介紹了五種高級檢索方式,現在我們來學習另外五種。事實上,今天要介紹的五種高級檢索方式有共通之處:都可以解決上節提到的最后一個用戶需求:幫助小明快速定位游戲分類,過濾掉其他分類內容。
六、BooleanQuery
BooleanQuery是布爾查詢,通過對其他查詢(如上節講到的TermQuery,PhraseQuery或其他BooleanQuery)的組合來實現邏輯運算。
BooleanQuery的邏輯運算符是通過BooleanQuery.Occur(文檔)來確定的。
- BooleanClause.Occur.MUST:與運算
- BooleanClause.Occur.SHOULD:或運算
- BooleanClause.Occur.MUST_NOT:非運算
- BooleanClause.Occur.FILTER:相當于與運算,但是不參與評分。
Lucene6.2.1版本廢棄了BooleanFilter,合并到了BooleanClause.OCCUR.FILTER中,那么Filter和Query有什么區別呢?
其實各種Query和各種Filter之間非常相似,可以互相轉換,最大的區別是:Query有評分操作,返回的結果集有相關性評分;Filter的結果集無相關性評分,返回的結果是無排序的。
這四者組合,妙用無窮:
- MUST和MUST:取得多個查詢子句的交集。?
- MUST和MUST_NOT:表示查詢結果中不能包含MUST_NOT所對應得查詢子句的檢索結果。?
- SHOULD與MUST_NOT:連用時,功能同MUST和MUST_NOT。
- SHOULD與MUST連用時,結果為MUST子句的檢索結果,但是SHOULD可影響排序,是在MUST搜出來的doc里面,根據SHOULD的query進行打分。
- SHOULD與SHOULD:表示“或”關系,最終檢索結果為所有檢索子句的并集。
- MUST_NOT和MUST_NOT:無意義,檢索無結果。
- 上述的MUST換成FILTER,就變成了不帶評分的過濾功能,
?對于小明的需求,我們就可以利用BooleanQuery來設計:查詢一是域為“分類”,搜索詞為“游戲”的TermQuery;查詢二為檢索搜索關鍵詞的某一Query;用BooleanQuery組合兩個查詢,查詢一的運算符為過濾,查詢二為MUST,這樣,就能查找所有分類為“游戲”的內容了。
七、MultiFieldQuery
MultiFieldQuery是多域查詢。比如用戶有這樣的需求:一個文檔中含有“標題”,“正文”等字段,搜索一個關鍵詞,不管它在標題中出現還是在正文中出現都算符合條件。這時,我們就用到了多域查詢。
1 package testAdvancedQuery; 2 import java.nio.file.Paths; 3 import java.io.*; 4 5 import org.apache.lucene.analysis.Analyzer; 6 import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer; 7 import org.apache.lucene.analysis.standard.StandardAnalyzer; 8 import org.apache.lucene.document.Document; 9 import org.apache.lucene.index.DirectoryReader; 10 import org.apache.lucene.queryparser.classic.MultiFieldQueryParser; 11 import org.apache.lucene.search.BooleanClause; 12 import org.apache.lucene.search.IndexSearcher; 13 import org.apache.lucene.search.Query; 14 import org.apache.lucene.search.ScoreDoc; 15 import org.apache.lucene.search.TopDocs; 16 import org.apache.lucene.store.Directory; 17 import org.apache.lucene.store.FSDirectory; 18 import org.apache.lucene.util.Version; 19 import IkAnalyzer.MyIkAnalyzer; 20 public class testMultiFieldQuery { 21 public static Version luceneVersion = Version.LATEST; 22 public static void indexSearch(){ 23 DirectoryReader reader = null; 24 try{ 25 Directory directory = FSDirectory.open(Paths.get("index3"));//在硬盤上生成Directory 26 reader = DirectoryReader.open(directory); 27 IndexSearcher searcher = new IndexSearcher(reader); 28 Analyzer analyzer1=new StandardAnalyzer(); 29 Analyzer analyzer2=new SmartChineseAnalyzer(); 30 Analyzer analyzer3=new MyIkAnalyzer(); 31 // //方法一:利用BooleanQuery,在兩個TermQuery之間做邏輯運算 32 // Term t1=new Term("key1","張飛"); 33 // Term t2=new Term("key2","劉備"); 34 // TermQuery q1=new TermQuery(t1); 35 // TermQuery q2=new TermQuery(t2); 36 // BooleanQuery query=new BooleanQuery.Builder().add(q1,BooleanClause.Occur.FILTER).add(q2,BooleanClause.Occur.MUST).build(); 37 // 38 //方法二:MultiFieldQueryParser類,實現多字段搜索,實際上只是一個封裝,用起來簡單,內部還是用BooleanQuery實現 39 String fields[]={"key1","key2"}; 40 String kws[]={"張飛","劉備"}; 41 //MUST:and;SHOULD:OR;MUST_NOT:NOT;FILTER:相當于MUST,但是不參與打分。 42 BooleanClause.Occur[] flags=new BooleanClause.Occur[]{BooleanClause.Occur.FILTER,BooleanClause.Occur.MUST}; 43 Query query=MultiFieldQueryParser.parse(kws,fields,flags,analyzer3); 44 String ss=query.toString(); 45 System.out.println(ss); 46 TopDocs tds = searcher.search(query, 20); 47 ScoreDoc[] sds = tds.scoreDocs; 48 int cou=0; 49 for(ScoreDoc sd:sds){ 50 cou++; 51 Document d = searcher.doc(sd.doc); 52 String output=cou+". "+d.get("skey1")+"\n"+d.get("skey2"); 53 System.out.println(output); 54 } 55 }catch(Exception e){ 56 e.printStackTrace(); 57 }finally{ 58 try { 59 reader.close(); 60 } catch (IOException e) { 61 e.printStackTrace(); 62 } 63 } 64 } 65 public static void main(String[] args) throws IOException 66 { 67 indexSearch(); //搜索的內容可以修改 68 } 69 } MultiFieldQueryMultiFieldQuery有兩種實現方法:
方法一是利用BooleanQuery在多個TermQuery之間做邏輯運算
1 //方法一:利用BooleanQuery,在兩個TermQuery之間做邏輯運算 2 Term t1=new Term("key1","張飛"); 3 Term t2=new Term("key2","劉備"); 4 TermQuery q1=new TermQuery(t1); 5 TermQuery q2=new TermQuery(t2); 6 BooleanQuery query=new BooleanQuery.Builder().add(q1,BooleanClause.Occur.FILTER).add(q2,BooleanClause.Occur.MUST).build(); 方法一方法二是MultiFieldQueryParser類,實現多域搜索,實際上只是一個封裝,用起來簡單,內部還是用BooleanQuery實現。
1 //方法二:MultiFieldQueryParser類,實現多字段搜索,實際上只是一個封裝,用起來簡單,內部還是用BooleanQuery實現 2 String fields[]={"key1","key2"}; 3 String kws[]={"張飛","劉備"}; 4 //MUST:and;SHOULD:OR;MUST_NOT:NOT;FILTER:相當于MUST,但是不參與打分。 5 BooleanClause.Occur[] flags=new BooleanClause.Occur[]{BooleanClause.Occur.FILTER,BooleanClause.Occur.MUST}; 6 Query query=MultiFieldQueryParser.parse(kws,fields,flags,analyzer3); 方法二 1 String ss=query.toString(); 2 System.out.println(ss); 打印查詢對象可以利用上述代碼把查詢對象打印出來,便于直觀感受lucene對查詢的解析。
可以看出,MultiFieldQuery本質上也是BooleanQuery的應用,具體內容可以參考官方文檔。
八、FieldQuery
FieldQuery是域搜索,用戶可以通過輸入符合語法規則的查詢語句指定一次查詢是在哪些域上進行。例如,如果索引的文檔包含兩個域,Title 和Content,用戶可以使用查詢 “Title: Lucene AND Content: Java” 來返回所有在 Title域上包含 Lucene 并且在 Content 域上包含 Java 的文檔。
1 package testAdvancedQuery; 2 import java.io.IOException; 3 import java.nio.file.Paths; 4 5 import org.apache.lucene.document.Document; 6 import org.apache.lucene.index.DirectoryReader; 7 import org.apache.lucene.queryparser.classic.QueryParser; 8 import org.apache.lucene.search.IndexSearcher; 9 import org.apache.lucene.search.ScoreDoc; 10 import org.apache.lucene.search.TopDocs; 11 import org.apache.lucene.store.Directory; 12 import org.apache.lucene.store.FSDirectory; 13 import org.apache.lucene.util.Version; 14 15 import IkAnalyzer.MyIkAnalyzer; 16 public class testFieldQuery { 17 public static Version luceneVersion = Version.LATEST; 18 public static void indexSearch(String keywords){ 19 DirectoryReader reader = null; 20 try{ 21 Directory directory = FSDirectory.open(Paths.get("index3")); 22 reader= DirectoryReader.open(directory); 23 IndexSearcher searcher = new IndexSearcher(reader); 24 QueryParser parser = new QueryParser("key1",new MyIkAnalyzer());//content表示搜索的域或者說字段 25 org.apache.lucene.search.Query query = parser.parse(keywords);//被搜索的內容 26 String ss=query.toString(); 27 System.out.println(ss); 28 TopDocs tds = searcher.search(query, 20); 29 ScoreDoc[] sds = tds.scoreDocs; 30 int cou=0; 31 for(ScoreDoc sd:sds){ 32 cou++; 33 Document d = searcher.doc(sd.doc); 34 String output=cou+". "+d.get("category2")+"\n"+d.get("skey1")+"\n"+d.get("skey2"); 35 System.out.println(output); 36 } 37 }catch(Exception e){ 38 e.printStackTrace(); 39 }finally{ 40 try { 41 reader.close(); 42 } catch (IOException e) { 43 e.printStackTrace(); 44 } 45 } 46 } 47 public static void main(String[] args) throws IOException 48 { 49 String keywords="key1:豬八戒 AND key2:孫悟空 "; 50 indexSearch(keywords); 51 52 } 53 } FieldQuery 1 String ss=query.toString(); 2 System.out.println(ss); 打印查詢對象打印查詢對象后發現,lucene對用戶查詢語句的處理與對BooleanQuery的處理結果是一樣的。
九、MultiSearcher
MultiSearcher是多索引搜索。可以這樣理解:
為了減少單個索引目錄的大小,時常將索引放在許多目錄中,這些索引的結構都是一致的。比如有一個城市的網站搜索引擎,隨著時間的增長,我們可能會將索引的目錄按照年份分成2003、2004、2005等。舊的索引目錄被搜索的幾率小,所以將其單獨分出去,這樣,可以減小新的索引目錄,加快搜索速度。但是有些時候,必須實現多個索引的同時搜索,因為我們需要存放在這些索引中的信息。要實現多索引搜索,只需要對每個索引目錄都用IndexSearcher搜索一遍,最后將搜索結果合并起來。
實際上,lucene6.2.1已經廢棄了MultiSearcher這個類,改用MultiReader來實現。
1 package testAdvancedQuery; 2 import java.nio.file.Paths; 3 import java.io.*; 4 5 import org.apache.lucene.analysis.Analyzer; 6 import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer; 7 import org.apache.lucene.analysis.standard.StandardAnalyzer; 8 import org.apache.lucene.document.Document; 9 import org.apache.lucene.index.DirectoryReader; 10 import org.apache.lucene.index.MultiReader; 11 import org.apache.lucene.index.Term; 12 import org.apache.lucene.queryparser.classic.MultiFieldQueryParser; 13 import org.apache.lucene.queryparser.classic.QueryParser; 14 import org.apache.lucene.search.BooleanClause; 15 import org.apache.lucene.search.BooleanClause.Occur; 16 import org.apache.lucene.search.BooleanQuery; 17 import org.apache.lucene.search.Explanation; 18 import org.apache.lucene.search.IndexSearcher; 19 import org.apache.lucene.search.Query; 20 import org.apache.lucene.search.ScoreDoc; 21 import org.apache.lucene.search.TermQuery; 22 import org.apache.lucene.search.TopDocs; 23 import org.apache.lucene.store.Directory; 24 import org.apache.lucene.store.FSDirectory; 25 import org.apache.lucene.util.Version; 26 import IkAnalyzer.MyIkAnalyzer; 27 28 public class testMultiSearcher { 29 public static Version luceneVersion = Version.LATEST; 30 public static void indexSearch(String keywords){ 31 String res = ""; 32 DirectoryReader reader1 = null; 33 DirectoryReader reader2 = null; 34 MultiReader mr=null; 35 try{ 36 Directory directory1 = FSDirectory.open(Paths.get("index1")); 37 Directory directory2 = FSDirectory.open(Paths.get("index2")); 38 reader1 = DirectoryReader.open(directory1); 39 reader2 = DirectoryReader.open(directory2); 40 mr=new MultiReader(reader1,reader2); 41 IndexSearcher searcher1 = new IndexSearcher(mr); 42 IndexSearcher searcher2=new IndexSearcher(reader1); 43 IndexSearcher searcher3=new IndexSearcher(reader2); 44 QueryParser parser = new QueryParser("key2",new MyIkAnalyzer());//content表示搜索的域或者說字段 45 Query query = parser.parse(keywords); 46 String ss=query.toString(); 47 System.out.println(ss); 48 TopDocs tds = searcher1.search(query, 20); 49 ScoreDoc[] sds = tds.scoreDocs; 50 int cou=0; 51 System.out.println("MultiSearcher的結果:"); 52 for(ScoreDoc sd:sds){ 53 cou++; 54 Document d = searcher1.doc(sd.doc); 55 String output=cou+". "+d.get("category2")+"\n"+d.get("skey1"); 56 System.out.println(output); 57 } 58 //************************************************* 59 System.out.println("只搜索百科的結果:"); 60 tds = searcher2.search(query, 10); 61 sds = tds.scoreDocs; 62 cou=0; 63 for(ScoreDoc sd:sds){ 64 cou++; 65 Document d = searcher2.doc(sd.doc); 66 String output=cou+". "+d.get("category2")+"\n"+d.get("skey1"); 67 System.out.println(output); 68 } 69 //******************************************** 70 System.out.println("只搜索課本的結果:"); 71 tds = searcher3.search(query, 10); 72 sds = tds.scoreDocs; 73 cou=0; 74 for(ScoreDoc sd:sds){ 75 cou++; 76 Document d = searcher3.doc(sd.doc); 77 String output=cou+". "+d.get("category2")+"\n"+d.get("skey1"); 78 System.out.println(output); 79 } 80 }catch(Exception e){ 81 e.printStackTrace(); 82 }finally{ 83 try { 84 mr.close(); 85 } catch (IOException e) { 86 e.printStackTrace(); 87 } 88 } 89 } 90 public static void main(String[] args) throws IOException 91 { 92 String keyword="眼睛"; 93 indexSearch(keyword); 94 } 95 } MultiSearcher這樣,我們可以為“游戲”,“課本”等獨立地建立索引,用戶想搜索游戲時,我們可以指定索引目錄為游戲,用戶想綜合所有類別搜索時,我們可以用MultiSearcher來搜索所有類目~
我們可能擔心,在索引的過程中,分散地存儲到多個索引目錄中,是否在搜索時能夠得到全局的相關度計算得分?其實Lucene的這個方法支持全局得分的計算,也就是說,雖然索引分布在多個索引目錄中,在搜索的時候還會將全部的索引數據聚合在一起進行查詢匹配和得分計算。
利用多域搜索還可以實現多線程搜索,這個有待研究~
十、QueryParser
QueryParse類不是一種Query,但可以通過設置QueryParser的參數,實現多字段搜索,也能實現BooleanQuery的一部分效果。
使用QueryParser解析多個關鍵詞,比如用戶搜索“love China”,打印查詢對象后發現,兩個關鍵詞是或的關系。
使用下面的方法后,QueryParser就可以對兩個關鍵詞取交了:
1 //方式一: 2 parser.setDefaultOperator(QueryParser.Operator.AND); 3 //方式二: 4 parser.createBooleanQuery("key1", keywords, Occur.MUST); QueryParser方法一:
1 parser.setDefaultOperator(QueryParser.Operator.AND); 方法一參數QueryParser.Operator.AND表示與,QueryParser.Operator.OR表示或。
方法二:
1 parser.createBooleanQuery("key1", keywords, Occur.MUST); 方法二參數一表示域,參數二表示搜索關鍵詞,參數三只有兩種,Occur.MUST和Occur.SHOULD,而且都表示邏輯與。
?
以上就是這節的五種高級檢索,我們發現,這五種方法都可以解決分類搜索或過濾問題,其中BooleanQuery是最基本的用法。
綜合一二兩節,我們已經學習了十種高級檢索方式,其實lucene內部還有很多方法,以后有機會我們再來一起探索。
接下來,我們會一起揭開“lucene近實時搜索”的神秘面紗~~
轉載于:https://www.cnblogs.com/itcsl/p/6843309.html
總結
以上是生活随笔為你收集整理的3.8 高级检索方式(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 系统下载好怎么解压不了怎么办 系统下载无
- 下一篇: 回看存储过程