2017-01-19 27 views
19

ElasticSearch 5.x向Suggestedter API(Documentation)引入了一些(突破性的)更改。最顯着的變化是:面向字的完成建議(ElasticSearch 5.x)

完成建議者是面向文檔的

建議都知道他們屬於 文件。現在,相關文檔(_source)是 作爲完成建議的一部分返回。

總之,一切完成查詢返回所有匹配文件,而不是隻匹配。這裏存在的問題是如果自動完成的單詞出現在多個文檔中,則會出現重複。

比方說,我們有這個簡單的映射:

{ 
    "my-index": { 
     "mappings": { 
     "users": { 
      "properties": { 
       "firstName": { 
        "type": "text" 
       }, 
       "lastName": { 
        "type": "text" 
       }, 
       "suggest": { 
        "type": "completion", 
        "analyzer": "simple" 
       } 
      } 
     } 
     } 
    } 
} 

少數測試文檔:

{ 
    "_index": "my-index", 
    "_type": "users", 
    "_id": "1", 
    "_source": { 
     "firstName": "John", 
     "lastName": "Doe", 
     "suggest": [ 
     { 
      "input": [ 
       "John", 
       "Doe" 
      ] 
     } 
     ] 
    } 
}, 
{ 
    "_index": "my-index", 
    "_type": "users", 
    "_id": "2", 
    "_source": { 
     "firstName": "John", 
     "lastName": "Smith", 
     "suggest": [ 
     { 
      "input": [ 
       "John", 
       "Smith" 
      ] 
     } 
     ] 
    } 
} 

而且一個由這本書查詢:

POST /my-index/_suggest?pretty 
{ 
    "my-suggest" : { 
     "text" : "joh", 
     "completion" : { 
      "field" : "suggest" 
     } 
    } 
} 

這將產生結果如下:

{ 
    "_shards": { 
     "total": 5, 
     "successful": 5, 
     "failed": 0 
    }, 
    "my-suggest": [ 
     { 
     "text": "joh", 
     "offset": 0, 
     "length": 3, 
     "options": [ 
      { 
       "text": "John", 
       "_index": "my-index", 
       "_type": "users", 
       "_id": "1", 
       "_score": 1, 
       "_source": { 
       "firstName": "John", 
       "lastName": "Doe", 
       "suggest": [ 
        { 
         "input": [ 
          "John", 
          "Doe" 
         ] 
        } 
       ] 
       } 
      }, 
      { 
       "text": "John", 
       "_index": "my-index", 
       "_type": "users", 
       "_id": "2", 
       "_score": 1, 
       "_source": { 
       "firstName": "John", 
       "lastName": "Smith", 
       "suggest": [ 
        { 
         "input": [ 
          "John", 
          "Smith" 
         ] 
        } 
       ] 
       } 
      } 
     ] 
     } 
    ] 
} 

總之,對於文本「joh」的完成建議,返回了兩個(2)文檔 - John和他們都具有相同的text屬性值。

但是,我想收到一個(1)。簡單的東西是這樣的:

{ 
    "_shards": { 
     "total": 5, 
     "successful": 5, 
     "failed": 0 
    }, 
    "my-suggest": [ 
     { 
     "text": "joh", 
     "offset": 0, 
     "length": 3, 
     "options": [ 
      "John" 
     ] 
     } 
    ] 
} 

問題:如何實現一個基於詞完成建議者。沒有必要返回任何文檔相關的數據,因爲我現在不需要它。

「完成建議」是否適合我的場景?還是應該使用完全不同的方法?


編輯: 正如你們許多人所指出的,額外的完成,只指數將是一個可行的解決方案。但是,我可以看到使用此方法的多個問題:

  1. 保持新索引同步。
  2. 自動填寫後續單詞可能是全球性的,而不是縮小範圍。例如,假設您在附加索引中有以下詞語:"John", "Doe", "David", "Smith"。查詢"John D"時,不完整單詞的結果應爲"Doe"而不是"Doe", "David"

爲了克服第二點,只索引單個單詞是不夠的,因爲您還需要將所有單詞映射到文檔以正確縮小自動完成後續單詞的範圍。與此一起,您實際上遇到與查詢原始索引相同的問題。因此,額外的索引不再有意義了。

+0

