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

И будем использовать интервал , чтобы получить значения в пределах целочисленного диапазона для оценки 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)

Сортировка в ФАСЕТАХ


По умолчанию результаты фасетов не сортируются и также ограничены 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