pdphilip/elasticsearch
Laravel Eloquent-style ORM for Elasticsearch. Use familiar models and query builder methods to create, update, paginate, delete, and run ES searches: term/phrase, match, fuzzy, geo distance, highlighting, and more—designed to feel native in Laravel.
Installation:
composer require pdphilip/elasticsearch
Add the service provider to bootstrap/providers.php (Laravel 11+) or config/app.php (Laravel 10 and below).
Configure .env:
ES_AUTH_TYPE=http
ES_HOSTS="http://localhost:9200"
ES_INDEX_PREFIX=my_app_
Update config/database.php with the Elasticsearch connection details.
First Use Case:
Extend your Eloquent model with PDPhilip\Elasticsearch\Eloquent\Model:
use PDPhilip\Elasticsearch\Eloquent\Model;
class UserLog extends Model
{
protected $connection = 'elasticsearch';
}
Run a basic search:
UserLog::searchTerm('Laravel')->get();
elastic:make for scaffolding models, elastic:re-index for migrations.PDPhilip\Elasticsearch\Eloquent\Model for core functionality.Basic CRUD:
// Create
UserLog::create(['user_id' => '123', 'ip' => '192.168.1.1']);
// Read
UserLog::where('status', 1)->get();
// Update
UserLog::where('status', 1)->update(['status' => 4]);
// Delete
UserLog::where('status', 4)->delete();
Search Queries:
// Full-text search
UserLog::searchTerm('Laravel')->get();
// Phrase search with prefix
UserLog::searchPhrasePrefix('loves espressos and t')->highlight()->get();
// Match query
UserLog::whereMatch('bio', 'PHP')->get();
// Fuzzy search
UserLog::whereFuzzy('description', 'qick brwn fx')->get();
Geo Queries:
// Geo-distance
UserLog::whereGeoDistance('location', '10km', [40.7185, -74.0025])->get();
Relationships:
// Load relationships (supports SQL and ES models)
UserLog::with('user')->get();
Aggregations:
UserLog::aggregate()
->terms('status')
->get();
Model Scaffolding:
php artisan elastic:make UserLog
Generates a model with the correct base class and starter mappingDefinition().
Re-indexing:
php artisan elastic:re-index UserLog
Automates index migration with 9 interactive phases (e.g., mapping analysis, data copy, verification).
Dynamic Indices:
Use dynamicIndex() in mappingDefinition() to enable dynamic indices:
public static function mappingDefinition(Blueprint $index): void
{
$index->dynamicIndex('user_logs_*');
}
Chunking:
UserLog::chunk(100, function ($records) {
foreach ($records as $record) {
// Process records in chunks
}
});
Hybrid Search: Combine SQL and Elasticsearch queries using relationships:
// SQL model with ES relationship
class User extends Model {}
class UserLog extends PDPhilip\Elasticsearch\Eloquent\Model {
public function user() { return $this->belongsTo(User::class); }
}
Custom Mappings:
Define mappings in mappingDefinition():
public static function mappingDefinition(Blueprint $index): void
{
$index->text('bio');
$index->keyword('status');
$index->geoPoint('location');
}
Highlighting:
UserLog::searchTerm('Laravel')->highlight(['bio'])->get();
Pagination:
UserLog::searchTerm('Laravel')->paginate(10);
Index Naming:
ES_INDEX_PREFIX is unique to avoid conflicts with other apps.dynamicIndex() carefully to avoid unintended index proliferation.Mapping Changes:
text to keyword) requires re-indexing.elastic:re-index to automate this process.Relationships:
Performance:
chunk() or cursor() for pagination to avoid memory issues.ES_OPT_DEFAULT_LIMIT higher than your typical query size (default: 1000).SSL/TLS:
ES_OPT_VERIFY_SSL=false (not recommended for production).ES_CLOUD_ID and API keys are correctly configured.Query DSL:
config/database.php:
'options' => [
'logging' => true,
],
storage/logs/elasticsearch.log.Mapping Errors:
Schema::compileMapping() to preview mappings before applying:
$mapping = UserLog::compileMapping();
dd($mapping);
Connection Issues:
php artisan elastic:ping
.env settings match your Elasticsearch cluster configuration.Re-indexing Failures:
elastic:re-index command logs for phase-specific errors.--force to skip confirmations during debugging:
php artisan elastic:re-index UserLog --force
Artisan Commands:
elastic:analyze: Analyze text with Elasticsearch analyzers.elastic:ping: Test connection to the Elasticsearch cluster.elastic:show: Display index mappings and settings.Testing:
PDPhilip\Elasticsearch\Testing\CreatesApplication for Laravel tests:
use PDPhilip\Elasticsearch\Testing\CreatesApplication;
class TestCase extends TestCase
{
use CreatesApplication;
}
Configuration:
ES_OPT_BYPASS_MAP_VALIDATION=true to skip mapping validation (useful for dynamic schemas).ES_OPT_ID_SORTABLE=true if you need to sort by _id (requires careful index design).Advanced Queries:
UserLog::whereNestedObject('tags', ['key' => 'laravel'])->get();
UserLog::searchTerm('Laravel')->fields(['bio', 'description'])->get();
Extensions:
mappingDefinition():
$index->analyzer('custom_analyzer', function (Blueprint $analyzer) {
$analyzer->tokenizer('standard');
$analyzer->filter('lowercase');
});
UserLog::scriptField('score', 'doc["views"].value * 1.5');
Performance Optimization:
mappingDefinition():
$index->settings(['index.refresh_interval' => '30s']);
bulk() for high-volume inserts/updates:
UserLog::bulk($records);
How can I help you explore Laravel packages today?