blog-post

多维搜索

多维搜索是寻找大海捞针的关键功能,为所有类型的应用程序提供更好的用户搜索体验。在本教程中,我们将弄清楚什么是多维搜索以及如何制作一个简单的多维搜索。

多维搜索是现代搜索应用程序中与 自动补全 、拼写校正和搜索关键词高亮等功能一样不可或缺的功能。尤其是在电子商务产品中。

当我们处理大量数据和彼此相关的各种属性时,单靠传统搜索往往不足以解决问题,无论是大小、颜色、制造商或其他因素。在查询大量数据时,搜索结果通常包括大量不符合用户预期的条目。多维搜索允许最终用户明确指定他们希望搜索结果满足的维度。

简单来说,它使得根据被搜索项目的属性进行搜索结果的过滤成为可能。

假设用户想找一些 starring Nicolas Cage 的电影,因此他在搜索框中输入了他的名字。这将返回来自不同年份、不同导演以及不同 IMDB 评分和评级的电影搜索结果。

这可能看起来是这样的:

img我们可以看到用户有34部与他最喜欢的演员相关的电影,但他如何从如此多的电影中选择观看的内容呢?

这就是多维搜索的用武之地。我们可以让用户通过提供选择额外参数的能力,例如“年份”、“评级”、“导演姓名”和“IMDB 评分”等,来找到他想看的电影。

这就是多维搜索结果的样子:

img通过多维搜索,我们能够确定用户真正想要的内容。通过选择不同的维度,用户可以获得更相关的搜索结果,这使得搜索更加出色、更方便,并在用户体验和商业目标方面更具意义。

所以,让我们看看多维搜索是如何工作的,以及你如何为你的搜索应用程序自己实现它。

实施电影多维搜索


在 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">

互动课程

如果您参加我们免费的“马丁科尔切分”互动课程,您可以更深入地了解分面搜索,该课程提供命令行和网络面板以便于学习。

安装Manticore Search

安装Manticore Search