Английский - не ваш любимый язык? Вы также можете прочитать этот блог на elastic.co на китайском, французском, немецком, японском, корейском, португальском или испанском языках.

Создание поискового опыта - тяжелая работа. На первый взгляд это может показаться простым: создайте панель поиска, поместите данные в базу данных, а затем попросите пользователя ввести топливные запросы к базе данных. Но есть много вещей, которые следует учитывать при моделировании данных, лежащей в основе логике и, конечно же, в общем дизайне и взаимодействии с пользователем.

Мы расскажем, как создать отличные возможности поиска на основе React, используя библиотеку Search UI Elastic с открытым исходным кодом. Это займет около 30 минут, после чего вы будете готовы запустить поиск в любое приложение.

Но сначала, что делает поиск зданий таким сложным?

Искать сложно

Несколько недель назад появилась отличная статья под названием Программисты лжи о поиске не верят. Он содержит исчерпывающий список ложных предположений, которые разработчики используют при разработке поиска.

Среди многих, кто верит в ложь:

  • «Клиенты, которые знают, что они ищут, будут искать это так, как вы ожидаете»
  • «Вы можете написать парсер запроса, который всегда будет успешно анализировать запрос»
  • «После настройки поиск будет работать так же на следующей неделе»
  • «Синонимы просты»
  • … И многие другие жемчужины - обязательно прочтите!

Вывод: у поиска есть много проблем, и они не только под капотом. Вам нужно подумать о том, как управлять состоянием, создавать компоненты для фильтрации, фасетирования, сортировки, разбивки на страницы, синонимов, языковой обработки и многого, многого… многого другого. Но в итоге:

Для создания качественного поиска требуются две сложные части: (1) поисковая машина, которая предоставляет API-интерфейсы для поиска, и (2) библиотека поиска, которая рисует поиск опыт.

Для поисковой системы мы рассмотрим эластичный поиск приложений.

Для поиска мы представим библиотеку поиска ОС: Пользовательский интерфейс поиска.

Когда мы закончим, он будет выглядеть вот так:

Поисковая система: эластичный поиск приложений

Поиск приложений доступен в виде платной управляемой службы или бесплатного самоуправляемого распространения. В этом руководстве мы будем использовать управляемую службу, но помните, что ваша команда может использовать пользовательский интерфейс поиска и поиск приложений с базовой лицензией бесплатно, если вы разместите ее самостоятельно.

План: проиндексируйте документы, которые представляют лучшие видеоигры всех времен, в поисковой системе, а затем спроектируйте и оптимизируйте поисковую систему для поиска по ним.

Во-первых, подпишитесь на 14-дневную пробную версию - кредитная карта не нужна.

Создайте двигатель. Вы можете выбирать из 13 разных языков.

Назовем его video-games и установим английский язык.

Загрузите набор данных лучших видеоигр, а затем загрузите его в поиск приложений с помощью импортера.

Затем щелкните Engine и выберите вкладку Учетные данные.

Создайте новый открытый ключ поиска с ограниченным доступом к движку только video-games.

Получите новый открытый ключ поиска и свой идентификатор хоста.

Несмотря на то, что это выглядело не так уж и много, теперь у нас есть полностью работающая поисковая система, которая готова выполнять поиск по нашим данным видеоигр с помощью усовершенствованного поискового API.

Вот что мы уже сделали:

  • Создал поисковик.
  • Загруженные документы.
  • Создал схему по умолчанию.
  • Получены одноразовые учетные данные с заданной областью действия, которые мы можем предоставить браузеру.

На этом пока что по поиску приложений.

Давайте перейдем к созданию возможностей поиска с помощью интерфейса поиска.

Библиотека поиска: интерфейс поиска

Мы собираемся использовать утилиту create-react-app scaffolding для создания приложения React:

npm install -g create-react-app
create-react-app video-game-search --use-npm
cd video-game-search

В рамках этой основы мы установим интерфейс поиска и коннектор поиска приложений:

npm install — save @elastic/react-search-ui @elastic/search-ui-app-search-connector

И запускаем приложение в режиме разработки:

npm start

Откройте src/App.js в своем любимом текстовом редакторе.

Мы начнем с шаблонного кода, а затем распакуем его.

Обратите внимание на комментарии!

