Typesense z Laravel Scout jest świetnym domyślnym wyborem dla szybkiego wyszukiwania, typo tolerance i zwykłych filtrów. Elasticsearch wygrywa przy złożonych agregacjach, głębokim strojeniu relevance, heterogenicznych dokumentach lub ogromnych korpusach — jeśli zespół będzie utrzymywał mappingi, shardy, upgrade'y i incydenty klastra.
Przypadki uzasadniające Elasticsearch
Wybierz go dla agregacji analitycznych na milionach dokumentów, własnych analyzerów per język, synonymów i scoringu query-time, struktur nested lub eksperymentów rankingu z wyjaśnialnym wynikiem. Nie migruj dlatego, że Elasticsearch brzmi enterprise. Zapisz wolne zapytanie, wymagane facety, latency i błąd rankingu, którego Typesense nie rozwiązuje.
Zostaw bazę jako source of truth i zachowaj granicę service-layer, DTO oraz reindex job.
<?php
declare(strict_types=1);
final readonly class ArticleSearchFilters
{
/** @param list<string> $tags */
public function __construct(public string $query, public array $tags, public int $page = 1) {}
}
interface ArticleSearch { public function search(ArticleSearchFilters $filters): SearchResult; }
Implementacja Elasticsearch buduje query i mapuje hity; kontroler nie poznaje pól indeksu. Observer może kolejkować małe zmiany, ale wersjonowany reindex job musi odbudować cały indeks z bazy, zweryfikować liczniki i aliasy, potem atomowo przełączyć odczyty. Indeks zewnętrzny jest eventual consistent.
Porównuj wymaganie produktu, nie markę
| Potrzeba | Typesense | Elasticsearch |
|---|---|---|
| Katalog i typowe facety | Mocny default | Większy koszt operacyjny |
| Agregacje na ogromnym korpusie | Słaby fit | Natywny model agregacji |
| Analizery językowe i scoring | Mniej strojenia | Głęboka kontrola relevance |
Kontroler przekazuje typed DTO do kontraktu, a nie nazwy pól indeksu. Pełny reindex tworzy wersjonowany indeks, czyta DB w chunkach, weryfikuje count i atomowo przełącza alias; nie buduj live indeksu w miejscu.
<?php
declare(strict_types=1);
namespace App\\Search;
final readonly class SearchResult
{
/** @param list<array{id: int, title: string}> $items */
public function __construct(public array $items, public int $total) {}
}
interface ArticleSearch
{
public function search(ArticleSearchFilters $filters): SearchResult;
}
Observer może kolejkuje mały update po commicie. Testuj mapping na próbkach korpusu, relevance na reprezentatywnych zapytaniach oraz monitoruj lag indeksowania, heap, disk watermark, błędy odrzuconych requestów i restore snapshotu.
Koszt operacyjny jest częścią relevance
Mapping to schema. Testuj zmiany na próbkach prawdziwego korpusu, monitoruj rejected requesty, heap, disk watermarks, rozmiar shardów i indexing lag oraz ćwicz restore snapshotów. Więcej pokręteł poprawia ranking, ale każde rozszerza powierzchnię upgrade'u i incydentu.
Kiedy tego NIE używać
Nie wybieraj Elasticsearch dla małego katalogu potrzebującego typo tolerance i podstawowych facetów: Typesense jest prostszy i zwykle tańszy operacyjnie. Nie pozwalaj kontrolerowi wołać klienta bezpośrednio i nie traktuj udanego zapisu indeksu jako dowodu, że wszystkie repliki zwrócą wynik.