Elasticsearch探索:部分匹配

2021-01-04 10:57:41 浏览数 (1)

简介

官网地址:https://www.elastic.co/guide/en/elasticsearch/reference/current/term-level-queries.html

部分匹配允许用户指定查找词的一部分并找出所有包含这部分片段的词。

Prefix Query 前缀查询

匹配包含具有指定前缀的项(not analyzed)的字段的文档。前缀查询对应Lucene的PrefixQuery。

代码语言:javascript复制
GET /_search
{
  "query": {
    "prefix": {
      "user.id": {
        "value": "ki"
      }
    }
  }
}
  • field:(Required, object) Field you wish to search.
  • value:(Required, string) Beginning characters of terms you wish to find in the provided<field>.
  • rewrite:(Optional, string) Method used to rewrite the query. For valid values and more information, see therewriteparameter.
  • case_insensitive:(Optional, Boolean) allows ASCII case insensitive matching of the value with the indexed field values when set to true. Default is false which means the case sensitivity of matching depends on the underlying field’s mapping.

speed up prefix queries

You can speed up prefix queries using theindex_prefixesmapping parameter. If enabled, Elasticsearch indexes prefixes between 2 and 5 characters in a separate field. This lets Elasticsearch run prefix queries more efficiently at the cost of a larger index.

您可以使用index_prefixes映射参数来加速前缀查询。 如果启用,Elasticsearch会在一个单独的字段中索引2到5个字符之间的前缀。 这使Elasticsearch可以以更大的索引为代价更有效地运行前缀查询。

allow expensive queries

Prefix queries will not be executed ifsearch.allow_expensive_queriesis set to false. However, ifindex_prefixesare enabled, an optimised query is built which is not considered slow, and will be executed in spite of this setting.

如果将search.allow_expensive_queries设置为false,则不会执行前缀查询。 但是,如果启用了index_prefixes,则会构建一个优化的查询,该查询并不算慢,尽管有此设置也将执行该查询。

Range Query 范围查询

Returns documents that contain terms within a provided range.

代码语言:javascript复制
GET /_search
{
  "query": {
    "range": {
      "age": {
        "gte": 10,
        "lte": 20,
        "boost": 2.0
      }
    }
  }
}
  • field:
  • gt:(Optional) Greater than.
  • gte:(Optional) Greater than or equal to.
  • lt:(Optional) Less than.
  • lte:(Optional) Less than or equal to.
  • format:(Optional, string) Date format used to convertdatevalues in the query.
    • By default, Elasticsearch uses thedateformatprovided in the<field>'s mapping. This value overrides that mapping format.
    • For valid syntax, seeformat.
  • relation:(Optional, string) Indicates how the range query matches values forrangefields. Valid values are:
    • INTERSECTS:匹配具有与查询范围相交的范围字段值的文档。
    • CONTAINS:使用范围字段值完全包含查询范围的文档进行匹配。
    • WITHIN:使用范围字段值完全在查询范围内的文档进行匹配。
  • time_zone:(Optional, string) Coordinated Universal Time (UTC) offset or IANA time zone used to convert date values in the query to UTC.
    • Valid values are ISO 8601 UTC offsets, such as 01:00 or -08:00, and IANA time zone IDs, such as America/Los_Angeles.
    • For an example query using the time_zone parameter, see Time zone in range queries.
  • boost:

数字范围

代码语言:javascript复制
{
    "query" : {
        "constant_score" : {
            "filter" : {
                "range" : {
                    "price" : {
                        "gte" : 20,
                        "lt"  : 40
                    }
                }
            }
        }
    }
}

日期范围

range查询同样可以应用在日期字段上:

代码语言:javascript复制
"range" : {
    "timestamp" : {
        "gt" : "2014-01-01 00:00:00",
        "lt" : "2014-01-07 00:00:00"
    }
}

当使用它处理日期字段时,range查询支持对日期计算(date math)进行操作,比方说,如果我们想查找时间戳在过去一小时内的所有文档:

代码语言:javascript复制
"range" : {
    "timestamp" : {
        "gt" : "now-1h"
    }
}

{
  "query": {
    "range": {
      "timestamp": {
        "gte": "now-1d/d",
        "lt": "now/d"
      }
    }
  }
}

