Lightweight dependency-free full-text search for PHP. Tokenises documents, stores an inverted index in a SQLite file, and scores results with Okapi BM25.
- BM25 ranked full-text search with fuzzy and boolean modes
- Search-as-you-type with built-in prefix matching
- Stopword filtering and Snowball stemming for 60+ languages
- Snippet extraction and result highlighting included
- One SQLite file per index — zero infrastructure
composer require onstage2426/fuzor- PHP 8.5+
- SQLite 3.37.0+
use Fuzor\Index;
// Create a new index
$index = Index::create('/path/to/articles.db');
// Open an existing index
$index = Index::open('/path/to/articles.db');Each document requires an id. All other fields are concatenated and indexed together.
// Add one item
$index->insert(['id' => 1, 'title' => 'Fast sedan', 'body' => 'Comfortable city car.']);
// Add many at once — faster than inserting one by one
$index->insertMany([
['id' => 1, 'title' => 'Fast sedan', 'body' => 'Comfortable city car with great fuel economy.'],
['id' => 2, 'title' => 'Off-road SUV', 'body' => 'Built for adventure. Handles any terrain.'],
['id' => 3, 'title' => 'Electric coupe','body' => 'Zero emissions, instant torque, sporty design.'],
]);
// Replace an item
$index->update(['id' => 1, 'title' => 'Updated sedan', 'body' => 'New content.']);
// Remove an item
$index->delete(2);$results = $index->search('city car');
$results = $index->search('economi', fuzzy: true); // tolerates typos
$results = $index->searchBoolean('sedan or coupe -electric');Pass a BCP 47 language tag to enable stopword filtering and stemming:
$index = Index::create($path, language: 'en');$snip = $index->snippeter();
echo $snip->snippet('fast connections', $doc['body']);
// "… offers fast broadband connections for …"
$hl = $index->highlighter();
echo $hl->highlight('fast sedan', $doc['title']);