当前位置: 首页 > news >正文

Elasticsearch:Serarch tutorial - 使用 Python 进行搜索 (二)

这个是继上一篇文章 “Elasticsearch:Serarch tutorial - 使用 Python 进行搜索 (一)” 的续篇。在今天的文章中,我们接着来完成如何进行分页及过滤。

分页 - pagination

应用程序处理大量结果通常是不切实际的。 因此,API 和 Web 服务使用分页控件来允许应用程序请求小块或页面的结果。

你可能已经注意到,Elasticsearch 默认情况下不会返回超过 10 个结果。 可以在搜索请求中给出可选的大小参数来更改此最大值。 以下示例要求最多返回 5 个搜索结果:

results = es.search(query={'multi_match': {'query': query,'fields': ['name', 'summary', 'content'],}}, size=5
)

要访问结果的其他页面,请使用 from_ 参数,该参数指示从完整结果列表中的位置开始(因为 from 是 Python 中的保留关键字,所以使用 from_ )。

下一个示例检索第二页 5 个结果:

results = es.search(query={'multi_match': {'query': query,'fields': ['name', 'summary', 'content'],}}, size=5, from_=5
)

让我们将 size 和 from_ 合并到 app.py 中的 handle_search() 端点中:

@app.post('/')
def handle_search():query = request.form.get('query', '')from_ = request.form.get('from_', type=int, default=0)results = es.search(query={'multi_match': {'query': query,'fields': ['name', 'summary', 'content'],}}, size=5, from_=from_)return render_template('index.html', results=results['hits']['hits'],query=query, from_=from_,total=results['hits']['total']['value'])

此处页面大小现已硬编码为 5(你可以随意使用您喜欢的任何其他数字)。 假定 from_ 参数作为提交表单中的附加字段给出,但该字段被认为是可选的,不存在时默认为 0。

index.html 中可用的搜索表单没有 from_ 字段,因此常规搜索将始终从第一个结果开始。 该模板显示有关所显示结果范围以及总数的信息。 以下是使用模板表达式完成此操作的方法:

<div class="col-sm-auto my-auto">Showing results {{ from_ + 1 }}-{{ from_ + results|length }} out of {{ total }}.
</div>

该模板还包括显示分页按钮以在结果列表中向前或向后移动的逻辑。 这是 “Previous results” 按钮的实现:

{% if from_ > 0 %}<div class="col-sm-auto my-auto"><a href="javascript:history.back(1)" class="btn btn-primary">← Previous page</a></div> 
{% endif %}

正如您所看到的,只有当 from_ 大于零时,“Previous page” 按钮才会呈现到页面。 该按钮的实现使用浏览器的历史 API 来返回一页。

“Next page” 按钮有一个更有趣的实现:

{% if from_ + results|length < total %}<div class="col-sm-auto my-auto"><form method="POST"><input type="hidden" name="query" value="{{ query }}"><input type="hidden" name="from_" value="{{ from_ + results|length }}"><button type="submit" class="btn btn-primary">Next page →</button></form></div>
{% endif %}

该按钮实际上并不是一个独立的按钮,而是一个完整的表单,除了按钮之外还有两个隐藏字段。 该表单与主搜索表单类似,但包含可选的 from_ 字段,调整为指向下一页结果。 单击此按钮时,Flask 应用程序将从该备用表单接收搜索请求,该表单使用相同的文本查询,但使用非零的 from_ 值。

通过这种小而巧妙的分页实现,你将能够浏览多页结果。

过滤 - Filters

许多应用程序需要让用户能够自定义查询,以补充搜索查询本身的功能。 在本章中,你将学习过滤,这是一种技术,可以指定仅对满足给定条件的索引中包含的文档子集执行搜索查询。

布尔查询简介

在实现过滤器之前,你必须了解复合查询是如何在 Elasticsearch 中实现的。

复合查询允许应用程序组合两个或多个单独的查询,以便它们一起执行,并在适当的情况下返回一组组合结果。 在 Elasticsearch 中创建复合查询的标准方法是使用布尔查询。

