【问题标题】:ElasticSearch and Regex queriesElasticSearch 和正则表达式查询
【发布时间】:2014-10-08 09:36:08
【问题描述】:

我正在尝试查询在“内容”字段的正文中包含日期的文档。

curl -XGET 'http://localhost:9200/index/_search' -d '{
    "query": {
        "regexp": {
            "content": "^(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.]((19|20)\\d\\d)$" 
            }
        }
    }'

也许会更近一些?

curl -XGET 'http://localhost:9200/index/_search' -d '{
        "filtered": {
        "query": {
            "match_all": {}
        },
        "filter": {
            "regexp":{
                "content" : "^(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.]((19|20)\\d\\d)$"
                }
            }
        }
    }'

我的正则表达式似乎已关闭。此正则表达式已在 regex101.com 上得到验证 以下查询仍然没有从我拥有的 175k 文档中返回任何内容。

curl -XPOST 'http://localhost:9200/index/_search?pretty=true' -d '{
        "query": {
            "regexp":{
                "content" : "/[0-9]{4}-[0-9]{2}-[0-9]{2}|[0-9]{2}-[0-9]{2}-[0-9]{4}|[0-9]{2}/[0-9]{2}/[0-9]{4}|[0-9]{4}/[0-9]{2}/[0-9]{2}/g"
            }
        }
    }'

我开始认为我的索引可能未针对此类查询设置。您必须使用什么类型的字段才能使用正则表达式?

mappings: {
    doc: {
        properties: {
            content: {
                type: string
            }title: {
                type: string
            }host: {
                type: string
            }cache: {
                type: string
            }segment: {
                type: string
            }query: {
                properties: {
                    match_all: {
                        type: object
                    }
                }
            }digest: {
                type: string
            }boost: {
                type: string
            }tstamp: {
                format: dateOptionalTimetype: date
            }url: {
                type: string
            }fields: {
                type: string
            }anchor: {
                type: string
            }
        }
    }

我想查找任何具有日期的记录并绘制该日期之前的文档量。第 1 步是让这个查询工作。步骤 2. 将提取日期并相应地按它们分组。有人可以建议一种让第一部分工作的方法,因为我知道第二部分真的很棘手。

谢谢!

【问题讨论】:

    标签: regex elasticsearch


    【解决方案1】:

    您应该仔细阅读 Elasticsearch 的 Regexp Query documentation,您对 regexp 查询的工作方式做出了一些错误的假设。

    在这里要了解的最重要的事情可能是您要匹配的字符串是什么。您正在尝试匹配 terms,而不是整个字符串。如果这是使用 StandardAnalyzer 编制索引,我会怀疑,您的日期将被分成多个术语:

    • “01/01/1901”变为标记“01”、“01”和“1901”
    • “01 01 1901”变为标记“01”、“01”和“1901”
    • “01-01-1901”变为标记“01”、“01”和“1901”
    • “01.01.1901”实际上将是一个单一的标记:“01.01.1901”(由于十进制处理,请参阅UAX #29

    您只能将单个完整标记与正则表达式查询匹配。

    Elasticsearch(和 lucene)不支持完全兼容 Perl 的正则表达式语法。

    在您的前几个示例中,您使用的是锚点,^$。不支持这些。您的正则表达式必须匹配整个令牌才能获得匹配,因此不需要锚。

    也不支持像\d(或\\d)这样的简写字符类。代替\\d\\d,使用[0-9]{2}

    在您的最后一次尝试中,您使用的是/{regex}/g,它也不支持。由于您的正则表达式需要匹配整个字符串,因此全局标志在上下文中甚至没有意义。除非您使用的查询解析器使用它们来表示正则表达式,否则您的正则表达式不应包含在斜杠中。

    (顺便说一句:这个是如何在 regex101 上验证的?你有一堆未转义的 /s。当我尝试它时它会抱怨我。)


    要支持对此类分析字段进行此类查询,您可能希望查看跨查询,尤其是Span MultitermSpan Near。也许是这样的:

    {
        "span_near" : {
            "clauses" : [
                { "span_multi" : { 
                    "match": {
                        "regexp": {"content": "0[1-9]|[12][0-9]|3[01]"}
                    }
                }},
                { "span_multi" : { 
                    "match": {
                        "regexp": {"content": "0[1-9]|1[012]"}
                    }
                }},
                { "span_multi" : { 
                    "match": {
                        "regexp": {"content": "(19|20)[0-9]{2}"} 
                    }
                }}
            ],
            "slop" : 0,
            "in_order" : true
        }
    }
    

    【讨论】:

    • 非常感谢您提供如此详细的答案。你是对的,我没有足够仔细地阅读文档。日期实际上在字符 (-/.) 处被标记化。我的正则表达式中确实有转义的正斜杠 (\/),所以 regex101 很高兴,但 ElasticSearch 抱怨它是一个“意外数字”,所以我删除了它。那是我把它粘贴到这个网站的时候。我发现这有效 [0-9]{4}?[0-9]{2}?[0-9]{2} 尽管我无法将它与带有破折号的正则表达式进行基准测试。我将尝试使用您的 span multiterm 建议并返回。我认为您对 +1 很有帮助
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多