分面搜索是寻找大海捞针的关键功能,为所有类型的应用程序提供更好的用户搜索体验。在本教程中,我们将弄清楚什么是分面搜索以及如何制作一个简单的分面搜索。
分面搜索是现代搜索应用程序中与 自动补全 、拼写纠正和搜索关键词高亮一样重要的功能。尤其是在电子商务产品中。
当我们处理大量数据和相互关联的各种属性时,它会派上用场,无论是大小、颜色、制造商还是其他东西。当查询大量数据时,搜索结果通常包含大量不符合用户期望的条目。分面搜索允许最终用户明确指定他们希望搜索结果满足的维度。
简单来说,它提供了按搜索项目的属性过滤搜索结果的能力。
假设用户想找一部尼古拉斯·凯奇主演的电影,他在搜索栏中输入他的名字。这将返回来自不同年份、不同导演以及不同IMDB评分和评级的电影搜索结果。
这可能看起来是这样的:
我们可以看到用户有34部与他最喜欢的演员相关的不同电影,但他如何从如此多的电影中选择观看的呢?
这就是分面搜索的用武之地。我们可以让用户通过选择额外的参数,如“年份”、“评分”、“导演姓名”和“IMDB评分”等,来找到他想观看的电影。
这就是分面搜索结果的样子:
通过分面搜索,我们使用户能够明确他们想要的内容。通过选择分面,用户获得了更相关的搜索结果,这使得搜索变得更好、更方便,并在用户体验和商业目标方面更有用。
那么,让我们看看分面搜索是如何工作的,以及您如何为自己的搜索应用程序制作它。
实现电影分面搜索
在Manticore Search中,有一种优化可以保留原始查询的结果集,并在每次分面计算时重用它。由于聚合应用于已经计算的文档子集,因此它们速度很快,并且在许多情况下,总执行时间仅比初始查询稍大。可以将分面添加到任何查询中,分面可以是任何属性或表达式。分面结果包含分面值和分面计数。可以使用 SQL SELECT 语句通过在查询的最后声明它们来获取分面,格式如下:
FACET {expr_list} [BY {expr_list}] [ORDER BY {expr | FACET()} {ASC | DESC}] [LIMIT [offset,] count]
当然,您可以在单个 多查询 中发送所有想要的查询,但如果您在寻找更优雅的解决方案,可以使用FACET子句。
现在我们将运行几个带有电影分面的查询,例如:
- Title_year
- Content_rating
- Director_name
并将使用 interval 获取IMDB评分的整数范围内的值。
让我们在自动补全课程的“电影”索引上尝试一些简单的分面,并对几个属性执行简单的分面。例如,让我们从“导演姓名”属性中选择“罗伯特·德尼罗”。
SELECT * FROM movies WHERE MATCH('robert de niro') LIMIT 10 FACET title_year FACET content_rating FACET director_name;
这会生成多个结果集,其中第一个是我们的主查询,其余的是分面。每个分面结果包含属性值和组计数。
+------------+----------+
| title_year | count(*) |
+------------+----------+
| 1987 | 1 |
| 1991 | 1 |
| 2005 | 1 |
| 1997 | 3 |
| 1974 | 1 |
| 2001 | 2 |
| 2002 | 2 |
| 1999 | 2 |
| 1985 | 1 |
| 1995 | 1 |
| 2016 | 2 |
| 2009 | 1 |
| 2004 | 4 |
| 1990 | 1 |
| 2013 | 3 |
| 2015 | 3 |
| 2011 | 2 |
| 2010 | 3 |
| 1996 | 3 |
| 1973 | 1 |
+------------+----------+
20 rows in set (0.10 sec)
+----------------+----------+
| content_rating | count(*) |
+----------------+----------+
| R | 37 |
| PG-13 | 12 |
| PG | 4 |
+----------------+----------+
3 rows in set (0.10 sec)
+----------------------+----------+
| director_name | count(*) |
+----------------------+----------+
| Brian De Palma | 1 |
| Martin Scorsese | 7 |
| John Polson | 1 |
| Quentin Tarantino | 1 |
| Francis Ford Coppola | 1 |
| John Herzfeld | 1 |
| Harold Ramis | 2 |
| Terry Gilliam | 1 |
| Michael Caton-Jones | 1 |
| James Mangold | 1 |
| Dan Mazer | 1 |
| Kirk Jones | 1 |
| Joel Schumacher | 1 |
| Nick Hamm | 1 |
| Peter Segal | 1 |
| Jonathan Jakubowicz | 1 |
| Scott Mann | 1 |
| David O. Russell | 2 |
| Gary McKendry | 1 |
| Jon Turteltaub | 1 |
+----------------------+----------+
20 rows in set (0.10 sec)
分面中的排序
默认情况下,分面结果未排序,并且限制为20行。每个分面可以有自己的限制子句。可以这样完成:
SELECT * FROM movies WHERE MATCH('robert de niro') LIMIT 10 FACET title_year LIMIT 5 FACET content_rating LIMIT 1 FACET director_name LIMIT 100;
+------------+----------+
| title_year | count(*) |
+------------+----------+
| 1987 | 1 |
| 1991 | 1 |
| 2005 | 1 |
| 1997 | 3 |
| 1974 | 1 |
+------------+----------+
5 rows in set (0.19 sec)
+----------------+----------+
| content_rating | count(*) |
+----------------+----------+
| R | 37 |
+----------------+----------+
1 row in set (0.19 sec)
+----------------------+----------+
| director_name | count(*) |
+----------------------+----------+
| Brian De Palma | 1 |
| Martin Scorsese | 7 |
| John Polson | 1 |
| Quentin Tarantino | 1 |
| Francis Ford Coppola | 1 |
.................................
| Tony Scott | 1 |
| Nancy Meyers | 1 |
| Frank Oz | 1 |
| Neil Jordan | 1 |
+----------------------+----------+
42 rows in set (0.19 sec)
如您所见,结果默认情况下未排序。每个分面可以有自己的排序规则,例如,我们可以使用COUNT(*)按计数对组进行排序:
SELECT * FROM movies WHERE MATCH('robert de niro') LIMIT 10 FACET title_year ORDER BY COUNT(*) DESC;
+------------+----------+
| title_year | count(*) |
+------------+----------+
| 2004 | 4 |
| 2013 | 3 |
| 2015 | 3 |
| 1997 | 3 |
| 2010 | 3 |
| 1996 | 3 |
| 2000 | 3 |
| 2001 | 2 |
| 2002 | 2 |
| 1999 | 2 |
| 2016 | 2 |
| 2011 | 2 |
| 2012 | 2 |
| 2008 | 2 |
| 1985 | 1 |
| 1991 | 1 |
| 1995 | 1 |
| 2009 | 1 |
| 1990 | 1 |
| 2005 | 1 |
+------------+----------+
20 rows in set (0.09 sec)
或者我们可以按属性排序,例如title_year:
SELECT * FROM movies WHERE MATCH('robert de niro') LIMIT 10 FACET title_year ORDER BY title_year DESC;
+------------+----------+
| title_year | count(*) |
+------------+----------+
| 2016 | 2 |
| 2015 | 3 |
| 2013 | 3 |
| 2012 | 2 |
| 2011 | 2 |
| 2010 | 3 |
| 2009 | 1 |
| 2008 | 2 |
| 2005 | 1 |
| 2004 | 4 |
| 2002 | 2 |
| 2001 | 2 |
| 2000 | 3 |
| 1999 | 2 |
| 1998 | 1 |
| 1997 | 3 |
| 1996 | 3 |
| 1995 | 1 |
| 1991 | 1 |
| 1990 | 1 |
+------------+----------+
20 rows in set (0.09 sec)
分面选择
在最简单的示例中,我们使用FACET attr_name,结果集将包含attr_name和count列。
但分面可以由多个属性组成:
SELECT * FROM movies WHERE MATCH('robert de niro') LIMIT 10 FACET director_facebook_likes,director_name BY director_name ORDER BY director_facebook_likes DESC;
+-------------------------+---------------------+----------+
| director_facebook_likes | director_name | count(*) |
+-------------------------+---------------------+----------+
| 17000 | Martin Scorsese | 7 |
| 16000 | Quentin Tarantino | 1 |
| 12000 | Tony Scott | 1 |
| 11000 | Harold Ramis | 2 |
| 737 | David O. Russell | 2 |
| 541 | Joel Schumacher | 1 |
| 517 | Michael Cimino | 1 |
| 446 | James Mangold | 1 |
| 287 | John Frankenheimer | 1 |
| 278 | Nancy Meyers | 1 |
| 277 | Neil Jordan | 1 |
| 272 | Barry Levinson | 3 |
| 226 | Jon Turteltaub | 1 |
| 116 | Jay Roach | 2 |
| 105 | Michael Caton-Jones | 1 |
| 102 | Martin Brest | 1 |
| 89 | Rodrigo Cortés | 1 |
| 88 | Peter Segal | 1 |
| 88 | George Tillman Jr. | 1 |
| 80 | Paul Weitz | 1 |
+-------------------------+---------------------+----------+
20 rows in set (0.01 sec)
分面中的表达式
在某些情况下,基于实际值的分面并不是我们想要的。最常见的例子是产品价格,因为我们可能有广泛的值范围。我们希望获得的不是实际值的分面,而是一个范围列表。这可以通过函数INTERVAL()轻松实现。
在我们的示例中,我们使用imdb_score,因为它是一个浮动值,我们显然不想按其分组,而是按仅整数值之间的范围进行分组:
SELECT * FROM movies WHERE MATCH('robert de niro') LIMIT 100 FACET title_year LIMIT 100 FACET content_rating LIMIT 100 FACET director_name LIMIT 100;
Empty set (0.09 sec)
+------------+----------+
| title_year | count(*) |
+------------+----------+
| 1987 | 1 |
| 1991 | 1 |
| 2005 | 1 |
| 1997 | 3 |
| 1974 | 1 |
| 2001 | 2 |
| 2002 | 2 |
| 1999 | 2 |
| 1985 | 1 |
| 1995 | 1 |
| 2016 | 2 |
| 2009 | 1 |
| 2004 | 4 |
| 1990 | 1 |
| 2013 | 3 |
| 2015 | 3 |
| 2011 | 2 |
| 2010 | 3 |
| 1996 | 3 |
| 1973 | 1 |
| 2000 | 3 |
| 1988 | 1 |
| 1977 | 1 |
| 1984 | 1 |
| 1980 | 1 |
| 2012 | 2 |
| 2008 | 2 |
| 1998 | 1 |
| 1976 | 1 |
| 1978 | 1 |
| 1989 | 1 |
+------------+----------+
31 rows in set (0.07 sec)
+----------------+----------+
| content_rating | count(*) |
+----------------+----------+
| R | 37 |
| PG-13 | 12 |
| PG | 4 |
+----------------+----------+
3 rows in set (0.08 sec)
+----------------------+----------+
| director_name | count(*) |
+----------------------+----------+
| Brian De Palma | 1 |
| Martin Scorsese | 7 |
| John Polson | 1 |
| Quentin Tarantino | 1 |
| Francis Ford Coppola | 1 |
| John Herzfeld | 1 |
| Harold Ramis | 2 |
| Terry Gilliam | 1 |
| Michael Caton-Jones | 1 |
| James Mangold | 1 |
| Dan Mazer | 1 |
| Kirk Jones | 1 |
| Joel Schumacher | 1 |
| Nick Hamm | 1 |
| Peter Segal | 1 |
| Jonathan Jakubowicz | 1 |
| Scott Mann | 1 |
| David O. Russell | 2 |
| Gary McKendry | 1 |
| Jon Turteltaub | 1 |
| Paul Weitz | 1 |
| Ethan Maniquis | 1 |
| Jerry Zaks | 1 |
| Jay Roach | 2 |
| George Tillman Jr. | 1 |
| Martin Brest | 1 |
| Garry Marshall | 1 |
| Sergio Leone | 1 |
| Rodrigo Cortés | 1 |
| Jon Avnet | 1 |
| John Frankenheimer | 1 |
| Bibo Bergeron | 1 |
| Barry Levinson | 3 |
| John Curran | 1 |
| Des McAnuff | 1 |
| Justin Zackham | 1 |
| Mary McGuckian | 1 |
| Michael Cimino | 1 |
| Tony Scott | 1 |
| Nancy Meyers | 1 |
| Frank Oz | 1 |
| Neil Jordan | 1 |
+----------------------+----------+
42 rows in set (0.08 sec)
<img src="Faceted-search-course-example-optimized.webp" alt="img">
互动课程
如果您参加我们的免费“Manticore Faceting”互动课程,您可以了解更多关于分面搜索的知识,该课程提供命令行和网络面板以便于学习。
