【问题标题】:How do I configure Elasticsearch to find substrings at the beginning OR at the end of a word (but not in middle)?如何配置 Elasticsearch 以在单词的开头或结尾(但不在中间)查找子字符串?
【发布时间】:2016-02-09 06:26:54
【问题描述】:

我开始学习 Elasticsearch,现在我正在尝试编写我的第一个分析器配置。我想要实现的是,如果子字符串位于单词的开头或结尾,则可以找到它们。如果我有“stackoverflow”这个词并且我搜索“stack”我想找到它,当我搜索“flow”时我想找到它,但我确实想在什么时候找到它搜索“ackov”(在我的用例中这没有意义)。

我知道有“Edge n gram tokenizer”,但一个分析器只能有一个 tokenizer,而 edge n-gram 可以是前面的也可以是后面的(但不能同时是两者)。

如果我理解正确,将两个版本的“Edge ngram filter”(正面和背面)应用于分析器,那么我也找不到,因为两个过滤器都需要返回 true,不是吗?因为“stack”不会出现在单词的结尾,所以后面的 n gram 过滤器会返回 false 并且找不到单词“stackoverflow”。

那么,如何配置我的分析器以查找单词末尾或开头的子字符串,而不是在中间?

【问题讨论】:

    标签: elasticsearch


    【解决方案1】:

    可以做的是定义两个分析器,一个用于匹配字符串的开头,另一个用于匹配字符串的结尾。在下面的索引设置中,我将前者命名为prefix_edge_ngram_analyzer,将后者命名为suffix_edge_ngram_analyzer。这两个分析器可以应用于text.prefix子字段的多字段字符串字段,分别应用于text.suffix字符串字段。

    {
      "settings": {
        "analysis": {
          "analyzer": {
            "prefix_edge_ngram_analyzer": {
              "tokenizer": "prefix_edge_ngram_tokenizer",
              "filter": ["lowercase"]
            },
            "suffix_edge_ngram_analyzer": {
              "tokenizer": "keyword",
              "filter" : ["lowercase","reverse","suffix_edge_ngram_filter","reverse"]
            }
          },
          "tokenizer": {
            "prefix_edge_ngram_tokenizer": {
              "type": "edgeNGram",
              "min_gram": "2",
              "max_gram": "25"
            }
          },
          "filter": {
            "suffix_edge_ngram_filter": {
              "type": "edgeNGram",
              "min_gram": 2,
              "max_gram": 25
            }
          }
        }
      },
      "mappings": {
        "test_type": {
          "properties": {
            "text": {
              "type": "string",
              "fields": {
                "prefix": {
                  "type": "string",
                  "analyzer": "prefix_edge_ngram_analyzer"
                },
                "suffix": {
                  "type": "string",
                  "analyzer": "suffix_edge_ngram_analyzer"
                }
              }
            }
          }
        }
      }
    }
    

    那么假设我们索引以下测试文档:

    PUT test_index/test_type/1
    { "text": "stackoverflow" }
    

    然后我们可以使用以下查询按前缀或后缀进行搜索:

    # input is "stack" => 1 result
    GET test_index/test_type/_search?q=text.prefix:stack OR text.suffix:stack
    
    # input is "flow" => 1 result
    GET test_index/test_type/_search?q=text.prefix:flow OR text.suffix:flow
    
    # input is "ackov" => 0 result
    GET test_index/test_type/_search?q=text.prefix:ackov OR text.suffix:ackov
    

    使用查询 DSL 进行查询的另一种方式:

    POST test_index/test_type/_search
    {
       "query": {
          "multi_match": {
             "query": "stack",
             "fields": [ "text.*" ]
          }
       }
    }
    

    更新

    如果您已经有一个字符串字段,您可以将其“升级”为一个多字段,并使用其分析器创建两个必需的子字段。这样做的方法是按顺序进行:

    1. 关闭索引以创建分析器

      POST test_index/_close
      
    2. 更新索引设置

      PUT test_index/_settings
      {
      "analysis": {
        "analyzer": {
          "prefix_edge_ngram_analyzer": {
            "tokenizer": "prefix_edge_ngram_tokenizer",
            "filter": ["lowercase"]
          },
          "suffix_edge_ngram_analyzer": {
            "tokenizer": "keyword",
            "filter" : ["lowercase","reverse","suffix_edge_ngram_filter","reverse"]
          }
        },
        "tokenizer": {
          "prefix_edge_ngram_tokenizer": {
            "type": "edgeNGram",
            "min_gram": "2",
            "max_gram": "25"
          }
        },
        "filter": {
          "suffix_edge_ngram_filter": {
            "type": "edgeNGram",
            "min_gram": 2,
            "max_gram": 25
          }
        }
      }
      }
      
    3. 重新打开你的索引

      POST test_index/_open
      
    4. 最后,更新文本字段的映射

      PUT test_index/_mapping/test_type
      {
        "properties": {
          "text": {
            "type": "string",
            "fields": {
              "prefix": {
                "type": "string",
                "analyzer": "prefix_edge_ngram_analyzer"
              },
              "suffix": {
                "type": "string",
                "analyzer": "suffix_edge_ngram_analyzer"
              }
            }
          }
        }
      }
      
    5. 您仍然需要重新索引所有文档,以便填充和分析新的子字段 text.prefixtext.suffix

    【讨论】:

    • 谢谢,所以只有我也更改映射(目前只是一个简单的字符串)才有可能?
    • 是的,我已经相应地更新了我的答案。最简单的方法是创建一个新索引,但也可以只修改现有索引,尽管您还有更多任务要做。
    • 谢谢,我是否总是需要查询text.prefix:stack OR text.suffix:stack?是否可以只查询text:stack 并隐式检查所有子字段?
    • 我再次更新了问题,使用multi_match 查询的另一种查询方式。
    • 太棒了,很高兴为您提供帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-18
    • 2015-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多