// Step #1, import Statements
import React from "react";
import AppSearchAPIConnector from "@elastic/search-ui-app-search-connector";
import { SearchProvider, Results, SearchBox } from "@elastic/react-search-ui";
import { Layout } from "@elastic/react-search-ui-views";
import "@elastic/react-search-ui-views/lib/styles/styles.css";
// Step #2, The Connector
const connector = new AppSearchAPIConnector({
  searchKey: "[YOUR_SEARCH_KEY]",
  engineName: "video-games",
  hostIdentifier: "[YOUR_HOST_IDENTIFIER]"
});
// Step #3: Configuration Options
const configurationOptions = {
  apiConnector: connector
  // Let's fill this in together.
};
// Step #4, SearchProvider: The Finishing Touches.
export default function App() {
  return (
    <SearchProvider config={configurationOptions}>
      <div className="App">
        <Layout
        // Let's fill this in together.
        />
      </div>
    </SearchProvider>
  );
}

Шаг 1: импорт заявлений

Нам нужно будет импортировать зависимости нашего пользовательского интерфейса поиска и React.

Основные компоненты, соединитель и компоненты представления содержатся в трех разных пакетах: @elastic/search-ui-app-search-connector, @elastic/react-search-ui и @elastic/react-search-ui-views. По мере продолжения мы узнаем больше о каждом из них.

import React from "react";
import AppSearchAPIConnector from "@elastic/search-ui-app-search-connector";
import { SearchProvider, Results, SearchBox } from "@elastic/react-search-ui";
import { Layout } from "@elastic/react-search-ui-views";
import "@elastic/react-search-ui-views/lib/styles/styles.css";

Мы также будем импортировать для этого проекта таблицу стилей по умолчанию, которая придаст нам приятный вид без написания строчки нашего собственного CSS:

import "@elastic/react-search-ui-views/lib/styles/styles.css";

Шаг 2: соединитель

У нас есть открытый ключ поиска и идентификатор хоста из поиска приложений.

Пора заставить их работать!

Объект коннектора в пользовательском интерфейсе поиска использует учетные данные для подключения к поиску приложений и расширенному поиску:

const connector = new AppSearchAPIConnector({
  searchKey: "[YOUR_SEARCH_KEY]",
  engineName: "video-games",
  hostIdentifier: "[YOUR_HOST_IDENTIFIER]"
});

Пользовательский интерфейс поиска работает с любым поисковым API. Но коннекторы делают так, что поисковый API «просто работает» без какой-либо более глубокой настройки.

Шаг 3: параметры конфигурации

Прежде чем мы углубимся в configurationOptions, давайте поразмышляем.

Мы импортировали набор данных в нашу поисковую систему. Но что это за данные?

Чем больше мы знаем о наших данных, тем лучше мы понимаем, как представить эти данные поисковикам. Это поможет нам настроить поисковую систему.

Давайте посмотрим на один объект, лучший из всех в этом наборе данных:

{
  "id": "final-fantasy-vii-ps-1997",
  "name": "Final Fantasy VII",
  "year": 1997,
  "platform": "PS",
  "genre": "Role-Playing",
  "publisher": "Sony Computer Entertainment",
  "global_sales": 9.72,
  "critic_score": 92,
  "user_score": 9,
  "developer": "SquareSoft",
  "image_url": "https://r.hswstatic.com/w_907/gif/finalfantasyvii-MAIN.jpg"
}

Мы видим, что у него есть несколько текстовых полей, таких как name, year, platform и так далее, и некоторые числовые поля, такие как critic_score, global_sales и user_score.

Если мы зададим три ключевых вопроса, мы узнаем достаточно, чтобы создать надежную систему поиска:

  1. Как большинство людей будет искать? По названию видеоигры.
  2. Что большинство людей захотят увидеть в результате? Название видеоигры, ее жанр, издатель, оценка и платформа.
  3. Как большинство людей будет фильтровать, сортировать и обрабатывать? По рейтингу, жанру, издателю и платформе.

Затем мы можем перевести эти ответы в наш configurationOptions:

const configurationOptions = {
  apiConnector: connector,
  searchQuery: {
    search_fields: {
      // 1. Search by name of video game.
      name: {}
    },
    // 2. Results: name of the video game, its genre, publisher, scores, and platform.
    result_fields: {
      name: {
        // A snippet means that matching search terms will be highlighted via <em> tags.
        snippet: {
          size: 75, // Limit the snippet to 75 characters.
          fallback: true // Fallback to a "raw" result.
        }
      },
      genre: {
        snippet: {
          size: 50,
          fallback: true
        }
      },
      publisher: {
        snippet: {
          size: 50,
          fallback: true
        }
      },
      critic_score: {
        // Scores are numeric, so we won't attempt to snippet these, we'll just use the raw
        // value.
        raw: {}
      },
      user_score: {
        raw: {}
      },
      platform: {
        snippet: {
          size: 50,
          fallback: true
        }
      },
      image_url: {
        raw: {}
      }
    },
    // 3. Facet by scores, genre, publisher, and platform, which we'll use to build filters later.
    facets: {
      user_score: {
        type: "range",
        ranges: [
          { from: 0, to: 5, name: "Not good" },
          { from: 5, to: 7, name: "Not bad" },
          { from: 7, to: 9, name: "Pretty good" },
          { from: 9, to: 10, name: "Must play!" }
        ]
      },
      critic_score: {
        type: "range",
        ranges: [
          { from: 0, to: 50, name: "Not good" },
          { from: 50, to: 70, name: "Not bad" },
          { from: 70, to: 90, name: "Pretty good" },
          { from: 90, to: 100, name: "Must play!" }
        ]
      },
      genre: { type: "value", size: 100 },
      publisher: { type: "value", size: 100 },
      platform: { type: "value", size: 100 }
    }
  }
};

Мы подключили интерфейс поиска к нашей поисковой системе, и теперь у нас есть параметры, которые определяют, как мы будем искать данные, отображать наши результаты, а затем изучать эти результаты. Но нам нужно что-то, чтобы связать все с динамическими компонентами интерфейса поиска.

Шаг 4: SearchProvider

Объект, который ими управляет; SearchProvider - это место, где вложены все остальные компоненты.

Пользовательский интерфейс поиска предоставляет Layout компонент, который используется для рисования типичного макета поиска. Существуют варианты глубокой настройки, но мы не будем вдаваться в них в этом руководстве.

Мы сделаем две вещи:

  1. Переходите от configurationOptions к SearchProvider.
  2. Поместите несколько структурных строительных блоков в Layout и добавьте два основных компонента: SearchBox и Results.
export default function App() {
  return (
    <SearchProvider config={configurationOptions}>
      <div className="App">
        <Layout
          header={<SearchBox />}
          // titleField is the most prominent field within a result: the result header.
          bodyContent={<Results titleField="name" urlField="image_url" />}
        />
      </div>
    </SearchProvider>
  );
}

На этом этапе у нас есть основы, настроенные для интерфейса. Есть еще несколько деталей, которые нужно проработать на сервере, прежде чем мы сможем запустить это. Мы также должны поработать над моделью релевантности, чтобы поиск точно соответствовал уникальным потребностям этого проекта.

Выключить Поиск приложений ~

Вернуться в лабораторию

Поиск приложений имеет мощные и усовершенствованные функции поисковой системы. Это делает некогда сложную настройку намного более приятной. Мы можем выполнять точную настройку релевантности и плавные изменения схемы за несколько щелчков мышью.

Сначала мы настроим схему, чтобы увидеть ее в действии.

Войдите в поиск приложений, введите video-games Engine, а затем нажмите Схема в разделе Управление.

Схема появляется. Каждое из 11 полей по умолчанию считается текстовым.

В объекте configurationOptions мы определили два аспекта диапазона, которые помогают нам искать числа: user_score и critic_score. Чтобы фасет диапазона работал должным образом, тип поля должен быть числом.

Мы также можем захотеть сделать некоторые улучшения и настройку релевантности на основе числовых значений.

Щелкните раскрывающееся меню рядом с каждым полем, измените его на число, затем нажмите Типы обновления:

Движок переиндексирует на лету. А позже - когда мы добавим компоненты фасетирования в наш макет - фильтры диапазона будут работать так, как мы ожидали.

А теперь перейдем к самым интересным вещам.

Этот раздел очень важен

Существует три основных функции релевантности: Синонимы, Настройки и Настройка релевантности.

Выберите каждую функцию в разделе Настройки поиска на боковой панели:

Синонимы

Кто-то водит машины, кто-то на автомобилях, кто-то может водить драндулет. Интернет глобален, и люди во всем мире используют разные слова для описания вещей. Синонимы помогают создавать наборы терминов, которые считаются одним и тем же.

Мы знаем, что в случае поисковой системы видеоигр люди захотят найти Final Fantasy. Но, возможно, вместо этого они наберут FF.