布尔查询充当两个或多个单独查询或子句的包装器。 有四种不同的方式来组合查询:

  • bool.must:子句必须匹配。 如果给出多个子句,则所有子句都必须匹配(类似于 AND 逻辑运算)。
  • bool.should:当不带 must 使用时,至少一个子句应该匹配(类似于 OR 逻辑运算)。 当与 must 结合使用时,每个匹配子句都会提高文档的相关性得分。
  • bool.filter:只有与子句匹配的文档才被视为搜索结果候选。
  • bool.must_not:只有与子句不匹配的文档才被视为搜索结果候选。

有关布尔查询的更多描述,请参阅文章 “开始使用 Elasticsearch (2)”。

正如你可能从上面猜到的那样,布尔查询涉及相当多的复杂性,并且可以通过多种方式使用。 在本章中,你将学习如何将前面章节中实现的多重匹配全文搜索子句与将结果限制为一类文档的过滤器相结合。 回想一下,本教程使用的数据集包含一个 category 字段,可以设置为 sharepoint、teams 或 github。

向查询添加过滤器

当前在教程应用程序中实现的多重匹配查询使用以下结构:

{'multi_match': {'query': "query text here",'fields': ['name', 'summary', 'content'],}
}

要添加将此搜索限制为特定类别的过滤器,必须按如下方式扩展查询:

{'bool': {'must': [{'multi_match': {'query': "query text here",'fields': ['name', 'summary', 'content'],}}],'filter': [{'term': {'category.keyword': {'value': "category to filter"}}}]}
}

让我们详细看看该查询中的新组件。

首先,multi_match 查询已移至 bool.must 子句内。 bool.must 子句通常是定义基本查询的地方。 请注意,must 接受要搜索的查询列表,因此这允许在需要时组合多个基本级查询。

过滤是在 bool.filter 部分中使用新的查询类型(term 查询)实现的。 对过滤器使用 match 或 multi_match 查询并不是一个好主意,因为这些是全文搜索查询。 为了过滤的目的,查询必须为每个文档返回绝对正确或错误的答案,而不是像匹配查询那样返回相关性分数。

Term 查询对给定字段中的值执行精确搜索。 这种类型的查询对于搜索标识符、标签、标记或本例中的类别很有用。

此查询不适用于为全文搜索建立索引的字段。 字符串字段被分配为默认的 text 类型,并在索引之前对其内容进行分析并分成单独的单词。 Elasticsearch 为字符串字段分配了辅助类型的 keyword 字段,该关键字将字段内容作为一个整体进行索引,使它们更适合使用术语(term)查询进行过滤。 通过在查询的过滤器部分中使用 category.keyword 的字段名称,将使用该字段的 keyword 类型变体,而不是默认的 text 变体。

更多有关 text 及 keyword 之间的区别,请详细阅读文章 “Elasticsearch:Text vs. Keyword - 它们之间的差异以及它们的行为方式”。

指定过滤器

在实现过滤查询之前,需要添加一种方式供最终用户输入所需的过滤器。 本教程中实现的解决方案将在搜索查询的文本中查找 “category:<category-name>” 模式。 让我们向 add.py 添加一个名为 extract_filters() 的函数来查找过滤器表达式:

def extract_filters(query):filter_regex = r'category:([^\s]+)\s*'m = re.search(filter_regex, query)if m is None:return {}, query  # no filtersfilters = {'filter': [{'term': {'category.keyword': {'value': m.group(1)}}}]}query = re.sub(filter_regex, '', query).strip()return filters, query

该函数接受用户输入的查询,并返回一个元组,其中包含在查询中找到的过滤器以及删除过滤器后修改后的查询。 为了查找过滤模式,它使用正则表达式。 如果需要,该功能可以通过附加过滤器进行扩展。

为了更好地理解此功能的工作原理,请启动 Python 会话(确保首先激活虚拟环境)并运行以下代码:

实施过滤搜索

