在 Elasticsearch 中实现自动完成功能 3:completion suggester

在这篇博文中,我们将讨论 complete suggester - 一种针对自动完成功能进行优化的 suggester,并且被认为比我们迄今为止讨论的方法更快。

Completion suggester 使用称为有限状态转换器的数据结构,该结构类似于 Trie 数据结构,并且针对更快的查找进行了优化。 这些数据结构存储在节点的内存中,以实现更快的搜索。 与 edge-n-gram 和 search_as_you_type 一样,这也通过使用我们提供的输入更新内存中的 FST 来完成索引时的大部分工作。

Elasticsearch 类型的一种特殊类型 —— complete,用于实现它:

PUT /movies
{"mappings": {"properties": {"title": {"type": "completion"}}}
}

映射还支持完成字段的 analyzer、search analyzer、max_input_length 参数。 分析器值默认为 simple analyzer,它将输入小写并对任何非字母字符,例如数字、空格、连字符等进行分词。完成类型上的分析器的行为与其他文本字段上的分析器不同。 经过分析后,分词不能单独使用 - 它们根据输入文本中的顺序放在一起并插入到 FST 中。 此外,我们无法在此方法中使用 _analyze 端点来测试我们的映射。 如果我们尝试这样做,Elasticsearch 会抛出一个错误,指出 “Can't process field [title], Analysis requests are only supported on tokenized fields”。

在索引文档时,我们指定输入和可选的权重参数 -

POST /movies/_doc/1001
{"title": [{"input": "Harry Potter and the Goblet of Fire","weight": 5},{"input": "Goblet of Fire","weight": 10}]
}POST /movies/_doc/1002
{"title": {"input": ["Harry Potter and the Goblet of Fire","Goblet of Fire"],"weight": 2}
}

我们可以使用 input 参数为单个文档指定多个匹配项。 weight 参数控制搜索结果中文档的排名。 它可以针对每个 input 进行指定,如上面的第一个文档(1001)中所示,或者可以对所有 input 保持相同,如第二个文档(1002)中所示。

使用 _search 端点的请求正文中的 suggest 子句查询建议字段。 在 ES 5.0 版本之前,有一个单独的端点 - _suggest 用于 suggester。 互联网上的许多示例都使用 _suggest。 从版本 5 开始,_search 端点本身也已更新以支持 suggester。

默认情况下,Elasticsearch 返回整个匹配文档。 如果我们只对建议文本感兴趣,我们可以使用 _source 选项并将其设置为“suggest”。 通过这种方式,我们可以最大限度地减少磁盘获取和传输开销:

GET /movies/_search?filter_path=**.harry_suggest
{"_source": "title.input","suggest": {"harry_suggest": {"prefix": "goblet of f","completion": {"field": "title"}}}
}

上面命令返回的结果为:

{"suggest": {"harry_suggest": [{"text": "goblet of f","offset": 0,"length": 11,"options": [{"text": "Goblet of Fire","_index": "movies","_id": "1001","_score": 10,"_source": {"title": [{"input": "Harry Potter and the Goblet of Fire"},{"input": "Goblet of Fire"}]}},{"text": "Goblet of Fire","_index": "movies","_id": "1002","_score": 2,"_source": {"title": {"input": ["Harry Potter and the Goblet of Fire","Goblet of Fire"]}}}]}]}
}

"Goblet of Fire" 在建议中返回了两次,因为我们已在两个文档中提供此文本作为输入。 这可以通过使用 skip_duplicates 选项来避免。

GET /movies/_search?filter_path=**.harry_suggest
{"_source": "title.input","suggest": {"harry_suggest": {"prefix": "goblet of f","completion": {"field": "title","skip_duplicates": true}}}
}
{"suggest": {"harry_suggest": [{"text": "goblet of f","offset": 0,"length": 11,"options": [{"text": "Goblet of Fire","_index": "movies","_id": "1001","_score": 10,"_source": {"title": [{"input": "Harry Potter and the Goblet of Fire"},{"input": "Goblet of Fire"}]}}]}]}
}

警告:当设置为 true 时,此选项会减慢搜索速度,因为需要访问更多建议才能找到前 N 个。

在 completion suggester 的情况下,Elasticsearch 从第一个字符开始一次匹配文档一个字符,在输入新字符时向前移动一个位置。如上所述,它保留 FST 中的输入顺序。 因此,它无法像基于 n-gram 的方法那样在输入中间进行匹配。 即,如果你有一部名为 “Harry Potter and the Goblet of Fire” 的电影,并且你输入 “goblet of fire”,它不会将文档作为匹配项返回。 但是,你可以使用输入选项来提供多个匹配项。 你可以手动对输入字符串进行分词,并将分词传递到输入选项中的 Elasticsearch,就像我们在上面的示例中通过提供 “Goblet of Fire” 作为附加输入所做的那样。

