在 Elasticsearch 中实现自动完成功能 2:n-gram

在第一部分中,我们讨论了使用前缀查询,这是一种自动完成的查询时间方法。 在这篇文章中,我们将讨论 n-gram - 一种索引时间方法,它在基本标记化后生成额外的分词,以便我们稍后在查询时能够获得更快的前缀匹配。 但在此之前,让我们先看看什么是 n-gram。 根据维基百科 -

n-gram 是给定文本或语音序列中 n 个项目的连续序列

有关 n-gram 的更多详细的介绍,请参阅之前的文章 “Elasticsearch: Ngrams, edge ngrams, and shingles”。

是的,就是这么简单,只是一系列文本。 这里的 “n” 项在字符级 n-gram 的情况下表示 “n” 个字符,在单词级 n-gram 的情况下表示 “n” 个单词。 词级 n-gram 也称为 shingles。 此外,根据 “n” 的值,这些被分类为 uni-gram(n=1)、bi-gram(n=2)、tri-gram(n=3)等。

下面的例子会更清楚:

Character n-grams for input string = "harry":n = 1 : ["h", "a", "r", "r", "y"]n = 2 : ["ha", "ar", "rr", "ry"]n = 3 : ["har", "arr", "rry"]Word n-grams for input string = "harry potter and the goblet of fire":n = 1 : ["harry", "potter", "and", "the", "goblet", "of", "fire"]n = 2 : ["harry potter", "potter and", "and the", "the goblet","goblet of", "of fire"]n = 3 : ["harry potter and", "potter and the", "and the goblet","the goblet of", "goblet of fire"]

在这篇文章中,我们将讨论两种基于 n-gram 的方法 - 首先使用 edge n-gram 分词器,然后使用内置的 search-as-you-type 类型,该类型也在内部使用 n-gram 分词器。 这些额外的分词在索引文档时被输出到倒排索引中,从而最大限度地减少搜索时间延迟。 在这里,Elasticsearch 只需将输入与这些分词进行比较,这与前缀查询方法不同,它需要检查单个分词是否以给定输入开头。

Edge-n-gram 分词器

正如我们已经看到的,文本字段被分析并存储在倒排索引中。 分词是这个三步分析过程中的第二步,在过滤字符之后但在应用分词过滤器之前运行。 Edge-n-gram 分词器是 Elasticsearch 中可用的内置分词器之一。 它首先将给定文本分解为分词,然后为每个分词生成字符级 n-grams。

让我们为电影创建一个索引,这次使用 edge-n-gram 分词器:

PUT /movies
{"settings": {"analysis": {"analyzer": {"custom_edge_ngram_analyzer": {"type": "custom","tokenizer": "customized_edge_tokenizer","filter": ["lowercase"]}},"tokenizer": {"customized_edge_tokenizer": {"type": "edge_ngram","min_gram": 2,"max_gram": 10,"token_chars": ["letter","digit"]}}}},"mappings": {"properties": {"title": {"type": "text","analyzer": "custom_edge_ngram_analyzer"}}}
}

在前缀查询示例中,我们没有将分析器参数传递给映射中的任何字段,而是依赖于默认的标准分析器。 上面,我们首先创建了一个自定义分析器custom_edge_ngram_analyzer,并传递给它类型为 edge_ngram 的自定义分词器 customized_edge_tokenizer。 Edge_ngram 分词器可以使用以下参数进行定制:

  • min_gram ⇒ 放入 gram 中的最小字符数,默认为 1,类似于上面看到的 uni-gram 示例
  • max_gram ⇒ 放入 gram 中的最大字符数,默认为 2,类似于上面看到的 bi-gram 示例
  • token_chars ⇒ 要保留在 token 中的字符,如果 Elasticsearch 遇到任何不属于提供的列表的字符,它将使用该字符作为新 token 的断点。 支持的字符类包括字母、数字、标点符号、符号和空格。 在上面的映射中,我们保留了字母和数字作为 token 的一部分。 如果我们将输入字符串传递为“harry potter: Deathly Hallows”,Elasticsearch 将通过打破空格和标点符号来生成 ["harry", "potter", "deathly", "hallows"]。

让我们使用 _analyze API 来测试我们的自定义边 n-gram 分析器的行为:

GET /movies/_analyze
{"field": "title","text": "Harry Potter and the Order of the Phoenix"
}

上面命令返回的结果为:

[ha, har, harr, harry, po, pot, pott, potte, potter, an,and, th, the, or, ord, orde, order, of, th, the, ph, pho,phoe, phoen, phoeni, phoenix]

为了保持简洁,我没有包含实际响应,其中包含一组对象,每 gram 一个对象,包含有关该 gram 的元数据。 无论如何,正如可以观察到的,我们的自定义分析器按设计工作 - 为传递的字符串发出 gram,小写且长度在最小 - 最大设置内。 让我们索引一些电影来测试自动完成功能 -

