Index-model field mapping
Define what gets indexed. Map your fields. Embed your relationships. Your index, your rules.
Defining a field Map
Section titled “Defining a field Map”By default, the Index Model indexes every field it finds on the Base Model during sync. To be explicit about what gets indexed, define a fieldMap():
use PDPhilip\ElasticLens\Builder\IndexBuilder; use PDPhilip\ElasticLens\Builder\IndexField;
class IndexedUser extends IndexModel{ protected $baseModel = User::class;
public function fieldMap(): IndexBuilder { return IndexBuilder::map(User::class, function (IndexField $field) { $field->text('first_name'); $field->text('last_name'); $field->text('email'); $field->bool('is_active'); //See attributes as fields $field->type('state', UserState::class); //Maps enum $field->text('created_at'); $field->text('updated_at'); }); }Attributes as fields
Section titled “Attributes as fields”If your Base Model has computed attributes you want searchable, map them like regular fields.
For example,
$field->bool('is_active')could come from a custom attribute on the Base Model:
// @property-read bool is_active public function getIsActiveAttribute(): bool { return $this->updated_at >= Carbon::now()->modify('-30 days'); }These values are stored in Elasticsearch as they were at sync time. They’re snapshots, not live calculations.
Relationships as embedded fields
Section titled “Relationships as embedded fields”Here’s the standout feature. Embed relationships into your Index Model and search across them as nested objects. No JOINs, no multiple queries.
Consider these relationships around the User model:
EmbedsMany
Section titled “EmbedsMany”User has many Profiles:
use PDPhilip\ElasticLens\Builder\IndexBuilder;use PDPhilip\ElasticLens\Builder\IndexField;
class IndexedUser extends IndexModel{ protected $baseModel = User::class;
public function fieldMap(): IndexBuilder { return IndexBuilder::map(User::class, function (IndexField $field) { $field->text('first_name'); $field->text('last_name'); $field->text('email'); $field->bool('is_active'); $field->type('type', UserType::class); $field->type('state', UserState::class); $field->text('created_at'); $field->text('updated_at'); $field->embedsMany('profiles', Profile::class)->embedMap(function (IndexField $field) { $field->text('profile_name'); $field->text('about'); $field->array('profile_tags'); }); }); }EmbedsOne
Section titled “EmbedsOne”Profile has one ProfileStatus:
use PDPhilip\ElasticLens\Builder\IndexBuilder;use PDPhilip\ElasticLens\Builder\IndexField;
class IndexedUser extends IndexModel{ protected $baseModel = User::class;
public function fieldMap(): IndexBuilder { return IndexBuilder::map(User::class, function (IndexField $field) { $field->text('first_name'); $field->text('last_name'); $field->text('email'); $field->bool('is_active'); $field->type('type', UserType::class); $field->type('state', UserState::class); $field->text('created_at'); $field->text('updated_at'); $field->embedsMany('profiles', Profile::class)->embedMap(function (IndexField $field) { $field->text('profile_name'); $field->text('about'); $field->array('profile_tags'); $field->embedsOne('status', ProfileStatus::class)->embedMap(function (IndexField $field) { $field->text('id'); $field->text('status'); }); }); }); }EmbedsBelongTo
Section titled “EmbedsBelongTo”User belongs to an Account:
use PDPhilip\ElasticLens\Builder\IndexBuilder;use PDPhilip\ElasticLens\Builder\IndexField;
class IndexedUser extends IndexModel{ protected $baseModel = User::class;
public function fieldMap(): IndexBuilder { return IndexBuilder::map(User::class, function (IndexField $field) { $field->text('first_name'); $field->text('last_name'); $field->text('email'); $field->bool('is_active'); $field->type('type', UserType::class); $field->type('state', UserState::class); $field->text('created_at'); $field->text('updated_at'); $field->embedsMany('profiles', Profile::class)->embedMap(function (IndexField $field) { $field->text('profile_name'); $field->text('about'); $field->array('profile_tags'); $field->embedsOne('status', ProfileStatus::class)->embedMap(function (IndexField $field) { $field->text('id'); $field->text('status'); }); }); $field->embedsBelongTo('account', Account::class)->embedMap(function (IndexField $field) { $field->text('name'); $field->text('url'); }); }); }Embedding without observing
Section titled “Embedding without observing”User belongs to Country, but countries don’t change often enough to justify observing:
use PDPhilip\ElasticLens\Builder\IndexBuilder;use PDPhilip\ElasticLens\Builder\IndexField;
class IndexedUser extends IndexModel{ protected $baseModel = User::class;
public function fieldMap(): IndexBuilder { return IndexBuilder::map(User::class, function (IndexField $field) { $field->text('first_name'); $field->text('last_name'); $field->text('email'); $field->bool('is_active'); $field->type('type', UserType::class); $field->type('state', UserState::class); $field->text('created_at'); $field->text('updated_at'); $field->embedsMany('profiles', Profile::class)->embedMap(function (IndexField $field) { $field->text('profile_name'); $field->text('about'); $field->array('profile_tags'); $field->embedsOne('status', ProfileStatus::class)->embedMap(function (IndexField $field) { $field->text('id'); $field->text('status'); }); }); $field->embedsBelongTo('account', Account::class)->embedMap(function (IndexField $field) { $field->text('name'); $field->text('url'); }); $field->embedsBelongTo('country', Country::class)->embedMap(function (IndexField $field) { $field->text('country_code'); $field->text('name'); $field->text('currency'); })->dontObserve(); // Don't observe changes in the country model }); }EmbedsMany with a query
Section titled “EmbedsMany with a query”User has many UserLogs and you only want the last 10:
use PDPhilip\ElasticLens\Builder\IndexBuilder;use PDPhilip\ElasticLens\Builder\IndexField;
class IndexedUser extends IndexModel{ protected $baseModel = User::class;
public function fieldMap(): IndexBuilder { return IndexBuilder::map(User::class, function (IndexField $field) { $field->text('first_name'); $field->text('last_name'); $field->text('email'); $field->bool('is_active'); $field->type('type', UserType::class); $field->type('state', UserState::class); $field->text('created_at'); $field->text('updated_at'); $field->embedsMany('profiles', Profile::class)->embedMap(function (IndexField $field) { $field->text('profile_name'); $field->text('about'); $field->array('profile_tags'); $field->embedsOne('status', ProfileStatus::class)->embedMap(function (IndexField $field) { $field->text('id'); $field->text('status'); }); }); $field->embedsBelongTo('account', Account::class)->embedMap(function (IndexField $field) { $field->text('name'); $field->text('url'); }); $field->embedsBelongTo('country', Country::class)->embedMap(function (IndexField $field) { $field->text('country_code'); $field->text('name'); $field->text('currency'); })->dontObserve(); // Don't observe changes in the country model $field->embedsMany('logs', UserLog::class, null, null, function ($query) { $query->orderBy('created_at', 'desc')->limit(10); // Limit the logs to the 10 most recent })->embedMap(function (IndexField $field) { $field->text('title'); $field->text('ip'); $field->array('log_data'); }); }); }IndexField Methods
Section titled “IndexField Methods”Field types:
Section titled “Field types:”text($field)integer($field)array($field)bool($field)type($field, $type)- Set own type (like Enums)embedsMany($field, $relatedModelClass, $whereRelatedField, $equalsLocalField, $query)embedsBelongTo($field, $relatedModelClass, $whereRelatedField, $equalsLocalField, $query)embedsOne($field, $relatedModelClass, $whereRelatedField, $equalsLocalField, $query)
Embedded field type methods:
Section titled “Embedded field type methods:”embedMap(function (IndexField $field) {})- Define the mapping for the embedded relationshipdontObserve()- Don’t observe changes in the related model