首页 > 解决方案 > 负前瞻正则表达式在 ES dsl 查询中不起作用

问题描述

我的 Elastic 搜索的映射如下所示:

{
  "settings": {
    "index": {
      "number_of_shards": "5",
      "number_of_replicas": "1"
    }
  },
  "mappings": {
    "node": {
      "properties": {
        "field1": {
          "type": "keyword"
        },
        "field2": {
          "type": "keyword"
        },
        "query": {
          "properties": {
            "regexp": {
              "properties": {
                "field1": {
                  "type": "keyword"
                },
                "field2": {
                  "type": "keyword"
                }
              }
            }
          }
        }
      }
    }
  }
}

问题是:

我正在使用 elasticsearch_dsl Q() 形成 ES 查询。当我的查询包含任何复杂的正则表达式时,它在大多数情况下都可以正常工作。但如果它包含正则表达式字符'!',它就完全失败了 在里面。当搜索词包含“!”时,它不会给出任何结果 在里面。

例如:

1.)Q('regexp', field1 = "^[a-z]{3}.b.*")(完美运行)

2.)Q('regexp', field1 = "^f04.*")(完美运行)

3.)Q('regexp', field1 = "f00.*")(完美运行)

4.)Q('regexp', field1 = "f04baz?")(完美运行)

在以下情况下失败:

5.)Q('regexp', field1 = "f04((?!z).)*")(失败,没有结果)

我尝试在字段中添加“分析器”:“关键字”以及“类型”:“关键字”,但在这种情况下没有任何效果。

在浏览器中,我尝试检查分析器:关键字在输入失败的情况下将如何工作:

http://localhost:9210/search/_analyze?analyzer=keyword&text=f04((?!z).) *

结果似乎在这里看起来不错:

{
  "tokens": [
    {
      "token": "f04((?!z).)*",
      "start_offset": 0,
      "end_offset": 12,
      "type": "word",
      "position": 0
    }
  ]
}

我正在运行如下查询:

search_obj = Search(using = _conn, index = _index, doc_type = _type).query(Q('regexp', field1 = "f04baz?"))
count = search_obj.count()
response = search_obj[0:count].execute()
logger.debug("total nodes(hits):" + " " + str(response.hits.total))

请帮助,这真的是一个烦人的问题,因为所有正则表达式字符在所有查询中都可以正常工作,除了!。

另外,我如何检查我的映射中当前应用了上述设置的分析器?

标签: regexelasticsearchelasticsearch-dslnegative-lookbehindelasticsearch-dsl-py

解决方案


ElasticSearch Lucene 正则表达式引擎不支持任何类型的环视。ES 正则表达式文档相当模棱两可,说匹配一切.*都非常慢以及使用环视正则表达式(这不仅模棱两可,而且是错误的,因为环顾四周,如果明智地使用,可能会大大加快正则表达式匹配)。

由于您想匹配任何包含f04和不包含的字符串z,您实际上可以使用

[^z]*fo4[^z]*

细节

  • [^z]*- 除了任何 0+ 字符z
  • fo4-fo4子串
  • [^z]*- 除 . 之外的任何 0+ 个字符z

如果您有一个要“排除”的多字符字符串(例如,z4而不是z),您可以使用补码运算符来使用您的方法:

.*f04.*&~(.*z4.*)

这意味着几乎相同但不支持换行符:

  • .*- 除换行符以外的任何字符,尽可能多
  • f04-f04
  • .*- 除换行符以外的任何字符,尽可能多
  • &- 和
  • ~(.*z4.*)- 除了具有的字符串之外的任何字符串z4

推荐阅读