1. 程式人生 > >elasticsearch-java api之搜尋(一)

elasticsearch-java api之搜尋(一)

1、全文搜尋兩個最重要的方面是:
1)相關性(Relevance)
它是評價查詢與其結果間的相關程度,並根據這種相關程度對結果排名的一種能力,這種計算方式可以是 TF/IDF 方法(參見 相關性的介紹)、地理位置鄰近、模糊相似,或其他的某些演算法。
2)分析(Analysis)
它是將文字塊轉換為有區別的、規範化的 token 的一個過程,(參見 分析的介紹) 目的是為了(a)建立倒排索引以及(b)查詢倒排索引。

官方各種例項:https://www.elastic.co/guide/cn/elasticsearch/guide/current/full-text-search.html

2、文字查詢可以劃分成兩大家族:

1)基於詞項的查詢

如 term 或 fuzzy 這樣的底層查詢不需要分析階段,它們對單個詞項進行操作。用 term 查詢詞項 Foo 只要在倒排索引中查詢 準確詞項 ,並且用 TF/IDF 演算法為每個包含該詞項的文件計算相關度評分 _score 。

記住 term 查詢只對倒排索引的詞項精確匹配,這點很重要,它不會對詞的多樣性進行處理(如, foo 或 FOO )。這裡,無須考慮詞項是如何存入索引的。如果是將 ["Foo","Bar"] 索引存入一個不分析的( not_analyzed )包含精確值的欄位,或者將 Foo Bar 索引到一個帶有 whitespace 空格分析器的欄位,兩者的結果都會是在倒排索引中有 Foo 和 Bar 這兩個詞。

2)基於全文的查詢
像 match 或 query_string 這樣的查詢是高層查詢,它們瞭解欄位對映的資訊:
如果查詢 日期(date) 或 整數(integer) 欄位,它們會將查詢字串分別作為日期或整數對待。
如果查詢一個( not_analyzed )未分析的精確值字串欄位, 它們會將整個查詢字串作為單個詞項對待。
但如果要查詢一個( analyzed )已分析的全文欄位, 它們會先將查詢字串傳遞到一個合適的分析器,然後生成一個供查詢的詞項列表。
一旦組成了詞項列表,這個查詢會對每個詞項逐一執行底層的查詢,再將結果合併,然後為每個文件生成一個最終的相關度評分。

我們很少直接使用基於詞項的搜尋,通常情況下都是對全文進行查詢,而非單個詞項。

注:

當我們想要查詢一個具有精確值的 not_analyzed 未分析欄位之前, 需要考慮,是否真的採用評分查詢,或者非評分查詢會更好。單詞項查詢通常可以用是、非這種二元問題表示,所以更適合用過濾, 而且這樣做可以有效利用快取

GET /_search
{
    "query": {
        "constant_score": {
            "filter": {
                "term": { "gender": "female" }
            }
        }
    }
}


一、全文檢索:

1、match 匹配查詢

1)match all:匹配所有文件

public static void matchAll(String indexName,String indexType) {
		QueryBuilder qb = QueryBuilders.matchAllQuery();
		SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType).setQuery(qb).get();
		SearchHits hits = searchResponse.getHits();
		long totalHits = hits.getTotalHits();
		SearchHit[] hits2 = hits.getHits();
		for (SearchHit sh:hits2) {
			Map<String, Object> source = sh.getSource();
			//sh.getId();
			System.out.println(source);
		}
	}

2)match query:
public static void matchQuery(String indexName,String indexType) {
		QueryBuilder qb = QueryBuilders.matchQuery("title","java hadoop").operator(Operator.OR);
		SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType).setQuery(qb).get();
		SearchHits hits = searchResponse.getHits();
		long totalHits = hits.getTotalHits();
		SearchHit[] hits2 = hits.getHits();
		for (SearchHit sh:hits2) {
			Map<String, Object> source = sh.getSource();
			String id = sh.getId();
			//sh.getScore();
			System.out.println(totalHits+","+source+","+id);
		}
	}

