1. 程式人生 > >Lucene搜尋引擎-搜尋

Lucene搜尋引擎-搜尋

文章目錄


如果對Lucene不熟悉的,請移步: Lucene搜尋引擎-分詞器


當分詞、索引儲存完畢,就可以開始進行搜尋了。
先看一段程式碼:

public class SearchBaseFlow {
    public static void main(String[] args) throws
IOException, ParseException { // 使用的分詞器 Analyzer analyzer = new IKAnalyzer4Lucene7(true); // 索引儲存目錄 Directory directory = FSDirectory.open(Paths.get("f:/test/indextest")); // 索引讀取器 IndexReader indexReader = DirectoryReader.open(directory); // 索引搜尋器 IndexSearcher indexSearcher = new IndexSearcher(indexReader)
; // 要搜尋的欄位 String filedName = "name"; // 查詢生成器(解析輸入生成Query查詢物件) QueryParser parser = new QueryParser(filedName, analyzer); // 通過parse解析輸入(分詞),生成query物件 Query query = parser.parse("Thinkpad"); // 搜尋,得到TopN的結果(結果中有命中總數,topN的scoreDocs(評分文件(文件id,評分))) TopDocs topDocs = indexSearcher.search(query, 10); //前10條 //獲得總命中數 System.out.println(topDocs.totalHits); // 遍歷topN結果的scoreDocs,取出文件id對應的文件資訊 for (ScoreDoc sdoc : topDocs.scoreDocs) { // 根據文件id取儲存的文件 Document hitDoc = indexSearcher.doc(sdoc.doc); // 取文件的欄位 System.out.println(hitDoc.get(filedName)); } // 使用完畢,關閉、釋放資源 indexReader.close(); directory.close(); } }

搜尋的核心API:
在這裡插入圖片描述

搜尋API詳解

IndexReader 索引讀取器

Open一個讀取器,讀取的是該時刻點的索引檢視。如果後續索引發生改變,需重新open一個讀取器。
獲得索引讀取器的方式:

  • DirectoryReader.open(IndexWriter indexWriter) 優先使用
  • DirectoryReader.open(Directory)
  • DirectoryReader.openIfChanged(DirectoryReader) 共享當前reader資源重新開啟一個(當索引變化時)

IndexReader分為兩類:

  • 葉子讀取器:支援獲取stored fields, doc values, terms(詞項), and postings (詞項對應的文件)
  • 複合讀取器:多個讀取器的複合,只可直接用它獲取stored fields 。在內部通過CompositeReader.getSequentialSubReaders 得到裡面的葉子讀取器來獲取其他資料

上述程式碼使用到的DirectoryReader 是 複合讀取器。
注意:IndexReader是執行緒安全的


IndexReader主要API:
在這裡插入圖片描述
LeafReader主要API:
在這裡插入圖片描述

IndexSearch 索引搜尋器

應用通過呼叫它的search(Query,int)過載方法在一個IndexReader上實現搜尋。出於效能的考慮,請使用一個IndexSearcher例項,除非索引發生變化。如索引更新了則通過DirectoryReader.openIfChanged(DirectoryReader) 取得新的讀取器,再建立新的搜尋器。
注意:IndexSearch是執行緒安全的
在這裡插入圖片描述

查詢結果

在這裡插入圖片描述

基本查詢詳解

常用的查詢API如下:
在這裡插入圖片描述

TermQuery 詞項查詢

詞項查詢,最基本、最常用的查詢,用來查詢指定欄位包含指定詞項的文件。

TermQuery tq = new TermQuery(new Term("fieldName", "term"));
TermQuery tq = new TermQuery(new Term(“name", “thinkpad"));

BooleanQuery 布林查詢

搜尋的條件往往是多個的,如要查詢名稱包含"電腦" 或 "thinkpad"的商品,就需要兩個詞項查詢做或合併。布林查詢就是用來組合多個子查詢的。每個子查詢稱為布林字句 BooleanClause,布林字句自身也可以是組合的。
組合關係支援如下四種:

  • Occur.SHOULD:或
  • Occur.MUST:且
  • Occur.MUST_NOT:且非
  • Occur.FILTER:同MUST,但該字句不參與評分

布林查詢預設的最大字句數為1024,在將萬用字元查詢這樣的查詢rewriter為布林查詢時,往往會產生很多的字句,可能丟擲TooManyClauses 異常。可通過BooleanQuery.setMaxClauseCount(int)設定最大字句數。

// 布林查詢
Query query1 = new TermQuery(new Term(filedName, "thinkpad"));
Query query2 = new TermQuery(new Term("simpleIntro", "英特爾"));
BooleanQuery.Builder booleanQueryBuilder = new BooleanQuery.Builder();
booleanQueryBuilder.add(query1, Occur.SHOULD);
booleanQueryBuilder.add(query2, Occur.MUST);
BooleanQuery booleanQuery = booleanQueryBuilder.build();

// 可像下一行這樣寫
// BooleanQuery booleanQuery = new BooleanQuery.Builder()
// 	.add(query1, Occur.SHOULD).add(query2, Occur.MUST).build();

PhraseQuery 短語查詢

最常用的查詢,匹配特點序列的多個詞項。PhraserQuery使用一個位置移動因子(slop)來決定任意兩個詞項的位置(詞項出現的次序)可最大移動多少個位置來進行匹配,預設為0。有兩種方式來構建物件:

  • 直接用構造方法
  • 用裡面的Builder來構建

在這裡插入圖片描述
注意:

  • Builder方式構造中的int 值為詞項的位置,後面加入的詞項的位置需>=前一詞項的位置
  • 所有加入的詞項都匹配才算匹配(即使是你在同一位置加入多個詞項)
  • 如果需要在同一位置匹配多個同義詞中的一個,適合用MultiPhraseQuery

短語查詢示例:

PhraseQuery phraseQuery1 = new PhraseQuery("name", "thinkpad", "carbon");
PhraseQuery phraseQuery2 = new PhraseQuery(1, "name", "thinkpad", "carbon");
PhraseQuery phraseQuery3 = new PhraseQuery("name", "膝上型電腦", "聯想");
PhraseQuery phraseQuery4 = new PhraseQuery.Builder()
	.add(new Term("name", "膝上型電腦"), 4)
	.add(new Term("name", "聯想"), 5).build();
PhraseQuery phraseQuery5 = new PhraseQuery.Builder()
	.add(new Term("name", "膝上型電腦"), 0)
	.add(new Term("name", "聯想"), 1).build();

移動因子slop說明:
slop是指兩個項的位置之間允許的最大間隔距離。

String name = “ThinkPad X1 Carbon 20KH0009CD/25CD 超極本輕薄膝上型電腦聯想”;

  • 如果想用 thinkpad carbon 來匹配 name,則需要如何移動才會和name中的ThinkPand [X1] Carbon匹配呢?這種情況比較簡單,只要計算兩者間的距離,thinkpad向左移動1即可,此時slop=1
  • 如果想用 *carbon thinkpad 來匹配 name,則只需要將carbon向右移動3位即可匹配ThinkPand [X1] Carbon,因此slop=3

MultiPhraseQuery 多重短語查詢

短語查詢的一種更通用的用法,支援同位置多個詞的OR匹配。通過裡面的Builder來構建MultiPhraseQuery
在這裡插入圖片描述
示例:

// 多重短語查詢
Term[] terms = new Term[2];
terms[0] = new Term("name", "筆記本");
terms[1] = new Term("name", "膝上型電腦");
Term t = new Term("name", "聯想");
MultiPhraseQuery multiPhraseQuery = new MultiPhraseQuery.Builder()
	.add(terms).add(t).build();

// 對比 PhraseQuery在同位置加入多個詞 ,同位置的多個詞都需匹配,所以查不出
PhraseQuery pquery = new PhraseQuery.Builder()
	.add(terms[0], 0).add(terms[1], 0).add(t, 1).build();

SpanNearQuery 臨近查詢(跨度查詢)

用於更復雜的短語查詢,可以指定詞間位置的最大間隔跨度。通過組合一系列的SpanQuery 例項來進行查詢,可以指定是否按順序匹配、slop、gap。
在這裡插入圖片描述
示例:

// SpanNearQuery 臨近查詢
SpanTermQuery tq1 = new SpanTermQuery(new Term("name", "thinkpad"));
SpanTermQuery tq2 = new SpanTermQuery(new Term("name", "carbon"));
SpanNearQuery spanNearQuery = new SpanNearQuery(new SpanQuery[] { tq1, tq2 }, 1, true);

// SpanNearQuery 臨近查詢 gap slop 使用
SpanNearQuery.Builder spanNearQueryBuilder = SpanNearQuery.newOrderedNearQuery("name");
spanNearQueryBuilder.addClause(tq1).addGap(0).setSlop(1).addClause(tq2);
SpanNearQuery spanNearQuery5 = spanNearQueryBuilder.build();

TermRangeQuery 詞項範圍查詢

用於查詢包含某個範圍內的詞項的文件,如以字母開頭a到c的詞項。詞項在反向索引中是排序的,只需指定的開始詞項、結束詞項,就可以查詢該範圍的詞項。
如果是做數值的範圍查詢則用 PointRangeQuery
在這裡插入圖片描述
引數說明:

  • field:欄位
  • lowerTerm:下邊界詞
  • upperTerm:上邊界詞
  • includeLower:是否包含下邊界
  • includeUpper:是否包含上邊界

示例:

// TermRangeQuery 詞項範圍查詢
TermRangeQuery termRangeQuery = TermRangeQuery.newStringRange("name", "carbon", "張三", false, true);

PrefixQuery, WildcardQuery, RegexpQuery

  • PrefixQuery 字首查詢
    查詢包含以xxx為字首的詞項的文件,是萬用字元查詢,如 app,實際是 app*
  • WildcardQuery 萬用字元查詢
    *表示0個或多個字元,?表示1個字元,\是轉義符。萬用字元查詢可能會比較慢,不可以萬用字元開頭(那樣就是所有詞項了)
  • RegexpQuery 正則表示式查詢
    詞項符合某正則表示式

這三種查詢可能會比較慢,使用時要謹慎

示例:

// PrefixQuery 字首查詢
PrefixQuery prefixQuery = new PrefixQuery(new Term("name", "think"));

// WildcardQuery 萬用字元查詢
WildcardQuery wildcardQuery = new WildcardQuery(new Term("name", "think*"));

// WildcardQuery 萬用字元查詢
WildcardQuery wildcardQuery2 = new WildcardQuery(new Term("name", "厲害了???"));

// RegexpQuery 正則表示式查詢
RegexpQuery regexpQuery = new RegexpQuery(new Term("name", "厲害.{4}"));

FuzzyQuery 模糊查詢

簡單地與索引詞項進行相近匹配,允許最大2個不同字元。常用於拼寫錯誤的容錯:如把 “thinkpad” 拼成 “thinkppd”或 “thinkd”,使用FuzzyQuery 仍可搜尋到正確的結果。
示例:

// FuzzyQuery 模糊查詢
FuzzyQuery fuzzyQuery = new FuzzyQuery(new Term("name", "thind"));

FuzzyQuery fuzzyQuery2 = new FuzzyQuery(new Term("name", "thinkd"), 2);

FuzzyQuery fuzzyQuery3 = new FuzzyQuery(new Term("name", "thinkpaddd"));

FuzzyQuery fuzzyQuery4 = new FuzzyQuery(new Term("name", "thinkdaddd"));

數值查詢

前提:查詢的數值欄位必須索引。
通過 IntPoint, LongPoint, FloatPoint, or DoublePoint 中的方法構建對應的查詢。
以IntPoint為例:
在這裡插入圖片描述
示例:

// 精確值查詢
Query exactQuery = IntPoint.newExactQuery("price", 1999900);

// 數值範圍查詢
Query pointRangeQuery = IntPoint.newRangeQuery("price", 499900,1000000);

// 集合查詢
Query setQuery = IntPoint.newSetQuery("price", 1999900, 1000000, 2000000);

QuerParser查詢解析器

使用者的查詢需求是多變的,我們無法事先知道,也就無法事先編寫好構建查詢的程式碼。不同的查詢需求只是不同欄位的不同基本查詢的組合。
比如需求如下:

(name:“聯想膝上型電腦” OR simpleIntro :“聯想膝上型電腦”) AND type:電腦 AND price:[800000 TO 1000000]

使用者的查詢需求被很好的描述出來了,我們的搜尋程式中得能解讀這個描述,並把它轉為對應的查詢組合。這就是 QueryParser包的功能。
核心API:
在這裡插入圖片描述
Lucene QueryPaser包中提供了兩類查詢解析器:

  • 傳統的解析器:QueryParser、MultiFieldQueryParser
  • 基於新的 flexible 框架的解析器:StandardQueryParser

QueryParser 傳統解析器

單預設欄位

// 使用的分詞器
Analyzer analyzer = new IKAnalyzer4Lucene7(true);
// 要搜尋的預設欄位
String defaultFiledName = "name";
// 查詢生成器(解析輸入生成Query查詢物件)
QueryParser parser = new QueryParser(defaultFiledName, analyzer);
// 通過parse解析輸入,生成query物件
Query query1 = parser.parse(
		"(name:\"聯想膝上型電腦\" OR simpleIntro:英特爾) AND type:電腦 AND price:999900");

MultiFieldQueryParser 傳統解析器

多預設欄位

// 傳統查詢解析器-多預設欄位
String[] multiDefaultFields = { "name", "type", "simpleIntro" };
MultiFieldQueryParser multiFieldQueryParser = new MultiFieldQueryParser(multiDefaultFields, analyzer);
// 設定預設的組合操作,預設是 OR
multiFieldQueryParser.setDefaultOperator(Operator.OR);
Query query4 = multiFieldQueryParser.parse("膝上型電腦 AND price:1999900");

StandardQueryParser 新標準解析器

StandardQueryParser queryParserHelper = new StandardQueryParser(analyzer);
// 設定預設欄位
// queryParserHelper.setMultiFields(CharSequence[] fields);
// queryParserHelper.setPhraseSlop(8);
// Query query = queryParserHelper.parse("a AND b", "defaultField");
Query query5 = queryParserHelper.parse(
	"(\"聯想膝上型電腦\" OR simpleIntro:英特爾) AND type:電腦 AND price:1999900","name");

查詢解析語法

  • Term詞項

    單個詞項的表示:電腦
    短語的表示:“聯想膝上型電腦”

  • Field欄位

    示例1: name:“聯想膝上型電腦” AND type:電腦
    如果name是預設欄位,則可寫成: “聯想膝上型電腦” AND type:電腦
    示例2:type:電腦 計算機 手機
    只有第一個是type的值,後兩個則是使用預設欄位。

  • Term Modifiers 詞項修飾符
    萬用字元

    • ? 單個字元
    • 0個或多個字元
    • 示例:te?t test* te*t
    • 注意:萬用字元不可用在開頭。

    模糊查詢

    • 示例: roam~
    • 模糊查詢最大支援兩個不同字元。
    • 示例: roam~1

    正則表示式

    • /xxxx/
    • 示例:/[mb]oat/

    臨近查詢

    • 短語後加~移動值
    • 示例:“jakarta apache”~10

    範圍查詢

    • mod_date:[20020101 TO 20030101] 包含邊界值
    • title:{Aida TO Carmen} 不包含邊界值

    詞項加權

    • 使該詞項的相關性更高,通過 ^數值來指定加權因子,預設加權因子值是1
    • 示例:如要搜尋包含 jakarta apache 的文章,jakarta更相關,則:jakarta^4 apache
    • 短語也可以: “jakarta apache”^4 “Apache Lucene”
  • 布林操作符

    Lucene支援的布林操作: AND, “+”, OR, NOT ,"-"
    OR:“jakarta apache” jakarta 等同於 “jakarta apache” OR jakarta
    AND:“jakarta apache” AND “Apache Lucene”
    +:表示必須包含,+jakarta lucene
    NOT:非,“jakarta apache” NOT “Apache Lucene”,NOT不能單獨使用,如NOT "Apache Lucene"是不行的
    -:同NOT,“jakarta apache” - “Apache Lucene”

  • 組合

    字句組合:(jakarta OR apache) AND website
    欄位組合:title:(+return +“pink panther”)

  • 轉義 \

    對語法字元: + - && || ! ( ) { } [ ] ^ “ ~ * ? : \ / 進行轉義。
    如要查詢包含 (1+1):2,則使用轉義(1+1):2

總結

  • 查詢字串應是由人輸入的,而不應是你程式設計產生。如果你為了用查詢解析器,而在你的應用中程式設計產生查詢字串,不可取,更應該直接使用基本查詢API;
  • 未分詞的欄位,應直接使用基本查詢API加入到查詢中,而不應使用查詢解析器;
  • 對於普通文字欄位,使用查詢解析器,而其他值欄位:如 時間、數值,則應使用基本查詢API