Elasticsearch:构建自动补全功能 - Autocomplete
什么是自动补全(autocomplete)功能呢?我们举一个很常见的例子。 每当你去谷歌并开始打字时,就会出现一个下拉列表,其中列出了建议。 这些建议与查询相关并帮助用户完成查询。
Autocomplete 正如维基百科所说的:
Autocomplete 或单词完成是一个功能,应用程序预测使用的其余单词正在键入
它也知道你键入或键入前方搜索。 它通过提示用户在键入文本的可能性和替代方案来帮助他们导航或指导用户。 它减少用户在执行任何搜索操作之前需要输入的字符数量,从而增强用户的搜索体验。
可以通过使用任何数据库来实现 Autocomplete。 在这篇文章中,我们将使用 Elasticsearch 构建自动补全功能。
Elasticsearch 是免费及开发,分布式和基于JSON的搜索引擎,建立在Lucene的顶部。更多关于 Elasticsearch 的介绍,请阅读文章 “Elasticsearch 简介”。
方法
在 Elasticsearch 中,可能有多种构建自动完成功能的方法。 我们将讨论以下方法。
- Prefix query
- Edge ngram
- Completion suggester
Prefiex query
这种方法涉及使用针对自定义字段的前缀查询(prefix query)。 该字段的值可以作为 keyword 存储,因此将多个 terms(单词)存储在一起作为一个术语。 可以使用关键字分词器(keyword tokenizer)来完成这一点。 这种方法遭受了缺点:
- 由于只支持词首匹配,不能匹配正文中间的 query。
- 这种类型的查询未针对大型数据集进行优化,可能会导致延迟增加。
- 由于这是一个查询,因此不会过滤掉重复的结果。 处理此方法的一种解决方法是使用聚合查询对结果进行分组,然后过滤掉结果。 这涉及服务器端的一些处理
Edge Ngrams
有关 edge ngrams 的介绍请参阅之前的文章 “Elasticsearch: Ngrams, edge ngrams, and shingles”。
这种方法涉及在索引和搜索时使用不同的分析器。 索引文档时,可以应用带有 edge n-gram 过滤器的自定义分析器。 在搜索时,可以应用标准分析器。 这可以防止查询被拆分。
Edge N-gram tokeniser 首先将文本分解为自定义字符(空格、特殊字符等)上的单词,然后仅从字符串的开头保留 n-gram。
这种方法也适用于匹配文本中间的查询。 这种方法通常查询速度很快,但可能会导致索引速度变慢和索引存储量变大。
Completion suggester
Elasticsearch 附带一个名为 Completion Suggester 的内部解决方案。 它使用称为有限状态传感器 (Finite State Transducer - FST) 的内存数据结构。 Elasticsearch 以每个段为基础存储 FST,这意味着建议会随着更多新节点的添加而水平扩展。
实施 Completion Suggester 时要记住的一些事情
- Autosuggest 项应将 completion 类型作为其字段类型。
- 输入字段可以为单个术语具有各种规范名称或别名。
- 可以为每个文档定义权重以控制它们的排名。
- 以小写形式存储所有术语有助于不区分大小写的匹配。
- 可以启用上下文 suggesters 以支持按特定标准进行过滤或提升。
这种方法是实现自动完成功能的理想方法,但是,它也有一些缺点
- 匹配总是从文本的开头开始。 所以在 movies 数据集中搜索 america 不会产生任何结果。 一种克服方法是在空格上标记输入文本并将所有短语保留为规范名称。 这样 Captain America: Civil War:内战将被存储为:
Captain America: Civil War
America: Civil War
Civil War
War
不支持突出(highlight)显示匹配的词。
没有可用的排序机制。 对建议进行排序的唯一方法是通过权重。 当需要任何自定义排序(如字母排序或按上下文排序)时,这会产生问题。
实现
让我们在 Elasticsearch 中实现上述方法。 我们将使用 movies 数据来构建我们的示例索引。 为了便于参考,我们使用如下的命令来创建 movies 索引:
PUT movies
{"settings": {"index": {"analysis": {"filter": {},"analyzer": {"keyword_analyzer": {"filter": ["lowercase","asciifolding","trim"],"char_filter": [],"type": "custom","tokenizer": "keyword"},"edge_ngram_analyzer": {"filter": ["lowercase"],"tokenizer": "edge_ngram_tokenizer"},"edge_ngram_search_analyzer": {"tokenizer": "lowercase"}},"tokenizer": {"edge_ngram_tokenizer": {"type": "edge_ngram","min_gram": 2,"max_gram": 5,"token_chars": ["letter"]}}}}},"mappings": {"properties": {"name": {"type": "text","fields": {"keywordstring": {"type": "text","analyzer": "keyword_analyzer"},"edgengram": {"type": "text","analyzer": "edge_ngram_analyzer","search_analyzer": "edge_ngram_search_analyzer"},"completion": {"type": "completion"}},"analyzer": "standard"}}}
}
如果我们看到映射,我们会发现 name 是一个 multi-fields 字段,其中包含多个字段,每个字段都以不同的方式进行分析。
- 使用关键字分词器分析 Fieldname.keywordstring,因此它将用于前缀查询方法
- 字段 name.edgengram 使用 Edge Ngram 分词器进行分析,因此它将用于 Edge Ngram 方法。
- Field name.completion 存储为 completion 类型,因此它将用于 Completion Suggester。
我们使用如下命令索引所有的电影:
POST movies/_bulk
{ "index" : {"_id" : "1"} }
{ "name" : "Spider-Man: Homecoming" }
{ "index" : {"_id" : "2"} }
{ "name" : "Ant-man and the Wasp" }
{ "index" : {"_id" : "3"} }
{ "name" : "Avengers: Infinity War Part 2" }
{ "index" : {"_id" : "4"} }
{ "name" : "Captain Marvel" }
{ "index" : {"_id" : "5"} }
{ "name" : "Black Panther" }
{ "index" : {"_id" : "6"} }
{ "name" : "Avengers: Infinity War" }
{ "index" : {"_id" : "7"} }
{ "name" : "Thor: Ragnarok" }
{ "index" : {"_id" : "8"} }
{ "name" : "Guardians of the Galaxy Vol 2" }
{ "index" : {"_id" : "9"} }
{ "name" : "Doctor Strange" }
{ "index" : {"_id" : "10"} }
{ "name" : "Captain America: Civil War" }
{ "index" : {"_id" : "11"} }
{ "name" : "Ant-Man" }
{ "index" : {"_id" : "12"} }
{ "name" : "Avengers: Age of Ultron" }
{ "index" : {"_id" : "13"} }
{ "name" : "Guardians of the Galaxy" }
{ "index" : {"_id" : "14"} }
{ "name" : "Captain America: The Winter Soldier" }
{ "index" : {"_id" : "15"} }
{ "name" : "Thor: The Dark World" }
{ "index" : {"_id" : "16"} }
{ "name" : "Iron Man 3" }
{ "index" : {"_id" : "17"} }
{ "name" : "Marvel’s The Avengers" }
{ "index" : {"_id" : "18"} }
{ "name" : "Captain America: The First Avenger" }
{ "index" : {"_id" : "19"} }
{ "name" : "Thor" }
{ "index" : {"_id" : "20"} }
{ "name" : "Iron Man 2" }
{ "index" : {"_id" : "21"} }
{ "name" : "The Incredible Hulk" }
{ "index" : {"_id" : "22"} }
{ "name" : "Iron Man" }
让我们从 prefix query 方法开始,尝试查找以 th 开头的电影。
查询将是:
GET movies/_search?filter_path=**.hits
{"query": {"prefix": {"name.keywordstring": {"value": "th"}}}
}
查询的结果是:
{"hits": {"hits": [{"_index": "movies","_id": "7","_score": 1,"_source": {"name": "Thor: Ragnarok"}},{"_index": "movies","_id": "15","_score": 1,"_source": {"name": "Thor: The Dark World"}},{"_index": "movies","_id": "19","_score": 1,"_source": {"name": "Thor"}},{"_index": "movies","_id": "21","_score": 1,"_source": {"name": "The Incredible Hulk"}}]}
}
结果是公平的,但是像 Captain America: The Winter Soldier,Guardians of the Galaxy 这样的电影被遗漏了,因为前缀查询只匹配文本的开头而不是中间。
让我们尝试寻找另一部以 am 开头的电影。
GET movies/_search?filter_path=**.hits
{"query": {"prefix": {"name.keywordstring": {"value": "am"}}}
}
这里我们没有得到任何结果,尽管 Captain America 满足这个条件。 这就印证了Prefix query 不能用于正文中间匹配的一点。
让我们运行相同的搜索,但使用 Edge Ngram 方法。
GET movies/_search?filter_path=**.hits
{"query": {"match": {"name.edgengram": "am"}}
}
上面运行的结果是:
{"hits": {"hits": [{"_index": "movies","_id": "10","_score": 1.5922177,"_source": {"name": "Captain America: Civil War"}},{"_index": "movies","_id": "14","_score": 1.3930962,"_source": {"name": "Captain America: The Winter Soldier"}},{"_index": "movies","_id": "18","_score": 1.3930962,"_source": {"name": "Captain America: The First Avenger"}}]}
}
让我们再次尝试寻找 Captain America,但这次使用更大的短语 captain america the:
GET movies/_search?filter_path=**.hits
{"query": {"match": {"name.edgengram": "captain america the"}}
}
使用 Edge N-gram 方法,我们得到以下电影:
{"hits": {"hits": [{"_index": "movies","_id": "21","_score": 1.0249562,"_source": {"name": "The Incredible Hulk"}},{"_index": "movies","_id": "17","_score": 0.9822227,"_source": {"name": "Marvel’s The Avengers"}},{"_index": "movies","_id": "2","_score": 0.94290996,"_source": {"name": "Ant-man and the Wasp"}},{"_index": "movies","_id": "13","_score": 0.94290996,"_source": {"name": "Guardians of the Galaxy"}},{"_index": "movies","_id": "15","_score": 0.906623,"_source": {"name": "Thor: The Dark World"}},{"_index": "movies","_id": "8","_score": 0.8730254,"_source": {"name": "Guardians of the Galaxy Vol 2"}},{"_index": "movies","_id": "14","_score": 0.7365507,"_source": {"name": "Captain America: The Winter Soldier"}},{"_index": "movies","_id": "18","_score": 0.7365507,"_source": {"name": "Captain America: The First Avenger"}}]}
}
如果我们观察我们的短语,只有两个建议是有意义的。 匹配这么多术语的原因是 match 子句的功能。 匹配包括所有包含 captain OR america OR the 的文件。 由于该字段是使用 ngram 分析的,因此也会包含更多建议(如果存在)。
让我们尝试使用针对相同短语 captain america the 的 suggest 查询。 建议查询的编写方式略有不同。
GET movies/_search?filter_path=suggest
{"suggest": {"movie-suggest": {"prefix": "captain america the","completion": {"field": "name.completion"}}}
}
结果我们得到以下电影:
{"suggest": {"movie-suggest": [{"text": "captain america the","offset": 0,"length": 19,"options": [{"text": "Captain America: The First Avenger","_index": "movies","_id": "18","_score": 1,"_source": {"name": "Captain America: The First Avenger"}},{"text": "Captain America: The Winter Soldier","_index": "movies","_id": "14","_score": 1,"_source": {"name": "Captain America: The Winter Soldier"}}]}]}
}
让我们尝试相同的查询,但这次使用了错别字 captain america the。
GET movies/_search?filter_path=suggest
{"suggest": {"movie-suggest": {"prefix": "captain amrica the","completion": {"field": "name.completion"}}}
}
上面的电影建议没有返回结果,因为不支持模糊性。 我们可以通过以下方式更新查询以包含对模糊性的支持:
GET movies/_search?filter_path=suggest
{"suggest": {"movie-suggest": {"prefix": "captain amrica the","completion": {"field": "name.completion","fuzzy": {"fuzziness": 1}}}}
}
以上查询返回以下结果:
{"suggest": {"movie-suggest": [{"text": "captain amrica the","offset": 0,"length": 18,"options": [{"text": "Captain America: The First Avenger","_index": "movies","_id": "18","_score": 10,"_source": {"name": "Captain America: The First Avenger"}},{"text": "Captain America: The Winter Soldier","_index": "movies","_id": "14","_score": 10,"_source": {"name": "Captain America: The Winter Soldier"}}]}]}
}
结论
可以使用多种方法在 ElasticSearch 中实现自动完成功能。 Completion Suggester 涵盖了实现功能齐全且快速的自动完成所需的大多数情况。
相关文章:
Elasticsearch:构建自动补全功能 - Autocomplete
什么是自动补全(autocomplete)功能呢?我们举一个很常见的例子。 每当你去谷歌并开始打字时,就会出现一个下拉列表,其中列出了建议。 这些建议与查询相关并帮助用户完成查询。 Autocomplete 正如维基百科所说的…...
One UI 5.1 更新来了
之前一直在关注One UI 5.0里提到的视频通话背景功能模块,结果5.0版本推送的时候没有引入,有先行者计划博主说是5.1里肯定会有的;前一两天One UI 5.1更新来了,然而该功能还是没有引入,表示很遗憾;本次更新新…...
Python学习笔记11:文件
文件 打开文件 函数open的参数mode的最常见取值 值描述‘r’读取模式(默认值)‘w’写入模式‘x’独占写入模式‘a’附加模式‘b’二进制模式(与其他模式结合使用)‘t’文本模式(默认值,与其他模式结合使…...
django-filter的使用
django-filter是一个通用的、可重用的应用程序,它可以减轻视图代码的编写工作量。具体来说,它允许用户根据模型的字段筛选查询集,并显示表单让他们这样做。 安装 pip install django-filter快速开始 在settings.py中添加如下配置: INSTAL…...
时序预测 | MATLAB实现IWOA-BiLSTM和BiLSTM时间序列预测(改进的鲸鱼算法优化双向长短期记忆神经网络)
时序预测 | MATLAB实现IWOA-BiLSTM和BiLSTM时间序列预测(改进的鲸鱼算法优化双向长短期记忆神经网络) 目录时序预测 | MATLAB实现IWOA-BiLSTM和BiLSTM时间序列预测(改进的鲸鱼算法优化双向长短期记忆神经网络)预测效果基本介绍程序设计参考资料预测效果 基本介绍 MATLAB实现IWO…...
【C++】string的成员函数、成员常量和非成员函数
目录 string 1. string的成员函数 1.1 构造、析构和赋值运算符重载 1.1.1 构造函数 1.1.2 析构函数 1.1.3 赋值运算符重载 1.2 迭代器 1.3 容量 1.4 元素访问 1.4.1 遍历方法 1.5 修改器 1.6 字符串操作 2. string的成员常量 3. string的非成员函数 string 以下…...
网络互连模型:OSI 七层模型
OSI 七层模型 七层模型,亦称 OSI(Open System Interconnection)。OSI 七层参考模型是国际标准化组织(ISO)制定的一个用于计算机或通信系统间网络互联的标准体系,一般称为 OSI 参考模型或七层模型。OSI 七层…...
18跨越语言:不同语言间进行RPC通信
在最开始介绍gRPC时我们讲到,gRPC具有灵活的兼容性,可以支持很多种编程语言,下面我们就使用在后端领域最常用的两种编程语言Go和Java,来体验一下gRPC在不同语言的项目间是如何进行通信的。 逻辑架构 由上图我们可以看出,Go语言设计gRPC的服务端,Java语言设计gRPC的客户端…...
解压缩工具:Bandizip 中文
bandizip是一款可靠和快速的压缩软件,它可以解压RAR、7Z、ZIP、ISO等数十种格式,也可以压缩7Z、ZIP、ISO等好几种常用格式,在压缩文件方面毫不逊色于winrar,适用于多核心压缩、快速拖放、高速压缩等功能,采用了先进快速…...
JAVA知识点全面总结2:面向对象
二.面向对象 1.面向对象有哪些重要的关键字?作用是什么? 2.理解多态的使用? 3.接口与抽象类的相同点和不同点? 4.equals和toString的判断? 5.新建对象的流程是什么?new一个对象? 6.深拷贝…...
DNS作用及工作原理
文章目录1. DNS作用2 DNS 三个组成部分:2.1 客户端2.2Local DNS2.3 权威域 DNS 服务器3 工作过程1. DNS作用 DNS 分为 Client 和 Server,Client 扮演发问的角色,也就是问 Server 一个 Domain Name,而 Server 必须要回答此 Domain…...
Android 9.0 wifi的随机mac地址修改为固定不变
1.前言 在9.0的系统rom产品定制化开发中,在系统默认的wifi的mac地址是会在联网前后会变化,因为默认是随机显示mac地址,所以会在连上wifi后mac地址会变动但是如果根据mac地址来升级 会引起一系列问题,为了避免这些问题 所以就要求固定mac地址,这就需要看wifi模块怎么改变ma…...
Apinto 网关 V0.11.1 版本发布,多协议互转,新增编码转换器,接入 Prometheus
Eolink 旗下 Apinto 开源网关再次更新啦~ 一起来看看是否有你期待的功能! 1、协议转换功能上线 之前发布的 Apinto v0.10.0 已经支持了多协议的基本功能,实现多协议支持的一次验证。本次最新版本可以支持 HTTP 与 gRPC、HTTP 与 Dubbo2 之间的协议转换。…...
Android 12.0 根据app包名授予app监听系统通知权限
1.概述 在12.0的系统rom产品定制化开发中,在一些产品rom定制化开发中,系统内置的第三方app需要开启系统通知权限,然后可以在app中,监听系统所有通知,来做个通知中心的功能,所以需要授权 获取系统通知的权限,然后来顺利的监听系统通知。来做系统通知的功能 2.根据app包名…...
mysql视图和存储过程
视图视图就是将一条sql查询语句封装起来,之后使用sql时,只需要查询视图即可,查询视图时会将这条sql语句再次执行一遍。视图不保存数据,数据还是在表中。SELECT 语句所查询的表称为视图的基表,而查询的结果集称为虚拟表…...
uniapp 实现人脸认证
前言 对于前端来说,需要后端提供一个人脸识别接口,前端传入图片,接口识别并返回结果,如此看来,其实前端只需实现图片传入即可,但是其实不然,在传入图片时,需要进行以下几点操作&…...
自学大数据第三天~终于轮到hadoop了
前面那几天是在找大数据的门,其实也是在搞一些linux的基本命令,现在终于轮到hadoop了 Hadoop hadoop的安装方式 单机模式: 就如字面意思,在一台机器上运行,存储是采用本地文件系统,没有采用分布式文件系统~就如我们一开始入门的时候都是从本地开始的; 伪分布式模式 存储采用…...
Unity 入门精要00---Unity提供的基础变量和宏以及一些基础知识
头文件引入: XXPROGRAM ... #include "UnityCG.cginc"; ... ENDXX 常用的结构体(在UnityCg.cginc文件中):在顶点着色器输入和输出时十分好用 。 关于如何使用这些结构体,可在Unity安装文件目录/Editor…...
Kubernetes的网络架构及其安全风险
本博客地址:https://security.blog.csdn.net/article/details/129137821 一、常见的Kubernetes网络架构 如图所示: 说明: 1、集群由多个节点组成。 2、每个节点上运行若干个Pod。 3、每个节点上会创建一个CNI网桥(默认设备名称…...
Blob分析+特征+(差分)
Blob分析特征0 前言1 概念2 方法2.1 图像采集2.2 图像分割2.3 特征提取3 主要应用场景:0 前言 在缺陷检测领域,halcon通常有6种处理方法,包括Blob分析特征、Blob分析特征差分、频域空间域、光度立体法、特征训练、测量拟合,本篇博…...
Flink 提交模式
Flink的部署方式有很多,支持Local,Standalone,Yarn,Docker,Kubernetes模式等。而根据Flink job的提交模式,又可以分为三种模式: 模式1:Application Mode Flink提交的程序,被当做集群内部Application,不再需要Client端做繁重的准备工作。(例如执行main函数,生成JobG…...
网络总结知识点(网络工程师必备)三
♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维课堂笔记,努力不一定有收获,但一定会有收获加油!一起努力,共赴美好人生! ♥️夕阳下,是最美的绽放,树高千尺,落叶归根人生不易,人间真情 目录 前言 51.什么是ARP代理?...
测开:前端基础-css
一、CSS介绍和引用 1.1 css概述 层叠样式表,是一种样式表语言,用来描述HTML和XML文档的呈现。 CSS 用于简化HTML标签,把关于样式部分的内容提取出来,进行单独的控制,使结构与样式分离开发。 CSS 是以HTML为基础&…...
Java学习记录之JDBC
JDBC JDBC 是 Java Database Connectivity 的缩写,是允许Java 程序访问并操作关系型数据库数据的一套 应用程序接口。本身就是一种规范,它提供的接口有一套完整的,可移植的访问底层数据库的程序。 JDBC 的架构 JDBC API支持两层和三层处理…...
矩阵翻硬币
题目描述 小明先把硬币摆成了一个 n 行 m 列的矩阵。 随后,小明对每一个硬币分别进行一次 Q 操作。 对第 x 行第 y 列的硬币进行 Q 操作的定义:将所有第 ix 行,第 jy 列的硬币进行翻转。...
【C语言跬步】——指针数组和数组指针(指针进阶)
一.指针数组和数组指针的区别 1.指针数组是数组,是一种存放指针的数组; 例如: int* arr[10]; 2.数组指针是指针,是一种指向数组的指针,存放的是数组的地址; 例如: int arr[5]; int (p)[5]&a…...
第十四届蓝桥杯模拟赛第三期(Python)
写在前面 包含本次模拟赛的10道题题解能过样例,应该可以AC若有错误,欢迎评论区指出本次题目除了最后两题有些难度,其余题目较为简单,我只将代码和结果给出,如果不能理解欢迎私信我,我会解答滴。start 2022…...
css-盒模型
巧妙运用margin负值盒模型和怪异盒模型(border padding 包含在内)display: block 能让textarea input 水平尺寸自适应父容器? – 不能 * {box-sizing: border-box; // bs: bb }<textarea/> 是替换元素,尺寸由内部元素决定,不受display水平影响. 当然可以直接设置宽度10…...
Linux | 调试器GDB的详细教程【纯命令行调试】
文章目录一、前言二、调试版本与发布版本1、见见gdb2、程序员与测试人员3、为什么Release不能调试但DeBug可以调试❓三、使用gdb调试代码1、指令集汇总2、命令演示⌨ 行号显示⌨ 断点设置⌨ 查看断点信息⌨ 删除断点⌨ 开启 / 禁用断点⌨ 运行 / 调试⌨ 逐过程和逐语句⌨ 打印 …...
wifi芯片大市场和个人小生活
3.3 是日也,天朗气清,惠风和畅。仰观宇宙之大,俯察论文论坛,所以游目骋怀,足以极视听之娱,信可乐也。 夫人之相与,俯仰一世,或取诸怀抱,悟言一室之内;或因寄所…...
彩票投资理财平台网站建设/深圳专业seo
技术宅改变世界 http://blog.geek18.com 是广州金矿信息科技有限公司的官方博客,主要搜集极客相关的信息和感兴趣的互联网资讯,也有一些编程方面的心得和教程等.转载于:https://www.cnblogs.com/iammatrix/p/5516311.html...
约小姐的微信小程序是什么/百度关键词排名优化
Prism框架对象DelegateCommand 1、常规命令支持 2、泛型命令支持 3、事件命令支持 4、事件聚合器-》Messenger(事件订阅发布) 定义一个基本消息类型,继承PubSubEvent 5、复合命令<prism:PrismApplication x:Class="Zhaoxi.PrismLesson.App"xmlns="http:/…...
网站开发专业公司有哪些/营销策划运营培训机构
一、数据库连接池 在程序启动时建立足够的数据库连接放置在内存中(List),这些连接组成连接池由程序动态的对池中的连接进行申请,使用,释放。 优势:资源重用;由于数据库连接得以重用,…...
企业公司网站模板/做网上推广
最近做IM的时候遇到一个问题,同时用到了这三个关键字。就是查询一个人的离线消息详情,我们服务端返回给客户端显示的这个详情包括了三个内容,第一个要求列出离线这段时间哪些人或者群给你发了消息,第二个这其中的某个人或者群发了…...
免费可以看的软件大全下载/邯郸seo优化公司
原标题:评测 | 我的轻型影像数据基地——联想个人云存储A1试用我是一名摄影行业的从业者,有时候会莫名其妙地对于本行业的某些现象忧思一下:我们那么喜欢按动快门,每天都产生大量图像内容,有的社交了、有的展览了、有的…...
cms wordpress 区别/长沙seo网站排名优化公司
/**************************************************************************************************** 都是网上的代码,自己组合起来的,刚开始报很多错误,花了很多时间终于搞定了 环境:VC6.0SDK(SDK必须…...