10

当使用带有 elasticsearch 的 ngram 过滤器时,当我搜索“test”之类的内容时,我会返回一个文档“latest”、“tests”和“test”。有没有办法让“与查询“test”完全匹配的文档总是在搜索结果中返回到更高的位置?

4

3 回答 3

6

这是 ngrams 的一个问题:你的排名中有很多误报。一种解决方案是将 ngram 与带状疱疹结合起来。基本上除了 ngram 之外,您还可以将完整的单词索引为单独的术语,甚至是单词的组合。带状疱疹基本上类似于 ngram,但使用的是单词而不是字符。

这样,与 shingle 术语的精确匹配比仅匹配 ngram 的分数更高。

更新。这是自定义分析器的示例。定义后,您可以在映射中使用它。在这种情况下,我使用 icu_normalizer 和折叠以及我的建议_shingle。所有这些都设置为默认分析器,因此我的所有字符串都以这种方式处理。

{
    "analyzer":{
        "default":{
            "tokenizer":"icu_tokenizer",
            "filter":"icu_normalizer,icu_folding,suggestions_shingle"
        }
    },
    "filter": {
        "suggestions_shingle": {
            "type": "shingle",
            "min_shingle_size": 2,
            "max_shingle_size": 5
        }
    }
}
于 2013-06-29T23:50:55.490 回答
1

您可以通过映射将字段内容复制到字段。例子:

  "fullName": {
    "type": "string",
    "search_analyzer": "str_search_analyzer",
    "index_analyzer": "str_index_analyzer",
    "fields": {
        "fullWord": { "type": "string" },
        "raw": { 
            "type":  "string",
            "index": "not_analyzed"
        }
    }
  }

注意 str_index_analyzer 在这里使用 nGram。然后,您可以构建您的搜索来搜索这些字段。例子:

{
    "query": {
      "bool": {
        "should": [{
          "multi_match": {
            "fields": [
              "firstName.fullWord",
              ...
            "query": query,
            "fuzziness": "0"
          }
        }],
        "must": [{
          "multi_match": {
            "fields": [
              "firstName",...],
            "query": query,
            "fuzziness": "AUTO"
          }
        }]
      }
    }
  };
}
于 2015-06-05T16:27:21.853 回答
0

您需要多字段和多匹配查询。

我有类似的问题。我需要按名字搜索,所以如果我输入搜索词“And”,我会得到第一个“Andy”,然后是“Mandy”。仅使用 nGram,我无法实现这一目标。

我添加了一个使用 front edgeNGram 的分析器(下面的代码用于 Spring Data Elasticsearch,但你可以理解)。

    setting.put("analysis.analyzer.word_parts.type", "custom");
    setting.put("analysis.analyzer.word_parts.tokenizer", "ngram_tokenizer");
    setting.put("analysis.analyzer.word_parts.filter", "lowercase");

    setting.put("analysis.analyzer.type_ahead.type", "custom");
    setting.put("analysis.analyzer.type_ahead.tokenizer", "edge_ngram_tokenizer");
    setting.put("analysis.analyzer.type_ahead.filter", "lowercase");

    setting.put("analysis.tokenizer.ngram_tokenizer.type", "nGram");
    setting.put("analysis.tokenizer.ngram_tokenizer.min_gram", "3");
    setting.put("analysis.tokenizer.ngram_tokenizer.max_gram", "50");
    setting.put("analysis.tokenizer.ngram_tokenizer.token_chars", new String[] { "letter", "digit" });

    setting.put("analysis.tokenizer.edge_ngram_tokenizer.type", "edgeNGram");
    setting.put("analysis.tokenizer.edge_ngram_tokenizer.min_gram", "2");
    setting.put("analysis.tokenizer.edge_ngram_tokenizer.max_gram", "20");

我将必填字段映射为多个字段:

@MultiField(mainField = @Field(type = FieldType.String, indexAnalyzer = "word_parts", searchAnalyzer = "standard"),
otherFields = @NestedField(dotSuffix = "autoComplete", type = FieldType.String, searchAnalyzer = "standard", indexAnalyzer = "type_ahead"))
private String firstName;

对于我使用多重匹配的查询,我首先指定“firstName.autoComplete”,而不仅仅是“firstName”

QueryBuilders.multiMatchQuery(searchTerm, new String[]{"firstName.autoComplete", "firstName"})

这似乎工作正常。

在您的情况下,如果您需要完全匹配,也许您可​​以只使用“标准”而不是“edgeNGram”。

于 2015-04-15T22:19:12.690 回答