個人專題目錄](http://www.reibang.com/p/140e2a59db2c)
elasticsearch文檔復合查詢及排序
1. 復合查詢
1.1 bool查詢
bool (布爾)過濾器污朽。 這是個復合過濾器(compound filter) 捺萌,它可以接受多個其他過濾器作為參數(shù)围俘,并將這些過濾器結(jié)合成各式各樣的布爾(邏輯)組合荔仁。
格式
一個 bool 過濾器由三部分組成:
{
"bool" : {
"must" : [],
"should" : [],
"must_not" : [],
}
}
參數(shù)定義:
must
所有的語句都 必須(must) 匹配,與 AND 等價春塌。
must_not
所有的語句都不能(must not) 匹配几晤,與 NOT 等價宫补。
should
至少有一個語句要匹配,與 OR 等價碎节。
例如查詢條件:
POST /book-index/_search
{
"from": 0,
"size": 100,
"query": {
"bool": {
"must": [
{
"match": {
"title": {
"query": "三星",
"operator": "OR",
"prefix_length": 0,
"max_expansions": 50,
"fuzzy_transpositions": true,
"lenient": false,
"zero_terms_query": "NONE",
"auto_generate_synonyms_phrase_query": true,
"boost": 1
}
}
}
],
"must_not": [
{
"term": {
"brandName": {
"value": "諾基亞",
"boost": 1
}
}
}
],
"should": [
{
"term": {
"categoryName": {
"value": "手機",
"boost": 1
}
}
},
{
"term": {
"categoryName": {
"value": "平板電視",
"boost": 1
}
}
}
],
"adjust_pure_negative": true,
"boost": 1
}
}
}
@Override
public void boolQuery(String indexName) throws Exception {
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
queryBuilder.should(QueryBuilders.termQuery("categoryName", "手機"));
queryBuilder.should(QueryBuilders.termQuery("categoryName", "平板電視"));
queryBuilder.must(QueryBuilders.matchQuery("title", "三星"));
queryBuilder.mustNot(QueryBuilders.termQuery("brandName", "諾基亞"));
builder(indexName, queryBuilder);
}
@Test
public void testBoolQuery() throws Exception {
baseQuery.boolQuery(Constants.INDEX_NAME);
}
1.2 booting查詢
該查詢用于將兩個查詢封裝在一起捧搞,并降低其中一個查詢所返回文檔的分值。
它接受一個positive查詢和一個negative查詢。只有匹配了positive查詢的文檔才會被包含到結(jié)果集中胎撇,但是同時匹配了negative查詢的文檔會被降低其相關度介粘,通過將文檔原本的_score和negative_boost參數(shù)進行相乘來得到新的_score。因此晚树,negative_boost參數(shù)必須小于1.0姻采。在上面的例子中,任何包含了指定負面詞條的文檔的_score都會是其原本_score的一半爵憎。
例如:
在互聯(lián)網(wǎng)上搜索"蘋果"也許會返回慨亲,水果或者各種食譜的結(jié)果。我們可以通過排除“水果 喬木 維生素”和這類單詞宝鼓,結(jié)合bool查詢中的must_not子句巡雨,將結(jié)果范圍縮小到只剩蘋果手機。
POST /book-index/_search
{
"query": {
"boosting": {
"positive": {
"match": {
"title": {
"query": "三星 手機 聯(lián)通",
"operator": "AND",
"prefix_length": 0,
"max_expansions": 50,
"fuzzy_transpositions": true,
"lenient": false,
"zero_terms_query": "NONE",
"auto_generate_synonyms_phrase_query": true,
"boost": 1.0
}
}
},
"negative": {
"match": {
"title": {
"query": "白色 黑色",
"operator": "OR",
"prefix_length": 0,
"max_expansions": 50,
"fuzzy_transpositions": true,
"lenient": false,
"zero_terms_query": "NONE",
"auto_generate_synonyms_phrase_query": true,
"boost": 1.0
}
}
},
"negative_boost": 0.1,
"boost": 1.0
}
}
}
@Override
public void boostingQuery(String indexName, String positiveField, String positiveKeyWord, String negativeField, String negativeKeyWord) throws Exception {
MatchQueryBuilder matchQueryPositiveBuilder = QueryBuilders.matchQuery(positiveField, positiveKeyWord);
MatchQueryBuilder matchQueryNegativeBuilder = QueryBuilders.matchQuery(negativeField, negativeKeyWord);
BoostingQueryBuilder boostingQueryBuilder = QueryBuilders.boostingQuery(matchQueryPositiveBuilder,
matchQueryNegativeBuilder).negativeBoost(0.1f);
builder(indexName, boostingQueryBuilder);
}
/**
* 它接受一個positive查詢和一個negative查詢席函。只有匹配了positive查詢的文檔才會被包含到結(jié)果集中铐望,
* 但是同時匹配了negative查詢的文檔會被降低其相關度,通過將文檔原本的_score和negative_boost參數(shù)進行相乘來得到新的_score茂附。
* 因此正蛙,negative_boost參數(shù)必須小于1.0。在上面的例子中营曼,任何包含了指定負面詞條的文檔的_score都會是其原本_score的一半乒验。
*
* @throws Exception
*/
@Test
public void testBoostingQuery() throws Exception {
//都可以查出來,只是SCORE值減少蒂阱,可以通過SCORE值來去掉排名在后面的
baseQuery.boostingQuery(Constants.INDEX_NAME, "title", "三星 手機 聯(lián)通", "title", "白色 黑色");
}
2. 排序(Sort)
默認情況下锻全,結(jié)果集會按照相關性進行排序 -- 相關性越高,排名越靠前录煤。
為了使結(jié)果可以按照相關性進行排序鳄厌,我們需要一個相關性的值。在ElasticSearch的查詢結(jié)果中妈踊, 相關性分值會用_score
字段來給出一個浮點型的數(shù)值了嚎,所以默認情況下,結(jié)果集以_score
進行倒序排列廊营。
注意:在Elasticsearch中歪泳,如果使用text類型的字段作為排序依據(jù),會有問題露筒。Elasticsearch需要對text類型字段數(shù)據(jù)做分詞處理呐伞。如果使用text類型字段做排序,Elasticsearch給出的排序結(jié)果未必友好慎式,畢竟分詞后伶氢,先使用哪一個單詞做排序都是不合理的假哎。所以Elasticsearch中默認情況下不允許使用text類型的字段做排序,如果需要使用字符串做結(jié)果排序鞍历,則可使用keyword類型字段作為排序依據(jù)舵抹,因為keyword字段不做分詞處理。
如果對一個text field進行排序劣砍,結(jié)果往往不準確惧蛹,因為分詞后是多個單詞,再排序就不是我們想要的結(jié)果了刑枝。
通常解決方案是香嗓,將一個text field建立兩次索引,一個分詞装畅,用來進行搜索靠娱;一個不分詞,用來進行排序掠兄。
fielddate:true
{
"query": {
"match_all": {}
},
"sort": [
{
"title.keyword": {
"order": "desc"
}
}
]
}
POST /book-index/_search
{
"query": {
"match": {
"brandName": {
"query": "三星",
"operator": "OR",
"prefix_length": 0,
"max_expansions": 50,
"fuzzy_transpositions": true,
"lenient": false,
"zero_terms_query": "NONE",
"auto_generate_synonyms_phrase_query": true,
"boost": 1
}
}
},
"sort": [
{
"id": {
"order": "desc"
}
}
]
}
@Override
public void sortQuery(String indexName, String field, String keyWord, String sort, SortOrder sortOrder) throws IOException {
SearchRequest searchRequest = new SearchRequest(indexName);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchQuery(field, keyWord));
searchSourceBuilder.sort(sort, sortOrder);
searchRequest.source(searchSourceBuilder);
log.info("source:" + searchRequest.source());
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits hits = searchResponse.getHits();
log.info("count:" + hits.getTotalHits());
SearchHit[] h = hits.getHits();
for (SearchHit hit : h) {
log.info("結(jié)果" + hit.getSourceAsMap() + ",score:" + hit.getScore());
}
}
@Test
public void testSortQueryBySort() throws IOException {
sortQuery.sortQuery(Constants.INDEX_NAME, "brandName", "三星", "id", SortOrder.DESC);
}