Інтеграція Algolia для пошуку на сайті

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

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Інтеграція Algolia для пошуку на сайті
Середня
~2-3 робочих дні
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Інтеграція Algolia для пошуку на сайті

Algolia — пошуковий SaaS з власними серверами індексації та пошуком із толерантністю до опечаток. Замість написання повнотекстового пошуку на 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') // Ключ адміна — тільки на 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  // Ключ тільки для пошуку, публічний
);

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 дні.