1.說明:

A、對query的詞使用分詞,然後去指定欄位的倒排索引中去搜索;

B、可以對query的詞指定operator邏輯,預設是or

2.執行步驟:

  • 檢查欄位型別 :標題 title 欄位是一個 string 型別( analyzed )已分析的全文欄位,這意味著查詢字串本身也應該被分析。
  • 分析查詢字串 :將查詢的字串 QUICK! 傳入標準分析器中,輸出的結果是單個項 quick 。因為只有一個單詞項,所以 match 查詢執行的是單個底層 term 查詢。
  • 查詢匹配文件 :用 term 查詢在倒排索引中查詢 quick 然後獲取一組包含該項的文件。
  • 為每個文件評分 :用 term 查詢計算每個文件相關度評分 _score ,這是種將 詞頻(term frequency,即詞 quick 在相關文件的 title 欄位中出現的頻率)和反向文件頻率(inverse document frequency,即詞 quick 在所有文件的 title 欄位中出現的頻率),以及欄位的長度(即欄位越短相關度越高)相結合的計算方式。

3)multi match query:對於query內容,可以同時指定多個field,不同field之間是或的關係

public static void matchMultiQuery(String indexName,String indexType) {
		QueryBuilder qb = QueryBuilders.multiMatchQuery("java code","title","name").operator(Operator.OR);
		SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType).setQuery(qb).get();
		SearchHits hits = searchResponse.getHits();
		long totalHits = hits.getTotalHits();
		SearchHit[] hits2 = hits.getHits();
		for (SearchHit sh:hits2) {
			Map<String, Object> source = sh.getSource();
			String id = sh.getId();
			//sh.getScore();
			System.out.println(totalHits+","+source+","+id);
		}
	}

2、query String:
public static void queryString(String indexName,String indexType) {
		QueryStringQueryBuilder queryBuilders = QueryBuilders.queryStringQuery("java hadoop")
				.field("title");  
		SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType).setQuery(queryBuilders).get();
		SearchHits hits = searchResponse.getHits();
		long totalHits = hits.getTotalHits();
		SearchHit[] hits2 = hits.getHits();
		for (SearchHit sh:hits2) {
			Map<String, Object> source = sh.getSource();
			String id = sh.getId();
			//sh.getScore();
			System.out.println(totalHits+","+source+","+id);
		}
	}
和match一樣。

二、詞項查詢:

1、term 短語查詢:

public static void termQuery(String indexName,String indexType) {
		QueryBuilder qb = QueryBuilders.termsQuery("title", "hadoop","lucene");
		
		SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType).setQuery(qb).get();
		SearchHits hits = searchResponse.getHits();
		long totalHits = hits.getTotalHits();
		SearchHit[] hits2 = hits.getHits();
		for (SearchHit sh:hits2) {
			Map<String, Object> source = sh.getSource();
			String id = sh.getId();
			//sh.getScore();
			System.out.println(totalHits+","+source+","+id);
		}
	}
對query的詞不經過分詞,去指定欄位的倒排中準確的匹配,一般對於該類查詢建議使用過濾器,應為過濾器有快取工程,可以提高效能。

2、字首查詢:

public static void prefixQuery(String indexName,String indexType) {
		QueryBuilder qb = QueryBuilders.prefixQuery("other", "學");
		
		SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType).setQuery(qb).get();
		SearchHits hits = searchResponse.getHits();
		long totalHits = hits.getTotalHits();
		SearchHit[] hits2 = hits.getHits();
		for (SearchHit sh:hits2) {
			Map<String, Object> source = sh.getSource();
			String id = sh.getId();
			//sh.getScore();
			System.out.println(totalHits+","+source+","+id);
		}
	}
對query的詞不經過分詞,去指定欄位的倒排中匹配符合query詞的字首文件

3、短語匹配:

1)查詢首先將查詢字串解析成一個詞項列表,然後對這些詞項進行搜尋,但只保留那些包含 全部 搜尋詞項,且 位置 與搜尋詞項相同的文件。預設中間不夾雜其他詞項,可以通過slop設定位置距離(位置距離可以迴圈)

