[Elasticsearch] 邻近匹配 (三) - 性能,关联单词查询以及Shingles

时间:2022-09-25 00:08:10

提高性能

短语和邻近度查询比简单的match查询在性能上更昂贵。match查询仅仅是查看词条是否存在于倒排索引(Inverted Index)中,而match_phrase查询则须要计算和比較多个可能反复词条(Multiple possibly repeated)的位置。

Lucene Nightly Benchmarks中,显示了一个简单的term查询比一个短语查询快大概10倍,比一个邻近度查询(一个拥有slop的短语查询)快大概20倍。

当然,这个代价是在搜索期间而不是索引期间付出的。

TIP

通常,短语查询的额外代价并不像这些数字说的那么吓人。

实际上,性能上的差异仅仅是说明了一个简单的term查询时多么的快。在标准全文数据上进行的短语查询通常可以在数毫秒内完毕,因此它们在实际生产环境下是全然可以使用的,即使在一个繁忙的集群中。

在某些特定的场景下。短语查询可能会非常耗费资源。可是这样的情况时不常有的。

一个典型的样例是DNA序列,此时会在非常多位置上出现非常之多的同样反复词条。使用高slop值会使位置计算发生大幅度的增长。

因此,怎样可以限制短语和邻近度查询的性能消耗呢?一个实用的方法是降低须要使用短语查询进行检查的文档总数。

结果的分值重计算(Rescoring
Results)

在上一节中,我们讨论了使用邻近度查询来调整相关度,而不是使用它来将文档从结果列表中加入或者排除。

一个查询可能会匹配百万计的结果。可是我们的用户非常可能仅仅对前面几页结果有兴趣。

一个简单的match查询已经通过排序将含有全部搜索词条的文档放在结果列表的前面了。而我们仅仅想对这些前面的结果进行又一次排序来给予那些同一时候匹配了短语查询的文档额外的相关度。

search API通过分值重计算(Rescoring)来支持这一行为。在分值重计算阶段。你可以使用一个更加昂贵的分值计算算法 - 比方一个短语查询 - 来为每一个分片的前K个结果又一次计算其分值。紧接着这些结果就会按其新的分值又一次排序。

该请求例如以下所看到的:

GET /my_index/my_type/_search
{
"query": {
"match": {
"title": {
"query": "quick brown fox",
"minimum_should_match": "30%"
}
}
},
"rescore": {
"window_size": 50,
"query": {
"rescore_query": {
"match_phrase": {
"title": {
"query": "quick brown fox",
"slop": 50
}
}
}
}
}
}

match查询用来决定哪些文档会被包括在终于的结果集合中。结果通过TF/IDF进行排序。 window_size是每一个分片上须要又一次计算分值的数量。

寻找关联的单词(Finding Associated Words)

虽然短语和邻近度查询非常管用,它们还是有一个缺点。

它们过于严格了:全部的在短语查询中的词条都必须出如今文档中。即使使用了slop。

通过slop获得的可以调整单词顺序的灵活性也是有代价的,由于你失去了单词之间的关联。虽然你可以识别文档中的sue。alligator和ate出如今一块,可是你不能推断是Sue ate还是alligator ate。

当单词结合在一起使用时,它们表达的意思比单独使用时要丰富。

"I’m not happy I’m working"和"I’m happy I’m not working"含有同样的单词,也拥有相近的邻近度,可是它们的意思大相径庭。

假设我们索引单词对,而不是索引独立的单词,那么我们就行保留很多其它关于单词使用的上下文信息。

对于句子"Sue ate the alligator",我们不仅索引每一个单词(或者Unigram)为一个词条:

["sue", "ate", "the", "alligator"]

我们同一时候会将每一个单词和它的邻近单词一起索引成一个词条:

["sue ate", "ate the", "the alligator"]

这些单词对(也叫做Bigram)就是所谓的Shingle。

TIP

Shingle不限于仅仅是单词对;你也能够索引三个单词(Word Triplet,也被称为Trigram)作为一个词条:

["sue ate the", "ate the alligator"]

Trigram可以给你更高的精度,可是也大大地添加了索引的不同词条的数量。在多数情况下,Bigram就足够了。

当然。仅仅有当用户输入查询的顺序和原始文档的顺序一致,Shingle才可以起作用。一个针对sue alligator的查询会匹配单独的单词,可是不会匹配不论什么Shingle。

幸运的是,用户会倾向于使用和他们正在搜索的数据中相似的结构来表达查询。可是这是非常重要的一点:仅使用Bigram是不够的。我们仍然须要Unigram。我们能够将匹配Bigram作为信号(Signal)来添加相关度分值。

产生Shingle

Shingle须要在索引期间,作为分析过程的一部分被创建。我们可以将Unigram和Bigram都索引到一个字段中,可是将它们放在不同的字段中会更加清晰,也可以让它们可以被独立地查询。Unigram字段形成了我们搜索的基础部分,而Bigram字段则用来提升相关度。