Completion suggester 支持 fuzzy queries,使我们能够在搜索文档时考虑拼写错误。 你还可以指定前缀文本作为正则表达式查询。 下面示例中的两个查询都返回 “Goblet of Fire” 作为建议 -

GET /movies/_search?filter_path=**.harry_suggest
{"_source": "title.input","suggest": {"harry_suggest": {"prefix": "gobet of f","completion": {"field": "title","fuzzy": {"fuzziness": 2}}}}
}
GET /movies/_search?filter_path=**.harry_suggest
{"_source": "title.input","suggest": {"harry_suggest": {"regex": "g[aieou]b","completion": {"field": "title"}}}
}

添加上下文到搜索

与其他查询不同,completion suggesters 不支持在查询中添加过滤器。 即,你无法根据文档中其他字段的值过滤掉建议。 假设我们有一个存储电影的索引,并且我们正在开发基于标题字段的自动完成功能。 假设我们已将 title 映射为完成类型,还有其他字段,如 genres、ratings、production companies 等。有一个 title 为 “Goblet of Fire” 的文档,其 genre 为 “action”。 现在,如果我们尝试根据  genre = "romance" 过滤掉自动完成建议,我们预计它不应该返回 “Goblet of Fire”:

POST /movies/_doc/1001
{"genre": "action","title": [{"input": "Harry Potter and the Goblet of Fire","weight": 5},{"input": "Goblet of Fire","weight": 10}]
}POST /movies/_doc/1002
{"genre": "fiction","title": {"input": ["Harry Potter and the Goblet of Fire","Goblet of Fire"],"weight": 2}
}GET /movies/_search
{"query": {"bool": {"filter": [{"term": {"genre": "romance"}}]}},"suggest": {"harry_suggest": {"prefix": "goblet","completion": {"field": "title"}}}
}

上述搜索将返回和之前一样的结果。仿佛那个过滤器根本就不存在。这并不像我们预期的那样工作 - 它返回 “Goblet of Fire” 作为建议,即使它属于 “action” 类型。 这种限制背后的主要原因是它的设计。 正如已经讨论过的,建议存储在单独的数据结构中 - 内存中 FST,而其他字段存储在磁盘上。 这种设计有助于通过内存中 FST 进行更快的搜索。 像上面这样的查询违背了这种设计。

然而,Elasticsearch 确实提供了上下文建议在一定程度上规避了这个问题。 要使用上下文建议器,我们必须在为索引创建映射时提供上下文:

DELETE moviesPUT /movies
{"mappings": {"properties": {"title": {"type": "completion","contexts": [{"name": "genre","type": "category"}]}}}
}

对于特定的 completion 字段,我们可以定义多个具有唯一名称的上下文。 支持两种类型的上下文:

  • Category => 你正在索引的事物的类别,例如 电影/歌曲的类型(genre)
  • Geo => 你正在索引的文档的地理点,允许根据经纬度过滤建议。

上述每种上下文类型都支持一些高级参数,例如精度 (precision)、地理上下文的邻居 (neighbours)、查询时的增强 (boost),以便具有特定类别的文档获得更高的分数。 请注意,对于启用上下文的完成字段,在索引文档以及查询文档时,上下文参数是必需的。

让我们在上面创建的索引中索引一些文档:

POST /movies/_doc/2001
{"title": {"input": "Harry Potter and the Chamber of Secrets","contexts": {"genre": "mystery"}}
}POST /movies/_doc/2002
{"title": {"input": "Harry Potter and the Prisoner of Azkaban","contexts": {"genre": "crime"}}
}

上面,我们将 "Harry Potter and the Prisoner of Azkaban" 索引为 “crime” 类型的电影,将 "Harry Potter and the Chamber of Secrets" 索引为 “mystery” 类型的电影。 让我们尝试获取前缀 “harry” 的建议:

GET /movies/_search?filter_path=**.harry_suggest
{"_source": "title.input","suggest": {"harry_suggest": {"prefix": "harry","completion": {"field": "title","contexts": {"genre": "crime"}}}}
}

上面查询的结果为:

{"suggest": {"harry_suggest": [{"text": "harry","offset": 0,"length": 5,"options": [{"text": "Harry Potter and the Prisoner of Azkaban","_index": "movies","_id": "2002","_score": 1,"_source": {"title": {"input": "Harry Potter and the Prisoner of Azkaban"}},"contexts": {"genre": ["crime"]}}]}]}
}

