Интеграция Algolia для поиска на сайте

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Интеграция Algolia для поиска на сайте
Средняя
~2-3 рабочих дня
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1214
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    852
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    823
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    815

Интеграция Algolia для поиска на сайте

Algolia — поисковый SaaS с собственными серверами индексации и typo-tolerant поиском. Вместо написания full-text search на PostgreSQL или Elasticsearch берётся готовый индекс, API и InstantSearch-компоненты для фронтенда. Основная работа — наладить синхронизацию данных и настроить релевантность.

Как работает Algolia

Данные загружаются в индекс (аналог таблицы) в виде JSON-объектов с атрибутами. Поиск идёт по настроенным полям с учётом опечаток, синонимов и весов. Каждый объект должен иметь уникальный objectID.

Ограничения бесплатного плана: 10 000 записей, 10 000 поисковых запросов в месяц. Для небольших сайтов хватает.

Установка и индексация

composer require algolia/algoliasearch-client-php
npm install algoliasearch instantsearch.js
# или для React:
npm install algoliasearch react-instantsearch

Простая индексация через PHP SDK:

use Algolia\AlgoliaSearch\SearchClient;

$client = SearchClient::create(
    config('algolia.app_id'),
    config('algolia.admin_api_key') // Admin key — только на backend!
);

$index = $client->initIndex('products');

// Индексация одного объекта
$index->saveObject([
    'objectID'    => 'product-' . $product->id,
    'name'        => $product->name,
    'description' => strip_tags($product->description),
    'category'    => $product->category->name,
    'price'       => $product->price,
    'in_stock'    => $product->stock > 0,
    'image_url'   => $product->thumbnail_url,
    'slug'        => $product->slug,
]);

// Пакетная индексация
$objects = Product::with('category')->get()->map(fn($p) => [
    'objectID'  => 'product-' . $p->id,
    'name'      => $p->name,
    'category'  => $p->category->name,
    'price'     => $p->price,
    'in_stock'  => $p->stock > 0,
])->toArray();

$index->saveObjects($objects);

Синхронизация через Observer

Индекс должен обновляться при изменении данных. Observer в Laravel:

class ProductObserver {
    private SearchClient $algolia;

    public function __construct() {
        $this->algolia = SearchClient::create(
            config('algolia.app_id'),
            config('algolia.admin_api_key')
        );
    }

    public function saved(Product $product): void {
        $this->algolia->initIndex('products')->saveObject(
            $this->toAlgoliaRecord($product)
        );
    }

    public function deleted(Product $product): void {
        $this->algolia->initIndex('products')->deleteObject('product-' . $product->id);
    }

    private function toAlgoliaRecord(Product $product): array {
        return [
            'objectID'    => 'product-' . $product->id,
            'name'        => $product->name,
            'description' => Str::limit(strip_tags($product->description), 300),
            'category'    => $product->category->name,
            'tags'        => $product->tags->pluck('name')->toArray(),
            'price'       => (float)$product->price,
            'in_stock'    => $product->stock > 0,
            'image_url'   => $product->thumbnail_url,
            'slug'        => $product->slug,
            'updated_at'  => $product->updated_at->timestamp,
        ];
    }
}

Регистрация в AppServiceProvider:

Product::observe(ProductObserver::class);

Настройка индекса: релевантность и фильтры

Через Dashboard или API настраиваются атрибуты для поиска и фильтрации:

$index->setSettings([
    // По каким полям ищем (порядок = приоритет)
    'searchableAttributes' => [
        'name',
        'unordered(category)',
        'unordered(description)',
        'unordered(tags)',
    ],

    // Атрибуты для фильтрации и фасетов
    'attributesForFaceting' => [
        'filterOnly(price)',
        'category',
        'in_stock',
    ],

    // Что показываем в результатах (исключаем тяжёлые поля)
    'attributesToRetrieve' => [
        'objectID', 'name', 'price', 'category', 'image_url', 'slug', 'in_stock',
    ],

    // Подсветка вхождений
    'attributesToHighlight' => ['name', 'category'],

    // Поиск с опечатками
    'typoTolerance' => true,
    'minWordSizefor1Typo' => 4,
    'minWordSizefor2Typos' => 8,

    // Ранжирование
    'ranking' => [
        'typo', 'geo', 'words', 'filters', 'proximity', 'attribute',
        'exact', 'custom',
    ],
    'customRanking' => ['desc(updated_at)'],
]);