剩下要做的就是更改 handle_search() 函数以发送更新的查询,该查询将全文搜索表达式与过滤器(如果用户给出了过滤器)组合在一起。 以下是该函数的新版本:

@app.post('/')
def handle_search():query = request.form.get('query', '')filters, parsed_query = extract_filters(query)from_ = request.form.get('from_', type=int, default=0)results = es.search(query={'bool': {'must': {'multi_match': {'query': parsed_query,'fields': ['name', 'summary', 'content'],}},**filters}}, size=5, from_=from_)return render_template('index.html', results=results['hits']['hits'],query=query, from_=from_,total=results['hits']['total']['value'])

查询现已更改为发送 bool 表达式,并且搜索表达式已移至其下方的 must 部分内。 extract_filters() 函数以需要发送到 Elasticsearch 的形式返回查询的过滤器部分,因此它也被插入到查询字典中的顶级 bool 键下。

尝试搜索查询(例如 work from home Category:sharepoint)以查看如何仅返回给定类别 (category) 的文档。

从上面的结果中,我们可以看出来所有的文档的 Category 都是 sharepoint。

Match-all 查询

在转到新主题之前,请尝试在搜索查询文本字段中仅输入过滤器,例如 category:github。 不幸的是,这不会返回任何结果,但在这种情况下的预期行为是接收与请求的类别匹配的所有结果。

发生的情况是 extract_filters() 函数返回一个元组,其中第一个元素中包含过滤器,第二个元素中包含空查询字符串。 match_all 查询接收空字符串,并返回空结果列表,因为没有任何内容与空字符串匹配。

为了解决这种特殊情况,当搜索文本为空时,可以将 multi_match 查询替换为 match_all。 下面的 handle_search() 函数版本添加了执行此操作的逻辑。 更新app.py 中的函数。

@app.post('/')
def handle_search():query = request.form.get('query', '')filters, parsed_query = extract_filters(query)from_ = request.form.get('from_', type=int, default=0)if parsed_query:search_query = {'must': {'multi_match': {'query': parsed_query,'fields': ['name', 'summary', 'content'],}}}else:search_query = {'must': {'match_all': {}}}results = es.search(query={'bool': {**search_query,**filters}}, size=5, from_=from_)return render_template('index.html', results=results['hits']['hits'],query=query, from_=from_,total=results['hits']['total']['value'])

在此版本中,你可以询问与某个类别匹配的所有文档。 请注意,所有返回的结果都具有相同的 1.0 分数,因为没有搜索词来计算分数。

恭喜,你已完成本教程的全文搜索部分! 单击此处查看到目前为止教程搜索应用程序的状态。你可以使用如下的命令来下载代码:

git clone https://github.com/liu-xiao-guo/search-tutorial-1

相关文章:

Elasticsearch:Serarch tutorial - 使用 Python 进行搜索 (二)

这个是继上一篇文章 “Elasticsearch&#xff1a;Serarch tutorial - 使用 Python 进行搜索 &#xff08;一&#xff09;” 的续篇。在今天的文章中&#xff0c;我们接着来完成如何进行分页及过滤。 分页 - pagination 应用程序处理大量结果通常是不切实际的。 因此&#xff0…...

力扣labuladong——一刷day84

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、力扣743. 网络延迟时间 前言 Dijkstra 算法&#xff08;一般音译成迪杰斯特拉算法&#xff09;无非就是一个 BFS 算法的加强版&#xff0c;它们都是从二叉…...

Linux环境vscode clang-format格式化:vscode clang format command is not available

问题现象 vscode安装了clang-format插件&#xff0c;但是使用就报错 问题原因 设置中配置的clang-format插件工具路径不正确。 解决方案 确认本地安装了clang-format工具&#xff1a;终端输入clang-format&#xff08;也可能是clang-format-13等版本&#xff0c;建议tab自…...

【KingbaseES】实现MySql函数WEEKS_BETWEEN

