ElasticSearch【有与无】【搜索引擎】【ES16】部分匹配

目录

1.简介

1.1.邮编与结构化数据

1.2.prefix 前缀查询

1.3.通配符与正则表达式查询

1.4.查询时输入即搜索

1.5.索引时优化

1.6.Ngrams 在部分匹配的应用

索引时输入即搜索

边界 n-grams 与邮编

1.7.Ngrams 在复合词的应用


1.简介

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

Elasticsearch 提供分析过程,倒排索引让我们不需要使用这种粗笨的技术。

在某些情况下部分匹配会比较有用,常见的应用如下:

  • 匹配邮编、产品序列号或其他 not_analyzed 未分析值,这些值可以是以某个特定前缀开始,也可以是与某种模式匹配的,甚至可以是与某个正则式相匹配的。

  • 输入即搜索(search-as-you-type) ——在用户键入搜索词过程的同时就呈现最可能的结果。

  • 匹配如德语或荷兰语这样有长组合词的语言,如: Weltgesundheitsorganisation (世界卫生组织,英文 World Health Organization)。

1.1.邮编与结构化数据

【举例】

美国目前使用的邮编形式(United Kingdom postcodes 标准)来说明如何用部分匹配查询结构化数据。这种邮编形式有很好的结构定义。

例如,邮编 W1V 3DG 可以分解成如下形式:

  • W1V :这是邮编的外部,它定义了邮件的区域和行政区:

    • W 代表区域( 1 或 2 个字母)

    • 1V 代表行政区( 1 或 2 个数字,可能跟着一个字符)

  • 3DG :内部定义了街道或建筑:

    • 3 代表街区区块( 1 个数字)

    • DG 代表单元( 2 个字母)

PUT /my_index
{
    "mappings": {
        "address": {
            "properties": {
                "postcode": {
                    "type":  "string",
                    "index": "not_analyzed"
                }
            }
        }
    }
}

PUT /my_index/address/1
{ "postcode": "W1V 3DG" }

PUT /my_index/address/2
{ "postcode": "W2F 8HW" }

PUT /my_index/address/3
{ "postcode": "W1F 7HW" }

PUT /my_index/address/4
{ "postcode": "WC1N 1LZ" }

PUT /my_index/address/5
{ "postcode": "SW5 0BE" }

 

1.2.prefix 前缀查询

为了找到所有以 W1 开始的邮编,可以使用简单的 prefix 查询:

【举例】

GET /my_index/address/_search
{
    "query": {
        "prefix": {
            "postcode": "W1"
        }
    }
}

【说明】

prefix 查询是一个词级别的底层的查询,它不会在搜索之前分析查询字符串,它假定传入前缀就正是要查找的前缀。

注意
默认状态下,prefix 查询不做相关度评分计算,它只是将所有匹配的文档返回,并为每条结果赋予评分值 1 。
它的行为更像是过滤器而不是查询。 
prefix 查询和 prefix 过滤器这两者实际的区别就是过滤器是可以被缓存的,而查询不行。

对于每个词,倒排索引都会将包含词的文档 ID 列入 倒排表(postings list) 。

为了支持前缀匹配,查询会做以下事情:

  1. 扫描词列表并查找到第一个以 W1 开始的词。
  2. 搜集关联的文档 ID 。
  3. 移动到下一个词。
  4. 如果这个词也是以 W1 开头,查询跳回到第二步再重复执行,直到下一个词不以 W1 为止。
注意
prefix 查询或过滤对于一些特定的匹配是有效的,但使用方式还是应当注意。
当字段中词的集合很小时,可以放心使用,但是它的伸缩性并不好,会对我们的集群带来很多压力。
可以使用较长的前缀来限制这种影响,减少需要访问的量。

1.3.通配符与正则表达式查询

【举例】

GET /my_index/address/_search
{
    "query": {
        "wildcard": {
            "postcode": "W?F*HW" 
        }
    }
}

