Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

The missing Elasticsearch ORM for Laravel

License

NotificationsYou must be signed in to change notification settings

matchory/elasticsearch

 
 

Repository files navigation

Latest Stable VersionTotal DownloadsLatest Unstable VersionLicense

Laravel Elasticsearch Integration

This is a fork of the excellent library by@basemkhirat, who sadly seems to haveabandoned it by now.
As we rely on this library quite heavily, we will attempt to keep it up to date and compatible with newer Laravel andElasticsearch versions.

Changes in this fork:

  • Support for Elasticsearch 7.10 and newer
  • Support for PHP 7.3 and newer (PHP 8 included!)
  • Broadened support for Laravel libraries, allowing you to use it with almost all versions of Laravel
  • Type hints in all supported places, giving confidence in all parameters
  • Docblock annotations for advanced autocompletion, extensive inline documentation
  • Clean separation of connection management into aConnectionManager class, whilepreserving backwards compatibility
  • Support formost Eloquent model behaviour (see below)
  • Removed dependencies on Laravel internals

If you're interested in contributing, please submit a PR or open an issue!

Features:

  • Fluent Elasticsearch query builder with an elegant syntax
  • Elasticsearch models inspired by Laravel's Eloquent
  • Index management using simple artisan commands
  • Can be used as aLaravel Scout driver
  • Parallel usage of multiple Elasticsearch connections
  • Built-in pagination based onLaravel Pagination
  • Caching queries using a caching layer based onlaravel cache.

Table of Contents

Requirements

  • PHP >=8.3
  • laravel/laravel >= 12.*, or another application using composer

Installation

This section describes the installation process for all supported application types.

Install package using composer

Whether you're using Laravel or another framework, start by installing the package using composer:

composer require matchory/elasticsearch

Laravel Installation

If you have package autodiscovery disabled, add the service provider and facade to yourconfig/app.php:

'providers' => [// ...Matchory\Elasticsearch\ElasticsearchServiceProvider::class,// ...    ],// ...'aliases' => [// ...'ES' =>Matchory\Elasticsearch\Facades\ES::class,// ...    ],

Lastly, publish the service provider to your configuration directory:

php artisan vendor:publish --provider="Matchory\Elasticsearch\ElasticsearchServiceProvider"

Generic app installation

You can install package with any composer-based application. While we can't provide general instructions, the followingexample should give you an idea of howit works:

require"vendor/autoload.php";useMatchory\Elasticsearch\ConnectionManager;useMatchory\Elasticsearch\Factories\ClientFactory;$connectionManager =newConnectionManager(['servers' => [        ["host" =>'127.0.0.1',"port" =>9200,'user' =>'','pass' =>'','scheme' =>'http',        ],    ],// Custom handlers// 'handler' => new MyCustomHandler(),'index' =>'my_index',],newClientFactory());$connection =$connectionManager->connection();// Access the query builder using created connection$documents =$connection->search("hello")->get();

Configuration (Laravel)

After publishing the service provider, a configuration file has been created atconfig/elasticsearch.php. Here, youcan add one or more Elasticsearch connections, with multiple servers each. Take a look at the following example:

# Here you can define the default connection name.'default' =>env('ELASTIC_CONNECTION','default'),# Here you can define your connections.'connections' => ['default' => ['servers' => [        ["host" =>env("ELASTIC_HOST","127.0.0.1"),"port" =>env("ELASTIC_PORT",9200),'user' =>env('ELASTIC_USER',''),'pass' =>env('ELASTIC_PASS',''),'scheme' =>env('ELASTIC_SCHEME','http'),        ]    ],// Custom handlers// 'handler' => new MyCustomHandler(),'index' =>env('ELASTIC_INDEX','my_index')]],# Here you can define your indices.'indices' => ['my_index_1' => ["aliases" => ["my_index"    ],'settings' => ["number_of_shards" =>1,"number_of_replicas" =>0,    ],'mappings' => ['posts' => ['properties' => ['title' => ['type' =>'string'                    ]                ]        ]    ]]]

If you'd like to use Elastic\Elasticsearch withLaravel Scout, youcan find the scout specific settings inconfig/scout.php.

Artisan commands (Laravel)

With the artisan commands included with this package, you can create or update settings, mappings and aliases. Note thatall commands use the default connectionby default. You can change this by passing the--connection <your_connection_name> option.

The following commands are available:

es:indices:list: List all indices on server

$ php artisan es:indices:list+----------------------+--------+--------+----------+------------------------+-----+-----+------------+--------------+------------+----------------+| configured (es.php)  | health | status | index    | uuid                   | pri | rep | docs.count | docs.deleted | store.size | pri.store.size |+----------------------+--------+--------+----------+------------------------+-----+-----+------------+--------------+------------+----------------+| yes                  | green  | open   | my_index | 5URW60KJQNionAJgL6Q2TQ | 1   | 0   | 0          | 0            | 260b       | 260b           |+----------------------+--------+--------+----------+------------------------+-----+-----+------------+--------------+------------+----------------+

es:indices:create: Create indices defined inconfig/es.php

Note that creating operation skips the index if exists.

# Create all indices in config file.php artisan es:indices:create# Create only 'my_index' index in config filephp artisan es:indices:create my_index

es:indices:update: Update indices defined inconfig/es.php

Note that updating operation updates indices setting, aliases and mapping and doesn't delete the indexed data.

# Update all indices in config file.php artisan es:indices:update# Update only 'my_index' index in config filephp artisan es:indices:update my_index

es:indices:drop: Drop index

Be careful when using this command, as you will lose your index data!
Running drop command with--force option will skip all confirmation messages.

# Drop all indices in config file.php artisan es:indices:drop# Drop specific index on sever. Not matter for index to be exist in config file or not.php artisan es:indices:drop my_index

Reindexing data (with zero downtime)

First, why reindexing?
Changing index mapping doesn't reflect without data reindexing, otherwise your search results will not work on the rightway.
To avoid down time, your application should work with indexalias not indexname.
The indexalias is a constant name that application should work with to avoid change index names.

Assume that we want to change mapping formy_index, this is how to do that:

  1. Addalias as examplemy_index_alias tomy_index configuration and make sure your application is working withit.

    "aliases" => ["my_index_alias"]
  2. Update index with command:

    php artisan es:indices:update my_index
  3. Create a new index as examplemy_new_index with your new mapping in configuration file.

    $ php artisan es:indices:create my_new_index
  4. Reindex data frommy_index intomy_new_index with command:

    php artisan es:indices:reindex my_index my_new_index# Control bulk size. Adjust it with your server.php artisan es:indices:reindex my_index my_new_index --bulk-size=2000# Control query scroll value.php artisan es:indices:reindex my_index my_new_index --bulk-size=2000 --scroll=2m# Skip reindexing errors such as mapper parsing exceptions.php artisan es:indices:reindex my_index my_new_index --bulk-size=2000 --skip-errors# Hide all reindexing errors and show the progres bar only.php artisan es:indices:reindex my_index my_new_index --bulk-size=2000 --skip-errors --hide-errors
  5. Removemy_index_alias alias frommy_index and add it tomy_new_index in configuration file and update withcommand:

    php artisan es:indices:update

Usage as a Laravel Scout driver

First, followLaravel Scout installation.
All you have to do is updating the following lines inconfig/scout.php:

# change the default driver to 'elasticsearch''driver' =>env('SCOUT_DRIVER','elasticsearch'),# link `elasticsearch` driver with default elasticsearch connection in config/es.php'elasticsearch' => ['connection' =>env('ELASTIC_CONNECTION','default'),],

Have a look atLaravel Scout documentation, too!

Elasticsearch models

Each index type has a corresponding"Model" which is used to interact with that type. Models allow you to query fordata in your types or indices, as well asinsert new documents into the type. Elasticsearch Models mimic Eloquent models as closely as possible: You can use modelevents, route bindings, advancedattribute methods and more.If there is any Eloquent functionality you're missing, open an issue, and we'll be happyto add it!.

Supported features:

  • Attributes
  • Events
  • Route bindings
  • Global and Local Query Scopes
  • Replicating models

A minimal model might look like this:

namespaceApp\Models;useMatchory\Elasticsearch\Model;class Postextends Model{// ...}

Index Names

This model is not specifically bound to any index and will simply use the index configured for the given Elasticsearchconnection. To specifically target anindex, you may define anindex property on the model:

namespaceApp\Models;useMatchory\Elasticsearch\Model;class Postextends Model{protected$index ='posts';}

Connection Names

By default, all Elasticsearch models will use the default connection that's configured for your application. If youwould like to specify a different connectionthat should be used when interacting with a particular model, you should define a $connection property on the model:

namespaceApp\Models;useMatchory\Elasticsearch\Model;class Postextends Model{protected$connection ='blag';}

Mapping type

If you're still using mapping types, you may add atype property to your model to indicate the mapping_type to beused for queries.

Mapping Types are deprecated:
Please note that Elastichasdeprecated mapping typesand will removethem in the next major release. You should not rely on them to continue working.

namespaceApp;useMatchory\Elasticsearch\Model;class Postextends Model{protected$type ='posts';}

Default Attribute Values

By default, a newly instantiated model instance will not contain any attribute values. If you would like to define thedefault values for some of your model'sattributes, you may define anattributes property on your model:

namespaceApp\Models;useMatchory\Elasticsearch\Model;class Postextends Model{protected$attributes = ['published' =>false,    ];}

Retrieving Models

Once you have created a model and its associated index type, you are ready to start retrieving data from your index. Youcan think of your Elasticsearch modelas a powerful query builder allowing you to fluently query the index associated with the model. The model'sall methodwill retrieve all the documents fromthe model's associated Elasticsearch index:

useApp\Models\Post;foreach (Post::all()as$post) {echo$post->title;}

Adding additional constraints

Theall method will return all the results in the model's index. However, since each Elasticsearch model serves as aquery builder, you may add additionalconstraints to queries, and then invoke theget() method to retrieve the results:

useApp\Models\Post;$posts = Post::where('status',1)             ->orderBy('created_at','desc')             ->take(10)             ->get();

Collections

As we have seen, Elasticsearch methods likeall andget retrieve multiple documents from the index. However, thesemethods don't return a plain PHP array.Instead, an instance ofMatchory\Elasticsearch\Collection is returned.

The ElasticsearchCollection class extends Laravel's baseIlluminate\Support\Collection class, which provides avariety of helpful methods for interacting with datacollections. For example, therejectmethod may be used to remove models from a collection based on the results of an invoked closure:

useApp\Models\Post;$posts = Post::where('sponsored',true)->get();$posts =$posts->reject($post =>$post->in_review);

In addition to the methods provided by Laravel's base collection class, the Elasticsearch collection class provides afew extra methods that are specificallyintended for interacting with collections of Elasticsearch models:

Result Meta data

Elasticsearch provides a few additional fields in addition to the hits of a query, like the total result amount, or thequery execution time. The Elasticsearchcollection provides getters for these properties:

useApp\Models\Post;$posts = Post::all();$total =$posts->getTotal();$maxScore =$posts->getMaxScore();$duration =$posts->getDuration();$isTimedOut =$posts->isTimedOut();$scrollId =$posts->getScrollId();$shards =$posts->getShards();

Iterating

Since all of Laravel's collections implement PHP'siterable interfaces, you may loop over collections as if they werean array:

foreach ($titleas$title) {echo$post->title;}

Chunking Results

Elasticsearch indices can grow quite huge. Your application may run out of memory if you would attempt to load tens ofthousands of Elasticsearch documents viatheall orget methods without an upper bound. Therefore, the default amount of documents fetched is set to10. Tochange this, use thetake method:

useApp\Models\Post;$posts = Post::take(500)->get();

Retrieving individual Models

In addition to retrieving all the documents matching a given query, you may also retrieve single documents using thefind,first, orfirstWhere methods.Instead of returning a collection of models, these methods return a single model instance:

useApp\Models\Post;// Retrieve a model by its ID...$posts = Post::find('AVp_tCaAoV7YQD3Esfmp');// Retrieve the first model matching the query constraints...$post = Post::where('published',1)->first();// Alternative to retrieving the first model matching the query constraints...$post = Post::firstWhere('published',1);```

Sometimes you may wish to retrieve the first result of a query or perform some other action if no results are found. ThefirstOr method will return the firstresult matching the query or, if no results are found, execute the given closure. The value returned by the closure willbe considered the result of thefirstOr method:

useApp\Models\Post;$model = Post::where('tags','>',3)->firstOr(function () {// ...});

Not Found Exceptions

Sometimes you may wish to throw an exception if a model is not found. This is particularly useful in routes orcontrollers. ThefindOrFail andfirstOrFailmethods will retrieve the first result of the query; however, if no result is found, aMatchory\Elasticsearch\Exceptions\DocumentNotFoundException will bethrown:

$post = Post::findOrFail('AVp_tCaAoV7YQD3Esfmp');$post = Post::where('published',true)->firstOrFail();

If theDocumentNotFoundException is not caught, a 404 HTTP response is automatically sent back to the client:

useApp\Models\Post;Route::get('/api/posts/{id}',function ($id) {return Post::findOrFail($id);});

Inserting and Updating Models

Inserts

To insert a new document into the index, you should instantiate a new model instance and set attributes on the model.Then, call thesave method on the modelinstance:

namespaceApp\Http\Controllers;useApp\Models\Post;useIlluminate\Http\Request;useIlluminate\Http\Response;useApp\Http\Controllers\Controller;class PostControllerextends Controller{/**     * Create a new post instance.     *     * @param  Request  $request     * @return Response     */publicfunctionstore(Request$request):Response    {// Validate the request...$post =newPost;$post->title =$request->title;$post->save();    }}

In this example, we assign thename field from the incoming HTTP request to thename attribute of theApp\Models\Post model instance. When we call thesave method, a document will be inserted into the index.

Alternatively, you may use thecreate method to "save" a new model using a single PHP statement. The inserted modelinstance will be returned to you by thecreate method:

useApp\Models\Post;$post = Post::create(['title' =>'Searching efficiently',]);

However, before using the create method, you will need to specify either afillable orguarded property on yourmodel class. These properties are requiredbecause all Elasticsearch models are protected against mass assignment vulnerabilities by default. To learn more aboutmass assignment, please consult themass assignment documentation.

Updates

Thesave method may also be used to update models that already exist in the index. To update a model, you shouldretrieve it and set any attributes you wishto update. Then, you should call the model'ssave method.

Thesave() method may also be used to update models that already exist. To update a model, you should retrieve it, setany attributes you wish to update, andthen call the save method.

useApp\Models\Post;$post = Post::find('AVp_tCaAoV7YQD3Esfmp');$post->title ='Modified Post Title';$post->save();

Examining Attribute Changes

Elasticsearch provides theisDirty,isClean, andwasChanged methods to examine the internal state of your modeland determine how its attributes havechanged from when the model was originally retrieved.

TheisDirty method determines if any of the model's attributes have been changed since the model was retrieved. Youmay pass a specific attribute name to theisDirty method to determine if a particular attribute isdirty. TheisClean will determine if an attribute hasremained unchanged since the model wasretrieved. This method also accepts an optional attribute argument:

useApp\Models\Author;$author = Author::create(['first_name' =>'Moritz','last_name' =>'Friedrich','title' =>'Developer',]);$author->title ='Painter';$author->isDirty();// true$author->isDirty('title');// true$author->isDirty('first_name');// false$author->isClean();// false$author->isClean('title');// false$author->isClean('first_name');// true$author->save();$author->isDirty();// false$author->isClean();// true

ThewasChanged method determines if any attributes were changed when the model was last saved within the currentrequest cycle. If needed, you may pass anattribute name to see if a particular attribute was changed:

useApp\Models\Author;$author = Author::create(['first_name' =>'Taylor','last_name' =>'Otwell','title' =>'Developer',]);$author->title ='Painter';$author->save();$author->wasChanged();// true$author->wasChanged('title');// true$author->wasChanged('first_name');// false

ThegetOriginal method returns an array containing the original attributes of the model regardless of any changes tothe model since it was retrieved. Ifneeded, you may pass a specific attribute name to get the original value of a particular attribute:

useApp\Models\Author;$author = Author::find(1);$author->name;// John$author->email;// john@example.com$author->name ="Jack";$author->name;// Jack$author->getOriginal('name');// John$author->getOriginal();// Array of original attributes...

Mass Assignment

You may use thecreate method to "save" a new model using a single PHP statement. The inserted model instance will bereturned to you by the method:

useApp\Models\Post;$post = Post::create(['title' =>'Searching effectively',]);

However, before using thecreate method, you will need to specify either afillable orguarded property on yourmodel class. These properties are requiredbecause all Elasticsearch models are protected against mass assignment vulnerabilities by default.

A mass assignment vulnerability occurs when a user passes an unexpected HTTP request field and that field changes afield in your index that you did not expect.

So, to get started, you should define which model attributes you want to make mass assignable. You may do this using thefillable property on the model. Forexample, let's make thetitle attribute of ourPost model mass assignable:

namespaceApp\Models;useMatchory\Elasticsearch\Model;class Postextends Model{/**     * The attributes that are mass assignable.     *     * @var array     */protected$fillable = ['title'];}

Once you have specified which attributes are mass assignable, you may use thecreate method to insert a new documentin the index. Thecreate method returnsthe newly created model instance:

$post = Post::create(['title' =>'Searching effectively']);

If you already have a model instance, you may use thefill method to populate it with an array of attributes:

$post->fill(['title' =>'Searching more effectively']);

Allowing Mass Assignment

If you would like to make all of your attributes mass assignable, you may define your model'sguarded property as anempty array. If you choose to un-guardyour model, you should take special care to always hand-craft the arrays passed to Elasticsearch'sfill,create, andupdate methods:

/** * The attributes that aren't mass assignable. * * @var array */protected$guarded = [];

Upserts

There is currently no convenience wrapper for upserting documents (inserting or updating depending on whether modelsexist). If you're interested in such acapability, please open an issue.

Deleting Models

To delete a model, call thedelete method on a model instance:

useApp\Models\Post;$post = Post::find('AVp_tCaAoV7YQD3Esfmp');$post->delete();

Deleting An Existing Model By Its ID

In the example above, we are retrieving the model from the index before calling thedelete method. However, if youknow the ID of the model, you may deletethe model without explicitly retrieving it by calling thedestroy method. In addition to accepting the single ID, thedestroy method will accept multipleIDs, an array of IDs, or a collection of IDs:

useApp\Models\Post;Post::destroy(1);Post::destroy(1,2,3);Post::destroy([1,2,3]);Post::destroy(collect([1,2,3]));

Important:
Thedestroy method loads each model individually and calls thedelete method so that thedeleting anddeletedevents are properly dispatched foreach model.

Query Scopes

Query scopes are implemented exactly the way as they are in Eloquent.

Global Scopes

Global scopes allow you to add constraints to all queries for a given model. Writing your own global scopes can providea convenient, easy way to make sureevery query for a given model receives certain constraints.

Writing Global Scopes

Writing a global scope is simple. First, define a class that implements theMatchory\Elasticsearch\Interfaces\ScopeInterface interface. Laravel does nothave a conventional location that youshould place scope classes, so you are free to place this class in any directory that you wish.

TheScopeInterface requires you to implement one method:apply. Theapply method may add constraints or othertypes of clauses to the query as needed:

namespaceApp\Scopes;useMatchory\Elasticsearch\Query;useMatchory\Elasticsearch\Model;useMatchory\Elasticsearch\Interfaces\ScopeInterface;class AncientScopeimplements ScopeInterface{/**     * Apply the scope to a given Elasticsearch query builder.     *     * @param  \Matchory\Elasticsearch\Query  $query     * @param  \Matchory\Elasticsearch\Model  $model     * @return void     */publicfunctionapply(Query$query,Model$model)    {$query->where('created_at','<',now()->subYears(2000));    }}
Applying Global Scopes

To assign a global scope to a model, you should override the model's booted method and invoke the model'saddGlobalScope method. TheaddGlobalScope methodaccepts an instance of your scope as its only argument:

namespaceApp\Models;useApp\Scopes\AncientScope;useMatchory\Elasticsearch\Model;class Postextends Model{/**     * The "booted" method of the model.     *     * @return void     */protectedstaticfunctionbooted()    {static::addGlobalScope(newAncientScope);    }}
Anonymous Global Scopes

Elasticsearch also allows you to define global scopes using closures, which is particularly useful for simple scopesthat do not warrant a separate class oftheir own. When defining a global scope using a closure, you should provide a scope name of your own choosing as thefirst argument to theaddGlobalScope method:

namespaceApp\Models;useMatchory\Elasticsearch\Query;useMatchory\Elasticsearch\Model;class Postextends Model{/**     * The "booted" method of the model.     *     * @return void     */protectedstaticfunctionbooted():void    {static::addGlobalScope('ancient',function (Query$query) {$query->where('created_at','<',now()->subYears(2000));        });    }}
Removing Global Scopes

If you would like to remove a global scope for a given query, you may use thewithoutGlobalScope method. This methodaccepts the class name of the globalscope as its only argument:

Post::withoutGlobalScope(AncientScope::class)->get();

Or, if you defined the global scope using a closure, you should pass the string name that you assigned to the globalscope:

Post::withoutGlobalScope('ancient')->get();

If you would like to remove several or even all of the query's global scopes, you may use thewithoutGlobalScopesmethod:

// Remove all of the global scopes...Post::withoutGlobalScopes()->get();
// Remove some of the global scopes...Post::withoutGlobalScopes([    FirstScope::class,    SecondScope::class])->get();

Local Scopes

Local scopes allow you to define common sets of query constraints that you may easily re-use throughout yourapplication. For example, you may need tofrequently retrieve all posts that are considered "popular".

Writing local scopes

To define a scope, prefix an Elasticsearch model method with scope. Scopes should always return a query builderinstance:

namespaceApp\Models;useMatchory\Elasticsearch\Model;class Postextends Model{/**     * Scope a query to only include popular posts.     *     * @param  \Matchory\Elasticsearch\Query  $query     * @return \Matchory\Elasticsearch\Query     */publicfunctionscopePopular(Query$query):Query    {return$query->where('votes','>',100);    }/**     * Scope a query to only include published posts.     *     * @param  \Matchory\Elasticsearch\Query  $query     * @return \Matchory\Elasticsearch\Query     */publicfunctionscopePublished(Query$query):Query    {return$query->where('published',1);    }}
Utilizing local scopes

Once the scope has been defined, you may call the scope methods when querying the model. However, you should not includethe scope prefix when calling themethod. You can even chain calls to various scopes:

useApp\Models\Post;$posts = Post::popular()->published()->orderBy('created_at')->get();

Dynamic Scopes

Sometimes you may wish to define a scope that accepts parameters. To get started, just add your additional parameters toyour scope method's signature. Scopeparameters should be defined after the$query parameter:

namespaceApp\Models;useMatchory\Elasticsearch\Model;class Postextends Model{/**     * Scope a query to only include posts of a given type.     *     * @param  \Matchory\Elasticsearch\Query  $query     * @param  mixed  $type     * @return \Matchory\Elasticsearch\Query     */publicfunctionscopeOfType(Query$query,$type):Query    {return$query->where('type',$type);    }}

Once the expected arguments have been added to your scope method's signature, you may pass the arguments when callingthe scope:

$posts = Post::ofType('news')->get();

Comparing Models

Sometimes you may need to determine if two models are the "same". The is method may be used to quickly verify two modelshave the same ID, index, type, andconnection:

if ($post->is($anotherPost)) {//}

Events

Elasticsearch models dispatch several events, allowing you to hook into the following moments in a model's lifecycle:retrieved,creating,created,updating,updated,saving,saved,deleting,deleted,restoring,restored, andreplicating.

Theretrieved event will dispatch when an existing model is retrieved from the index. When a new model is saved forthe first time, thecreating andcreated events will dispatch. Theupdating /updated events will dispatch when an existing model is modified, andthesave method is called. Thesaving /saved events will dispatch when a model is created or updated - even if the model's attributes have notbeen changed.

To start listening to model events, define adispatchesEvents property on your Elasticsearch model. This property mapsvarious points of the Elasticsearchmodel's lifecycle to your ownevent classes. Each model event class should expectto receive an instance of the affectedmodel via its constructor:

namespaceApp\Models;useMatchory\Elasticsearch\Model;useApp\Events\UserDeleted;useApp\Events\UserSaved;class Postextends Model{/**     * The event map for the model.     *     * @var array     */protected$dispatchesEvents = ['saved' => PostSaved::class,'deleted' => PostDeleted::class,    ];}

After defining and mapping your events, you mayuseevent listeners to handle the events.

Using Closures

Instead of using custom event classes, you may register closures that execute when various model events are dispatched.Typically, you should register theseclosures in thebooted method of your model:

namespaceApp\Models;useMatchory\Elasticsearch\Model;class Postextends Model{/**     * The "booted" method of the model.     *     * @return void     */protectedstaticfunctionbooted():void    {static::created(function ($post) {//        });    }}

If needed, you mayutilizequeueable anonymous event listenerswhen registering modelevents. This will instruct Laravel to execute the model event listener in the background using yourapplication'squeue:

usefunctionIlluminate\Events\queueable;static::created(queueable(function ($post):void {//}));
Accessors & Mutators
Defining An Accessor

To define anaccessor, create agetFooAttribute method on your model whereFoo is the "studly" cased name of thefield you wish to access. In thisexample, we'll define an accessor for thetitle attribute. The accessor will automatically be called by model whenattempting to retrieve the value of thetitle attribute:

namespaceApp;useMatchory\Elasticsearch\Model;class postextends Model{/**     * Get the post title.     *     * @param  string  $value     * @return string     */publicfunctiongetTitleAttribute(string$value):string    {returnucfirst($value);    }}

As you can see, the original value of the field is passed to the accessor, allowing you to manipulate and return thevalue. To access the value of the accessor,you may simply access thetitle attribute on a model instance:

$post =App\Post::find(1);$title =$post->title;

Occasionally, you may need to add array attributes that do not have a corresponding field in your index. To do so,simply define an accessor for the value:

publicfunctiongetIsPublishedAttribute():bool{return$this->attributes['status'] ===1;}

Once you have created the accessor, just add the value to theappends property on the model:

protected$appends = ['is_published'];

Once the attribute has been added to the appends list, it will be included in model's array.

Defining A Mutator

To define a mutator, define asetFooAttribute method on your model whereFoo is the "studly" cased name of the fieldyou wish to access. So, again, let'sdefine a mutator for thetitle attribute. This mutator will be automatically called when we attempt to set the valueof thetitleattribute on the model:

namespaceApp;useMatchory\Elasticsearch\Model;class postextends Model{/**     * Set the post title.     *     * @param  string  $value     * @return string     */publicfunctionsetTitleAttribute(string$value):string    {returnstrtolower($value);    }}

The mutator will receive the value that is being set on the attribute, allowing you to manipulate the value and set themanipulated value on the model'sinternal$attributes property. So, for example, if we attempt to set the title attribute toAwesome post to read:

$post =App\Post::find(1);$post->title ='Awesome post to read';

In this example, the setTitleAttribute function will be called with the valueAwesome post to read. The mutator willthen apply the strtolower function to thename and set its resulting value in the internal $attributes array.

Muting Events

You may occasionally need to temporarily "mute" all events fired by a model. You may achieve this using thewithoutEvents method. ThewithoutEvents methodaccepts a closure as its only argument. Any code executed within this closure will not dispatch model events. Forexample, the following example will fetch anddelete anApp\Models\Post instance without dispatching any model events. Any value returned by the closure will bereturned by thewithoutEvents method:

useApp\Models\Post;$post = Post::withoutEvents(function ()use () {Post::findOrFail(1)->delete();return Post::find(2);});

Saving A Single Model Without Events

Sometimes you may wish to "save" a given model without dispatching any events. You may accomplish this using thesaveQuietly method:

$post = Post::findOrFail(1);$post->title ='Other search strategies';$post->saveQuietly();

Replicating Models

You may create an unsaved copy of an existing model instance using the replicate method. This method is particularlyuseful when you have model instances thatshare many of the same attributes:

useApp\Models\Address;$shipping = Address::create(['type' =>'shipping','line_1' =>'123 Example Street','city' =>'Victorville','state' =>'CA','postcode' =>'90001',]);$billing =$shipping->replicate()->fill(['type' =>'billing']);$billing->save();

Mutators and Casting

Accessors, mutators, and attribute casting allow you to transform Elasticsearch attribute values when you retrieve orset them on model instances. For example,you may want to use theLaravel encrypter to encrypt a value while it isstored in the index, and then automaticallydecrypt the attribute when you access it on an Elasticsearch model. Or, you may want to convert a JSON string that isstored in your index to an array when itis accessed via your Elasticsearch model.

Accessors & Mutators

Defining An Accessor

An accessor transforms an Elasticsearch attribute value when it is accessed. To define an accessor, create aget{Attribute}Attribute method on your modelwhere{Attribute} is the "studly" cased name of the field you wish to access.

In this example, we'll define an accessor for thefirst_name attribute. The accessor will automatically be called byElasticsearch when attempting to retrievethe value of thefirst_name attribute:

namespaceApp\Models;useMatchory\Elasticsearch\Model;class Userextends Model{/**     * Get the user's first name.     *     * @param  string  $value     * @return string     */publicfunctiongetFirstNameAttribute(string$value):string    {returnucfirst($value);    }}

As you can see, the original value of the field is passed to the accessor, allowing you to manipulate and return thevalue. To access the value of the accessor,you may simply access thefirst_name attribute on a model instance:

useApp\Models\User;$user = User::find(1);$firstName =$user->first_name;

You are not limited to interacting with a single attribute within your accessor. You may also use accessors to returnnew, computed values from existingattributes:

/** * Get the user's full name. * * @return string */publicfunctiongetFullNameAttribute():string{return"{$this->first_name}{$this->last_name}";}
Defining A Mutator

A mutator transforms an Elasticsearch attribute value when it is set. To define a mutator, define aset{Attribute}Attribute method on your model where{Attribute} is the "studly" cased name of the field you wish to access.

Let's define a mutator for thefirst_name attribute. This mutator will be automatically called when we attempt to setthe value of thefirst_name attributeon the model:

namespaceApp\Models;useMatchory\Elasticsearch\Model;class Userextends Model{/**     * Set the user's first name.     *     * @param  string  $value     * @return void     */publicfunctionsetFirstNameAttribute(string$value):void    {$this->attributes['first_name'] =strtolower($value);    }}

The mutator will receive the value that is being set on the attribute, allowing you to manipulate the value and set themanipulated value on the Elasticsearchmodel's internal$attributes property. To use our mutator, we only need to set thefirst_name attribute on anElasticsearch model:

useApp\Models\User;$user = User::find(1);$user->first_name ='Sally';

In this example, thesetFirstNameAttribute function will be called with the valueSally. The mutator will then applythestrtolower function to the nameand set its resulting value in the internal$attributes array.

Attribute Casting

Attribute casting provides functionality similar to accessors and mutators without requiring you to define anyadditional methods on your model. Instead, yourmodel's$casts property provides a convenient method of converting attributes to common data types.

The$casts property should be an array where the key is the name of the attribute being cast, and the value is thetype you wish to cast the field to. Thesupported cast types are:

  • array
  • boolean
  • collection
  • date
  • datetime
  • decimal:<digits>
  • double
  • encrypted
  • encrypted:array
  • encrypted:collection
  • encrypted:object
  • float
  • integer
  • object
  • real
  • string
  • timestamp

To demonstrate attribute casting, let's cast theis_admin attribute, which is stored in our index as an integer (0or1) to a boolean value:

namespaceApp\Models;useMatchory\Elasticsearch\Model;class Userextends Model{/**     * The attributes that should be cast.     *     * @var array     */protected$casts = ['is_admin' =>'boolean',    ];}

After defining the cast, theis_admin attribute will always be cast to a boolean when you access it, even if theunderlying value is stored in the index as aninteger:

$user =App\Models\User::find(1);if ($user->is_admin) {//}

Note: Attributes that arenull will not be cast.

Date Casting

You may cast date attributes by defining them within your model's$cast property array. Typically, dates should becast using thedatetime cast.

When defining adate ordatetime cast, you may also specify the date's format. This format will be used when themodel is serialized to an array or JSON:

/** * The attributes that should be cast. * * @var array */protected$casts = ['created_at' =>'datetime:Y-m-d',];

When a field is cast as a date, you may set its value to a UNIX timestamp, date string (Y-m-d), date-time string, or aDateTime /Carbon instance. Thedate's value will be correctly converted and stored in your index:

You may customize the default serialization format for all of your model's dates by defining aserializeDate method onyour model. This method does not affecthow your dates are formatted for storage in the index:

/** * Prepare a date for array / JSON serialization. * * @param  \DateTimeInterface  $date * @return string */protectedfunctionserializeDate(DateTimeInterface$date){return$date->format('Y-m-d');}

To specify the format that should be used when actually storing a model's dates within your index, you should define a$dateFormat property on your model:

/** * The storage format of the model's date fields. * * @var string */protected$dateFormat ='U';

Custom Casts

Laravel has a variety of built-in, helpful cast types; however, you may occasionally need to define your own cast types.You may accomplish this by defining aclass that implements theCastsAttributes interface.

Classes that implement this interface must define aget andset method. Theget method is responsible fortransforming a raw value from the index into acast value, while theset method should transform a cast value into a raw value that can be stored in the index. As anexample, we will re-implement thebuilt-injson cast type as a custom cast type:

Note: Due to type incompatibility, you will need to use different casts for Eloquent and Elasticsearch models, oromit the parameter type.

namespaceApp\Casts;useIlluminate\Contracts\Database\Eloquent\CastsAttributes;class Jsonimplements CastsAttributes{/**     * Cast the given value.     *     * @param  \Illuminate\Database\Eloquent\Model|\Matchory\Elasticsearch\Model  $model     * @param  string  $key     * @param  mixed  $value     * @param  array  $attributes     * @return array     */publicfunctionget($model,$key,$value,$attributes)    {returnjson_decode($value,true);    }/**     * Prepare the given value for storage.     *     * @param  \Illuminate\Database\Eloquent\Model|\Matchory\Elasticsearch\Model  $model     * @param  string  $key     * @param  array  $value     * @param  array  $attributes     * @return string     */publicfunctionset($model,$key,$value,$attributes)    {returnjson_encode($value);    }}

Once you have defined a custom cast type, you may attach it to a model attribute using its class name:

namespaceApp\Models;useApp\Casts\Json;useMatchory\Elasticsearch\Model;class Userextends Model    {/**         * The attributes that should be cast.         *         * @var array         */protected$casts = ['options' => Json::class,        ];    }
Value Object Casting

You are not limited to casting values to primitive types. You may also cast values to objects. Defining custom caststhat cast values to objects is very similarto casting to primitive types; however, theset method should return an array of key / value pairs that will be usedto set raw, storable values on the model.

As an example, we will define a custom cast class that casts multiple model values into a singleAddress value object.We will assume theAddress value hastwo public properties:lineOne andlineTwo:

namespaceApp\Casts;useApp\Models\AddressasAddressModel;useIlluminate\Contracts\Database\Eloquent\CastsAttributes;useInvalidArgumentException;class Addressimplements CastsAttributes{/**     * Cast the given value.     *     * @param  \Illuminate\Database\Eloquent\Model|\Matchory\Elasticsearch\Model  $model     * @param  string  $key     * @param  mixed  $value     * @param  array  $attributes     * @return \App\Models\Address     */publicfunctionget($model,$key,$value,$attributes)    {returnnewAddressModel($attributes['address_line_one'],$attributes['address_line_two']        );    }/**     * Prepare the given value for storage.     *     * @param  \Illuminate\Database\Eloquent\Model|\Matchory\Elasticsearch\Model  $model     * @param  string  $key     * @param  \App\Models\Address  $value     * @param  array  $attributes     * @return array     */publicfunctionset($model,$key,$value,$attributes)    {if (!$valueinstanceof AddressModel) {thrownewInvalidArgumentException('The given value is not an Address instance.');        }return ['address_line_one' =>$value->lineOne,'address_line_two' =>$value->lineTwo,        ];    }}

When casting to value objects, any changes made to the value object will automatically be synced back to the modelbefore the model is saved:

useApp\Models\User;$user = User::find(1);$user->address->lineOne ='Updated Address Value';$user->save();

Tip: If you plan to serialize your Elasticsearch models containing value objects to JSON or arrays, you shouldimplement theIlluminate\Contracts\Support\Arrayable andJsonSerializable interfaces on the value object.

Array / JSON Serialization

When an Elasticsearch model is converted to an array or JSON using thetoArray andtoJson methods, your custom castvalue objects will typically beserialized as well as long as they implement theIlluminate\Contracts\Support\Arrayable andJsonSerializableinterfaces. However, when using value objectsprovided by third-party libraries, you may not have the ability to add these interfaces to the object.

Therefore, you may specify that your custom cast class will be responsible for serializing the value object. To do so,your custom class cast should implementtheIlluminate\Contracts\Database\Eloquent\SerializesCastableAttributes interface. This interface states that yourclass should contain aserialize methodwhich should return the serialized form of your value object:

/** * Get the serialized representation of the value. * * @param  \Illuminate\Database\Eloquent\Model|\Matchory\Elasticsearch\Model  $model * @param  string  $key * @param  mixed  $value * @param  array  $attributes * @return mixed */publicfunctionserialize($model,string$key,$value,array$attributes){return (string)$value;}
Inbound Casting

Occasionally, you may need to write a custom cast that only transforms values that are being set on the model and doesnot perform any operations whenattributes are being retrieved from the model. A classic example of an inbound only cast is a "hashing" cast. Inboundonly custom casts should implement theCastsInboundAttributes interface, which only requires aset method to be defined.

namespaceApp\Casts;useIlluminate\Contracts\Database\Eloquent\CastsInboundAttributes;class Hashimplements CastsInboundAttributes{/**     * The hashing algorithm.     *     * @var string     */protected$algorithm;/**     * Create a new cast class instance.     *     * @param  string|null  $algorithm     * @return void     */publicfunction__construct($algorithm =null)    {$this->algorithm =$algorithm;    }/**     * Prepare the given value for storage.     *     * @param  \Illuminate\Database\Eloquent\Model|\Matchory\Elasticsearch\Model  $model     * @param  string  $key     * @param  array  $value     * @param  array  $attributes     * @return string     */publicfunctionset($model,$key,$value,$attributes)    {returnis_null($this->algorithm)                    ?bcrypt($value)                    :hash($this->algorithm,$value);    }}
Cast Parameters

When attaching a custom cast to a model, cast parameters may be specified by separating them from the class name using a: character and comma-delimitingmultiple parameters. The parameters will be passed to the constructor of the cast class:

/** * The attributes that should be cast. * * @var array */protected$casts = ['secret' => Hash::class.':sha256',];
Castables

You may want to allow your application's value objects to define their own custom cast classes. Instead of attaching thecustom cast class to your model, youmay alternatively attach a value object class that implements theIlluminate\Contracts\Database\Eloquent\Castableinterface:

useApp\Models\Address;protected$casts = ['address' => Address::class,];

Objects that implement theCastable interface must define acastUsing method that returns the class name of thecustom caster class that is responsible forcasting to and from theCastable class:

namespaceApp\Models;useIlluminate\Contracts\Database\Eloquent\Castable;useApp\Casts\AddressasAddressCast;class Addressimplements Castable{/**     * Get the name of the caster class to use when casting from / to this cast target.     *     * @param  array  $arguments     * @return string     */publicstaticfunctioncastUsing(array$arguments):string    {return AddressCast::class;    }}

When usingCastable classes, you may still provide arguments in the$casts definition. The arguments will be passedto thecastUsing method:

useApp\Models\Address;protected$casts = ['address' => Address::class.':argument',];
Castables & Anonymous Cast Classes

By combining "castables" with PHP'sanonymous classes, youmay define a value object and itscasting logic as a single castable object. To accomplish this, return an anonymous class from your value object'scastUsing method. The anonymous class shouldimplement theCastsAttributes interface:

namespaceApp\Models;useIlluminate\Contracts\Database\Eloquent\Castable;useIlluminate\Contracts\Database\Eloquent\CastsAttributes;class Addressimplements Castable{// .../**     * Get the caster class to use when casting from / to this cast target.     *     * @param  array  $arguments     * @return object|string     */publicstaticfunctioncastUsing(array$arguments)    {returnnewclassimplements CastsAttributes        {publicfunctionget($model,$key,$value,$attributes)            {returnnewAddress($attributes['address_line_one'],$attributes['address_line_two']                );            }publicfunctionset($model,$key,$value,$attributes)            {return ['address_line_one' =>$value->lineOne,'address_line_two' =>$value->lineTwo,                ];            }        };    }}

Route Model Binding

When injecting a model ID to a route or controller action, you will often query the Elasticsearch index to retrieve themodel that corresponds to that ID.Laravel route model binding provides a convenient way to automatically inject the model instances directly into yourroutes. For example, instead of injecting auser's ID, you can inject the entire User model instance that matches the given ID.

Implicit Binding

Laravel automatically resolves Elasticsearch models defined in routes or controller actions whose type-hinted variablenames match a route segment name. Forexample:

useApp\Models\Post;Route::get('/posts/{post}',function (Post$post) {return$post->content;});

Since the$post variable is type-hinted as theApp\Models\Post Elasticsearch model, and the variable name matchesthe{post} URI segment, Laravel willautomatically inject the model instance that has an ID matching the corresponding value from the request URI. If amatching model instance is not found in thedatabase, a404 HTTP response will automatically be generated.

Of course, implicit binding is also possible when using controller methods. Again, note the{post} URI segment matchesthe$post variable in the controllerwhich contains anApp\Models\Post type-hint:

useApp\Http\Controllers\PostController;useApp\Models\Post;// Route definition...Route::get('/posts/{post}', [PostController::class,'show']);// Controller method definition...publicfunctionshow(Post$post):View{returnview('post.full', ['post' =>$post]);}

Customizing The Key

Sometimes you may wish to resolve Elasticsearch models using a field other than_id. To do so, you may specify thefield in the route parameter definition:

useApp\Models\Post;Route::get('/posts/{post:slug}',fn(Post$post):Post =>$post);

If you would like model binding to always use an index field other than_id when retrieving a given model class, youmay override thegetRouteKeyName methodon the Elasticsearch model:

/** * Get the route key for the model. * * @return string */publicfunctiongetRouteKeyName():string{return'slug';}

Customizing Missing Model Behavior

Typically, a404 HTTP response will be generated if an implicitly bound model is not found. However, you may customizethis behavior by calling the missingmethod when defining your route. The missing method accepts a closure that will be invoked if an implicitly bound modelcan not be found:

useApp\Http\Controllers\LocationsController;useIlluminate\Http\Request;Route::get('/locations/{location:slug}', [LocationsController::class,'show'])    ->missing(fn(Request$request) => Redirect::route('locations.index')    ->name('locations.view');

Explicit Binding

You are not required to use Laravel's implicit, convention based model resolution in order to use model binding. You canalso explicitly define how routeparameters correspond to models. To register an explicit binding, use the router's model method to specify the class fora given parameter. You should defineyour explicit model bindings at the beginning of theboot method of yourRouteServiceProvider class:

useApp\Models\Post;useIlluminate\Support\Facades\Route;/** * Define your route model bindings, pattern filters, etc. * * @return void */publicfunctionboot():void{    Route::model('post', Post::class);// ...}

Next, define a route that contains a{post} parameter:

useApp\Models\Post;Route::get('/posts/{post}',function (Post$post) {// ...});

Since we have bound all{post} parameters to theApp\Models\Post model, an instance of that class will be injectedinto the route. So, for example, arequest toposts/1 will inject thePost instance from the index which has an ID of1.

If a matching model instance is not found in the index, a404 HTTP response will be automatically generated.

Customizing The Resolution Logic

If you wish to define your own model binding resolution logic, you may use theRoute::bind method. The closure youpass to the bind method will receive thevalue of the URI segment and should return the instance of the class that should be injected into the route. Again, thiscustomization should take place in theboot method of your application'sRouteServiceProvider:

useApp\Models\Post;useIlluminate\Support\Facades\Route;/** * Define your route model bindings, pattern filters, etc. * * @return void */publicfunctionboot():void{    Route::bind('post',function (string$value):Post {return Post::where('title',$value)->firstOrFail();    });// ...}

Alternatively, you may override theresolveRouteBinding method on your Elasticsearch model. This method will receivethe value of the URI segment and shouldreturn the instance of the class that should be injected into the route:

/** * Retrieve the model for a bound value. * * @param  mixed  $value * @param  string|null  $field * @return \Matchory\Elasticsearch\Model|null */publicfunctionresolveRouteBinding($value, ?string$field =null): ?self{return$this->where('name',$value)->firstOrFail();}

If a route is utilizing implicit binding scoping, theresolveChildRouteBinding method will be used to resolve thechild binding of the parent model:

/** * Retrieve the child model for a bound value. * * @param  string  $childType * @param  mixed  $value * @param  string|null  $field * @return \Matchory\Elasticsearch\Model|null */publicfunctionresolveChildRouteBinding(string$childType,$value, ?string$field): ?self{returnparent::resolveChildRouteBinding($childType,$value,$field);}

Usage as a query builder

You can use theES facade to access the query builder directly, from anywhere in your application.

Creating a new index

ES::create('my_index');# orES::index('my_index')->create();

Creating index with custom options (optional)

useMatchory\Elasticsearch\Facades\ES;useMatchory\Elasticsearch\Index;ES::index('my_index')->create(function(Index$index) {$index->shards(5)->replicas(1)->mapping(['my_type' => ['properties' => ['first_name' => ['type' =>'string',                ],'age' => ['type' =>'integer'                ]            ]        ]    ])});# orES::create('my_index',function(Index$index){$index->shards(5)->replicas(1)->mapping(['my_type' => ['properties' => ['first_name' => ['type' =>'string',                  ],'age' => ['type' =>'integer'                  ]              ]          ]      ])});

Dropping an index

ES::drop("my_index");# orES::index("my_index")->drop();

Running queries

To run a query, start by (optionally) selecting the connection and index.

$documents =ES::connection("default")                ->index("my_index")                ->type("my_type")                ->get();# return a collection of results

You can shorten the above query to:

$documents =ES::type("my_type")->get();# return a collection of results

Explicitly setting connection or index name in the query overrides configuration inconfig/es.php.

Getting documents by id

ES::type("my_type")->id(3)->first();# orES::type("my_type")->_id(3)->first();

Sorting

ES::type("my_type")->orderBy("created_at","desc")->get();# Sorting with text search scoreES::type("my_type")->orderBy("_score")->get();

Limit and offset

ES::type("my_type")->take(10)->skip(5)->get();

Select only specific fields

ES::type("my_type")->select("title","content")->take(10)->skip(5)->get();

Where clause

ES::type("my_type")->where("status","published")->get();# orES::type("my_type")->where("status","=","published")->get();

Where greater than

ES::type("my_type")->where("views",">",150)->get();

Where greater than or equal

ES::type("my_type")->where("views",">=",150)->get();

Where less than

ES::type("my_type")->where("views","<",150)->get();

Where less than or equal

ES::type("my_type")->where("views","<=",150)->get();

Where like

ES::type("my_type")->where("title","like","foo")->get();

Where field exists

ES::type("my_type")->where("hobbies","exists",true)->get();# orES::type("my_type")->whereExists("hobbies",true)->get();

Where in clause

ES::type("my_type")->whereIn("id", [100,150])->get();

Where between clause

ES::type("my_type")->whereBetween("id",100,150)->get();# orES::type("my_type")->whereBetween("id", [100,150])->get();

Where not clause

ES::type("my_type")->whereNot("status","published")->get();# orES::type("my_type")->whereNot("status","=","published")->get();

Where not greater than

ES::type("my_type")->whereNot("views",">",150)->get();

Where not greater than or equal

ES::type("my_type")->whereNot("views",">=",150)->get();

Where not less than

ES::type("my_type")->whereNot("views","<",150)->get();

Where not less than or equal

ES::type("my_type")->whereNot("views","<=",150)->get();

Where not like

ES::type("my_type")->whereNot("title","like","foo")->get();

Where not field exists

ES::type("my_type")->whereNot("hobbies","exists",true)->get();# orES::type("my_type")->whereExists("hobbies",true)->get();

Where not in clause

ES::type("my_type")->whereNotIn("id", [100,150])->get();

Where not between clause

ES::type("my_type")->whereNotBetween("id",100,150)->get();# orES::type("my_type")->whereNotBetween("id", [100,150])->get();

Search by a distance from a geo point

ES::type("my_type")->distance("location", ["lat" => -33.8688197,"lon" =>151.20929550000005],"10km")->get();# orES::type("my_type")->distance("location","-33.8688197,151.20929550000005","10km")->get();# orES::type("my_type")->distance("location", [151.20929550000005, -33.8688197],"10km")->get();

Search using array queries

ES::type("my_type")->body(["query" => ["bool" => ["must" => [                 ["match" => ["address" =>"mill" ] ],                 ["match" => ["address" =>"lane" ] ]             ]         ]     ]])->get();# Note that you can mix between query builder and array queries.# The query builder will will be merged with the array query.ES::type("my_type")->body(["_source" => ["content"]"query" => ["bool" => ["must" => [             ["match" => ["address" =>"mill" ] ]         ]     ]],"sort" => ["_score"]     ])->select("name")->orderBy("created_at","desc")->take(10)->skip(5)->get();# The result query will be/*Array(    [index] => my_index    [type] => my_type    [body] => Array        (            [_source] => Array                (                    [0] => content                    [1] => name                )            [query] => Array                (                    [bool] => Array                        (                            [must] => Array                                (                                    [0] => Array                                        (                                            [match] => Array                                                (                                                    [address] => mill                                                )                                        )                                )                        )                )            [sort] => Array                (                    [0] => _score                    [1] => Array                        (                            [created_at] => desc                        )                )        )    [from] => 5    [size] => 10    [client] => Array        (            [ignore] => Array                (                )        ))*/

Search the entire document

ES::type("my_type")->search("hello")->get();# search with Boost = 2ES::type("my_type")->search("hello",2)->get();# search within specific fields with different weightsES::type("my_type")->search("hello",function($search){$search->boost(2)->fields(["title" =>2,"content" =>1])})->get();

Search with highlight fields

$doc =ES::type("my_type")->highlight("title")->search("hello")->first();# Multiple fields Highlighting is allowed.$doc =ES::type("my_type")->highlight("title","content")->search("hello")->first();# Return all highlights as array using $doc->getHighlights() method.$doc->getHighlights();# Also you can return only highlights of specific field.$doc->getHighlights("title");

Return only first document

ES::type("my_type")->search("hello")->first();

Return only count

ES::type("my_type")->search("hello")->count();

Scan-and-Scroll queries

# These queries are suitable for large amount of data.# A scrolled search allows you to do an initial search and to keep pulling batches of results# from Elasticsearch until there are no more results left.# It’s a bit like a cursor in a traditional database$documents =ES::type("my_type")->search("hello")                 ->scroll("2m")                 ->take(1000)                 ->get();# Response will contain a hashed code `scroll_id` will be used to get the next result by running$documents =ES::type("my_type")->search("hello")                 ->scroll("2m")                 ->scrollID("DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAFMFlJQOEtTdnJIUklhcU1FX2VqS0EwZncAAAAAAAABSxZSUDhLU3ZySFJJYXFNRV9laktBMGZ3AAAAAAAAAU4WUlA4S1N2ckhSSWFxTUVfZWpLQTBmdwAAAAAAAAFPFlJQOEtTdnJIUklhcU1FX2VqS0EwZncAAAAAAAABTRZSUDhLU3ZySFJJYXFNRV9laktBMGZ3")                 ->get();# And so on ...# Note that you don't need to write the query parameters in every scroll. All you need the `scroll_id` and query scroll time.# To clear `scroll_id`ES::type("my_type")->scrollID("DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAFMFlJQOEtTdnJIUklhcU1FX2VqS0EwZncAAAAAAAABSxZSUDhLU3ZySFJJYXFNRV9laktBMGZ3AAAAAAAAAU4WUlA4S1N2ckhSSWFxTUVfZWpLQTBmdwAAAAAAAAFPFlJQOEtTdnJIUklhcU1FX2VqS0EwZncAAAAAAAABTRZSUDhLU3ZySFJJYXFNRV9laktBMGZ3")        ->clear();

Paginate results with 5 documents per page

$documents =ES::type("my_type")->search("hello")->paginate(5);# Getting pagination links$documents->links();# Bootstrap 4 pagination$documents->links("bootstrap-4");# Simple bootstrap 4 pagination$documents->links("simple-bootstrap-4");# Simple pagination$documents->links("simple-default");

These are all pagination methods you may use:

$documents->count()$documents->currentPage()$documents->firstItem()$documents->hasMorePages()$documents->lastItem()$documents->lastPage()$documents->nextPageUrl()$documents->perPage()$documents->previousPageUrl()$documents->total()$documents->url($page)

Getting the query array without execution

ES::type("my_type")->search("hello")->where("views",">",150)->toArray();

Getting the original elasticsearch response

ES::type("my_type")->search("hello")->where("views",">",150)->response();

Ignoring bad HTTP response

ES::type("my_type")->ignore(404,500)->id(5)->first();

Query Caching (Laravel)

Package comes with a built-in caching layer based on laravel cache.

ES::type("my_type")->search("hello")->remember(10)->get();# Specify a custom cache keyES::type("my_type")->search("hello")->remember(10,"last_documents")->get();# Caching using other available driverES::type("my_type")->search("hello")->cacheDriver("redis")->remember(10,"last_documents")->get();# Caching with cache key prefixES::type("my_type")->search("hello")->cacheDriver("redis")->cachePrefix("docs")->remember(10,"last_documents")->get();

Executing elasticsearch raw queries

ES::raw()->search(["index" =>"my_index","type"  =>"my_type","body" => ["query" => ["bool" => ["must" => [                    ["match" => ["address" =>"mill" ] ],                    ["match" => ["address" =>"lane" ] ]                ]            ]        ]    ]]);

Insert a new document

ES::type("my_type")->id(3)->insert(["title" =>"Test document","content" =>"Sample content"]);# A new document will be inserted with _id = 3.# [id is optional] if not specified, a unique hash key will be generated.

Bulk insert a multiple of documents at once.

# Main queryES::index("my_index")->type("my_type")->bulk(function ($bulk){# Sub queries$bulk->index("my_index_1")->type("my_type_1")->id(10)->insert(["title" =>"Test document 1","content" =>"Sample content 1"]);$bulk->index("my_index_2")->id(11)->insert(["title" =>"Test document 2","content" =>"Sample content 2"]);$bulk->id(12)->insert(["title" =>"Test document 3","content" =>"Sample content 3"]);});# Notes from the above query:# As index and type names are required for insertion, Index and type names are extendable. This means that:# If index() is not specified in subquery:# -- The builder will get index name from the main query.# -- if index is not specified in main query, the builder will get index name from configuration file.# And# If type() is not specified in subquery:# -- The builder will get type name from the main query.# you can use old bulk code style using multidimensional array of [id => data] pairsES::type("my_type")->bulk([10 => ["title" =>"Test document 1","content" =>"Sample content 1"],11 => ["title" =>"Test document 2","content" =>"Sample content 2"] ]);# The two given documents will be inserted with its associated ids

Update an existing document

ES::type("my_type")->id(3)->update(["title" =>"Test document","content" =>"sample content"]);# Document has _id = 3 will be updated.# [id is required]
# Bulk updateES::type("my_type")->bulk(function ($bulk){$bulk->id(10)->update(["title" =>"Test document 1","content" =>"Sample content 1"]);$bulk->id(11)->update(["title" =>"Test document 2","content" =>"Sample content 2"]);});

Incrementing field

ES::type("my_type")->id(3)->increment("views");# Document has _id = 3 will be incremented by 1.ES::type("my_type")->id(3)->increment("views",3);# Document has _id = 3 will be incremented by 3.# [id is required]

Decrementing field

ES::type("my_type")->id(3)->decrement("views");# Document has _id = 3 will be decremented by 1.ES::type("my_type")->id(3)->decrement("views",3);# Document has _id = 3 will be decremented by 3.# [id is required]

Update using script

# increment field by scriptES::type("my_type")->id(3)->script("ctx._source.$field += params.count",    ["count" =>1]);# add php tag to tags array listES::type("my_type")->id(3)->script("ctx._source.tags.add(params.tag)",    ["tag" =>"php"]);# delete the doc if the tags field contain mongodb, otherwise it does nothing (noop)ES::type("my_type")->id(3)->script("if (ctx._source.tags.contains(params.tag)) { ctx.op = 'delete' } else { ctx.op = 'none' }",    ["tag" =>"mongodb"]);

Delete a document

ES::type("my_type")->id(3)->delete();# Document has _id = 3 will be deleted.# [id is required]
# Bulk deleteES::type("my_type")->bulk(function ($bulk){$bulk->id(10)->delete();$bulk->id(11)->delete();});

Releases

See therelease page.

Authors

Basem Khirat -basemkhirat@gmail.com -@basemkhirat
Moritz Friedrich -moritz@matchory.com

Bugs, Suggestions and Contributions

Thanks toeveryone who has contributed to theoriginal project andeveryone else who has contributed to this fork!
Please useGithub for reporting bugs, and making comments or suggestions.

If you're interested in helping out, the most pressing issues would be modernizing the query builder to provide bettersupport for Elasticsearch features aswell as completing the test suite!

License

MIT

Have a happy searching..


[8]ページ先頭

©2009-2025 Movatter.jp