首页 文章

Elasticsearch与分析字段完全匹配

提问于
浏览
12

有没有办法让ElasticSearch识别分析字段的完全匹配?理想情况下,我想小写,标记,干,甚至语音化我的文档,然后让查询拉出“精确”匹配 .

我的意思是,如果我将“汉堡包”和“汉堡包”编入索引,它们将被分析为[“汉堡包”,“包子”]和[“汉堡包”] . 如果我搜索“汉堡包”,它将仅返回“汉堡包”文档,因为这是“精确”匹配 .

我尝试过使用关键字tokenizer,但这不会阻止个别令牌 . 我是否需要做一些事情以确保令牌的数量相等?

我熟悉多字段并使用“not_analyzed”类型,但这比我正在寻找的更具限制性 . 我想要完全匹配,后期分析 .

3 回答

  • 5

    使用带状疱疹标记器以及阻塞和其他任何需要的东西 . 添加 token_count 类型的子字段,该字段将计算字段中的标记数 .

    在搜索时,您需要添加一个额外的过滤器,以使索引中的标记数与搜索文本中的标记数相匹配 . 在执行实际搜索时,您需要一个额外的步骤来计算搜索字符串中的标记 . 这是这样的,因为带状符会创建令牌的多个排列,您需要确保它与搜索文本的大小相匹配 .

    尝试这个,只是为了给你一个想法:

    {
      "settings": {
        "analysis": {
          "filter": {
            "filter_shingle": {
              "type": "shingle",
              "max_shingle_size": 10,
              "min_shingle_size": 2,
              "output_unigrams": true
            },
            "filter_stemmer": {
              "type": "porter_stem",
              "language": "_english_"
            }
          },
          "analyzer": {
            "ShingleAnalyzer": {
              "tokenizer": "standard",
              "filter": [
                "lowercase",
                "snowball",
                "filter_stemmer",
                "filter_shingle"
              ]
            }
          }
        }
      },
      "mappings": {
        "test": {
          "properties": {
            "text": {
              "type": "string",
              "analyzer": "ShingleAnalyzer",
              "fields": {
                "word_count": {
                  "type": "token_count",
                  "store": "yes",
                  "analyzer": "ShingleAnalyzer"
                }
              }
            }
          }
        }
      }
    }
    

    和查询:

    {
      "query": {
        "filtered": {
          "query": {
            "match_phrase": {
              "text": {
                "query": "HaMbUrGeRs BUN"
              }
            }
          },
          "filter": {
            "term": {
              "text.word_count": "2"
            }
          }
        }
      }
    }
    

    shingles 过滤器在这里很重要,因为它可以创建令牌组合 . 更重要的是,这些是保持订单或令牌的组合 . Imo,这里最难实现的要求是更改标记(词干,小写等),并且还要回收原始文本 . 除非您定义自己的"concatenation"过滤器,否则我认为除了使用 shingles 过滤器之外没有其他任何方法 .

    但是对于 shingles ,还有另一个问题:它创建了不需要的组合 . 对于像 "Hamburgers buns in Los Angeles" 这样的文本,你会得到一长串的带状疱疹:

    "angeles",
              "buns",
              "buns in",
              "buns in los",
              "buns in los angeles",
              "hamburgers",
              "hamburgers buns",
              "hamburgers buns in",
              "hamburgers buns in los",
              "hamburgers buns in los angeles",
              "in",
              "in los",
              "in los angeles",
              "los",
              "los angeles"
    

    如果您只对那些符合 exactly 含义的文档感兴趣,则上述文档仅在您搜索"hamburgers buns in los angeles"时匹配(并且与"any hamburgers buns in los angeles"不匹配),那么您需要一种方法来过滤那长长的带状疱疹列表 . 我看到的方式是使用 word_count .

  • 9

    您可以将分析器保持为您的预期(小写,标记,词干......),并使用 query_string 作为主查询, match_phrase 作为搜索的提升查询 . 像这样的东西:

    {
       "bool" : {
          "should" : [
             {
                "query_string" : {
                   "default_field" : "your_field",
                   "default_operator" : "OR",
                   "phrase_slop" : 1,
                   "query" : "Hamburger"
                }
             },
             {
                "match_phrase": {
                   "your_field": {
                      "query": "Hamburger"
                   }
                }
             }
          ]
       }
    }
    

    它将匹配两个文档,并且完全匹配(match_phrase)将位于顶部,因为查询匹配 should 子句(并获得更高的分数)

    default_operator 设置为OR,它将帮助查询"Hamburger Buns"(匹配 hamburgerbun )也匹配文档"Hamburger" . phrase_slop 设置为1以匹配距离= 1的项,例如搜索 Hamburger Buns 将与文档 Hamburger Big Buns 不匹配 . 您可以根据您的要求进行调整 .

    您可以参考Closer is betterQuery string了解更多详情 .

  • 4

    您可以为此目的使用multi-fields并在 analyzed 字段中有一个 not_analyzed 子字段(在本例中我们称之为 item ) . 您的映射必须如下所示:

    {
      "yourtype": {
        "properties": {
          "item": {
            "type": "string",
            "fields": {
              "raw": {
                "type": "string",
                "index": "not_analyzed"
              }
            }
          }
        }
      }
    }
    

    通过这种映射,您可以检查分析器相对于多字段 itemitem.raw 的每个值 HamburgersHamburger Buns 是如何"viewed"

    对于 Hamburger

    curl -XGET 'localhost:9200/yourtypes/_analyze?field=item&pretty' -d 'Hamburger'
    {
      "tokens" : [ {
        "token" : "hamburger",
        "start_offset" : 0,
        "end_offset" : 10,
        "type" : "<ALPHANUM>",
        "position" : 1
      } ]
    }
    curl -XGET 'localhost:9200/yourtypes/_analyze?field=item.raw&pretty' -d 'Hamburger'
    {
      "tokens" : [ {
        "token" : "Hamburger",
        "start_offset" : 0,
        "end_offset" : 10,
        "type" : "word",
        "position" : 1
      } ]
    }
    

    对于 Hamburger Buns

    curl -XGET 'localhost:9200/yourtypes/_analyze?field=item&pretty' -d 'Hamburger Buns'
    {
      "tokens" : [ {
        "token" : "hamburger",
        "start_offset" : 0,
        "end_offset" : 10,
        "type" : "<ALPHANUM>",
        "position" : 1
      }, {
        "token" : "buns",
        "start_offset" : 11,
        "end_offset" : 15,
        "type" : "<ALPHANUM>",
        "position" : 2
      } ]
    }
    curl -XGET 'localhost:9200/yourtypes/_analyze?field=item.raw&pretty' -d 'Hamburger Buns'
    {
      "tokens" : [ {
        "token" : "Hamburger Buns",
        "start_offset" : 0,
        "end_offset" : 15,
        "type" : "word",
        "position" : 1
      } ]
    }
    

    正如您所看到的, not_analyzed 字段将完全按照输入的方式进行索引 .

    现在,让我们索引两个示例文档来说明这一点:

    curl -XPOST localhost:9200/yourtypes/_bulk -d '
    {"index": {"_type": "yourtype", "_id": 1}}
    {"item": "Hamburger"}
    {"index": {"_type": "yourtype", "_id": 2}}
    {"item": "Hamburger Buns"}
    '
    

    最后,为了回答你的问题,如果你想在 Hamburger 上进行完全匹配,你可以在你的子字段 item.raw 中搜索(注意案例也必须匹配):

    curl -XPOST localhost:9200/yourtypes/yourtype/_search -d '{
      "query": {
        "term": {
          "item.raw": "Hamburger"
        }
      }
    }'
    

    你会得到:

    {
      ...
      "hits" : {
        "total" : 1,
        "max_score" : 0.30685282,
        "hits" : [ {
          "_index" : "yourtypes",
          "_type" : "yourtype",
          "_id" : "1",
          "_score" : 0.30685282,
          "_source":{"item": "Hamburger"}
        } ]
      }
    }
    

    UPDATE (see comments/discussion below and question re-edit)

    从评论中获取您的示例并尝试使 HaMbUrGeR BuNs 匹配 Hamburger buns 您可以使用 match 这样的查询来实现它 .

    curl -XPOST localhost:9200/yourtypes/yourtype/_search?pretty -d '{
      "query": {
        "match": {
          "item": {
            "query": "HaMbUrGeR BuNs",
            "operator": "and"
          }
        }
      }
    }'
    

    基于上述相同的两个索引文档将产生

    {
      ...
      "hits" : {
        "total" : 1,
        "max_score" : 0.2712221,
        "hits" : [ {
          "_index" : "yourtypes",
          "_type" : "yourtype",
          "_id" : "2",
          "_score" : 0.2712221,
          "_source":{"item": "Hamburger Buns"}
        } ]
      }
    }
    

相关问题