Щелкните Синонимы, затем выберите Создать набор синонимов и введите условия:

Нажмите Сохранить. Вы можете добавить любое количество наборов синонимов.

Теперь поисковый запрос FF будет иметь такой же вес, что и поисковый запрос Final Fantasy.

Курирования

Кураторы - фаворит. Что делать, если кто-то выполняет поиск по запросу Final Fantasy или FF? В серии много игр - что они получат?

По умолчанию первая пятерка результатов выглядит так:

  1. Последняя фантазия VIII
  2. Последняя фантазия X
  3. Final Fantasy Tactics
  4. Последняя фантазия IX
  5. Последняя фантазия XIII

Это не кажется правильным ... Final Fantasy VII была лучшей игрой Final Fantasy из всех. И Final Fantasy XIII была не очень хороша! 😜

Можем ли мы сделать так, чтобы кто-то, ищущий Final Fantasy, получил Final Fantasy VII в качестве первого результата? И можем ли мы удалить Final Fantasy XII из результатов поиска?

Мы можем!

Нажмите Курирования и введите запрос: Final Fantasy.

Затем перетащите документ Final Fantasy VII в раздел Продвигаемые документы, взявшись за руль в крайней левой части стола. Затем нажмите кнопку Скрыть результат в документе Final Fantasy XIII - глаз с проходящей через него линией:

Любой, кто будет искать Final Fantasy или FF, сначала увидит Final Fantasy VII.

… И они не увидят Final Fantasy XIII. Ха!

Мы можем продвигать и скрывать многие документы. Мы даже можем сортировать продвигаемые документы, чтобы полностью контролировать то, что отображается в верхней части каждого запроса.

Настройка релевантности

Нажмите Настройка релевантности на боковой панели.

Мы ищем в одном текстовом поле: поле name. Но что, если у нас есть несколько текстовых полей, которые люди будут искать, например поле name и поле description? Набор данных видеоигр, который мы используем, не содержит поля описания, поэтому мы будем подделывать некоторые документы, чтобы все продумать.

Скажем, наши документы выглядели примерно так:

{ 
  "name":"Magical Quest",
  "description": "A dangerous journey through caves and such." 
},
{ 
  "name":"Dangerous Quest",
  "description": "A magical journey filled with magical magic. Highly magic." 
}

Если кто-то хотел найти игру Magical Quest, он вводил это как запрос. Но первым результатом будет Опасный квест:

Почему? Потому что слово «магический» трижды присутствует в описании Dangerous Quest, и поисковая система не узнает, что одно поле важнее другого. После этого он получит более высокий рейтинг Dangerous Quest. Эта загадка объясняет, почему существует настройка релевантности.

Мы можем выбрать поле и, среди прочего, повысить его релевантность:

Мы видим, что при увеличении веса правый элемент - Magical Quest - поднимается наверх, потому что поле name становится более значимым. Все, что нам нужно сделать, это перетащить ползунок на более высокое значение и нажать Сохранить.

Теперь мы использовали Поиск приложений, чтобы:

  1. Измените схему и замените поля user_score и critic_score на число.
  2. Настройте модель релевантности.

И на этом завершаются причудливые функции «приборной панели» - у каждой есть соответствующая конечная точка API, которую вы можете использовать для программной работы, если графический интерфейс вам не нравится.

А теперь давайте закончим пользовательский интерфейс.

Последние штрихи

На этом этапе ваш пользовательский интерфейс должен быть работоспособным. Попробуйте несколько запросов и покопайтесь. Первое, что бросается в глаза, это то, что нам не хватает инструментов для изучения наших результатов, таких как фильтрация, фасетирование, сортировка и т. Д., Но поиск работает. Нам нужно будет дополнить пользовательский интерфейс.

В исходный файл src/App.js мы импортировали три основных компонента:

import { SearchProvider, Results, SearchBox } from "@elastic/react-search-ui";

Давайте добавим еще кое-что, учитывая то, что мы определили для наших параметров конфигурации.

Импорт следующих компонентов активирует недостающие возможности пользовательского интерфейса:

  • PagingInfo: отображение информации на текущей странице.
  • ResultsPerPage: Настройте количество результатов, отображаемых на каждой странице.
  • Paging: переходить по разным страницам.
  • Facet: фильтруйте и исследуйте данные способами, уникальными для каждого типа данных.
  • Sorting: изменение ориентации результатов для данного поля.