POST /movies/_doc
{"title": "Harry Potter and the Half-Blood Prince"
}POST /movies/_doc
{"title": "Harry Potter and the Deathly Hallows – Part 1"
}

Edge-n-grammed 字段也支持中缀匹配。 即,你也可以通过传递 “har” 和 “dead” 来匹配标题为 “harry potter and the deathly hallows” 的文档。 这使得它适合自动完成实现,其中输入文本中的单词没有固定的顺序。

GET /movies/_search?filter_path=**.hits
{"query": {"match": {"title": {"query": "deathly "}}}
}

上面命令返回结果:

{"hits": {"hits": [{"_index": "movies","_id": "fb-HHIsByaLf0EuT7s0I","_score": 4.0647593,"_source": {"title": "Harry Potter and the Deathly Hallows – Part 1"}}]}
}
GET /movies/_search?filter_path=**.hits
{"query": {"match": {"title": {"query": "harry pot"}}}
}

上面的命令返回:

{"hits": {"hits": [{"_index": "movies","_id": "fL-HHIsByaLf0EuT5M2i","_score": 1.1879652,"_source": {"title": "Harry Potter and the Half-Blood Prince"}},{"_index": "movies","_id": "fb-HHIsByaLf0EuT7s0I","_score": 1.1377401,"_source": {"title": "Harry Potter and the Deathly Hallows – Part 1"}}]}
}
GET /movies/_search?filter_path=**.hits
{"query": {"match": {"title": {"query": "potter har"}}}
}
{"hits": {"hits": [{"_index": "movies","_id": "fL-HHIsByaLf0EuT5M2i","_score": 1.3746086,"_source": {"title": "Harry Potter and the Half-Blood Prince"}},{"_index": "movies","_id": "fb-HHIsByaLf0EuT7s0I","_score": 1.3159354,"_source": {"title": "Harry Potter and the Deathly Hallows – Part 1"}}]}
}

默认情况下,对分析字段(上例中的 title)的搜索查询也会对搜索词运行分析器。 如果你将搜索词指定为“deathly potter”,希望它只匹配第二个文档,你会感到惊讶,因为它匹配两个文档。 这是因为搜索词 “deathly potter” 也将被分词,将 “deathly” 和 “potter” 输出为单独的分词。 尽管 “Harry Potter and the Deathly Hallows – Part 1” 与最高分相匹配,但输入查询分词是单独匹配的,从而为我们提供了两个文档作为结果。 如果你认为这可能会导致问题,你也可以为搜索查询指定分析器。

因此,edge-n-gram 通过在倒排索引中保存额外的分词来克服前缀查询的限制,从而最大限度地减少查询时间延迟。 但是,这些额外的分词确实会占用节点上的额外空间,并可能导致性能下降。 我们在选择 n-gram 字段时应该小心,因为某些字段的值可能具有无限大小,并且可能会导致索引膨胀。

Search_as_you_type

Search_as_you 类型数据类型是在 Elasticsearch 7.2 中引入的,旨在为自动完成功能提供开箱即用的支持。 与 edge-n-gram 方法一样,这也通过生成额外的分词来优化自动完成查询来完成索引时的大部分工作。 当特定字段映射为 search_as_you_type 类型时,会在内部为其创建其他子字段。 让我们将标题字段类型更改为 search_as_you_type:

DELETE moviesPUT /movies
{"mappings": {"properties": {"title": {"type": "search_as_you_type","max_shingle_size": 3}}}
}

对于上述索引中的标题属性,将创建三个子字段。 这些子字段使用 shingle token 过滤器。 Shingles 只不过是一组连续的单词(单词 n-gram,如上所示)。

  • 根 title 字段 ⇒ 使用映射中提供的分析器进行分析,如果未提供,则使用默认值
  • title._2gram ⇒ 这会将标题分成各有两个单词的部分,即大小为 2 的 shingles。
  • title._3gram ⇒ 这会将标题分成每个包含三个单词的部分。
  • title._index_prefix ⇒ 这将对 title._3gram 下生成的分词执行进一步的 edge ngram 分词。

我们可以使用我们最喜欢的 _analyze API 来测试它的行为:

GET movies/_analyze
{"text": "Harry Potter and the Goblet of Fire","field": "title"
}

上面返回:

harry, potter, and, the, goblet, of, fire
GET movies/_analyze
{"text": "Harry Potter and the Goblet of Fire","field": "title._2gram"
}

上面的命令返回:

harry potter, potter and, and the, the goblet, goblet of, of fire
GET movies/_analyze
{"text": "Harry Potter and the Goblet of Fire","field": "title._3gram"
}

上面的命令返回:

harry potter and,potter and the,and the goblet,the goblet of,goblet of fire
GET movies/_analyze
{"text": "Harry Potter and the Goblet of Fire","field": "title._index_prefix"
}

上面的命令返回:

h, ha, har, harr, harry, harry[空隔], harry p, harry po, harry pot, harry pott, 
harry potte, harry potter, harry potter[空隔], harry potter a, harry potter an,
harry potter and,
p, po, pot, pott, potte, potter, potter[空隔], potter a, potter an, potter and,
potter and[空隔], potter and t, potter and th, potter and the,
a, an, and, and[空隔], and t, and th, and the, and the[空隔], and the g, and the go,
and the gob, and the gobl, and the goble, and the goblet,
t, th, the, the[空隔], the g, the go, the gob, the gobl, the goble, the goblet,
the goblet[空隔], the goblet o, the goblet of,
g, go, gob, gobl, goble, goblet, goblet o, goblet of, goblet of[空隔], goblet of f,
goblet of fi, goblet of fir, goblet of fire,
o, of, of[空隔], of f, of fi, of fir, of fire, of fire[空隔],
f, fi, fir, fire, fire[空隔], fire[空隔][空隔] 

要创建多少个子字段由 max_shingle_size 参数决定,默认为 3,可以设置为 2、3 或 4。Search_as_you_type 是一个类似文本的字段,因此我们为文本字段使用其他选项(例如分析器、 还支持索引、存储、search_analyzer)。

你现在一定已经猜到了,它支持前缀和中缀匹配。 在查询时,我们需要使用 multi_match 查询,因为我们也需要定位其子字段:

POST movies/_doc
{"title": "Harry Potter and the Goblet of Fire"
}
GET /movies/_search?filter_path=**.hits
{"query": {"multi_match": {"query": "the goblet","type": "bool_prefix","analyzer": "keyword","fields": ["title","title._2gram","title._3gram"]}}
}

上面的查询返回:

{"hits": {"hits": [{"_index": "movies","_id": "fr-sHIsByaLf0EuThM2C","_score": 3,"_source": {"title": "Harry Potter and the Goblet of Fire"}}]}
}

我们在这里将查询类型设置为 bool_prefix。 查询将匹配具有任何顺序的 title 的文档,但具有与查询中的文本匹配的顺序的文档将排名更高。 在上面的示例中,我们将 “the goblet” 作为查询文本传递,因此标题为 “the goblet of fire” 的文档的排名将高于标题为 “fire goblet” 的文档。

另外,我们将查询分析器指定为关键字,这样我们的查询文本 “the goblet” 就不会被分析,而是按原样进行匹配。 如果没有这个,标题为 “Harry Potter and the Goblet of Fire” 的文档以及标题为 “Harry Potter and the Deathly Hallows – Part 1” 的文档也会匹配。

这不是查询 search_as_you_type 字段的唯一方法,但肯定更适合我们的自动完成用例。

与 edge-n-gram 一样,search_as_you_type 通过存储针对自动完成进行优化的数据来克服前缀查询方法的限制。 因此,在这种方法中,我们也必须小心使用该字段存储的内容。 需要额外的空间来存储这些 n-gram 分词。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/107441.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL InnoDB引擎深入学习的一天(InnoDB架构 + 事务底层原理 + MVCC)

目录 逻辑存储引擎 架构 概述 内存架构 Buffer Pool Change Buffe Adaptive Hash Index Log Buffer 磁盘结构 System Tablespace File-Per-Table Tablespaces General Tablespaces Undo Tablespaces Temporary Tablespaces Doublewrite Buffer Files Redo Log 后台线程 事务原…

30天工作量,推荐4个ai写作生成器工具,一键搞定!

全新升级!畅销热门AI写作工具盘点40强,助你一键呈现顶尖文案! AI写作工具,引领时代潮流,让办公生活更高效!小编特意整理了市面上最好用的AI写作工具,共计40款!你使用过哪些&#xff…

【JAVA-Day45】Java常用类StringBuffer解析

Java常用类StringBuffer解析 Java常用类StringBuffer解析一、什么是StringBuffer类二、StringBuffer类的方法2.1 append方法2.2 insert方法2.3 delete方法2.4 replace方法2.5 reverse方法2.6 toString方法2.7 capacity方法2.8 length方法 三、StringBuffer类的应用场景深入了解…

选择适合自身业务的HTTP代理有哪些因素决定?

相信对很多爬虫工作者和数据采集的企业来说,如何选购适合自己业务的HTTP代理是一个特别特别困扰的选题,市面上那么多HTTP代理厂商,好像这家有这些缺点,转头又看到另外一家的缺点,要找一家心仪的仿佛大海捞针。今天我们…

【音视频|ALSA】SS528开发板编译Linux内核ALSA驱动、移植alsa-lib、采集与播放usb耳机声音

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C、数据结构、音视频🍭 🤣本文内容🤣&a…