GET /my_index/address/_search
{
    "query": {
        "regexp": {
            "postcode": "W[0-9].+
        }
    }
}

【注意】

这也意味着需要同样注意前缀查询存在性能问题,对有很多唯一词的字段执行这些查询可能会消耗非常多的资源,所以要避免使用左通配这样的模式匹配(如: *foo 或 .*foo 这样的正则式)。

数据在索引时的预处理有助于提高前缀匹配的效率,而通配符和正则表达式查询只能在查询时完成,尽管这些查询有其应用场景,但使用仍需谨慎。

prefix 、wildcard 和 regexp 查询是基于词操作的,如果用它们来查询 analyzed 字段,
它们会检查字段里面的每个词,而不是将字段作为整体来处理。

 "Quick brown fox"

会匹配以下这个查询:

{ "regexp": { "title": "br.*" }}

但是不会匹配以下两个查询:

{ "regexp": { "title": "Qu.*" }} // 在索引里的词是 quick 而不是 Quick 。
{ "regexp": { "title": "quick br*" }} // quick 和 brown 在词表中是分开的。

 

1.4.查询时输入即搜索

即时搜索(instant search) 或 输入即搜索(search-as-you-type)

在 短语匹配 中,引入了 match_phrase 短语匹配查询,它匹配相对顺序一致的所有指定词语,对于查询时的输入即搜索,可以使用 match_phrase 的一种特殊形式, match_phrase_prefix 查询

【举例】

{
    "match_phrase_prefix" : {
        "brand" : "johnnie walker bl"
    }
}

{
    "match_phrase_prefix" : {
        "brand" : {
            "query": "walker johnnie bl", 
            "slop":  10
        }
    }
}

【说明】

这种查询的行为与 match_phrase 查询一致,不同的是它将查询字符串的最后一个词作为前缀使用

"johnnie walker bl*"

max_expansions 】

{
    "match_phrase_prefix" : {
        "brand" : {
            "query":          "johnnie walker bl",
            "max_expansions": 50
        }
    }
}

参数 max_expansions 控制着可以与前缀匹配的词的数量,它会先查找第一个与前缀 bl 匹配的词,然后依次查找搜集与之匹配的词(按字母顺序),直到没有更多可匹配的词或当数量超过 max_expansions 时结束

 

1.5.索引时优化

可以通过在索引时处理数据提高搜索的灵活性以及提升系统性能。为此仍然需要付出应有的代价:增加的索引空间与变慢的索引能力,但这与每次查询都需要付出代价不同,索引时的代价只用付出一次。

 

1.6.Ngrams 在部分匹配的应用

在索引时准备数据意味着要选择合适的分析链,这里部分匹配使用的工具是 n-gram 。可以将 n-gram 看成一个在词语上 滑动窗口, n 代表这个 “窗口” 的长度。如果要 n-gram quick 这个词 —— 它的结果取决于 n 的选择长度:

  • 长度 1(unigram): [ quick ]
  • 长度 2(bigram): [ quuiicck ]
  • 长度 3(trigram): [ quiuicick ]
  • 长度 4(four-gram): [ quicuick ]
  • 长度 5(five-gram): [ quick ]

朴素的 n-gram 对 词语内部的匹配 非常有用。但对于输入即搜索(search-as-you-type)这种应用场景,我们会使用一种特殊的 n-gram 称为 边界 n-grams (edge n-grams)。所谓的边界 n-gram 是说它会固定词语开始的一边,以单词 quick 为例,它的边界 n-gram 的结果为:

  • q
  • qu
  • qui
  • quic
  • quick

索引时输入即搜索

【举例】
 

创建索引、实例化 token 过滤器和分析器

