Elasticsearch与分析字段完全匹配

时间:2015-05-28 22:09:06

标签: elasticsearch

有没有办法让ElasticSearch识别分析字段的完全匹配?理想情况下,我想小写,标记,干,甚至拼音我的文档,然后查询"确切"匹配。

我的意思是,如果我索引"汉堡包子"和"汉堡包",他们将被分析为["汉堡"," bun"]和[" hamburger"]。如果我搜索"汉堡",它只会返回"汉堡" doc,因为那是"确切的"比赛。

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

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

3 个答案:

答案 0 :(得分:11)

使用带状疱疹标记器以及阻塞和其他任何需要的东西。添加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,这里最难实现的要求是更改标记(词干,小写等),并且还要回收原始文本。除非你定义自己的"连接"过滤器我认为除了使用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"

如果您只对那些符合完全含义的文档感兴趣,则上述文档仅在您搜索洛杉矶的"汉堡包时才会匹配" (并且没有匹配像#34;洛杉矶的任何汉堡包和#34;)那么你需要一种方法来过滤那长长的带状疱疹列表。我看到的方式是使用word_count

答案 1 :(得分:5)

您可以将multi-fields用于此目的,并在not_analyzed字段中设置analyzed子字段(在此示例中,我们将其称为item) 。您的映射必须如下所示:

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

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

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"}
    } ]
  }
}

更新(请参阅下面的评论/讨论并重新编辑问题)

从评论中提取您的示例并尝试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"}
    } ]
  }
}

答案 2 :(得分:5)

您可以将分析器保持为您的预期(小写,标记,词干......),并使用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,它将有助于查询&#34; Hamburger Buns&#34; (匹配hamburgerbun)匹配文档&#34;汉堡&#34;也。 phrase_slop设置为1,以匹配距离= 1的字词,例如搜索Hamburger Buns与文档Hamburger Big Buns不匹配。您可以根据您的要求进行调整。

您可以参考Closer is betterQuery string了解详情。

相关问题