首先。我们须要使用shingle词条过滤器来创建解析器:

DELETE /my_index

PUT /my_index
{
"settings": {
"number_of_shards": 1,
"analysis": {
"filter": {
"my_shingle_filter": {
"type": "shingle",
"min_shingle_size": 2,
"max_shingle_size": 2,
"output_unigrams": false
}
},
"analyzer": {
"my_shingle_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"my_shingle_filter"
]
}
}
}
}
}

默认Shingle的min/max值就是2。因此我们也能够不显式地指定它们。 output_unigrams被设置为false。用来避免将Unigram和Bigram索引到同样字段中。

让我们使用analyze API来測试该解析器:

GET /my_index/_analyze?analyzer=my_shingle_analyzer
Sue ate the alligator

不出所料,我们得到了3个词条:

  • sue ate
  • ate the
  • the alligator

如今我们就能够创建一个使用新解析器的字段了。

多字段(Multifields)

将Unigram和Bigram分开索引会更加清晰,因此我们将title字段创建成一个多字段(Multifield)(參见字符串排序和多字段(String
Sorting and Multifields)
):

PUT /my_index/_mapping/my_type
{
"my_type": {
"properties": {
"title": {
"type": "string",
"fields": {
"shingles": {
"type": "string",
"analyzer": "my_shingle_analyzer"
}
}
}
}
}
}

有了上述映射,JSON文档中的title字段会以Unigram(title字段)和Bigram(title.shingles字段)的方式索引,从而让我们能够独立地对这两个字段进行查询。

最后,我们能够索引演示样例文档:

POST /my_index/my_type/_bulk
{ "index": { "_id": 1 }}
{ "title": "Sue ate the alligator" }
{ "index": { "_id": 2 }}
{ "title": "The alligator ate Sue" }
{ "index": { "_id": 3 }}
{ "title": "Sue never goes anywhere without her alligator skin purse" }

搜索Shingles

为了理解加入的shingles字段的优点,让我们首先看看一个针对"The hungry alligator ate Sue"的简单match查询的返回结果:

GET /my_index/my_type/_search
{
"query": {
"match": {
"title": "the hungry alligator ate sue"
}
}
}

该查询会返回全部的3份文档。可是注意文档1和文档2拥有同样的相关度分值,由于它们含有同样的单词:

{
"hits": [
{
"_id": "1",
"_score": 0.44273707,
"_source": {
"title": "Sue ate the alligator"
}
},
{
"_id": "2",
"_score": 0.44273707,
"_source": {
"title": "The alligator ate Sue"
}
},
{
"_id": "3",
"_score": 0.046571054,
"_source": {
"title": "Sue never goes anywhere without her alligator skin purse"
}
}
]
}

如今让我们将shingles字段也加入到查询中。记住我们会将shingle字段作为信号 - 以添加相关度分值 - 我们仍然须要将基本的title字段包括到查询中:

GET /my_index/my_type/_search
{
"query": {
"bool": {
"must": {
"match": {
"title": "the hungry alligator ate sue"
}
},
"should": {
"match": {
"title.shingles": "the hungry alligator ate sue"
}
}
}
}
}

我们仍然匹配了3分文档。可是文档2如今排在了第一位,由于它匹配了Shingle词条"ate sue":

{
"hits": [
{
"_id": "2",
"_score": 0.4883322,
"_source": {
"title": "The alligator ate Sue"
}
},
{
"_id": "1",
"_score": 0.13422975,
"_source": {
"title": "Sue ate the alligator"
}
},
{
"_id": "3",
"_score": 0.014119488,
"_source": {
"title": "Sue never goes anywhere without her alligator skin purse"
}
}
]
}

即使在查询中包括了没有在不论什么文档中出现的单词hungry,我们仍然通过使用单词邻近度得到了最相关的文档。

性能

Shingle不仅比短语查询更灵活,它们的性能也更好。相比每次搜索须要为短语查询付出的代价,对Shingle的查询和简单match查询一样的高效。仅仅是在索引期间会付出一点小代价,由于很多其它的词条须要被索引,意味着使用了Shingle的字段也会占用很多其它的磁盘空间。可是,多数应用是写入一次读取多次的,因此在索引期间花费一点代价来让查询更迅速是有意义的。

这是一个你在ES中常常会碰到的主题:让你在搜索期间可以做非常多事情,而不须要不论什么预先的设置。

一旦更好地了解了你的需求,就行通过在索引期间正确地建模来得到更好的结果和更好的性能。