WEEKS_BETWEEN CREATE OR REPLACE FUNCTION weeks_between(start_date date, end_date date) RETURNS integer AS $$ BEGIN RETURN EXTRACT(WEEK FROM end_date) - EXTRACT(WEEK FROM start_date); END; $$ LANGUAGE plpgsql IMMUTABLE;结果展示...

@Scheduled定时任务现状与改进

项目场景&#xff1a; 定时任务现状&#xff1a;每个项目都会有一些配置信息&#xff0c;这些信息我们是都放在一个配置服务中&#xff0c;这个服务会定时从配置表中加载所有配置存入本地JVM内存&#xff0c;以供调用方获取&#xff08;调用方集成了配置服务的SDK&#xff0c;…...

python+selenium爬虫笔记

本文只是做例子&#xff0c;具体网站路径麻烦你们换下&#xff0c;还有xpath路径也换下 一、安装所需要的组件&#xff08;此处采用谷歌&#xff09; 1、安装驱动 查看你的浏览器版本&#xff0c;去安装对应的版本 下载驱动 下载驱动路径 之前版本的 输入这个路径下载下来解压…...

【LMM 009】MiniGPT-4:使用 Vicuna 增强视觉语言理解能力的多模态大模型

论文描述&#xff1a;MiniGPT-4: Enhancing Vision-Language Understanding with Advanced Large Language Models 论文作者&#xff1a;Deyao Zhu∗ Jun Chen∗ Xiaoqian Shen Xiang Li Mohamed Elhoseiny 作者单位&#xff1a;King Abdullah University of Science and Techn…...

SpringBoot学习(三)-整合JDBC、Druid、MyBatis

注&#xff1a;此为笔者学习狂神说SpringBoot的笔记&#xff0c;其中包含个人的笔记和理解&#xff0c;仅做学习笔记之用&#xff0c;更多详细资讯请出门左拐B站&#xff1a;狂神说!!! 一、整合JDBC使用&#xff08;理解&#xff09; 创建项目 勾选依赖启动器 查看依赖 …...

如何选择合适的语音呼叫中心?

市场上不同的语音呼叫中心提供商&#xff0c;都有其独特的优势和不足。企业在选择语音呼叫中心服务公司时&#xff0c;主要考虑以下因素&#xff1a;服务质量、价格、技术支持、客户支持等。 首先&#xff0c;服务质量是选择语音呼叫中心需关注的最重要因素之一。 为确保语音…...

使用qtquick调用python程序

