部分匹配(Partial Matching)
敏锐的读者可能已经发现到眼下为止,介绍的查询都是在整个词条层面进行操作的。
匹配的最小单元必须是一个词条。你仅仅能找到存在于倒排索引(Inverted Index)中的词条。
可是假设你想匹配词条的一部分,而不是整个词条呢?部分匹配(Partial Matching)同意用户指定词条的一部分然后找到含有该部分的不论什么单词。
匹配词条一部分这一需求在全文搜索引擎领域比你想象的要不那么常见。假设你有SQL的背景。你可能有过使用以下的SQL语句来实现一个简单的全文搜索功能的经历:
WHERE text LIKE "*quick*" AND text LIKE "*brown*" AND text LIKE "*fox*"
当然,通过ES我们能够借助分析过程(Analysis Process)和倒排索引来避免这样的"蛮力"技术。
为了同一时候匹配"fox"和"foxes",我们能够简单地使用一个词干提取器,然后将词干进行索引。这样就没有必要进行部分匹配了。
即便如此,在某些场合下部分匹配还是有作用的。常见的用例比方:
- 匹配邮政编码,产品序列号。或者其他以某个特定前缀开头的或者可以匹配通配符甚至正則表達式的not_analyzed值。
- 即时搜索(Search-as-you-type) - 在用户完毕搜索词条的输入前就展示最有可能的结果。
- 匹配德语或者荷兰语这一类语言,它们韩哟长复合单词。比方Weltgesundheitsorganisation(World Health Organization)。
我们以针对精确值not_analyzed字段的前缀匹配開始。介绍部分匹配的技术。
邮政编码和结构化数据
我们以英国的邮政编码来说明怎样在结构化数据上使用部分匹配。
英国邮政编码是一个定义清晰的结构。比方,W1V 3DG这个邮政编码能够被分解成下面几个部分:
- W1V:这个部分表明了邮政地域和地区(Postal Area and District):
- W 表明了地域(Area)。使用一个或者两个字母。
- 1V 表明了地区(District)。使用一个或者两个数字,可能尾随一个字母。
- 3DG:该部分表明了街道或者建筑:
- 3 表明了区域(Sector)。使用一个数字。
- DG 表明了单元,使用两个字母。
如果我们将邮政编码索引为精确值的not_analyzed字段,因此我们能够创建例如以下索引:
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" }
如今我们的数据就准备就绪了。
前缀查询(Prefix Query)
我们能够通过一个简单的prefix查询来得到全部以W1开头的邮政编码:
GET /my_index/address/_search{ "query": { "prefix": { "postcode": "W1" } }}
prefix查询是一个工作在词条级别的低级查询。它不会在搜索前对查询字符串进行解析。它如果用户会传入一个须要查询的精确前缀。
TIP
默认情况下。prefix查询不会计算相关度分值。它仅仅是进行文档匹配,匹配的文档的分值为1。
事实上,相比查询它更像一个过滤器。
prefix查询和prefix过滤器的唯一差别在于过滤器能够被缓存。
之前。我们提到过"你仅仅能找到存在于倒排索引中的词条"。可是对于这些邮政编码我们并没有进行不论什么特殊处理;每一个邮政编码仅仅是被当做精确值被简单地索引。那么prefix查询是怎样工作的呢?
记住倒排索引是由唯一词条得有序列表构成的(此种情况下。即邮政编码)。对于每一个词条。它会列举全部含有该词条的文档ID。对于我们的演示样例文档,倒排索引例如以下所看到的:
Term: Doc IDs:-------------------------"SW5 0BE" | 5"W1F 7HW" | 3"W1V 3DG" | 1"W2F 8HW" | 2"WC1N 1LZ" | 4-------------------------
为了支持前缀匹配。查询会运行下面的步骤:
- 遍历词条列表并找到以W1开头的词条。
- 收集相应的文档ID。
- 移动到下一个词条。
- 假设该词条也以W1开头。那么反复步骤2;否则结束操作。
虽然以上的步骤对于我们的小样例而言能非常好地工作,想象一下当倒排索引含有一百万个以W1开头的邮政编码时的情景,prefix查询须要訪问一百万个词条来得到结果。
而前缀越短,就意味着须要訪问越多的词条。
假设我们查询前缀为W,而不是W1的词条。可能会匹配多达一千万个词条。
注意
prefix查询和过滤器对于即时(Ad-hoc)的前缀匹配是实用处的,可是在使用它们的时候须要小心。
对于拥有少量词条的字段能够任意地使用。可是它们的扩展性较差,可能会让你的集群承受过多的压力。
能够通过使用一个较长的前缀来限制它们对于集群的影响。这能够降低须要訪问的词条的数量。
在本章的后面部分,我们将推出一个解决方案,使指数在更有效的前缀匹配。但首先,让我们来看看两个相关查询:wildcard以及regexp询。