Elasticsearchのクエリの力を持つLaravel ScoutのElasticsearchドライバー。
作曲家経由
composer require hilsonxhero/elasticvisionインデックスを定義するには、構成ファイルが必要です。
php artisan vendor:publish --tag=elasticvision-configまた、Laravel Scoutのインストール手順に従って忘れずに、Laravel Scoutの構成では、ドライバーをelasticに設定してください。
ElasticVision構成ファイルのインデックスのマッピングを定義することができます。
return [
' indexes ' => [
App Models Product ::class,
],
];php artisan scout:index productsphp artisan scout:import " AppModelsProduct " 最後のケースでは、 Exploredインターフェイスを実装し、 mappableAs()関数を使用してマッピングを上書きすることができます。
基本的に、これは、モデルですべてを一緒に持っているのか、それとも構成ファイルに個別に持っているのかがあなた次第であることを意味します。
提供されたフィールドに正確な用語を含むドキュメントを返します。
クエリという用語を使用して、価格、製品ID、ユーザー名などの正確な値に基づいてドキュメントを見つけることができます。
use App Models Post ;
$ posts = Post :: search ( ' lorem ' )
-> filter ( new Term ( ' published ' , true ))
-> get ();提供されたフィールドに1つ以上の正確な用語を含むドキュメントを返します。
用語クエリは、複数の値を検索できることを除いて、用語クエリと同じです。ドキュメントは、少なくとも1つの用語が含まれている場合に一致します。複数の一致する用語を含むドキュメントを検索する。
use App Models Post ;
$ posts = Post :: search ( ' lorem ' )
-> should ( new Terms ( ' tags ' , [ ' featured ' ], 2 ))
-> get ();field
(オプション、オブジェクト)検索したいフィールド。
boost
(オプション、フロート)クエリの関連スコアを減少または増加させるために使用される浮動小数点数。デフォルトは1.0です。
デフォルトでは、ElasticSearchによると、検索結果はスコアによってソートされます。 Laravel ScoutのデフォルトのorderBy()関数を使用して、介入してソートに影響を与えたい場合は、そうすることができます。
use App Models Post ;
$ results = Post :: search ( ' Self-steering ' )
-> orderBy ( ' published_at ' , ' desc ' )
-> get ();提供された範囲内の用語を含むドキュメントを返します。
use App Models User ;
$ results = User :: search ( ' fugiat ' )-> must ( new Range ( ' age ' ,[ ' gte ' => 18 , ' lte ' => 35 ]))-> get ();正規表現に一致する用語を含むドキュメントを返します。
正規表現は、オペレーターと呼ばれるプレースホルダー文字を使用してデータのパターンを一致させる方法です。 Regexpクエリでサポートされている演算子のリストについては、正規表現の構文を参照してください。
use App Models User ;
$ results = User :: search ( ' fugiat ' )-> must ( new RegExp ( ' username ' , ' k.*y ' , ' ALL ' , false ))-> get ();field
(オプション、オブジェクト)検索したいフィールド。
value
(必須、文字列)提供された<field>で見つけたい用語の正規表現。サポートされているオペレーターのリストについては、正規表現構文を参照してください。
flags
(オプション、文字列)正規表現のオプションの演算子を有効にします。有効な値と詳細については、正規表現の構文を参照してください。
case_insensitive
(オプション、Boolean)は、Trueに設定すると、正規表現値とインデックス化されたフィールド値との症例の鈍感なマッチングを許可します。デフォルトはfalseです。つまり、マッチングの症例感度は、基礎となるフィールドのマッピングに依存します。
boost
(オプション、フロート)クエリの関連スコアを減少または増加させるために使用される浮動小数点数。デフォルトは1.0です。
ワイルドカードパターンに一致する用語を含むドキュメントを返します。
ワイルドカードオペレーターは、1つ以上の文字に一致するプレースホルダーです。たとえば、 *ワイルドカードオペレーターはゼロ以上の文字と一致します。ワイルドカードオペレーターと他のキャラクターを組み合わせて、ワイルドカードパターンを作成できます。
use App Models User ;
$ users = User :: search ()
-> should ( new Wildcard ( ' username ' , ' ki*y ' ))
-> get ();提供されたテキスト、番号、日付、またはブール値に一致するドキュメントを返します。提供されたテキストは、一致する前に分析されます。
一致クエリは、ファジーマッチングのオプションを含む、フルテキスト検索を実行するための標準クエリです。
use App Models Article ;
$ articles = Article :: search ()
-> must ( new Matching ( ' title ' , ' ipsum ' ))
-> get ();提供されたテキストの単語を含むドキュメントを、提供されたものと同じ順序で返します。提供されたテキストの最後の用語は、その用語で始まる単語と一致するプレフィックスとして扱われます。
use App Models User ;
$ users = User :: search ()
-> should ( new MatchPhrasePrefix ( ' message ' , ' quick brown f ' ))
-> get ();match_phraseクエリはテキストを分析し、分析されたテキストからphraseクエリを作成します。例えば:
use App Models User ;
$ users = User :: search ()
-> should ( new MatchPhrase ( ' message ' , ' this is a test ' ))
-> get ();ネストされたフィールドを検索するために別のクエリをラップします。
nestedクエリは、ネストされたフィールドオブジェクトを、まるで個別のドキュメントとしてインデックス化されているかのように検索します。オブジェクトが検索と一致する場合、 nestedクエリはroot親文書を返します。
use App Models Product ;
$ products = Product :: search ()
-> must ( new Nested ( ' category ' , new Term ( ' category.id ' , 2 )))
-> get (); use App Models Product ;
$ search = Product :: search ( " lorem " );
// $feature_ids = array([4 => [1,2], 5 => [1,2]])
foreach ( request ()-> feature_id as $ key => $ value ) {
$ query = new BoolQuery ();
$ query -> must ( new Term ( ' features.feature_id ' , $ key ));
$ query -> must ( new Terms ( ' features.feature_value_id ' , $ value ));
$ boolQuery -> add ( ' must ' , new Nested ( ' features ' , $ query ));
}
$ search -> newCompound ( $ boolQuery );
$ products = $ search -> paginate ( 15 );フレーズクエリは、任意の順序で構成可能なスロップ(デフォルトは0にデフォルト)までの用語と一致します。転置された項には2のスロップがあります。
アナライザーは、テキスト上の分析プロセスを実行するアナライザーを制御するように設定できます。デフォルトは、フィールドの明示的なマッピング定義、またはデフォルトの検索アナライザーなどです。
インデックスのマッピングを通じて行う構成のほとんど。ただし、たとえば、アナライザーやトークナザーなどのより高度なElasticsearch設定を定義する場合は、インデックス設定を使用してそうする必要があります。
インデックス設定を変更するときはいつでも、インデックスを再現する必要があることに注意してください。
インデックス設定の使用を開始するには、 indexSettings機能を使用してPOSTモデルを展開して、アナライザーを設定します。
<?php
namespace App Models ;
use Illuminate Database Eloquent Factories HasFactory ;
use Illuminate Database Eloquent Model ;
use Hilsonxhero ElasticVision Application Explored ;
use Hilsonxhero ElasticVision Application IndexSettings ;
use Laravel Scout Searchable ;
class Post extends Model implements Explored , IndexSettings
{
use HasFactory ;
use Searchable ;
protected $ fillable = [ ' title ' , ' published ' ];
public function mappableAs (): array
{
return [
' id ' => ' keyword ' ,
' title ' => ' text ' ,
' published ' => ' boolean ' ,
' created_at ' => ' date ' ,
];
}
public function indexSettings (): array
{
return [
' analysis ' => [
' analyzer ' => [
' standard_lowercase ' => [
' type ' => ' custom ' ,
' tokenizer ' => ' standard ' ,
' filter ' => [ ' lowercase ' ],
],
],
],
];
}
}テキスト分析は、インデックス設定の一部として設定されています。
次の例では、同義語アナライザーが作成されます。最終結果は、「Vue」を検索するときに(また)「React」の結果を取得することになります。同義語がすべてのケースと一致することを確認するために、 lowercaseフィルターも実行されます。
<?php
namespace App Models ;
use Illuminate Database Eloquent Factories HasFactory ;
use Illuminate Database Eloquent Model ;
use Hilsonxhero ElasticVision Application Explored ;
use Hilsonxhero ElasticVision Application IndexSettings ;
use Hilsonxhero ElasticVision Domain Analysis Analysis ;
use Hilsonxhero ElasticVision Domain Analysis Analyzer StandardAnalyzer ;
use Hilsonxhero ElasticVision Domain Analysis Filter SynonymFilter ;
use Laravel Scout Searchable ;
class Post extends Model implements Explored , IndexSettings
{
use HasFactory ;
use Searchable ;
protected $ fillable = [ ' title ' , ' published ' ];
public function mappableAs (): array
{
return [
' id ' => ' keyword ' ,
' title ' => [
' type ' => ' text ' ,
' analyzer ' => ' frameworks ' ,
],
' published ' => ' boolean ' ,
' created_at ' => ' date ' ,
];
}
public function indexSettings (): array
{
$ synonymFilter = new SynonymFilter ();
$ synonymFilter -> setSynonyms ([ ' vue => react ' ]);
$ synonymAnalyzer = new StandardAnalyzer ( ' frameworks ' );
$ synonymAnalyzer -> setFilters ([ ' lowercase ' , $ synonymFilter ]);
return ( new Analysis ())
-> addAnalyzer ( $ synonymAnalyzer )
-> addFilter ( $ synonymFilter )
-> build ();
}
}集約は検索クエリの一部であり、データを要約できます。 Aggregationsの詳細については、Elasticsearchの公式文書をご覧ください。現時点では、すべての集約タイプが組み込まれているわけではありませんが、行方不明のタイプを作成することは実行可能です(そして、これらのパッケージへの追加は大歓迎です)。
集約を追加すると、検索クエリがより高度になります。デモアプリケーションの例は次のとおりです。
$ search = Cartographer :: search ();
$ search -> aggregation ( ' places ' , new TermsAggregation ( ' place ' ));
$ results = $ search -> raw ();
$ aggregations = $ results -> aggregations ();これにより、ElasticSearchインデックスにすべての場所が存在する回数に関するメトリックの配列が返されます。
同義語フィルターとアナライザーを作成するのは非常に簡単ですが、ElasticSearchのために実行するには「高価」であることに注意してください。同義語に目を向ける前に、ワイルドカードまたはファジークエリを使用できるかどうかを確認してください。
Laravel Scoutの文書には、「条項が現在サポートされていない」「より高度」であると述べています。標準のファジー用語検索以外に、IDの簡単なチェックのみが可能です。
$ categories = Category :: search ( ' lorem ipsum ' )-> filter ( new MatchPhrase ( ' status ' , ' enable ' ))-> take ( 15 )-> get ();ElasticVisionは、クエリビルダーを使用して可能性を拡張して、より複雑なクエリを書き込みます。
class Product extends Model implements Explored
{
public function mappableAs (): array
{
return [
' id ' => ' keyword ' ,
' title_fa ' => [
' type ' => ' text ' ,
' analyzer ' => ' my_analyzer ' ,
],
' title_en ' => [
' type ' => ' text ' ,
],
' status ' => [
' type ' => ' text ' ,
],
' category ' => ' nested ' ,
' features ' => ' nested ' ,
' variants ' => ' nested ' ,
' has_stock ' => ' boolean ' ,
];
}
public function toSearchableArray (): array
{
return [
' id ' => $ this -> id ,
' title ' => $ this -> title ,
' status ' => $ this -> status ,
' category ' => $ this -> category ,
' features ' => $ this -> features ,
' variants ' => ProductVariantResource :: collection ( $ this -> variants )-> toArray ( true ),
' has_stock ' => $ this -> has_stock ,
];
}
public function indexSettings (): array
{
return [
" analysis " => [
" analyzer " => [
" my_analyzer " => [
" type " => " custom " ,
" tokenizer " => " standard " ,
" filter " => [ " lowercase " , " my_filter " ]
]
],
" filter " => [
" my_filter " => [
" type " => " ngram " ,
" min_gram " => 2 ,
]
]
],
" index " => [
" max_ngram_diff " => 13
]
];
}
/**
* Get the name of the index associated with the model.
*
* @return string
*/
public function searchableAs ()
{
return ' products ' ;
}
public function category ()
{
return $ this -> belongsTo ( Category ::class);
}
public function variants ()
{
return $ this -> hasMany ( ProductVariant ::class);
}
public function features ()
{
return $ this -> hasMany ( ProductFeature ::class);
}
/**
* check inventory of product variations
*
* @return IlluminateDatabaseEloquentCastsAttribute
*/
protected function hasStock (): Attribute
{
return Attribute :: make (
get: fn ( $ value ) => $ this -> variants ()-> sum ( ' stock ' ) > 0 ? true : false
);
}
}
たとえば、すべての投稿を取得するには:
この検索クエリを実行できます。
$ boolQuery = new BoolQuery ();
$ search = Product :: search ( " ipsum " )
-> field ( ' title ' )
-> field ( ' description ' )
-> filter ( new MatchPhrase ( ' status ' , ' enable ' ))
-> must ( new Nested ( ' category ' , new MatchPhrase ( ' category.id ' , 2 )));
if ( request ()-> filled ( ' available_stock ' )) {
$ search -> filter ( new Term ( ' has_stock ' , true ));
}
// request feature_ids value
// $feature_ids = array([4 => [1,2], 5 => [1,2]])
if ( request ()-> filled ( ' feature_ids ' )) {
foreach ( request ()-> feature_ids as $ key => $ value ) {
$ query = new BoolQuery ();
$ query -> must ( new MatchPhrase ( ' features.feature_id ' , $ key ));
$ query -> must ( new Terms ( ' features.feature_value_id ' , $ value ));
$ boolQuery -> add ( ' must ' , new Nested ( ' features ' , $ query ));
}
}
if ( request ()-> filled ( ' max_price ' ) && request ()-> filled ( ' min_price ' )) {
$ boolQuery -> add ( ' must ' , new Nested ( ' variants ' , new Range (
' variants.selling_price ' ,
[ ' gte ' => request ()-> min_price ]
)));
$ boolQuery -> add ( ' must ' , new Nested ( ' variants ' , new Range (
' variants.selling_price ' ,
[ ' lte ' => request ()-> max_price ]
)));
$ boolQuery -> add ( ' must_not ' , new Nested ( ' variants ' , new Range (
' variants.selling_price ' ,
[ ' lt ' => request ()-> min_price ]
)));
$ boolQuery -> add ( ' must_not ' , new Nested ( ' variants ' , new Range (
' variants.selling_price ' ,
[ ' gt ' => request ()-> max_price ]
)));
}
$ search -> newCompound ( $ boolQuery );
$ products = $ search -> paginate ( 15 );
return $ products ;特定の結果がなぜか、返されないのか疑問に思うかもしれません。
複雑なクエリはありませんが、ElasticVisionデモアプリの例を次に示します。
class SearchController
{
public function __invoke ( SearchFormRequest $ request )
{
$ people = Cartographer :: search ( $ request -> get ( ' keywords ' ))-> get ();
return view ( ' search ' , [
' people ' => $ people ,
]);
}
}この検索クエリをデバッグするには、Laravel Scoutの弾性エンジンの静的debugメソッドを呼び出すことができます。
use Hilsonxhero ElasticVision Infrastructure Scout ElasticEngine ;
$ debug = ElasticEngine :: debug ();この方法が返されるデバッグクラスは、最後に実行されたクエリを配列またはJSONとして提供できます。 JSONをElasticSearchへの直接クエリとしてコピーすることができるはずです。
$ lastQueryAsArray = ElasticEngine :: debug ()-> array ();
$ lastQueryAsJson = ElasticEngine :: debug ()-> json ();Laravel Scout Importコマンドを使用して、インデックスを作成および更新します。
php artisan scout:import <model>
たとえば、モデルが「app models post」の場合、コマンドは次のようになります。
php artisan scout:import "AppModelsPost"
インデックスを再作成する場合は、最初に削除されていることを確認してから作成します。スカウトのインポートをフォローアップして、インデックスを補充します。
エイリアスインデックスを使用している場合は、 scout:import
php artisan elastic:update <index?>
インデックスを指定するか、それを省略することを選択できます。コマンドはすべてのインデックスを更新します。たとえば、モデルが「app model post」であり、インデックスが「投稿」の場合:
php artisan elastic:update posts
php artisan scout:delete-index <model>
Laravel Scount Delete-Indexコマンドを使用して、インデックスを削除します。
最初にconfig/elasticvision.phpでインデックスを構成し、スカウトコマンドを実行していることを確認してください。