【问题标题】:Using Exact Prefix/MatchPhrase Prefix Queries with Ngram Filter使用带有 Ngram 过滤器的精确前缀/匹配短语前缀查询
【发布时间】:2020-11-28 01:10:29
【问题描述】:

我的目标是搜索长度为一到两个字符的查询文本。 这是我对索引的设置。

"settings" : {
      "index" : {
        "number_of_shards" : "5",
        "provided_name" : "my_user",
        "analysis" : {
          "filter" : {
            "ngrammed" : {
              "type" : "ngram",
              "min_gram" : "3",
              "max_gram" : "50"
            }
          },
          "analyzer" : {
            "ngrammed_ci" : {
              "filter" : [
                "lowercase",
                "ngrammed"
              ],
              "type" : "custom",
              "tokenizer" : "standard"
            },
            "keyword_ci" : {
              "filter" : [
                "lowercase"
              ],
              "type" : "custom",
              "tokenizer" : "keyword"
            }
          }
        }
      }
    }

我有一组用户,其显示名称字段包含以下分析器。假设我有几个用户的名字像 Allen, Alec, Kimball, Polly 我面临的问题是,当我使用al 以及AllenAlec 之类的2 个字符长度的查询字符串进行搜索时,它也与Kimball 匹配,因为ngram 过滤器将Kimball 标记为@987654331 @ 在倒排索引中。我试图避免这种情况。还想知道是否有任何方法可以在不更改索引方面的任何内容的情况下实现此功能,并且仅针对查询方面进行更改。

"user_display_name" : {
  "type" : "text",
  "fields" : {
    "ci" : {
    "type" : "text",
    "analyzer" : "keyword_ci"
    }
  "cs" : {
    "type" : "keyword"
    }
  },
  "analyzer" : "ngrammed_ci",
  "search_analyzer" : "standard"
}

【问题讨论】:

  • 这是一个非常大的索引。怎么查询都会很慢。
  • 另外,这是在云端还是在您的本地?

标签: elasticsearch search n-gram


【解决方案1】:

正如您所提到的,您想要一个不需要更改索引的解决方案,我建议您使用前缀查询,但在发送前缀查询之前,请确保 确保如我所见,您将搜索字词小写,您在索引中使用了keyword_ci 将您的用户名小写,以提供不区分大小写的搜索。

让我向您展示一个关于您的示例数据的工作示例

我创建了低于最低要求的映射