这个过滤器会一直查找时间戳在过去一个小时内的所有文档,让过滤器作为一个时间滑动窗口(sliding window)来过滤文档。

日期计算还可以被应用到某个具体的时间,并非只能是一个像 now 这样的占位符。只要在某个日期后加上一个双管符号 (||) 并紧跟一个日期数学表达式就能做到:

代码语言:javascript复制
"range" : {
    "timestamp" : {
        "gt" : "2014-01-01 00:00:00",
        "lt" : "2014-01-01 00:00:00|| 1M"   #早于 2014 年 1 月 1 日加 1 月(2014 年 2 月 1 日 零时)
    }
}

日期计算是日历相关(calendar aware)的,所以它不仅知道每月的具体天数,还知道某年的总天数(闰年)等信息。更详细的内容可以参考:时间格式参考文档。

You can use thetime_zoneparameter to convertdatevalues to UTC using a UTC offset. For example:

您可以使用time_zone参数使用UTC偏移量将日期值转换为UTC。 例如:

代码语言:javascript复制
GET /_search
{
  "query": {
    "range": {
      "timestamp": {
        "time_zone": " 01:00",  // Indicates that date values use a UTC offset of  01:00.      
        "gte": "2020-01-01T00:00:00", // With a UTC offset of  01:00, Elasticsearch converts this date to 2019-12-31T23:00:00 UTC.
        "lte": "now"     // The time_zone parameter does not affect the now value.             
      }
    }
  }
}

格式化的日期将使用指定的默认format(格式)解析date(日期)字段,但可以通过将格式参数传递到range (范围)查询来覆盖默认格式:

代码语言:javascript复制
GET _search
{
    "query": {
        "range" : {
            "timestamp" : {
                "gte": "01/01/2012",
                "lte": "2013",
                "format": "dd/MM/yyyy||yyyy"
            }
        }
    }
}

字符串范围

range查询同样可以处理字符串字段, 字符串范围可采用字典顺序(lexicographically)或字母顺序(alphabetically)。例如,下面这些字符串是采用字典序(lexicographically)排序的:

在倒排索引中的词项就是采取字典顺序(lexicographically)排列的,这也是字符串范围可以使用这个顺序来确定的原因。

如果我们想查找从ab(不包含)的字符串,同样可以使用range查询语法:

代码语言:javascript复制
"range" : {
    "title" : {
        "gte" : "a",
        "lt" :  "b"
    }
}

数字和日期字段的索引方式使高效地范围计算成为可能。 但字符串却并非如此,要想对其使用范围过滤,Elasticsearch 实际上是在为范围内的每个词项都执行 term 过滤器,这会比日期或数字的范围过滤慢许多。

字符串范围在过滤 低基数(low cardinality) 字段(即只有少量唯一词项)时可以正常工作,但是唯一词项越多,字符串范围的计算会越慢。

Regexp Query 正则

Regular Expressions 搜索也即正则搜索是非常耗时的。正则表达式是一种使用 placeholder(称为运算符)匹配数据中的模式的方法。 有关regexp查询支持的运算符的列表,请参阅 Regular expression syntax

regexp允许使用正则表达式进行term查询.注意regexp如果使用不正确,会给服务器带来很严重的性能压力。比如.*开头的查询,将会匹配所有的倒排索引中的关键字,这几乎相当于全表扫描,会很慢。因此如果可以的话,最好在使用正则前,加上匹配的前缀。在正则中如果使用.*?或者 都会降低查询的性能。

代码语言:javascript复制
GET /_search
{
  "query": {
    "regexp": {
      "user.id": {
        "value": "k.*y",
        "flags": "ALL",
        "case_insensitive": true,
        "max_determinized_states": 10000,
        "rewrite": "constant_score"
      }
    }
  }
}
  • field:
  • value:
  • flags:(Optional, string) Enables optional operators for the regular expression. For valid values and more information, seeRegular expression syntax.
  • case_insensitive:(Optional, Boolean) allows case insensitive matching of the regular expression value with the indexed field values when set to true. Default is false which means the case sensitivity of matching depends on the underlying field’s mapping.
  • max_determinized_states:(Optional, integer) Maximum number of automaton states required for the query. Default is 10000.
  • rewrite:(Optional, string) Method used to rewrite the query. For valid values and more information, see therewriteparameter.

如果将search.allow_expensive_queries设置为false,则将不会执行正则表达式查询。

