Supercharge Laravel Search: A Hands-On Guide to Elasticsearch Integration
When your application scales and your database grows, traditional relational database queries like WHERE name LIKE '%term%' quickly become a major bottleneck. High CPU usage, sluggish response times, and sub-optimal ranking degrade your user experience.
To solve this and introduce capabilities like Full-Text Search, typo tolerance, relevance scoring, and millisecond-level filtering across millions of records, Elasticsearch is the industry standard.
1. Run Elasticsearch with Docker
The cleanest and fastest way to run Elasticsearch in your local environment is via Docker:
docker run -d --name elasticsearch \
-p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e "xpack.security.enabled=false" \
elasticsearch:8.11.0
Verify the node is running:
curl http://localhost:9200
2. Install the official client
Install the official Elasticsearch PHP client using Composer:
composer require elasticsearch/elasticsearch
Add your node host to your .env file:
ELASTICSEARCH_HOST=http://localhost:9200
3. Register the Service Provider
Create a dedicated Service Provider to manage the client singleton:
php artisan make:provider ElasticsearchServiceProvider
Register the binding inside app/Providers/ElasticsearchServiceProvider.php:
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Elastic\Elasticsearch\ClientBuilder;
use Elastic\Elasticsearch\Client;
class ElasticsearchServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->singleton(Client::class, function () {
return ClientBuilder::create()
->setHosts([env('ELASTICSEARCH_HOST', 'http://localhost:9200')])
->build();
});
}
}
4. Index data via chunked Artisan command
Never use Model::all() when indexing large datasets to avoid memory exhaust errors. Use chunking instead:
Product::chunk(200, function ($products) use ($elasticsearch) {
foreach ($products as $product) {
$elasticsearch->index([
'index' => 'products',
'id' => (string) $product->id,
'body' => [
'name' => $product->name,
'description' => $product->description,
'price' => (float) $product->price,
'is_active' => (bool) $product->is_active,
],
]);
}
});
5. Search with multi-criteria filtering
Build your search endpoint with boolean queries to combine text search with exact range filters:
$results = $elasticsearch->search([
'index' => 'products',
'body' => [
'query' => [
'bool' => [
'must' => [
[
'multi_match' => [
'query' => $searchTerm,
'fields' => ['name^3', 'description']
]
]
],
'filter' => [
['term' => ['is_active' => true]],
['range' => ['price' => ['gte' => 50, 'lte' => 500]]]
]
]
]
]
]);
Summary checklist
| Step | Action | Benefit |
|---|---|---|
| Node | Single-node Docker setup | Quick local isolation |
| Client | Singleton service provider | Clean dependency injection |
| Indexing | Chunked streaming | Zero memory exhaustion |
| Queries | Bool must + filter | Fast cached query execution |
| Sync | Laravel observers / queues | Real-time index updates |