爲[在這個問題]在暗示(https://github.com/elastic/elasticsearch/issues/21676),這種新的行爲是 「設計」並沒有計劃改變它。他們的建議是爲完成建議者創建另一個索引。正如下面的@EdgarVonk所建議的那樣。 – Val

+0

關於當前索引的自定義查詢呢?也許用獨特的查詢(術語聚合)爲所有建議創建額外的NGram字段?至於額外的建議索引,我可以確定一些問題,這些問題實際上與您提出的解決方案相矛盾(請參閱我的更新問題)。 – alesc

+0

當然,術語聚合也可以實現類似的目標,但它取決於您擁有的文檔的負載。我不是在提出這個解決方案,埃德加和ES人(見問題)是;-) – Val

回答

12

正如在評論中暗示的那樣,實現此目的的另一種方式是不創建重複文檔,即爲包含該字段的ngram的firstname字段創建一個子字段。首先定義你的映射是這樣的:

PUT my-index 
{ 
    "settings": { 
    "analysis": { 
     "analyzer": { 
     "completion_analyzer": { 
      "type": "custom", 
      "filter": [ 
      "lowercase", 
      "completion_filter" 
      ], 
      "tokenizer": "keyword" 
     } 
     }, 
     "filter": { 
     "completion_filter": { 
      "type": "edge_ngram", 
      "min_gram": 1, 
      "max_gram": 24 
     } 
     } 
    } 
    }, 
    "mappings": { 
    "users": { 
     "properties": { 
     "autocomplete": { 
      "type": "text", 
      "fields": { 
      "raw": { 
       "type": "keyword" 
      }, 
      "completion": { 
       "type": "text", 
       "analyzer": "completion_analyzer", 
       "search_analyzer": "standard" 
      } 
      } 
     }, 
     "firstName": { 
      "type": "text" 
     }, 
     "lastName": { 
      "type": "text" 
     } 
     } 
    } 
    } 
} 

然後你指數的幾個文件:

POST my-index/users/_bulk 
{"index":{}} 
{ "firstName": "John", "lastName": "Doe", "autocomplete": "John Doe"} 
{"index":{}} 
{ "firstName": "John", "lastName": "Deere", "autocomplete": "John Deere" } 
{"index":{}} 
{ "firstName": "Johnny", "lastName": "Cash", "autocomplete": "Johnny Cash" } 

然後你就可以查詢joh,並得到一個結果爲John,另一個用於Johnny

{ 
    "size": 0, 
    "query": { 
    "term": { 
     "autocomplete.completion": "john d" 
    } 
    }, 
    "aggs": { 
    "suggestions": { 
     "terms": { 
     "field": "autocomplete.raw" 
     } 
    } 
    } 
} 

結果:

{ 
    "aggregations": { 
    "suggestions": { 
     "doc_count_error_upper_bound": 0, 
     "sum_other_doc_count": 0, 
     "buckets": [ 
     { 
      "key": "John Doe", 
      "doc_count": 1 
     }, 
     { 
      "key": "John Deere", 
      "doc_count": 1 
     } 
     ] 
    } 
    } 
} 
+0

如果您想要搜索多個字段,該怎麼辦?理想情況下,我會有一個名爲'suggest'的額外多值字段,其中包含我想自動完成的所有值(名稱,姓氏,用戶名,電子郵件等)。 – alesc

+0

這將是同樣的事情,包含在該字段中的每個令牌都將被編入索引 – Val

+0

但是聚合是否也起作用,以便它可以刪除重複的條目? – alesc

1

我們面對完全相同的問題。在Elasticsearch 2.4中,您所描述的方法曾經爲我們工作得很好,但現在如您所說,建議器已成爲基於文檔的,而像您一樣,我們只關注唯一字,而不是在文檔中。

到目前爲止,我們唯一能想到的解決方案是爲我們想要執行建議查詢的單詞創建一個單獨的索引,並且在這個單獨的索引中確保以某種方式相同的單詞只索引一次。然後你可以在這個單獨的索引上執行建議查詢。這遠非理想,因爲我們需要確保該索引與我們其他查詢所需的其他索引保持同步。

+0

您能否詳細說明如何創建機制來保持此索引同步?以及如何避免後續單詞的全球建議? – alesc

2

其他字段skip_duplicates將在下一個版本6.x中添加。

從文檔在https://www.elastic.co/guide/en/elasticsearch/reference/master/search-suggesters-completion.html#skip_duplicates

POST music/_search?pretty 
{ 
    "suggest": { 
     "song-suggest" : { 
      "prefix" : "nor", 
      "completion" : { 
       "field" : "suggest", 
       "skip_duplicates": true 
      } 
     } 
    } 
} 
+0

感謝您的信息! 6.x發佈日期是否已知? – alesc

+0

請注意''skip_duplicates「:true'就像一個魅力,但只適用於ES6.1,它可能是最好的解決方案。對於ES6.0,這是我的情況,它不起作用。 – sashaegorov