[Elasticsearch] 邻近匹配 (三) - 性能,关联单词查询以及Shingles的更多相关文章

  1. elasticsearch 关联单词查询以及Shingles

    Shingle Token Filter A token filter of type shingle that constructs shingles (token n-grams) from a ...

  2. [Elasticsearch] 邻近匹配 (一) - 短语匹配以及slop參数

    本文翻译自Elasticsearch官方指南的Proximity Matching一章. 邻近匹配(Proximity Matching) 使用了TF/IDF的标准全文搜索将文档,或者至少文档中的每一 ...

  3. [Elasticsearch] 部分匹配 (三) - 查询期间的即时搜索

    本章翻译自Elasticsearch官方指南的Partial Matching一章. 查询期间的即时搜索(Query-time Search-as-you-type) 如今让我们来看看前缀匹配可以怎样 ...

  4. [Elasticsearch] 邻近匹配 (二) - 多值字段,邻近程度与相关度

    多值字段(Multivalue Fields) 在多值字段上使用短语匹配会产生古怪的行为: PUT /my_index/groups/1 { "names": [ "Jo ...

  5. ElasticSearch查询 第三篇:词条查询

    <ElasticSearch查询>目录导航: ElasticSearch查询 第一篇:搜索API ElasticSearch查询 第二篇:文档更新 ElasticSearch查询 第三篇: ...

  6. 完爆Facebook&sol;GraphQL,APIJSON全方位对比解析&lpar;三&rpar;-表关联查询

    相关阅读: 完爆Facebook/GraphQL,APIJSON全方位对比解析(一)-基础功能 完爆Facebook/GraphQL,APIJSON全方位对比解析(二)-权限控制 自APIJSON发布 ...

  7. Elasticsearch 邻近查询示例

    Elasticsearch 邻近查询示例(全切分分词) JAVA API方式: SpanNearQueryBuilder span = QueryBuilders.spanNearQuery(); s ...

  8. MySQL 三种关联查询的方式&colon; ON vs USING vs 传统风格

    看看下面三个关联查询的 SQL 语句有何区别? 1SELECT * FROM film JOIN film_actor ON (film.film_id = film_actor.film_id) 2 ...

  9. Elasticsearch从0到千万级数据查询实践(非转载)

    1.es简介 1.1 起源 https://www.elastic.co/cn/what-is/elasticsearch,es的起源,是因为程序员Shay Banon在使用Apache Lucene ...

随机推荐

  1. javascrip中parentNode和offsetParent之间的区别

    首先是 parentNode 属性,这个属性好理解,就是在 DOM 层次结构定义的上下级关系,如果元素A包含元素B,那么元素B就可以通过 parentElement 属性来获取元素A. 要明白 off ...

  2. CentOS 下部署 ASP&period;NET Core环境

    一.安装dotnet 1.下载运行环境 https://www.microsoft.com/net/download/linux 下载Runtime:https://go.microsoft.com/ ...

  3. 除非 Windows Activation Service &lpar;WAS&rpar;和万维网发布服务&lpar;W3SVC&rpar;均处于运行状态,否则无法启动网站。目前,这两项服务均处于停止状态。

    win7 IIS 所有网站都停止了,启动提示: 除非 Windows Activation Service (WAS)和万维网发布服务(W3SVC)均处于运行状态,否则无法启动网站.目前,这两项服务均 ...

  4. 微信jssdk,实现多图上传的一点心得

    一.首先在common.js里封装一个函数,在需要调用jsSDK的页面引用此方法即可实现微信的信息配置function signatureJSSDK() { var url = window.loca ...

  5. ecshop 设置管理员

    <?php define('IN_ECS', true); require(dirname(__FILE__) . '/includes/init.php'); $admin_name=trim ...

  6. oracle存储过程写法。

    create or replace procedure testwzm(v_gdjdm in varchar2) isv_id varchar2(10);v_xlname varchar2(100); ...

  7. Spring的工作原理核心组件和应用

    Spring框架 Spring 是管理多个java类的容器框架,注意是类不管理接口. Spring 的主要功能 Ioc 反转控制和 DI 依赖注入. 注入的方式可以是构造函数赋值也可以是 set方法赋 ...

  8. JPA的Embeddable注解

    来源于http://zjsword2000.blog.163.com/blog/static/4583983320083184844734/ 在hibernate中实现自定义类型,只要实现UserTy ...

  9. HoloLens开发手记 - 构建2D应用 Building 2D apps

    HoloLens可以让我们在真实世界中看到全息图像内容.但是它本质上还是一台Windows 10设备,这意味着HoloLens可以以2D应用形式运行Windows Store里的大部分UWP应用. 目 ...

  10. 【初码干货】关于&period;NET玩爬虫这些事

    这几天在微信群里又聊到.NET可以救中国但是案例太少不深的问题,我说.NET玩爬虫简直就是宇宙第一,于是大神朱永光说,你为何不来写一篇总结一下? 那么今天就全面的来总结一下,在.NET生态下,如何玩爬 ...