二、深度测试(Z Test)

1.是什么 ①从渲染管线出发 ②书面上理解 所谓深度测试,就是针对当前对象在屏幕上(更准确的说是frame buffer)对应的像素点,讲对象自身的深度值与当前该像素点缓存的深度值进行比较,如果通过了,本对象再改…

使用 Github Actions 工作流自动部署 Github Pages

GitHub-Actions actions顾名思义就是一堆动作,是一个持续集成服务,持续集成包含了拉代码、运行测试、编译代码、登录远程服务器,发布到第三方服务等等的操作,GitHub将这些操作称为actions。 概念:Workflows, Events,…

【Java学习之道】网络编程的基本概念

引言 这一章我们将一同进入网络编程的世界。在开始学习网络编程之前,我们需要先了解一些基本概念。那么,我们就从“什么是网络编程”这个问题开始吧。 一、网络编程的基本概念 1.1 什么是网络编程 网络编程,顾名思义,就是利用…

【算法-动态规划】完全背包问题

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…

使用vue3+element-ui plus 快速构建后台管理模板

一、安装 vue3 脚手架 npm create vuelatestcd vue-ui-template #切换到刚刚创建好的vue项目根目录中 npm install #下载项目所需要的依赖包 npm run dev #启动运行项目服务项目启动后,默认页面显示如下: 二、安装element-ui plus 官网链接:…

Vue-3.0路由

生活中的路由:设备和ip的映射关系 路由就是一种映射关系 Vue中路由:路径和组件的映射关系,根据路由就能知道不同路径的,应该匹配渲染哪个组件 VueRouter的介绍 作用:修改地址栏路径时,切换显示匹配的组…

idea中父工程Project创建

1.file-->new-->Project 2.选择maven包和JavaSDK 3.填写项目名,选择文件目录,项目包等 4.配置maven tip:约定>配置>编码 5.设置项目编码 6.注解生效激活,便于项目中使用注解 7.Java编译版本选择8 8.File Type 过滤&a…

php74 安装sodium

下载编译安装libsodium wget https://download.libsodium.org/libsodium/releases/libsodium-1.0.18-stable.tar.gz tar -zxf libsodium-1.0.18-stable.tar.gz cd libsodium-stable ./configure --without-libsodium make && make check sudo make install下载编译安装…

ChatGPT DALL-E 3的系统提示词大全

每当给出图像的描述时,使用dalle来创建图像,然后用纯文本总结用于生成图像的提示。如果用户没有要求创建特定数量的图像,默认创建四个标题,这些标题应尽可能多样化。发送给Dalle的所有标题都必须遵循以下策略:1.如果描…

GitLab 502问题解决方案

由于最近 gitlab 切换到另一台服务器上部署的 gitlab 后,经常出现 502。平时重启 gitlab 后都能解决,今天突然重启多次后都还是 502(重启日志是正常的),遂通过 gitlab-ctl tail 查看日志进行排查。 gitlab-ctl tail通…

到底什么是5G-R?

近日,工信部向中国国家铁路集团有限公司(以下简称“国铁集团”)批复5G-R试验频率的消息,引起了行业内的广泛关注。 究竟什么是5G-R?为什么工信部会在此时批复5G-R的试验频率? 今天,小枣君就通过…

让ChatGPT等模型学会自主思考!开创性技术“自主认知”框架

ChatGPT、百度文心一言、Bard等大语言模型展现出了超强的创造能力,加速了生成式AI的应用进程。但AI模型只能基于训练数据执行各种任务,无法像人类一样利用生活知识、过往经验用于复杂的推理和决策。 例如,在玩游戏时,人类可以利用…

Python 控制语句

目录 1 选择结构1.1 单分支选择结构1.2 条件表达式详解1.3 条件表达式中,不能有赋值操作符“”1.4 双分支选择结构1.5 三元条件运算符1.6 多分支选择结构1.7 选择结构嵌套 2 循环结构2.1 while循环2.2 for循环和可迭代对象遍历2.3 可迭代对象2.4 range对象2.5 嵌套循…

2023年淘宝天猫双11红包领取活时间什么时候开始领天猫淘宝双十一红包优惠券?

2023年淘宝天猫双11红包领取活动开始与结束时间 2023年10月24日20:00开始领取至11月11日23:59结束; 2023年淘宝天猫双11红包活动使用开始与结束时间 第一波:2023年10月31日20:00开始使用至11月3日23:59 第二波:2023年11月10日20:00开始使用…

【Linux】使用timer_create()创建定时器发送信号并使用sigaction()处理信号

0x00 前言 最后更新时间&#xff1a;2023-10-16 0x01 主要函数及结构体介绍 1.sigaction函数 #include <signal.h> int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);功能&#xff1a; 用于改变进程接收到特定信号后的行为。 参数…