Skip to content

ElasticLens

Search your Laravel models with Eloquent ease and Elasticsearch power

Scout’s simplicity • Elasticsearch’s power • Your rules

Latest Stable VersionGitHub Tests Action StatusTotal Downloads
// Add a trait. Search your models.
User::search('loves espressos');
// Except you're not stuck with basic text search.
User::viaIndex()
->searchPhrase('loves espressos')
->where('status', 'active')
->where('logs.country', 'Norway')
->orderByDesc('created_at')
->paginate(10);
// Geo queries. Fuzzy matching. Regex. Aggregations. On your Laravel models.
User::viaIndex()
->whereGeoDistance('home.location', '5km', [40.7128, -74.0060])
->orderByGeo('home.location', [40.7128, -74.0060])
->get();

Scout gives you a search box behind a black box. ElasticLens gives you a search engine you can open up.

Every index is a real Eloquent model you own. You define the field mappings. You define the Elasticsearch schema. You see exactly what’s indexed and how. No magic, no guessing, no driver abstraction sitting between you and your data.


viaIndex() is the gateway. Everything after it runs against the full Elasticsearch query builder. Results come back as your Laravel models.

Diagram
User::viaIndex() // Cross into Elasticsearch
->searchTerm('max') // full-text search
->where('state', 'active') // familiar Eloquent syntax
->where('logs.country', 'Norway') // query embedded relations with dot notation
->orderByDesc('created_at') // ordering, pagination, aggregations
->paginate(10); // Returns: Paginator of User models

Everything between viaIndex() and get()/paginate() is the Elasticsearch query builder from Laravel-Elasticsearch. Full-text search, geo queries, fuzzy matching, regex, nested objects, aggregations, highlighting. All of it.

Full search capabilities ->

Here’s the magic. Flatten your relational data into Elasticsearch and search across everything with dot notation.

Diagram

Define what gets embedded in your index model:

class IndexedUser extends IndexModel
{
protected $baseModel = User::class;
public function fieldMap(): IndexBuilder
{
return IndexBuilder::map(User::class, function (IndexField $field) {
$field->text('name');
$field->text('email');
$field->embedsMany('profiles', Profile::class)->embedMap(function ($field) {
$field->text('bio');
$field->array('tags');
});
$field->embedsBelongTo('company', Company::class)->embedMap(function ($field) {
$field->text('name');
$field->text('industry');
});
$field->embedsMany('logs', UserLog::class, null, null, function ($query) {
$query->orderBy('created_at', 'desc')->limit(10);
})->embedMap(function ($field) {
$field->text('action');
});
});
}
}

Then search across all of it:

User::viaIndex()->where('profiles.bio', 'like', '%elasticsearch%')->get();

Related models are observed too. Update a Profile and the parent IndexedUser rebuilds automatically.

Full Embedded Relations guide ->
  • Auto-sync - Every create, update, and delete on your base model is reflected in the index. Automatically.
  • Embedded relations - Flatten hasMany, hasOne, belongsTo into searchable nested objects
  • Full Elasticsearch query builder - Term, phrase, fuzzy, regex, geo, nested, aggregations, highlighting. All returning your Laravel models.
  • Index migrations - Define your Elasticsearch mapping with a Blueprint, just like database migrations
  • Conditional indexing - Control which records get indexed with excludeIndex()
  • Soft delete support - Configure whether soft-deleted records keep their index
  • CLI tools - Status, health, build, migrate, and make commands

Version
PHP8.2+
Laravel10 / 11 / 12 / 13
Elasticsearch8.x +