public static void matchPhraseQuery(String indexName,String indexType) {
		QueryBuilder qb = QueryBuilders.matchPhraseQuery("title", "kafka java").slop(3);
		
		SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType).setQuery(qb).get();
		SearchHits hits = searchResponse.getHits();
		long totalHits = hits.getTotalHits();
		SearchHit[] hits2 = hits.getHits();
		for (SearchHit sh:hits2) {
			Map<String, Object> source = sh.getSource();
			String id = sh.getId();
			//sh.getScore();
			System.out.println(totalHits+","+source+","+id);
		}
	}

4、短語字首匹配:

1)查詢首先將查詢字串解析成一個詞項列表,然後對這些詞項進行搜尋,但只保留那些包含 全部 搜尋詞項,且 位置 與搜尋詞項相同的文件。預設中間不夾雜其他詞項,可以通過slop設定位置距離(位置距離可以迴圈)
2)還可以設定max_expansion 來決定字首匹配的最大數量

public static void matchPhrasePrefixQuery(String indexName,String indexType) {
		QueryBuilder qb = QueryBuilders.matchPhrasePrefixQuery("title", "elasticsearch java s").prefixLength(2).slop(3).maxExpansions(2);
		
		SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType).setQuery(qb).get();
		SearchHits hits = searchResponse.getHits();
		//long totalHits = hits.getTotalHits();
		SearchHit[] hits2 = hits.getHits();
		for (SearchHit sh:hits2) {
			Map<String, Object> source = sh.getSource();
			String id = sh.getId();
			float score = sh.getScore();
			System.out.println("source:"+source+",id:"+id+",score:"+score);
		}
	}
5、模糊查詢(fuzzy):

對查詢的詞進行模糊處理(例如:輸入jiva,但實際想查詢java),es使用編輯距離來做模糊查詢的條件,可以設定如下引數:

1)fuzziness:模糊度,推薦auto
2)prefixLength:不會“模糊化”的初始字元數,這有助於減少必須檢查的術語數量,預設為0。
3)maxExpansions:模糊查詢將擴充套件到的最大項數,預設為50。越小越有助於效能

對於字串,使用編輯距離進行模糊;對於數值或者日期型別,使用加減一個模糊範圍進行查詢。

public static void fuzzyQuery(String indexName,String indexType) {
		QueryBuilder qb = QueryBuilders.fuzzyQuery("title", "jaui").fuzziness(Fuzziness.fromEdits(2)).prefixLength(2).maxExpansions(50);
		
		SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType).setQuery(qb).get();
		SearchHits hits = searchResponse.getHits();
		//long totalHits = hits.getTotalHits();
		SearchHit[] hits2 = hits.getHits();
		for (SearchHit sh:hits2) {
			Map<String, Object> source = sh.getSource();
			String id = sh.getId();
			float score = sh.getScore();
			System.out.println("source:"+source+",id:"+id+",score:"+score);
		}
	}

6、萬用字元查詢(wildcard):

wildcard 和 regexp 查詢的工作方式與 prefix 查詢完全一樣,它們也需要掃描倒排索引中的詞列表才能找到所有匹配的詞,然後依次獲取每個詞相關的文件 ID ,與 prefix 查詢的唯一不同是:它們能支援更為複雜的匹配模式。

public static void wildCardQuery(String indexName,String indexType) {
		QueryBuilder qb = QueryBuilders.wildcardQuery("title", "el*");
		SearchResponse searchResponse = transportClient.prepareSearch(indexName).setTypes(indexType).setQuery(qb).get();
		SearchHits hits = searchResponse.getHits();
		//long totalHits = hits.getTotalHits();
		SearchHit[] hits2 = hits.getHits();
		for (SearchHit sh:hits2) {
			Map<String, Object> source = sh.getSource();
			String id = sh.getId();
			float score = sh.getScore();
			System.out.println("source:"+source+",id:"+id+",score:"+score);
		}
	}