2017-01-19 25 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