从上面的响应中可以看出,即使传递的前缀与上面索引的两个文档都匹配,也仅返回 “crime” 类型的 “Harry Potter and the Prisoner of Azkaban”。

这就是我们在 Elasticsearch 中实现自动完成的第三种方法。 那么,completion suggester 与迄今为止看到的其他方法相比如何? 它绝对是最快的,因为要搜索的数据在内存中可用,但是如果我们决定使用它实现自动完成,我们需要记住一些事情:

  • 必须注意索引的大小,因为建议存储在内存中。
  • 中缀 (infix) 匹配,例如 不支持按中间名匹配。
  • 不支持对文档中其他字段的建议进行高级过滤。

总结一下,我们可以说在选择在 Elasticsearch 中实现自动完成功能的方法时应考虑以下因素:

  • 数据是否已建立索引? 以什么格式? 我们可以重新索引它以使其更适合自动完成功能吗? 如果数据已经被索引为文本字段并且我们无法重新索引它,我们将需要采用查询时间方法 - 即前缀查询 (prefix queries) !
  • 该字段可以通过哪些方式查询? 以多种方式存储它有意义吗?
  • 是否需要支持中缀 (infix) 匹配? 文本中的单词顺序是固定的吗? 用户是否熟悉该顺序? complete suggesters 不支持中缀匹配,并且不适合具有众所周知的顺序的字段。
  • 将作为值提供给我们的字段的文本的最大大小是多少? 如果保存在内存中会产生问题吗? completion suggesters 将数据保存在内存中,基于 n-gram 的方法在基本分词化后创建附加分词以实现更快的匹配。
  • 我们需要为这个字段建立一个单独的索引吗? 如果这里提到的所有三种方法都不能满足你的要求,那么你将需要创建另一个索引。 在该索引中,只有 auto-complete 功能所需的字段才会存储为唯一文档,而不是与同一索引中的其他数据一起保存。 这将最大限度地减少节点膨胀的可能性,并且还可以提供更快的建议。 但是,是的,它毕竟是一个单独的索引,你必须保持主索引和新索引之间的数据同步。 管理另一个索引也有开销。

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

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

相关文章

Net6 用imagesharp 实现跨平台图片处理并存入oss

项目要求:生成电子证书 一、模板文件在OSS中,直接加载 二、向模板文件添加二维码 三、向模板文件添加多行文字 四、生成二维码,存入本地, 五、向模板文件添加二维码 代码实现步骤 一、建立.net 6 API项目,安装N…

启航kp OpenHarmony环境搭建

前提 启航kp OpenHarmony环境搭建 搭建好OpenHarmony环境 未搭建好可以参考OpenHarmony docker环境搭建 安装vscode 下载好启航kp所需的开发包和样例 下载地址 搭建过程 进入正确文件夹 首先要进入 /home/openharmony 目录下,如果没有打开在vsc左上角找到文…

JUC并发编程——JUC并发编程概述及Lock锁(重点)(基于狂神说的学习笔记)

基于bilibili狂神说JUC并发编程视频所做笔记 概述 什么是JUC JUC时java.util工具包中的三个包的简称 java.util.concurrent java.util.concurrent.atomic java.util.concurrent.locks 业务:普通的线程代码中,我们常使用Runnable接口 但Runnable没有返…

百度开放平台第三方代小程序开发,授权事件、消息与事件通知总结

大家好,我是小悟 关于百度开放平台第三方代小程序开发的两个事件接收推送通知,是开放平台代小程序实现业务的重要功能。 授权事件推送和消息与事件推送类型都以event的值判断。 授权事件推送通知 授权事件推送包括:推送票据、授权成功、取…

【使用教程】在Ubuntu下PMM60系列一体化伺服电机通过SDO跑循环同步位置模式详解

本教程将指导您在Ubuntu操作系统下使用SDO(Service Data Object)来配置和控制PMM60系列一体化伺服电机以实现循环同步位置模式。我们将介绍必要的步骤和命令,以确保您能够成功地配置和控制PMM系列一体化伺服电机。 01.准备工作 在正式介绍之…

一种更具破坏力的DDoS放大攻击新模式

近日,内容分发网络(CDN)运营商Akamai表示,一种使网站快速瘫痪的DDoS放大攻击新方法正在被不法分子所利用。这种方法是通过控制数量巨大的中间设备(middlebox,主要是指配置不当的服务器)&#xf…

【VR】【Unity】白马VR课堂系列-VR开发核心基础03-项目准备-VR项目设置

