Extending the Base model
In this section, we’ll dive into how to hook your Laravel models into OpenSearch by extending the base model, allowing you to work with OpenSearch indices as if they were regular Eloquent models.
Extending Your Model
Every model you want to index in OpenSearch should extend the PDPhilip\OpenSearch\Eloquent\Model
. This base model extends Laravel’s Eloquent model, so you can use it just like you would any other Eloquent model.
namespace App\Models;
use PDPhilip\OpenSearch\Eloquent\Model;
class Product extends Model{ protected $connection = 'opensearch';}
Just like a regular model, the index name will be inferred from the name of the model. In this example, the corresponding index for the Product
model is products
. In most cases, the OpenSearch connection won’t be the default connection, and you’ll need to include protected $connection = 'opensearch'
in your model.
Model IDs
id alias for _id
$model->id
and is an alias for OpenSearch’s _id
field. This is to maintain consistency with Laravel’s Eloquent ORM.
You can use either $model->id
or $model->_id
to access the ID of the model, but it’s recommended to use $model->id
for consistency with Laravel’s Eloquent ORM.
$model = MyModel::where('id','123')->first();
echo $model->id; // 123echo $model->_id; // 123
Laravel vs OpenSearch generated IDs
By default, OpenSearch will generate a unique ID for each document. However, this requires an additional API call to retrieve the ID after the document is created.
To avoid this, you can generate the ID on the Laravel side by using:
1. GeneratesUuids
trait
This trait will generate a time-based UUID for the ID field when creating a new model instance.
use PDPhilip\OpenSearch\Eloquent\Model; use PDPhilip\OpenSearch\Eloquent\GeneratesUuids;
class Product extends Model{ use GeneratesUuids;
protected $connection = 'opensearch';
Example id:
{ "id": "OWU3ZTllNDEtNmFmNS00MjdkLWE5MmEtNmJhMTBkZWI0Y2Vh"}
2. GeneratesElasticIds
trait
This trait will generate an imitation of an OpenSearch ID for the ID field when creating a new model instance.
use PDPhilip\OpenSearch\Eloquent\Model; use PDPhilip\OpenSearch\Eloquent\GeneratesElasticIds;
class Product extends Model{ use GeneratesElasticIds;
protected $connection = 'opensearch';
Example id:
{ "id": "GXUGv5UBt-dmV3b48pjF"}
Model properties
$table
To change the inferred index name, pass in the $table
property:
namespace App\Models;
use PDPhilip\OpenSearch\Eloquent\Model;
class Product extends Model{ protected $connection = 'opensearch'; protected $table = 'my_products';}
Timestamps
By default, the base model will automatically set the created_at
and updated_at
fields. As is the case with Eloquent, you can disable this by setting the CREATED_AT and UPDATED_AT constants to null in your model
namespace App\Models;
use PDPhilip\OpenSearch\Eloquent\Model;
class Product extends Model{ protected $connection = 'opensearch'; const CREATED_AT = null; const UPDATED_AT = null;}
Limits
If your query does not include a limit, (->take(200)
) then OpenSearch will default to 10.
This tends to be impractical in a Laravel context with hybrid data, thus this integration overrides that value and defaults to 1000.
You can change this default limit by:
- Setting the
protected $defaultLimit
property in your model which will apply to that model only.
use PDPhilip\OpenSearch\Eloquent\Model;
class Product extends Model{ protected $defaultLimit = 10000; protected $connection = 'opensearch';}
- Setting the
ES_OPT_DEFAULT_LIMIT
environment variable in your.env
file which will apply to all models that do not have a$defaultLimit
property set.
ES_OPT_DEFAULT_LIMIT=10000
Query Field Map
OpenSearch requires specific field types for certain operations such as aggregation, sorting, or filtering. For example, while a text
field cannot be used directly for sorting or term-based filtering, a corresponding keyword
subfield is typically employed. However, this isn’t restricted to just keywords; other field types may also need specific mappings depending on the operation.
To automate the process of using the correct field type at runtime, this package performs a mapping call unless the bypass_map_validation
connection option is set to true. Enabling this setting avoids the overhead of an additional API call to fetch mappings for each query, which could affect performance. To facilitate efficient query processing without extra API calls, the model includes a $queryFieldMap
property. This is an associative array where the keys are the column names and the values are the specific fields required for operations, ensuring the correct mappings are utilized.
namespace App\Models;
use PDPhilip\OpenSearch\Eloquent\Model;
class Product extends Model{ protected $connection = 'opensearch'; protected $queryFieldMap = [ 'name' => 'name.keyword', // Example of a keyword subfield 'date' => 'date.long' // Hypothetical example for date fields requiring long type ];}
This modification ensures all necessary operations are efficiently managed by leveraging the correct field mappings, enhancing performance and capability of the OpenSearch integration in your Laravel application.
Mutators & Casting
You can use mutators and casting in your models just like you would with any other Eloquent model.
In the context of the Laravel-Opensearch integration, the foundational BaseModel inherits all the features of Laravel’s Eloquent model, including mutators and casting. You can use mutators and casts exactly like any standard Eloquent model.
For a comprehensive understanding of how to implement and use attribute mutators and casts within your models, refer to the official Laravel documentation on Eloquent Mutators & Casting: Laravel - Eloquent: Mutators & Casting.
OpenSearch Collections
Once a query is executed, the query meta is stored in the model instance as an OpenCollection
.
An OpenCollection
is a collection of an OpenSearch record; akin to Laravel’s Eloquent\Collection
and augmented with Meta Data.
You can access the query meta by calling the getMeta()
method on the model instance.
$product = Product::where('color', 'green')->first();
return $product->getMeta()->toArray();// or $product->getMetaAsArray();
returns:
{ "score": 1, "index": "os12_products", "table_prefix": "os12_", "table": "products", "table_suffix": "", "doc_count": null, "sort": [], "cursor": [], "highlights": []}