React-компонент поиска

import algoliasearch from 'algoliasearch/lite';
import {
    InstantSearch,
    SearchBox,
    Hits,
    RefinementList,
    Pagination,
    Highlight,
    Configure,
} from 'react-instantsearch';

const searchClient = algoliasearch(
    import.meta.env.VITE_ALGOLIA_APP_ID,
    import.meta.env.VITE_ALGOLIA_SEARCH_KEY  // Search-only key, публичный
);

const ProductHit: React.FC<{ hit: ProductRecord }> = ({ hit }) => (
    <a href={`/products/${hit.slug}`} className="flex gap-3 p-3 hover:bg-gray-50 rounded">
        <img src={hit.image_url} alt={hit.name} className="w-12 h-12 object-cover rounded" />
        <div>
            <p className="font-medium">
                <Highlight attribute="name" hit={hit} />
            </p>
            <p className="text-sm text-gray-500">
                <Highlight attribute="category" hit={hit} />
            </p>
            <p className="font-semibold">{hit.price.toLocaleString()} ₽</p>
        </div>
    </a>
);

export const SiteSearch: React.FC = () => (
    <InstantSearch searchClient={searchClient} indexName="products">
        <Configure hitsPerPage={20} filters="in_stock:true" />
        <SearchBox
            placeholder="Поиск товаров..."
            classNames={{ input: 'w-full border rounded-lg px-4 py-2' }}
        />
        <div className="flex gap-6 mt-4">
            <aside className="w-48 shrink-0">
                <p className="font-medium mb-2">Категория</p>
                <RefinementList attribute="category" />
            </aside>
            <div className="flex-1">
                <Hits hitComponent={ProductHit} />
                <Pagination className="mt-4" />
            </div>
        </div>
    </InstantSearch>
);

Поиск в модальном окне (Command Palette)

Для быстрого доступа — поиск в оверлее, без перехода на страницу результатов:

import { useSearchBox, useHits } from 'react-instantsearch';

const CommandSearch: React.FC<{ onSelect: (hit: any) => void }> = ({ onSelect }) => {
    const { query, refine } = useSearchBox();
    const { hits } = useHits();

    return (
        <div className="fixed inset-0 bg-black/50 z-50 flex items-start justify-center pt-20">
            <div className="bg-white rounded-xl shadow-2xl w-full max-w-xl overflow-hidden">
                <input
                    autoFocus
                    value={query}
                    onChange={e => refine(e.target.value)}
                    placeholder="Поиск..."
                    className="w-full px-4 py-3 text-lg outline-none border-b"
                />
                <div className="max-h-96 overflow-y-auto">
                    {hits.map(hit => (
                        <button key={hit.objectID} onClick={() => onSelect(hit)}
                            className="w-full text-left px-4 py-2 hover:bg-blue-50">
                            {hit.name}
                        </button>
                    ))}
                </div>
            </div>
        </div>
    );
};

Аналитика поиска

Algolia собирает клики и конверсии для улучшения релевантности. Подключение:

import { createInsightsMiddleware } from 'instantsearch.js/es/middlewares';
import aa from 'search-insights';

aa('init', {
    appId: import.meta.env.VITE_ALGOLIA_APP_ID,
    apiKey: import.meta.env.VITE_ALGOLIA_SEARCH_KEY,
    useCookie: true,
});

// В InstantSearch
<InstantSearch
    searchClient={searchClient}
    indexName="products"
    middlewares={[createInsightsMiddleware({ insightsClient: aa })]}
    insights
>

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

Сроки реализации

Базовая интеграция с индексацией через Observer и поиском на фронтенде: 1–2 дня. Добавление фасетной фильтрации, Command Palette, аналитики и настройки релевантности: 3–4 дня. Мультиязычные индексы (отдельный индекс для каждого языка) с синхронизацией: плюс 1–2 дня.