【内容】 详细说明 在设置Camera Rig前,我们需要针对VR游戏做一些特别的Project设置。 点击Edit菜单,Project Settings,选中最下方的XR Plugin Management,在右边面板点击Install。 安装完成后,我们需要选中相应安卓平台下的Pico VR套件,关于怎么安装PICO VR插件,请参…

PyCharm运行Nosetests并导出测试报告

1. Pycharm运行Nosetests PyCharm可以使用两种方法,运行Nosetests测试文件: 1) 图形用户界面GUI a) 在PyCharm中,选中测试文件,如Tests/test_demo.py b) 鼠标右键选择Run Nosetests in test_demo.py即可执行测试 注1&#xff…

极简c++(4)类的静态成员

静态数据成员 ::是作用域操作符&#xff01; #include<iostream> using namespace std;class Point{private:int x,y;public:point(int x 0,int y 0):x(x),y(y){}~point();int getX(){return x;}int getY(){return x;} }假设需要统计点的个数&#xff0c;考虑添加一个…

【mfc/VS2022】计图实验:绘图工具设计知识笔记

绘制曲线&#xff08;贝塞尔曲线&#xff09;&#xff1a; 转自&#xff1a;CDC 类 | Microsoft Learn 绘制一条或多条贝塞尔曲线。 BOOL PolyBezier(const POINT* lpPoints,int nCount);参数 lpPoints 指向包含曲线端点和控制点的 POINT 数据结构数组。 nCount 指定 lpPo…

使用kaliber与imu_utils进行IMU、相机+IMU联合标定

目录 1 标定工具编译 1.1 IMU标定工具 imu_utils 1.2 相机标定工具 kaliber 2 标定数据录制 3 开始标定 3.1 IMU标定 3.2 相机标定 3.3 相机IMU联合标定 4 将参数填入ORBSLAM的文件中 1 标定工具编译 1.1 IMU标定工具 imu_utils 标定IMU我们使用imu_utils软件进行标定…

如何使用前端包管理器(如npm、Yarn)?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

Linux CentOS8安装gitlab_ce步骤

1 下载安装包 wget --content-disposition https://packages.gitlab.com/gitlab/gitlab-ce/packages/el/8/gitlab-ce-15.0.2-ce.0.el8.x86_64.rpm/download.rpm2 安装gitlab yum install policycoreutils-python-utilsrpm -Uvh gitlab-ce-15.0.2-ce.0.el8.x86_64.rpm3 更新配…

Stm32_标准库_12_串口_发送数据

波特率&#xff1a;约定的传输速率&#xff0c;1000bps,1s发1000位 引脚 结构 数据帧的传输特点 代码&#xff1a; #include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h"GPIO_InitTypeDef GPIO_InitStruct; USART…

2023年建筑电工(建筑特殊工种)证考试题库及建筑电工(建筑特殊工种)试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年建筑电工(建筑特殊工种)证考试题库及建筑电工(建筑特殊工种)试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#xff09;特种作业人员操作证考试大纲和&#xff08;质检局&#xff09;特种设备作业人…

【算法-动态规划】两个字符串的删除操作-力扣 583

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

Idea创建springboot工程的时候,发现pom文件没有带<parent>标签

今天创建springboot工程&#xff0c;加载maven的时候报错&#xff1a; 这个问题以前遇到过&#xff0c;这是因为 mysql-connector-j 没有带版本号的原因&#xff0c;但是springboot的依赖的版本号不是都统一交给spring-boot-starter-parent管理了吗&#xff0c;为什么还会报错&…

Redis 集群 Redis 事务 Redis 流水线 Redis 发布订阅 Redis Lua脚本操作

Redis 集群 & Redis 事务 & Redis 流水线 & Redis 发布订阅 Redis 集群linux安装redis主从配置查看当前实例主从信息 Redis Sentinelsentinel Redis Cluster Redis 事务Redis 流水线Redis 发布订阅Redis Lua脚本操作 Redis 集群 linux安装redis 下载安装包&#…

五子棋(C语言实现)

目录 构思 1、主程序 2、初始化 3、游戏菜单 4、打印棋盘 6、玩家下棋 7、判断输赢 8、功能整合 人机下棋 完整版&#xff1a; game.h game.c text.c 测试功能代码 构思 五子棋不必多介绍了&#xff0c;大家小时候都玩过哈。 我们要通过程序实现这个小游戏&…

C++算法:最短回文串

题目 给定一个字符串 s&#xff0c;你可以通过在字符串前面添加字符将其转换为回文串。找到并返回可以用这种方式转换的最短回文串。 示例 1&#xff1a; 输入&#xff1a;s “aacecaaa” 输出&#xff1a;“aaacecaaa” 示例 2&#xff1a; 输入&#xff1a;s “abcd” 输…