多维搜索是寻找大海捞针的关键功能,为所有类型的应用程序提供更好的用户搜索体验。在本教程中,我们将弄清楚什么是多维搜索以及如何制作一个简单的多维搜索。
多维搜索是现代搜索应用程序中与 自动补全 、拼写校正和搜索关键词高亮等功能一样不可或缺的功能。尤其是在电子商务产品中。
当我们处理大量数据和彼此相关的各种属性时,单靠传统搜索往往不足以解决问题,无论是大小、颜色、制造商或其他因素。在查询大量数据时,搜索结果通常包括大量不符合用户预期的条目。多维搜索允许最终用户明确指定他们希望搜索结果满足的维度。
简单来说,它使得根据被搜索项目的属性进行搜索结果的过滤成为可能。
假设用户想找一些 starring Nicolas Cage 的电影,因此他在搜索框中输入了他的名字。这将返回来自不同年份、不同导演以及不同 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
并将使用 区间 来获取 IMDB 评分的整数范围内的值。
让我们尝试在“movies”索引的自动补全课程上进行一些简单的维度选择,并对几个属性进行简单的维度操作。例如,让我们从“导演姓名”属性中选择“robert de niro”。
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)
FACETS 中的排序
默认情况下,分面结果不进行排序,并且限制为 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 行,用时 (0.19 秒)
+----------------+----------+
| content_rating | count(*) |
+----------------+----------+
| R | 37 |
+----------------+----------+
1 行,用时 (0.19 秒)
+----------------------+----------+
| 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 行,用时 (0.19 秒)
正如你所看到的,结果默认是不排序的。每个分面可以有自己的排序规则,例如我们可以使用 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 行,用时 (0.09 秒)
或者我们可以按属性排序,例如 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 行,用时 (0.09 秒)
分面选择
在最简单的示例中,我们使用 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 行,用时 (0.01 秒)
分面中的表达式
在某些情况下,对实际值进行分面并不是我们想要的。最常见的例子是产品价格,因为我们可能有很大范围的值。与其对实际值进行分面,我们更希望得到一个范围列表。这可以通过函数 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;
空集 (0.09 秒)
+------------+----------+
| 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 行在集合中 (0.07 秒)
+----------------+----------+
| content_rating | count(*) |
+----------------+----------+
| R | 37 |
| PG-13 | 12 |
| PG | 4 |
+----------------+----------+
3 行在集合中 (0.08 秒)
+----------------------+----------+
| 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 行在集合中 (0.08 秒)
<img src="Faceted-search-course-example-optimized.webp" alt="img">
互动课程
如果您参加我们免费的“马丁科尔切分”互动课程,您可以更深入地了解分面搜索,该课程提供命令行和网络面板以便于学习。