PUT /my_index
{
    "settings": {
        "number_of_shards": 1, 
        "analysis": {
            "filter": {
                "autocomplete_filter": { // 自定义的 edge_ngram token 过滤器(autocomplete_filter) ,这个分析器使用 standard 分词器将字符串拆分为独立的词,并且将它们都变成小写形式,然后为每个词生成一个边界 n-gram
                    "type":     "edge_ngram",
                    "min_gram": 1,
                    "max_gram": 20
                }
            },
            "analyzer": {
                "autocomplete": {// 自定义分析器 autocomplete,这个分析器使用 standard 分词器将字符串拆分为独立的词,并且将它们都变成小写形式,然后为每个词生成一个边界 n-gram
                    "type":      "custom",
                    "tokenizer": "standard",
                    "filter": [
                        "lowercase",
                        "autocomplete_filter" 
                    ]
                }
            }
        }
    }
}

【测试】

GET /my_index/_analyze?analyzer=autocomplete
quick brown

【结果】

  • q
  • qu
  • qui
  • quic
  • quick
  • b
  • br
  • bro
  • brow
  • brown

【可以用 update-mapping API 将这个分析器应用到具体字段】

PUT /my_index/_mapping/my_type
{
    "my_type": {
        "properties": {
            "name": {
                "type":     "string",
                "analyzer": "autocomplete"
            }
        }
    }
}

【查询】

GET /my_index/my_type/_search
{
    "query": {
        "match": {
            "name": "brown fo"
        }
    }
}

[结果]

{

  "hits": [
     {
        "_id": "1",
        "_score": 1.5753809,
        "_source": {
           "name": "Brown foxes"
        }
     },
     {
        "_id": "2",
        "_score": 0.012520773,
        "_source": {
           "name": "Yellow furballs"
        }
     }
  ]
}

【分析】

GET /my_index/my_type/_validate/query?explain
{
    "query": {
        "match": {
            "name": "brown fo"
        }
    }
}

【结果】不正确返回 Brown foxes 这个文档

name:b name:br name:bro name:brow name:brown name:f name:fo

name:f 条件可以满足第二个文档,因为 furballs 是以 f 、 fu 、 fur 形式索引的。回过头看这并不令人惊讶,相同的 autocomplete 分析器同时被应用于索引时和搜索时,这在大多数情况下是正确的,只有在少数场景下才需要改变这种行为

GET /my_index/my_type/_search
{
    "query": {
        "match": {
            "name": {
                "query":    "brown fo",
                "analyzer": "standard"  // 覆盖了 name 字段 analyzer 的设置。
            }
        }
    }
}

PUT /my_index/my_type/_mapping
{
    "my_type": {
        "properties": {
            "name": {
                "type":            "string",
                "index_analyzer":  "autocomplete",  // 在索引时,使用 autocomplete 分析器生成边界 n-grams 的每个词。
                "search_analyzer": "standard"  // 在搜索时,使用 standard 分析器只搜索用户输入的词。
            }
        }
    }
}

GET /my_index/my_type/_validate/query?explain
{
    "query": {
        "match": {
            "name": "brown fo"
        }
    }
}

【结果】正确返回 Brown foxes 这个文档

name:brown name:fo
补全提示(Completion Suggester)

使用边界 n-grams 进行输入即搜索(search-as-you-type)的查询设置简单、灵活且快速,但有时候它并不够快,特别是当试图立刻获得反馈时,
延迟的问题就会凸显,很多时候不搜索才是最快的搜索方式。

Elasticsearch 里的 {ref}/search-suggesters-completion.html[completion suggester] 采用与上面完全不同的方式,
需要为搜索条件生成一个所有可能完成的词列表,然后将它们置入一个 有限状态机(finite state transducer) 内,这是个经优化的图结构。
为了搜索建议提示,Elasticsearch 从图的开始处顺着匹配路径一个字符一个字符地进行匹配,
一旦它处于用户输入的末尾,Elasticsearch 就会查找所有可能结束的当前路径,然后生成一个建议列表。

