许多应用程序需要为用户提供自定义查询的功能,以补充搜索查询的不足。本章将学习过滤技术,该技术可以指定只对索引中满足给定条件的文档子集执行搜索查询。

布尔查询简介

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

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

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

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

综上所述,您可能已经猜到布尔查询的复杂性,而且其使用方式多种多样。在本章中,你将学习如何将前几章中实现的多匹配全文搜索子句与将搜索结果限制为一类文档的过滤器结合起来。请注意,本教程使用的数据集包含一个category 字段,可设置为sharepointteamsgithub

为查询添加过滤器

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

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

让我们详细了解一下该查询的新组件。

首先,multi_match 查询已移至bool.must 子句内。bool.must 子句通常是定义基本查询的地方。请注意,must 接受要搜索的查询列表,因此可以根据需要将多个基础查询组合起来。

过滤功能在bool.filter 部分实现,使用一种新的查询类型,即term 查询。在过滤器中使用matchmulti_match 查询不是一个好主意,因为这些都是全文搜索查询。为了过滤的目的,查询必须为每个文档返回一个绝对的 "真 "或 "假 "答案,而不是像匹配查询那样返回一个相关性得分。

术语 查询对给定 字段 中的某个值进行精确搜索。这类查询可用于搜索标识符、标签、标记,或者本例中的类别。

此查询对全文检索索引的字段效果不佳。字符串字段被指定为默认的文本类型,并在索引前对其内容进行分析和分隔成单个单词。Elasticsearch 会为字符串 字段 分配二级 关键字 类型,将字段内容作为一个整体进行索引,使其更适合使用term 查询进行筛选。在查询的过滤部分使用category.keyword 字段名,就会使用keyword 字段的键入变量,而不是默认的text 字段。

指定过滤器

在执行过滤查询之前,有必要为最终用户添加一种输入所需过滤项的方法。本教程中实施的解决方案将在搜索查询文本中查找category:<category-name> 模式。让我们在app.py中添加一个名为extract_filters() 的函数,以查找过滤器表达式:

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

当找到一个过滤器时,filters 列表就会使用相应的过滤器表达式进行扩展,在本例中,该表达式基于term 查询,如上所述。

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

函数返回的元组应为

执行过滤搜索

剩下要做的就是修改handle_search() 函数,以发送更新的查询,将全文检索表达式与过滤器(如果用户提供了过滤器)结合起来。以下是该功能的新版本:

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

试试work from home category:sharepoint 这样的搜索查询,看看如何只返回给定类别的文档。

范围过滤器

term 过滤器外,Elasticsearch 还支持多种过滤器。另一个常用的过滤器是range ,它可以处理数字和日期。让我们添加一个year 过滤器,根据updated_at 字段中给出的最后更新年份来限制结果。

以下是extract_filters() 函数的更新版本,它同时查找category:<category>year:<yyyy> 作为过滤器:

该版本增加了第二个正则表达式,用于查找查询字符串中的year:yyyy 。它为updated_at 字段创建了一个range 过滤器,并将范围的下限和上限设置为冒号后的年份,在正则表达式匹配中将其捕获为m.group(1)

有一点小麻烦,因为updated_at 字段包含完整的日期,而在此过滤器中只需要查看年份。幸运的是,当范围筛选器与日期字段一起使用时,范围的边界可以通过日期数学来增强。在gte (下限值)和lte (上限值)参数上添加的后缀||/y 表示给定值是一个年份,必须填写完整的日期才能与字段进行比较。

有了这一更改,您就可以使用year:2020 work from home 等查询功能,只查看所要求年份的结果。查询还可以包括两个筛选器,例如year:2020 category:teams work from home

全匹配查询

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

extract_filters() 函数会返回一个元组,第一个元素是过滤器,第二个元素是空查询字符串。multi_match 查询接收空字符串,并返回空结果列表,因为没有任何结果与空字符串匹配。

针对这种特殊情况,当搜索文本为空时,可将multi_match 查询替换为match_all 。下面的handle_search() 函数版本增加了实现这一功能的逻辑。更新app.py 中的函数。

有了这个版本,您可以要求提供与某个类别相匹配的所有文件。请注意所有返回结果的得分都是 1.0,因为没有搜索词来计算得分。

此前

分页

下一个

分面搜索

准备好打造最先进的搜索体验了吗?

足够先进的搜索不是一个人的努力就能实现的。Elasticsearch 由数据科学家、ML 操作员、工程师以及更多和您一样对搜索充满热情的人提供支持。让我们联系起来,共同打造神奇的搜索体验,让您获得想要的结果。

亲自试用