import {
  PagingInfo,
  ResultsPerPage,
  Paging,
  Facet,
  SearchProvider,
  Results,
  SearchBox,
  Sorting
} from "@elastic/react-search-ui";

После импорта компоненты могут быть помещены в компоновку.

Компонент Layout делит страницу на разделы, и компоненты могут быть помещены в эти разделы с помощью свойств.

Он содержит разделы для:

  1. header: Поле / панель поиска.
  2. bodyContent: Контейнер результатов.
  3. sideContent: Боковая панель, содержащая фасеты и параметры сортировки.
  4. bodyHeader: «Обертка» результатов с контекстной информацией, такой как текущая страница, количество результатов на странице.
  5. bodyFooter: Опции перелистывания для быстрой навигации между страницами.

Компоненты отображают данные. Данные выбираются на основе настроек поиска, которые мы предоставили в configurationOptions. Теперь мы разместим каждый компонент в соответствующем разделе Layout.

Например, в configurationOptions мы описали 5 измерений фасетирования, поэтому создадим 5 Facet компонентов. Каждый Facet компонент будет использовать field опору как ключ к нашим данным.

Мы поместим их в раздел sideContent вместе с нашим Sorting компонентом, а затем поместим компоненты Paging, PagingInfo, ResultsPerPage в разделы, которые им больше всего подходят:

<Layout
  header={<SearchBox />}
  bodyContent={<Results titleField="name" urlField="image_url" />}
  sideContent={
    <div>
      <Sorting
        label={"Sort by"}
        sortOptions={[
          {
            name: "Relevance",
            value: "",
            direction: ""
          },
          {
            name: "Name",
            value: "name",
            direction: "asc"
          }
        ]}
      />
      <Facet field="user_score" label="User Score" />
      <Facet field="critic_score" label="Critic Score" />
      <Facet field="genre" label="Genre" />
      <Facet field="publisher" label="Publisher" isFilterable={true} />
      <Facet field="platform" label="Platform" />
    </div>
  }
  bodyHeader={
    <>
      <PagingInfo />
      <ResultsPerPage />
    </>
  }
  bodyFooter={<Paging />}
/>

Теперь давайте посмотрим на опыт поиска в локальной среде разработки.

Намного лучше! У нас есть богатые возможности для изучения результатов поиска.

Мы добавили пару дополнительных плюсов, таких как несколько вариантов сортировки, и сделали фильтруемый аспект издателя, добавив один флаг. Попробуйте выполнить поиск с пустым запросом и изучите все варианты.

Наконец ... давайте взглянем на одну последнюю особенность поиска . Это популярный…

Автозаполнение.

Вы меняете автозаполнение

Поисковики любят автозаполнение, потому что оно обеспечивает мгновенную обратную связь. Предложения бывают двух видов: результат и запрос. В зависимости от того, какой вариант, поисковик получит релевантные результаты или потенциальные запросы, которые могут привести к результатам.

Мы собираемся сосредоточиться на автозаполнении как форме «предложения запроса».

Это требует двух быстрых изменений.

Во-первых, нам нужно добавить автозаполнение к объекту configurationOptions:

const configurationOptions = {
  // ...
  autocompleteQuery: {
    suggestions: {
      types: {
        documents: {
          // Which fields to search for suggestions.
          fields: ["name"]
        },
        // How many suggestions appear.
        size: 5
      }
    }
  }
  // ...
};

Во-вторых, нам нужно включить автозаполнение как функцию SearchBox:

// ...
<Layout
  // ...
  header={<SearchBox autocompleteSuggestions={true} />}
/>
// ...

Ага, вот и все.

Попробуйте выполнить поиск - по мере ввода появляются подсказки для автозаполнения.

Резюме

Теперь у нас есть красивый и функциональный поиск. И мы избежали целого ряда ловушек, которые часто ловят людей при попытке реализовать поиск. Неплохо для 30 минут, не так ли?



Search UI - это гибкий современный фреймворк на React для быстрой разработки возможностей поиска. Elastic App Search - это надежная поисковая система, построенная на базе Elasticsearch. Это платная управляемая услуга, или вы можете запустить ее самостоятельно с достаточной базовой лицензией.

Нам будет интересно узнать, что вы создаете с помощью интерфейса поиска.

Зайдите в Gitter и подумайте о том, чтобы внести свой вклад в проект!