【问题标题】:Elasticsearch wildcard query with hyphens and lowercase filter带有连字符和小写过滤器的 Elasticsearch 通配符查询
【发布时间】:2014-09-22 08:30:31
【问题描述】:

我想对QNMZ-1900进行通配符查询

正如我在文档中阅读并自己尝试过的那样,Elasticsearch 的标准分词器会拆分连字符上的单词,例如 QNMZ-1900 将拆分为 QNMZ1900

为了防止这种行为,我使用了not_analyzed 功能。

curl -XPUT 'localhost:9200/test-idx' -d '{
"mappings": {
    "doc": {
        "properties": {
            "foo" : {
                "type": "string",
                "index": "not_analyzed"
            }
        }
    }
}
}'

我正在将一些东西放入我的索引中:

curl -XPUT 'localhost:9200/test-idx/doc/1' -d '{"foo": "QNMZ-1900"}'

刷新它:

curl -XPOST 'localhost:9200/test-idx/_refresh'

现在我可以使用通配符查询并找到QNMZ-1900

curl 'localhost:9200/test-idx/doc/_search?pretty=true' -d '{
"query": {
     "wildcard" : { "foo" : "QNMZ-19*" }
}

我的问题:

如何使用小写搜索词运行通配符查询?

我试过了:

curl -XDELETE 'localhost:9200/test-idx'
curl -XPUT 'localhost:9200/test-idx' -d '{
"mappings": {
    "doc": {
        "properties": {
            "foo" : {
                "type": "string",
                "index": "not_analyzed",
                "filter": "lowercase"
            }
        }
    }
}
}'
curl -XPUT 'localhost:9200/test-idx/doc/1' -d '{"foo": "QNMZ-1900"}'
curl -XPOST 'localhost:9200/test-idx/_refresh'

但我的小写查询:

curl 'localhost:9200/test-idx/doc/_search?pretty=true' -d '{
"query": {
     "wildcard" : { "foo" : "qnmz-19*" }
}
}'

什么也没找到。

如何解决?

【问题讨论】:

  • 通配符查询不是默认不分析的吗?

标签: search autocomplete elasticsearch


【解决方案1】:

一种解决方案是使用

定义自定义分析器
  • keyword 标记器(保持输入值不变,就好像它是 not_analyzed
  • lowercase 令牌过滤器

我试过了:

POST test-idx
{
  "index":{
    "analysis":{
      "analyzer":{
        "lowercase_hyphen":{
          "type":"custom",
          "tokenizer":"keyword",
          "filter":["lowercase"]
        }
      }
    }
  }
}

PUT test-idx/doc/_mapping
{
  "doc":{
    "properties": {
        "foo" : {
          "type": "string",
          "analyzer": "lowercase_hyphen"
        }
    }      
  }
}

POST test-idx/doc
{
  "foo":"QNMZ-1900"
}

正如您所看到的那样使用 _analyze 端点:

GET test-idx/_analyze?analyzer=lowercase_hyphen&text=QNMZ-1900

只输出一个小写的标记,但不分割连字符:

{
   "tokens": [
      {
         "token": "qnmz-1900",
         "start_offset": 0,
         "end_offset": 9,
         "type": "word",
         "position": 1
      }
   ]
}

然后,使用相同的查询:

POST test-idx/doc/_search
{
  "query": {
    "wildcard" : { "foo" : "qnmz-19*" }    
  }
}

我有这个结果,这就是你想要的:

{
   "took": 66,
   "timed_out": false,
   "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "hits": {
      "total": 1,
      "max_score": 1,
      "hits": [
         {
            "_index": "test-idx",
            "_type": "doc",
            "_id": "wo1yanIjQGmvgfScMg4hyg",
            "_score": 1,
            "_source": {
               "foo": "QNMZ-1900"
            }
         }
      ]
   }
}

但是,请注意,这将只允许您使用小写值进行查询。 正如 Andrei 在评论中所说,具有值 QNMZ-19* 的相同查询不会返回任何内容。

原因可以在documentation 中找到:在搜索时,没有分析该值。

【讨论】:

  • 似乎不适用于 POST test-idx/doc/_search { "query": { "wildcard" : { "foo" : "QNMZ-19*" } } }
  • 确实,通配符查询输入没有被分析,但是这样总是可以对小写的值进行搜索。但是,我更新了我的答案。
  • @ThomasC 这个答案对 ElasticSearch 6.1 仍然有效吗?这种搜索有没有更新、更方便的功能?您如何看待不同类型的建模。例如,我们可以将“foo”复制到 json 中包含“qnmz-1900”的“foo_lowercase”字段中。然后我们可以通过 curl 'localhost:9200/test-idx/doc/_search?pretty=true' -d '{"query": {"wildcard" : { "foo" : "qnmz-19*" }} 搜索还是您认为这种建模是弹性搜索的反模式?
  • @RadosławOsiński 我还没有深入了解 ES 6.1。但是,从 ES 2.X 开始,string 数据类型已被替换为文本(用于分析的字符串)和关键字(用于未分析的字符串)。在我的回答中,string应该在映射中替换为text。关于您的建议,这实际上是我将如何实施它。使用多字段映射将原始值存储在主字段中,并将每个分析版本(每个分析器一个)存储在子字段中。这是一个很好的做法,唯一的缺点是这些字段会占用一些空间并且索引会更长一些。希望这会有所帮助:)
  • 感谢您的精彩解释,这个答案应该被接受。
【解决方案2】:

我已经在基于 ES 6.1 的宠物项目中检查了这种方法。像下面这样的数据模型允许按预期进行搜索:

PUT test-idx
{
    "settings": {
        "analysis": {
            "analyzer": {
                "keylower": {
                    "type": "custom",
                    "tokenizer": "keyword",
                    "filter": ["lowercase"]
                }
            }
        }
    }
}

POST /test-idx/doc/_mapping
{
    "properties": {
        "foo": {
            "type": "text",
            "fields": {
                "raw": {
                    "type": "keyword"
                },
                "lowercase_foo": {
                    "type": "text",
                    "analyzer": "keylower"
                }
            }
        }
    }
}

PUT /test-idx/doc/1
{"foo": "QNMZ-1900"}

检查这两个搜索的结果。首先将结果一击。第二个将返回 0 次点击。

GET /test-idx/doc/_search
{
  "query": {
     "wildcard" : { "foo.lowercase_foo" : "qnmz-19*" }
  }
}

GET /test-idx/doc/_search
{
  "query": {
     "wildcard" : { "foo" : "qnmz-19*" }
  }
}

感谢@ThomasC 的意见。请小心我的回答。我只是在学习 Elasticsearch。我不是这个数据库的专家。我不知道这是生产就绪的建议!

【讨论】:

    猜你喜欢
    • 2016-04-25
    • 2016-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-09
    • 1970-01-01
    • 2012-11-28
    • 1970-01-01
    相关资源
    最近更新 更多