本数据结构存于内存中,能使前缀查找非常快,比任何一种基于词的查询都要快很多,
这对名字或品牌的自动补全非常适用,因为这些词通常是以普通顺序组织的:用 “Johnny Rotten” 而不是 “Rotten Johnny” 。

当词序不是那么容易被预见时,边界 n-grams 比完成建议者(Completion Suggester)更合适。
即使说不是所有猫都是一个花色,那这只猫的花色也是相当特殊的。

边界 n-grams 与邮编

边界 n-gram 的方式可以用来查询结构化的数据,比如邮编(postcode)。当然 postcode 字段需要 analyzed 而不是 not_analyzed ,不过可以用 keyword 分词器来处理它,就好像他们是 not_analyzed 的一样。

keyword 分词器是一个非操作型分词器,这个分词器不做任何事情,它接收的任何字符串都会被原样发出,
因此它可以用来处理 not_analyzed 的字段值,但这也需要其他的一些分析转换,如将字母转换成小写。

【举例】

{
    "analysis": {
        "filter": {
            "postcode_filter": {
                "type":     "edge_ngram",
                "min_gram": 1,
                "max_gram": 8
            }
        },
        "analyzer": {
            "postcode_index": { // 析器使用 postcode_filter 将邮编转换成边界 n-gram 形式。
                "tokenizer": "keyword",
                "filter":    [ "postcode_filter" ]
            },
            "postcode_search": {  // 分析器可以将搜索词看成 not_analyzed 未分析的。
                "tokenizer": "keyword"
            }
        }
    }
}

1.7.Ngrams 在复合词的应用

【举例】

假设某个 n-gram 是一个词上的滑动窗口,那么任何长度的 n-gram 都可以遍历这个词。我们既希望选择足够长的值让拆分的词项具有意义,又不至于因为太长而生成过多的唯一词。一个长度为 3 的 trigram 可能是一个不错的开始:

PUT /my_index
{
    "settings": {
        "analysis": {
            "filter": {
                "trigrams_filter": {
                    "type":     "ngram",
                    "min_gram": 3,
                    "max_gram": 3
                }
            },
            "analyzer": {
                "trigrams": {
                    "type":      "custom",
                    "tokenizer": "standard",
                    "filter":   [
                        "lowercase",
                        "trigrams_filter"
                    ]
                }
            }
        }
    },
    "mappings": {
        "my_type": {
            "properties": {
                "text": {
                    "type":     "string",
                    "analyzer": "trigrams" // text 字段用 trigrams 分析器索引它的内容,这里 n-gram 的长度是 3 。
                }
            }
        }
    }
}

[测试]

GET /my_index/_analyze?analyzer=trigrams
Weißkopfseeadler

[返回]

wei, eiß, ißk, ßko, kop, opf, pfs, fse, see, eea,ead, adl, dle, ler

POST /my_index/my_type/_bulk
{ "index": { "_id": 1 }}
{ "text": "Aussprachewörterbuch" }
{ "index": { "_id": 2 }}
{ "text": "Militärgeschichte" }
{ "index": { "_id": 3 }}
{ "text": "Weißkopfseeadler" }
{ "index": { "_id": 4 }}
{ "text": "Weltgesundheitsorganisation" }
{ "index": { "_id": 5 }}
{ "text": "Rindfleischetikettierungsüberwachungsaufgabenübertragungsgesetz" }

GET /my_index/my_type/_search
{
    "query": {
        "match": {
            "text": "Adler"
        }
    }
}

[结果]

{
  "hits": [
     {
        "_id": "3",
        "_score": 3.3191128,
        "_source": {
           "text": "Weißkopfseeadler"
        }
     }
  ]
}

[结果排除]

GET /my_index/my_type/_search
{
    "query": {
        "match": {
            "text": {
                "query":                "Gesundheit",
                "minimum_should_match": "80%"
            }
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

琴 韵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值