Search by

illuma-law / laravel-hybrid-search

miguelenes

Portable Full-Text Search (FTS) schema macros and the Reciprocal Rank Fusion (RRF) algorithm.

Package info

github.com/eloquage/laravel-hybrid-search

pkg:composer/illuma-law/laravel-hybrid-search

Fund package maintenance!

illuma-law

Statistics

Installs: 65

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 2

v1.0.8 2026-10-23 14:01 UTC

README

Laravel Hybrid Search result streams merging

Portable full-text search macros and Reciprocal Rank Fusion (RRF) for Laravel. Use the same query API across PostgreSQL, MySQL, SQL Server, and SQLite, then combine ranked results from separate search systems.

Tests Latest Version License

Requires PHP 8.3+ and Laravel 11, 12, or 13.

Install

composer require illuma-law/laravel-hybrid-search

Create and query a full-text index

The hybridFullText migration macro uses native full-text indexes on PostgreSQL and MySQL. SQLite uses FTS5 with an external-content table and triggers, and expects an integer id primary key. SQL Server requires you to create a full-text catalog and index manually.

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

Schema::table('articles', function (Blueprint $table): void {
    $table->hybridFullText(['title', 'body'], 'articles_search');
});

Query with whereHybridFullText on a query builder or Eloquent model:

$articles = Article::query()
    ->whereHybridFullText(['title', 'body'], 'laravel macros')
    ->limit(20)
    ->get();

// Exclude matching rows:
$otherArticles = Article::query()
    ->whereHybridFullText('title', 'deprecated', not: true)
    ->get();

Use $table->dropHybridFullText('articles_search') in the rollback migration.

Combine ranked result lists

RRF combines ranked IDs from sources such as keyword and vector search. This package does not generate embeddings or run vector searches; pass it the ranked results from your search providers.

use IllumaLaw\HybridSearch\ReciprocalRankFusion;

$scores = ReciprocalRankFusion::combine([
    'keyword' => collect([12, 8, 3]),
    'vector' => collect([8, 5, 12]),
]);

$rankedIds = $scores->keys(); // IDs sorted by descending RRF score

The default RRF constant is 60; pass k: to override it.

Optional Scout helpers

Use EnsuresScoutKeyIsString when a Scout engine requires string keys. The optional ScoutEngineCheck requires spatie/laravel-health:

composer require spatie/laravel-health
use IllumaLaw\HybridSearch\HealthChecks\ScoutEngineCheck;
use Spatie\Health\Facades\Health;

Health::checks([ScoutEngineCheck::new()]);

The check supports Meilisearch, Typesense, and Algolia; local Scout drivers are skipped.

Test

composer test
composer analyse
vendor/bin/pint --test

MIT licensed. See LICENSE.