blog-post

分面搜索

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

分面搜索是现代搜索应用程序中与 自动补全 、拼写纠正和搜索关键词高亮一样重要的功能。尤其是在电子商务产品中。

当我们处理大量数据和相互关联的各种属性时,它会派上用场,无论是大小、颜色、制造商还是其他东西。当查询大量数据时,搜索结果通常包含大量不符合用户期望的条目。分面搜索允许最终用户明确指定他们希望搜索结果满足的维度。

简单来说,它提供了按搜索项目的属性过滤搜索结果的能力。

假设用户想找一部尼古拉斯·凯奇主演的电影,他在搜索栏中输入他的名字。这将返回来自不同年份、不同导演以及不同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

并将使用 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”互动课程,您可以了解更多关于分面搜索的知识,该课程提供命令行和网络面板以便于学习。

安装Manticore Search

安装Manticore Search