laravel-search maintained by shammaa
Laravel Search
A professional, high-performance search library for Laravel with MySQL Full-Text Search, advanced query builder, Arabic support, and search analytics.
✨ Features
Core Features
- 🚀 MySQL Full-Text Search - Fast search using native FULLTEXT indexes (no external dependencies)
- 🔍 Advanced Query Builder - Powerful, fluent query builder with filters, facets, and sorting
- 🌍 Arabic Language Support - Excellent Arabic search capabilities
- ⚡ Auto-Indexing - Automatic FULLTEXT index creation and management
- 📊 Search Analytics - Track searches, popular queries, and statistics
- 🎯 Faceted Search - Advanced faceted search with multiple filters
- 💡 Auto-complete - Built-in autocomplete functionality
Performance & Scalability
- 🔄 Queue Support - Non-blocking index creation with queue jobs
- 💾 Smart Caching - Intelligent caching for search results and popular queries
- ⚙️ Zero Configuration - Works out of the box with sensible defaults
- 📈 Scalable - Handles millions of records efficiently
Developer Experience
- 🎨 Type Safe - Full type hints and strict types (PHP 8.1+)
- 🛠️ Artisan Commands - Comprehensive CLI tools for index management
- 📦 Laravel Integration - Seamless integration with Laravel ecosystem
- 🔌 Facade Support - Clean, expressive API
📋 Requirements
- PHP >= 8.1
- Laravel >= 9.0
- MySQL >= 5.6 (with FULLTEXT support) or MariaDB >= 10.0
📦 Installation
1. Install via Composer
composer require shammaa/laravel-search
2. Publish Configuration (Optional)
php artisan vendor:publish --tag=search-config
This creates config/search.php with all available options.
3. Configure Environment (Optional)
Add these to your .env file if you want to customize defaults:
# MySQL Search Mode
MYSQL_SEARCH_MODE=NATURAL_LANGUAGE # or BOOLEAN
# Indexing Settings
SEARCH_AUTO_INDEX=true
SEARCH_QUEUE_INDEX=true
SEARCH_QUEUE_NAME=search
# Cache Settings
SEARCH_CACHE_ENABLED=true
SEARCH_CACHE_TTL=3600
# Analytics
SEARCH_ANALYTICS_ENABLED=true
🚀 Quick Start
Step 1: Add Searchable Trait to Your Model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Shammaa\LaravelSearch\Traits\Searchable;
class Article extends Model
{
use Searchable;
/**
* Define searchable fields with priority weights.
* Higher priority = more important in search results.
*/
protected $searchable = [
'title' => ['priority' => 2.0], // 2x more important
'content' => ['priority' => 1.0], // Base importance
'description' => ['priority' => 1.5], // 1.5x more important
];
/**
* Define filterable fields for WHERE clauses.
*/
protected $filterable = [
'status',
'category_id',
'author_id',
'published_at',
'tags',
];
/**
* Define sortable fields.
*/
protected $sortable = [
'published_at',
'views',
'created_at',
];
}
Step 2: Create FULLTEXT Index
php artisan search:index "App\Models\Article"
This creates a FULLTEXT index on your searchable fields. The index is created automatically when you first create a model, but you can also create it manually.
Step 3: Start Searching!
// Simple search
$results = Article::search('Laravel')->get();
// Advanced search with filters
$results = Article::search('Laravel')
->where('status', 'published')
->where('category_id', 5)
->whereBetween('published_at', [$start, $end])
->orderBy('relevance', 'desc')
->paginate(20);
// Autocomplete
$suggestions = Article::autocomplete('Lara');
📖 Usage Guide
Basic Search
// Simple search
$articles = Article::search('Laravel')->get();
// Search with pagination
$articles = Article::search('Laravel')->paginate(20);
// Limit and offset
$articles = Article::search('Laravel')
->limit(50)
->offset(100)
->get();
// Count results
$count = Article::search('Laravel')->count();
Advanced Filtering
WHERE Clauses
// Simple where
Article::search('Laravel')
->where('status', 'published')
->where('category_id', 5)
->where('is_featured', true)
->get();
// Where with operator
Article::search('Laravel')
->where('views', '>', 1000)
->get();
WHERE IN / NOT IN
// WhereIn
Article::search('Laravel')
->whereIn('tags', ['php', 'laravel', 'symfony'])
->get();
// WhereNotIn
Article::search('Laravel')
->whereNotIn('author_id', [1, 2, 3])
->get();
NULL Checks
// WhereNull
Article::search('Laravel')
->whereNull('deleted_at')
->get();
// WhereNotNull
Article::search('Laravel')
->whereNotNull('published_at')
->get();
Range Queries
// WhereBetween
Article::search('Laravel')
->whereBetween('published_at', ['2024-01-01', '2024-12-31'])
->get();
// WhereBetween with dates
Article::search('Laravel')
->whereBetween('created_at', [
now()->subDays(30),
now()
])
->get();
Faceted Search
Faceted search allows you to get aggregated counts for different field values:
$results = Article::search('Laravel')
->facets(['category', 'author', 'tags', 'year'])
->get();
// Access facets
$facets = $results->facets;
// [
// 'category' => [
// ['value' => 'Tutorial', 'count' => 45],
// ['value' => 'News', 'count' => 23],
// ],
// 'author' => [...],
// ...
// ]
// Facets work with pagination too
$paginated = Article::search('Laravel')
->facets(['category', 'author'])
->paginate(20);
$facets = $paginated->facets;
Sorting
// Sort by relevance (default)
Article::search('Laravel')
->orderBy('relevance', 'desc')
->get();
// Sort by field
Article::search('Laravel')
->orderBy('published_at', 'desc')
->get();
// Multiple sorts
Article::search('Laravel')
->orderBy('relevance', 'desc')
->orderBy('published_at', 'desc')
->orderBy('views', 'desc')
->get();
Autocomplete
// Get autocomplete suggestions
$suggestions = Article::autocomplete('Lara');
// Returns: ['Laravel', 'Laravel Nova', 'Laravel Horizon']
// With custom limit
$suggestions = Article::autocomplete('Lara', 20);
// Using the facade
use Shammaa\LaravelSearch\Facades\Search;
$suggestions = Search::autocomplete(Article::class, 'Lara', 10);
Arabic Search
The package automatically supports Arabic text search:
// Search in Arabic
$articles = Article::search('برمجة')->get();
// Mixed Arabic and English
$articles = Article::search('Laravel برمجة')->get();
Using the Facade
use Shammaa\LaravelSearch\Facades\Search;
// Search
$results = Search::search(Article::class, 'Laravel')
->where('status', 'published')
->get();
// Autocomplete
$suggestions = Search::autocomplete(Article::class, 'Lara');
// Popular searches
$popular = Search::popularSearches(10);
// Search statistics
$stats = Search::stats();
// Clear cache
Search::clearCache();
🎯 Artisan Commands
Index Management
# Create FULLTEXT index for a model
php artisan search:index "App\Models\Article"
# Use queue for indexing (recommended for large datasets)
php artisan search:index "App\Models\Article" --queue
# Reindex (drops and recreates FULLTEXT index)
php artisan search:reindex "App\Models\Article"
# Clear index
php artisan search:clear "App\Models\Article"
Statistics & Monitoring
# Show all indexes statistics
php artisan search:stats
# Show specific model stats
php artisan search:stats --model="App\Models\Article"
# Show analytics (popular searches, etc.)
php artisan search:stats --analytics
⚙️ Configuration
The config/search.php file contains all configuration options:
MySQL Configuration
'mysql' => [
// Search mode: NATURAL_LANGUAGE or BOOLEAN
'search_mode' => env('MYSQL_SEARCH_MODE', 'NATURAL_LANGUAGE'),
// Minimum word length for FULLTEXT search
'min_word_length' => env('MYSQL_MIN_WORD_LENGTH', 4),
// Index name prefix
'index_prefix' => env('MYSQL_INDEX_PREFIX', 'ft_'),
],
Indexing Configuration
'indexing' => [
// Auto-create indexes when models are created
'auto' => env('SEARCH_AUTO_INDEX', true),
// Use queue for indexing operations
'queue' => env('SEARCH_QUEUE_INDEX', true),
// Queue name for indexing jobs
'queue_name' => env('SEARCH_QUEUE_NAME', 'search'),
// Batch size for bulk operations
'batch_size' => env('SEARCH_BATCH_SIZE', 1000),
// Chunk size for processing
'chunk_size' => env('SEARCH_CHUNK_SIZE', 100),
],
Cache Configuration
'cache' => [
// Enable search result caching
'enabled' => env('SEARCH_CACHE_ENABLED', true),
// Cache store to use
'store' => env('SEARCH_CACHE_STORE', 'default'),
// Cache TTL in seconds (1 hour)
'ttl' => env('SEARCH_CACHE_TTL', 3600),
// Popular searches cache TTL (2 hours)
'popular_ttl' => env('SEARCH_CACHE_POPULAR_TTL', 7200),
// Autocomplete cache TTL (30 minutes)
'autocomplete_ttl' => env('SEARCH_CACHE_AUTOCOMPLETE_TTL', 1800),
],
Analytics Configuration
'analytics' => [
// Enable search analytics
'enabled' => env('SEARCH_ANALYTICS_ENABLED', true),
// Analytics table name
'table' => env('SEARCH_ANALYTICS_TABLE', 'search_analytics'),
// Track searches with no results
'track_empty' => env('SEARCH_ANALYTICS_TRACK_EMPTY', false),
],
Performance Configuration
'performance' => [
// Eager load relationships
'eager_load' => env('SEARCH_EAGER_LOAD', true),
// Select only needed fields
'select_fields' => env('SEARCH_SELECT_FIELDS', false),
// Maximum results per query
'max_results' => env('SEARCH_MAX_RESULTS', 10000),
],
🔧 Advanced Usage
Custom Search Modes
MySQL supports two search modes:
Natural Language Mode (Default)
// In config/search.php
'mysql' => [
'search_mode' => 'NATURAL_LANGUAGE',
],
Best for: Natural language queries, relevance ranking
Boolean Mode
// In config/search.php
'mysql' => [
'search_mode' => 'BOOLEAN',
],
Allows operators like +, -, *, "phrase":
// Must contain "Laravel"
Article::search('+Laravel')->get();
// Must contain "Laravel" but not "Vue"
Article::search('+Laravel -Vue')->get();
// Exact phrase
Article::search('"Laravel Framework"')->get();
// Wildcard
Article::search('Lara*')->get();
Queue Configuration
For large datasets, use queues for indexing:
// In config/search.php
'indexing' => [
'queue' => true,
'queue_name' => 'search',
],
Make sure your queue worker is running:
php artisan queue:work --queue=search
Manual Index Management
use App\Models\Article;
// Manually create index for a model instance
$article = Article::find(1);
$article->searchable();
// This is automatically called on model creation if auto-indexing is enabled
📊 Performance
Benchmarks
Based on testing with typical hardware:
| Records | Search Time | Memory Usage |
|---|---|---|
| 10K | 10-50ms | ~2MB |
| 100K | 50-150ms | ~5MB |
| 1M | 100-500ms | ~10MB |
| 10M | 500ms-2s | ~20MB |
Performance varies based on hardware, query complexity, and data characteristics.
Optimization Tips
- Use Caching: Enable caching for frequently searched queries
- Queue Indexing: Use queues for large datasets
- Limit Results: Use
limit()to reduce result set size - Select Fields: Only select needed fields with
select() - Index Optimization: Ensure your MySQL FULLTEXT indexes are optimized
- MySQL Configuration: Adjust
ft_min_word_lenfor shorter words
MySQL FULLTEXT Configuration
To search for words shorter than 4 characters, modify MySQL configuration:
# In my.cnf or my.ini
[mysqld]
ft_min_word_len = 2
Then rebuild your indexes:
php artisan search:reindex "App\Models\Article"
🔍 Troubleshooting
No Search Results
-
Check if index exists:
php artisan search:stats --model="App\Models\Article" -
Verify minimum word length:
- MySQL default is 4 characters
- Words shorter than this won't be indexed
- Solution: Adjust
ft_min_word_lenin MySQL config
-
Check searchable fields:
// Make sure fields are defined protected $searchable = [ 'title' => ['priority' => 1.0], 'content' => ['priority' => 1.0], ];
Slow Search Performance
-
Enable caching:
SEARCH_CACHE_ENABLED=true -
Use queue for indexing:
SEARCH_QUEUE_INDEX=true -
Limit results:
Article::search('query')->limit(100)->get(); -
Check MySQL FULLTEXT index:
SHOW INDEX FROM articles WHERE Index_type = 'FULLTEXT';
Index Creation Fails
-
Check table engine:
- Must be InnoDB or MyISAM
- InnoDB recommended for MySQL 5.6+
-
Check column types:
- FULLTEXT only works with TEXT and VARCHAR columns
-
Check MySQL version:
- MySQL 5.6+ required for InnoDB FULLTEXT
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Development Setup
# Clone repository
git clone https://github.com/shammaa/laravel-search.git
cd laravel-search
# Install dependencies
composer install
# Run tests (when available)
composer test
📄 License
This package is open-sourced software licensed under the MIT license.
👨💻 Author
Shadi Shammaa
- Email: shadi.shammaa@gmail.com
- GitHub: @shammaa
🙏 Credits
This package uses MySQL Full-Text Search instead of external search engines, making it:
- ✅ Easy to install (no external dependencies)
- ✅ Cost-effective (100% free)
- ✅ Simple to maintain (uses your existing MySQL database)
- ✅ Production-ready (battle-tested MySQL technology)
Perfect for applications that need powerful search without the complexity of managing external search servers like Elasticsearch or Meilisearch.
📚 Additional Resources
🗺️ Roadmap
- Add comprehensive test suite
- Add highlighting support
- Add search suggestions based on analytics
- Add multi-language stemming
- Add search result export
- Add search query builder UI component
- Add Livewire components for search UI
Made with ❤️ for the Laravel community