字符

含义

.

只能代指任意一个字符

*

重复前一位匹配(0次或者多次)

?

重复前一位匹配(0次或者1次)

重复前一位匹配(1次或者多次)

代码语言:javascript复制
{
  "query": {
    "regexp": {
      "drugname.keyword": {
        "value": ".*西地吡.罗昔康片.*",
        "flags_value": 65535,
        "max_determinized_states": 10000,
        "boost": 1
      }
    }
  }
}

BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
String reg = ".*"   CharacterUtil.replaceAllForRegexp(keyword)   ".*";

BoolQueryBuilder drugNameBuilder = QueryBuilders.boolQuery();
drugNameBuilder.must(QueryBuilders.regexpQuery("drugname.keyword", reg));

备注:如果keyword里边有特殊字符,可以手动将特殊字符替换成(.)
q.replace(""", ".").replace("?", ".").replace("*", ".").replace("^", ".").replace("$", ".").replace(" ", ".").replace("|", ".").replace("{", ".")       .replace("}", ".").replace("[", ".") .replace("]", ".").replace("(", ".").replace(")", ".").replace("\", ".")

特殊字符:. ? * | { } [ ] ( ) "

实际使用 regexp 搜索时,我们必须记住如下的事项:

  • 避免通配符在前面,比如上面的 .*work。可能以避免使用前导通配符的方式对数据建立索引
  • 通常,正则表达式可能会很昂贵

如果您确实需要匹配 token 的末尾,只需使用 reverse 过滤器为它们建立索引。下面,我们用一个具体的例子来实现。

首先,我们为 reverse_example 建立一个 mapping:

代码语言:javascript复制
PUT reverse_example
{
  "settings": {
    "analysis": {
      "analyzer": {
        "whitespace_reverse": {
          "tokenizer": "whitespace",
          "filter": [
            "reverse"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "content": {
        "type": "text",
        "fields": {
          "reversed": {
            "type": "text",
            "analyzer": "whitespace_reverse"
          }
        }
      }
    }
  }
}

在这里 content 是一个 multi-field 的字段。content.reversed 将使用 whitespace_reverse 分析器来对我们的字段进行分词。这个分析器将会对术语进行倒序处理。比如:

代码语言:javascript复制
GET reverse_example/_analyze
{
  "tokenizer": "standard",
  "filter": [
    "reverse"
  ],
  "text": "quick fox jumps"
}

Response:
{
    "tokens":[
        {
            "token":"kciuq",
            "start_offset":0,
            "end_offset":5,
            "type":"<ALPHANUM>",
            "position":0
        },
        {
            "token":"xof",
            "start_offset":6,
            "end_offset":9,
            "type":"<ALPHANUM>",
            "position":1
        },
        {
            "token":"spmuj",
            "start_offset":10,
            "end_offset":15,
            "type":"<ALPHANUM>",
            "position":2
        }
    ]
}
代码语言:javascript复制
PUT reverse_example/_doc/1
{
  "content": "This is a good network"
}

GET reverse_example/_search
{
  "query": {
    "regexp": {
      "content.reversed": "krow.*"
    }
  }
}

我们通过上面的方法把通配符在前面的搜索修改成为通配符在后面的 regexp 搜索。

参考:https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-reverse-tokenfilter.html

Wildcard Query 通配符

prefix前缀查询的特性类似,wildcard通配符查询也是一种底层基于词的查询,与前缀查询不同的是它允许指定匹配的正则式。它使用标准的 shell 通配符查询:?匹配任意字符,*匹配 0 或多个字符。

以下搜索返回文档,其中user.id字段包含以ki开头和y结尾的术语。 这些匹配项可以包括kiy,kity或kimchy。

代码语言:javascript复制
GET /_search
{
  "query": {
    "wildcard": {
      "user.id": {
        "value": "ki*y",
        "boost": 1.0,
        "rewrite": "constant_score"
      }
    }
  }
}
  • field:
  • value:(Required, string) Wildcard pattern for terms you wish to find in the provided <field>.This parameter supports two wildcard operators:
    • ?, which matches any single character
    • *, which can match zero or more characters, including an empty one
  • boost:
  • rewrite:
  • case_insensitive:

Wildcard queries will not be executed ifsearch.allow_expensive_queriesis set to false.

0 人点赞