使用弹性搜索自动完成功能

时间:2016-10-31 21:56:43

标签: elasticsearch autocomplete

我有一个包含以下文档的弹性搜索索引,我想在指定的字段上拥有自动完成功能:

映射:https://gist.github.com/anonymous/0609b1d110d91dceb9a90faa76d1d5d4

USECASE:

我的查询是表格前缀类型,例如" sta"," star"," star w" .."开始战争"等附加过滤器作为标签="科幻小说"。此外,查询可以匹配其他字段,如描述,演员(在演员字段中,这不是嵌套的)。我也想知道它匹配的字段。

我调查了两种方法,但是没有方法似乎解决了上面的用例:

1)建议者自动完成:

https://www.elastic.co/guide/en/elasticsearch/reference/1.7/search-suggesters-completion.html

有了这个,似乎我必须添加另一个名为&#34的字段;建议"复制不可取的数据。

2)使用前缀过滤器/查询:

https://www.elastic.co/guide/en/elasticsearch/reference/1.7/query-dsl-prefix-filter.html

这使得整个文档不再是完全匹配的术语。

有没有一种干净的方法来实现这一点,请告知。

2 个答案:

答案 0 :(得分:1)

不要单独创建映射,直接将数据插入索引。它将为此创建默认映射。使用以下查询进行自动完成。

GET /netflix/movie/_search
{
"query": {
    "query_string": {
        "query": "sta*"
    }
  }
}

答案 1 :(得分:1)

我认为completion suggester是最干净的方法,但如果不合适,可以在名称字段中使用aggregations

这是一个示例索引(我假设您使用的是 ES 1.7

PUT netflix
{
  "settings": {
    "analysis": {
      "analyzer": {
        "prefix_analyzer": {
          "tokenizer": "keyword",
          "filter": [
            "lowercase",
            "trim",
            "edge_filter"
          ]
        },
        "keyword_analyzer": {
          "tokenizer": "keyword",
          "filter": [
            "lowercase",
            "trim"
          ]
        }
      },
      "filter": {
        "edge_filter": {
          "type": "edge_ngram",
          "min_gram": 1,
          "max_gram": 20
        }
      }
    }
  },
  "mappings": {
    "movie":{
      "properties": {
        "name":{
          "type": "string",
          "fields": {
            "prefix":{
            "type":"string",
            "index_analyzer" : "prefix_analyzer",
            "search_analyzer" : "keyword_analyzer"
            },
            "raw":{
              "type": "string",
              "analyzer": "keyword_analyzer"
            }
          }
        },
        "tags":{
          "type": "string", "index": "not_analyzed"
        }
      }
    }
  }
}

使用multi-fields name 字段以不同方式进行分析。 name.prefix 正在keyword tokenizer使用edge ngram filter 所以字符串星球大战可以分解为 s st sta 等,但在搜索时, keyword_analyzer ,以便搜索查询不会被分解为多个小标记。 name.raw 将用于聚合。

以下查询将提供前10个建议。

GET netflix/movie/_search
{
  "query": {
    "filtered": {
      "filter": {
        "term": {
          "tags": "sci-fi"
        }
      },
      "query": {
        "match": {
          "name.prefix": "sta"
        }
      }
    }
  },
  "size": 0,
  "aggs": {
    "unique_movie_name": {
      "terms": {
        "field": "name.raw",
        "size": 10
      }
    }
  }
}

结果将类似于

"aggregations": {
      "unique_movie_name": {
         "doc_count_error_upper_bound": 0,
         "sum_other_doc_count": 0,
         "buckets": [
            {
               "key": "star trek",
               "doc_count": 1
            },
            {
               "key": "star wars",
               "doc_count": 1
            }
         ]
      }
   }

更新

我认为您可以使用highlighting来实现此目的。突出显示部分将为您提供整个单词以及匹配的字段。您还可以使用inner hits并在其中突出显示以获取嵌套文档。

{
  "query": {
    "query_string": {
      "query": "sta*"
    }
  },
  "_source": false,
  "highlight": {
    "fields": {
      "*": {}
    }
  }
}