一. 内容简介 使用qtquick调用python程序 二. 软件环境 2.1vsCode 2.2Anaconda version: conda 22.9.0 2.3pytorch 安装pytorch(http://t.csdnimg.cn/GVP23) 2.4QT 5.14.1 新版QT6.4,&#xff0c;6.5在线安装经常失败&#xff0c;而5.9版本又无法编译64位程序&#xf…...

【Axure高保真原型】树形表格_多选效果

今天和大家分享树形表格_多选效果的原型模板&#xff0c;点击树的箭头可以展开或者收起子节点&#xff0c;点击多选按钮可以选中或取消选择该行以及子级行内容&#xff0c;同时反选父级行内容&#xff0c;父级行内容能根据子级选中的数量自动反选&#xff0c;包括全选、半选和未…...

【Filament】加载obj和fbx模型

1 前言 3D 模型的常用格式主要有 obj、fbx、gltf 等&#xff0c;Filament 中的 filamesh.exe 工具可以将 obj、fbx 格式转换为 filamesh 格式&#xff0c;然后再加载显示。对于 gltf 格式模型&#xff0c;可以通过 ModelViewer 加载显示&#xff0c;这不在本文的讨论范围内。 1…...

[USACO04OPEN] The Cow Lineup

题目描述 约翰的 N &#xff08; 1 ≤ N ≤ 100000 &#xff09; N &#xff08; 1 \leq N \leq 100000 &#xff09; N&#xff08;1≤N≤100000&#xff09; 只奶牛站成了一列。每只奶牛都写有一个号牌&#xff0c;表示她的品种&#xff0c;号牌上的号码在 1 … K &#x…...

软件工具集合

代码文档自动生成工具&#xff1a; Doxygen download 软件分析工具&#xff1a; perf gdb flamegraph 代码量统计&#xff1a; vscode插件&#xff1a;VS Code Counter 代码备注 vsocde插件&#xff1a; Line Note...

C#利用openvino部署PP-TinyPose人体姿态识别

【官方框架地址】 github.com/PaddlePaddle/PaddleDetection 【算法介绍】 关键点检测算法往往需要部署在轻量化、边缘端设备上&#xff0c;因此长期以来都存在一个难题&#xff1a;精度高、速度则慢、算法体积也随之增加。而PP-TinyPose的出世彻底打破了这个僵局&#xff0c…...

MindSpore Serving与TGI框架 の 对比

一、MindSpore Serving MindSpore Serving是一款轻量级、高性能的服务工具&#xff0c;帮助用户在生产环境中高效部署在线推理服务。 使用MindSpore完成模型训练>导出MindSpore模型&#xff0c;即可使用MindSpore Serving创建该模型的推理服务。 MindSpore Serving包含以…...

两阶段提交协议三阶段提交协议

两阶段提交协议 分布式事务是指会涉及到操作多个数据库的事务,在分布式系统中&#xff0c;各个节点之间在物理上相互独立&#xff0c;通过网络进行沟通和协调。 XA 就是 X/Open DTP 定义的交易中间件与数据库之间的接口规范&#xff08;即接口函数&#xff09;&#xff0c;交易…...

6-Docker Compose-tomcat application(指定官方镜像)

1.创建docker-compose.yml文件,添加如下内容并保存 vim docker-compose.yml [root@centos79 ~]# cat docker-compose.yml #yml文件 version: 3 #版本号,默认为3 services:tomcat-ztj: #定…...

宝塔安装的imagemagick不能用,必须自己手动安装

1 安装 用composer安装 2 宝塔安装的imagemagick不能用&#xff0c;必须自己手动安装&#xff08;3.4.3版本 php 7.3&#xff09; 1 步骤&#xff1a; wget https://pecl.php.net/get/imagick-3.4.3.tgz tar -zxf imagick-3.4.3.tgz cd imagick-3.4.3 /www/server/php/73…...

解决在test以外的目录下导入junit无效

以上引用来自src目录下的文件&#xff0c;可以看到&#xff0c;和junit有关的导入都飘红&#xff0c;但明明junit已经被正确导入进了项目中。 再看右侧的Maven的依赖下方&#xff0c;junit的右边有一个很不起眼的(test) 这是因为junit作为测试框架&#xff0c;可能包含仅适用于…...

vscode里如何用git

打开vs终端执行如下&#xff1a; 1 初始化 Git 仓库&#xff08;如果尚未初始化&#xff09; git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署&#xff0c;直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型&#xff0c;但是目前国内可能使用不多&#xff0c;至少实践例子很少看见。开发训练模型就不介绍了&am…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

Springboot社区养老保险系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;社区养老保险系统小程序被用户普遍使用&#xff0c;为方…...

面向无人机海岸带生态系统监测的语义分割基准数据集

描述&#xff1a;海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而&#xff0c;目前该领域仍面临一个挑战&#xff0c;即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join

纯 Java 项目&#xff08;非 SpringBoot&#xff09;集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

初探用uniapp写微信小程序遇到的问题及解决(vue3+ts)

零、关于开发思路 (一)拿到工作任务,先理清楚需求 1.逻辑部分 不放过原型里说的每一句话,有疑惑的部分该问产品/测试/之前的开发就问 2.页面部分(含国际化) 整体看过需要开发页面的原型后,分类一下哪些组件/样式可以复用,直接提取出来使用 (时间充分的前提下,不…...

2025 后端自学UNIAPP【项目实战:旅游项目】7、景点详情页面【完结】

1、获取景点详情的请求【my_api.js】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http(/login/getWXSessionKey, {code,avatar}); };//…...