teamtnt/tntsearch
TNTSearch is a pure-PHP full-text search engine with fuzzy search, “search as you type”, geo-search, stemming, custom tokenizers, BM25 ranking, boolean queries, highlighting, and dynamic index updates—fast to integrate and deploy via Packagist.
## Getting Started
### Minimal Setup in Laravel
1. **Install the package**:
```bash
composer require teamtnt/tntsearch
Configure the connection in config/tntsearch.php (or publish it first):
php artisan vendor:publish --provider="TeamTNT\TNTSearch\TNTSearchServiceProvider"
Example config:
'connections' => [
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST'),
'database' => env('DB_DATABASE'),
'username' => env('DB_USERNAME'),
'password' => env('DB_PASSWORD'),
'storage' => storage_path('app/tntsearch'),
],
],
First use case: Basic search
use TeamTNT\TNTSearch\TNTSearch;
$tnt = new TNTSearch;
$tnt->loadConfig(config('tntsearch.connections.mysql'));
// Create an index
$indexer = $tnt->createIndex('articles.index');
$indexer->query('SELECT id, title, content FROM articles');
$indexer->run();
// Search
$results = $tnt->search('laravel full text search', 10);
createIndex() with a SQL query to index existing data.
$indexer = $tnt->createIndex('products.index');
$indexer->query('SELECT id, name, description FROM products WHERE active = 1');
$indexer->run();
$index = $tnt->getIndex('articles.index');
$index->insert(['id' => 123, 'title' => 'New Article', 'content' => '...']);
$index->update(123, ['title' => 'Updated Title']);
$index->delete(456);
$results = $tnt->searchBoolean('(laravel AND "full text") NOT tutorial');
$tnt->fuzziness(true); // Enable fuzzy matching
$results = $tnt->search('laravel', 5); // Matches "laravel", "laravell", etc.
$geoSearch = $tnt->geoSearch('restaurants.index');
$nearby = $geoSearch->findNearest(
['latitude' => 40.7128, 'longitude' => -74.0060], // NYC
5, // 5km radius
10 // Top 10 results
);
laravel-scout-tntsearch-driver for seamless Scout integration:
composer require teamtnt/laravel-scout-tntsearch-driver
Configure in config/scout.php:
'driver' => 'tntsearch',
Now use Scout’s familiar API:
$user->search('John Doe'); // Uses TNTSearch under the hood
$classifier = new \TeamTNT\TNTSearch\Classifier\TNTClassifier();
$classifier->learn('Laravel is awesome', 'framework');
$classifier->learn('Symfony is great', 'framework');
$guess = $classifier->predict('This uses PHP');
// Returns: ['label' => 'framework', 'confidence' => 0.92]
Save/load for reuse:
$classifier->save(storage_path('app/sports.cls'));
$loaded = new \TeamTNT\TNTSearch\Classifier\TNTClassifier();
$loaded->load(storage_path('app/sports.cls'));
$tokenizer = new class extends \TeamTNT\TNTSearch\Tokenizer\AbstractTokenizer {
protected static $pattern = '/[^\w#]+/'; // Include hashtags
};
$indexer->setTokenizer($tokenizer);
$tnt->loadConfig(['stemmer' => \TeamTNT\TNTSearch\Stemmer\GermanStemmer::class]);
Storage Permissions:
storage path in config is writable:
chmod -R 775 storage_path('app/tntsearch')
PDOException: unable to open database file → Fix permissions.Primary Key Assumptions:
id as the primary key. Override with:
$indexer->setPrimaryKey('custom_id');
Boolean Search Syntax:
"full text".(A AND B) OR C.- for exclusion: laravel -tutorial.Fuzzy Search Limits:
fuzzy_distance (e.g., >3) slows queries. Default is 2.$tnt->fuzziness(false).Geo-Search Precision:
Index Corruption:
$indexer->reindex();
$tnt->setLogger(new \Monolog\Logger('tntsearch', [...]));
$index = $tnt->getIndex('articles.index');
var_dump($index->getStats()); // Shows indexed docs, words, etc.
$indexer->query() in a transaction to test SQL:
DB::transaction(function () use ($indexer) {
$indexer->query('SELECT * FROM articles LIMIT 10')->run();
});
$indexer->query('SELECT id, title, content FROM articles');
$tnt->fuzziness(false);
insert() in bulk (e.g., via collect()):
$newArticles = Article::where('updated_at', '>', now()->subHour())->get();
$index->insert($newArticles->toArray());
$cacheKey = "tntsearch:articles:{$query}";
return Cache::remember($cacheKey, now()->addMinutes(5), function () use ($tnt, $query) {
return $tnt->search($query, 20);
});
\TeamTNT\TNTSearch\Driver\AbstractDriver for new storage backends (e.g., PostgreSQL).highlight() method (v5.0+):
$highlighted = $tnt->highlight('laravel', $results, ['title', 'content']);
TokenizerInterface or StemmerInterface for custom logic.Scout::searching(function ($model) {
// Custom logic before search
});
AppServiceProvider:
if (!class_exists(\TeamTNT\TNTSearch\TNTSearchServiceProvider::class)) {
$this->mergeConfigFrom(__DIR__.'/../../../vendor/teamtnt/tntsearch/config/tntsearch.php', 'tntsearch');
}
How can I help you explore Laravel packages today?