{
  "settings": {
    "index": {
      "analysis": {
        "analyzer": {
          "keyword_ci": {
            "filter": [
              "lowercase"
            ],
            "type": "custom",
            "tokenizer": "keyword"
          }
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "user_display_name": {
        "type": "text",
        "analyzer": "keyword_ci"
      }
    }
  }
}

为您的四个用户编制索引

{
  "user_display_name" : "Polly"
}

搜索查询,请注意前缀查询不是小写的,所以你需要在你的应用程序中做小写,然后再使用下面的前缀查询

{
  "query": {
    "prefix" : { "user_display_name" : "al" }
  }
}

以下是您的预期结果

 "hits": [
      {
        "_index": "internaledgepre",
        "_type": "_doc",
        "_id": "1",
        "_score": 1.0,
        "_source": {
          "user_display_name": "Allen"
        }
      },
      {
        "_index": "internaledgepre",
        "_type": "_doc",
        "_id": "2",
        "_score": 1.0,
        "_source": {
          "user_display_name": "Alec"
        }
      }
    ]

我还写了blog post on various techniques of partial searchmy this SO answer 讨论如何根据各种因素选择部分搜索方法。请仔细阅读以深入了解。

【讨论】:

  • 在上面的示例中,您没有创建 ngram 标记,这就是搜索得到适当结果的原因,例如,我的倒排索引将 Kimball 标记为 -> kim , imb, mba, bal, all, kimb, imba, mbal, ball, @9876543@39@, mball, kimbal, @987654341, @9876 987654343@, kimball 所以它将标记all 与前缀查询匹配。这就是我的理解,如果我遗漏了什么,请纠正我。
  • @k3np4ch1 感谢您回来,我得到适当结果的原因是我没有创建 ngram 标记,但我正在创建基于小写的关键字标记的标记,以提供不区分大小写的搜索。是的,您是正确的,由于 ngram 令牌,您的前缀查询与 kimball 匹配 all 前缀查询不正确。
  • @k3np4ch1 另外,如果我的回答有帮助,请点赞并接受答案,提前谢谢?
  • 感谢您的回复,我唯一的问题是我已经在使用 ngramfilter 以及关键字过滤器创建标记,并且我不想更改该功能,是否有任何解决方法修改我们的索引?
  • @k3np4ch1,你使用的是 ngramfilter,而不是 edge-ngram,因此你不会得到更好的前缀结果,我给你的不需要太多改变,它会带来巨大的好处,希望您浏览了博客文章和我的其他 SO 答案以更好地理解权衡,您将不得不改变一些东西,而我的建议需要最少的改变。
【解决方案2】:

在您的情况下,您需要从单词开头开始的 ngram。在这种情况下,改用edge ngrams 更有意义。

添加一个包含索引映射、索引数据、搜索查询和搜索结果的工作示例。

映射:

{
  "settings": {
    "analysis": {
      "filter": {
        "ngrammed": {
          "type": "edge_ngram",     <<-- note this
          "min_gram": "2",
          "max_gram": "50"
        }
      },
      "analyzer": {
        "ngrammed_ci": {
          "filter": [
            "lowercase",
            "ngrammed"
          ],
          "type": "custom",
          "tokenizer": "standard"
        },
        "keyword_ci": {
          "filter": [
            "lowercase"
          ],
          "type": "custom",
          "tokenizer": "keyword"
        }
      }
    },
    "index.max_ngram_diff": 50
  },
  "mappings": {
    "properties": {
      "user_display_name": {
        "type": "text",
        "fields": {
          "ci": {
            "type": "text",
            "analyzer": "keyword_ci"
          },
          "cs": {
            "type": "keyword"
          }
        },
        "analyzer": "ngrammed_ci",
        "search_analyzer": "standard"
      }
    }
  }
}

将生成以下令牌:

GET/_analyze

{
  "analyzer" : "ngrammed_ci",
  "text" : "Allen"
}

"tokens": [
    {
      "token": "al",
      "start_offset": 0,
      "end_offset": 5,
      "type": "<ALPHANUM>",
      "position": 0
    },
    {
      "token": "all",
      "start_offset": 0,
      "end_offset": 5,
      "type": "<ALPHANUM>",
      "position": 0
    },
    {
      "token": "alle",
      "start_offset": 0,
      "end_offset": 5,
      "type": "<ALPHANUM>",
      "position": 0
    },
    {
      "token": "allen",
      "start_offset": 0,
      "end_offset": 5,
      "type": "<ALPHANUM>",
      "position": 0
    }
  ]

索引数据:

{ "user_display_name" : "Allen" }
{ "user_display_name" : "Alec" }
{ "user_display_name" : "Kimball" }
{ "user_display_name" : "Polly" }

搜索查询:

    {
  "query": {
    "query_string": {
      "query": "al",
      "default_field": "user_display_name"
    }
  }
}

搜索结果:

 "hits": [
      {
        "_index": "my-index",
        "_type": "_doc",
        "_id": "1",
        "_score": 1.0087044,
        "_source": {
          "user_display_name": "Allen"
        }
      },
      {
        "_index": "my-index",
        "_type": "_doc",
        "_id": "2",
        "_score": 1.0087044,
        "_source": {
          "user_display_name": "Alec"
        }
      }
    ]

【讨论】:

  • @k3np4ch1 你有没有机会仔细阅读我的回答,期待得到你的反馈:)
  • 嗯,这将涉及更改我的索引,对吧?我正在考虑仅在查询方面进行更改。不更改索引设置会导致需要大量工作并且需要更新大量数据
  • @k3np4ch1 是的,这将涉及更改索引,但改用 edge_ngram 更有意义
猜你喜欢
  • 2018-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-30
  • 2014-10-21
  • 1970-01-01
  • 1970-01-01
  • 2018-03-23
相关资源
最近更新 更多