Elasticsearch Driver pour Laravel Scout avec la puissance des requêtes d'Elasticsearch.
Via le compositeur
composer require hilsonxhero/elasticvisionVous aurez besoin du fichier de configuration pour définir vos index:
php artisan vendor:publish --tag=elasticvision-config N'oubliez pas non plus de suivre les instructions d'installation pour Laravel Scout, et dans votre configuration Laravel Scout, définissez le pilote sur elastic .
Vous pouvez définir l'index de mappage pour votre index dans le fichier de configuration ElasticVision:
return [
' indexes ' => [
App Models Product ::class,
],
];php artisan scout:index productsphp artisan scout:import " AppModelsProduct " Dans le dernier cas, vous pouvez implémenter l'interface Explored et écraser le mappage avec la fonction mappableAs() .
Essentiellement, cela signifie que c'est à vous de décider que vous aimiez avoir tout ensemble dans le modèle, ou séparément dans le fichier de configuration.
Renvoie des documents contenant un terme exact dans un champ fourni.
Vous pouvez utiliser le terme requête pour trouver des documents basés sur une valeur précise telle qu'un prix, un identifiant de produit ou un nom d'utilisateur.
use App Models Post ;
$ posts = Post :: search ( ' lorem ' )
-> filter ( new Term ( ' published ' , true ))
-> get ();Renvoie des documents contenant un ou plusieurs termes exacts dans un champ fourni.
Les termes requête sont les mêmes que le terme requête, sauf que vous pouvez rechercher plusieurs valeurs. Un document correspondra s'il contient au moins l'un des termes. Pour rechercher des documents contenant plus d'un terme correspondant.
use App Models Post ;
$ posts = Post :: search ( ' lorem ' )
-> should ( new Terms ( ' tags ' , [ ' featured ' ], 2 ))
-> get (); field
(Facultatif, objet) Champ que vous souhaitez rechercher.
boost
(Facultatif, flottant) Numéro de point flottant utilisé pour diminuer ou augmenter les scores de pertinence d'une requête. Par défaut est 1.0.
Par défaut, vos résultats de recherche seront triés par leur score selon Elasticsearch. Si vous souhaitez intervenir et influencer le tri, vous pouvez le faire en utilisant la fonction orderBy() par défaut de Laravel Scout.
use App Models Post ;
$ results = Post :: search ( ' Self-steering ' )
-> orderBy ( ' published_at ' , ' desc ' )
-> get ();Renvoie des documents contenant des termes dans une plage fournie.
use App Models User ;
$ results = User :: search ( ' fugiat ' )-> must ( new Range ( ' age ' ,[ ' gte ' => 18 , ' lte ' => 35 ]))-> get ();Renvoie des documents qui contiennent des termes correspondant à une expression régulière.
Une expression régulière est un moyen de faire correspondre les modèles dans les données en utilisant des caractères d'espace réservé, appelées opérateurs. Pour une liste d'opérateurs pris en charge par la requête Regexp, voir Syntaxe d'expression régulière.
use App Models User ;
$ results = User :: search ( ' fugiat ' )-> must ( new RegExp ( ' username ' , ' k.*y ' , ' ALL ' , false ))-> get (); field
(Facultatif, objet) Champ que vous souhaitez rechercher.
value
(Requis, chaîne) Expression régulière pour les termes que vous souhaitez trouver dans le <field> fourni. Pour une liste des opérateurs pris en charge, voir Syntaxe d'expression régulière.
flags
(Facultatif, String) Active les opérateurs en option pour l'expression régulière. Pour des valeurs valides et plus d'informations, voir Syntaxe d'expression régulière.
case_insensitive
(Facultatif, Boolean) permet une correspondance insensible au cas de la valeur d'expression régulière avec les valeurs de champ indexées lorsqu'elles sont définies sur true. La valeur par défaut est fausse, ce qui signifie que la sensibilité à la casse de la correspondance dépend du mappage du champ sous-jacent.
boost
(Facultatif, flottant) Numéro de point flottant utilisé pour diminuer ou augmenter les scores de pertinence d'une requête. Par défaut est 1.0.
Renvoie des documents qui contiennent des termes correspondant à un motif de joker.
Un opérateur générique est un espace réservé qui correspond à un ou plusieurs personnages. Par exemple, l'opérateur * Wildcard correspond à zéro ou plus de caractères. Vous pouvez combiner les opérateurs de joker avec d'autres personnages pour créer un modèle de joker.
use App Models User ;
$ users = User :: search ()
-> should ( new Wildcard ( ' username ' , ' ki*y ' ))
-> get ();Renvoie des documents qui correspondent à un texte, un numéro, une date ou une valeur booléenne fournis. Le texte fourni est analysé avant l'appariement.
La requête de correspondance est la requête standard pour effectuer une recherche en texte intégral, y compris les options de correspondance floue.
use App Models Article ;
$ articles = Article :: search ()
-> must ( new Matching ( ' title ' , ' ipsum ' ))
-> get ();Renvoie des documents qui contiennent les mots d'un texte fourni, dans le même ordre que celui prévu. Le dernier terme du texte fourni est traité comme un préfixe, correspondant à tous les mots qui commencent par ce terme.
use App Models User ;
$ users = User :: search ()
-> should ( new MatchPhrasePrefix ( ' message ' , ' quick brown f ' ))
-> get (); La requête match_phrase analyse le texte et crée une requête phrase à partir du texte analysé. Par exemple:
use App Models User ;
$ users = User :: search ()
-> should ( new MatchPhrase ( ' message ' , ' this is a test ' ))
-> get ();Enveloppe une autre requête pour rechercher des champs imbriqués.
La requête nested fouille les objets de champ imbriqué comme s'ils étaient indexés comme des documents distincts. Si un objet correspond à la recherche, la requête nested renvoie le document parent racine.
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 );Une requête de phrase correspond aux termes à une pente configurable (qui est par défaut à 0) dans n'importe quel ordre. Les termes transposés ont une pente de 2.
L'analyseur peut être défini pour contrôler quel analyseur effectuera le processus d'analyse sur le texte. Il est par défaut de la définition de mappage explicite du champ, ou de l'analyseur de recherche par défaut, par exemple:
La majeure partie de la configuration que vous ferez via le mappage de votre index. Cependant, si par exemple, vous souhaitez définir des paramètres plus avancés ElasticSearch tels que les analyseurs ou les tokenseurs, vous devez le faire en utilisant des paramètres d'index.
Sachez que chaque fois que vous modifiez les paramètres d'index, vous devez recréer l'index.
Pour commencer à utiliser les paramètres d'index, nous allons développer le modèle de post avec une fonction indexSettings pour définir un analyseur.
<?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 ' ],
],
],
],
];
}
}L'analyse du texte est définie dans le cadre des paramètres d'index.
L'exemple suivant crée un analyseur de synonyme, le résultat final serait que lorsque vous recherchez «Vue», vous obtenez (également) les résultats de «réagir». Pour s'assurer que les synonymes correspondent à tous les cas, le filtre lowercase est également exécuté.
<?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 ();
}
}Les agrégations font partie de votre requête de recherche et peuvent résumer vos données. Vous pouvez en savoir plus sur les agrégations dans Elasticsearch dans la documentation officielle. En ce moment, tous les types d'agrégations ne sont pas intégrés, mais la création de celles manquantes doit être faisable (et ces ajouts au package sont les bienvenus).
L'ajout d'agrégations rend votre requête de recherche plus avancée. Voici un exemple de l'application de démonstration:
$ search = Cartographer :: search ();
$ search -> aggregation ( ' places ' , new TermsAggregation ( ' place ' ));
$ results = $ search -> raw ();
$ aggregations = $ results -> aggregations ();Cela renverra un éventail de mesures sur le nombre de fois où chaque place est présente dans l'indice Elasticsearch.
Il est très facile de créer des filtres et des analyseurs de synonymes, mais sachez qu'ils sont «coûteux» à exécuter pour Elasticsearch. Avant de vous tourner vers les synonymes, voyez si vous pouvez utiliser des jilèges ou des requêtes floues.
La documentation de Laravel Scout stipule que "plus avancée" où "les clauses ne sont pas actuellement soutenues". Seule une vérification simple pour l'ID est possible en plus de la recherche de terme flou standard:
$ categories = Category :: search ( ' lorem ipsum ' )-> filter ( new MatchPhrase ( ' status ' , ' enable ' ))-> take ( 15 )-> get ();ElasticVision étend vos possibilités à l'aide de Builders de requête pour écrire des requêtes plus complexes.
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
);
}
}
Par exemple, pour obtenir tous les messages:
Vous pouvez exécuter cette requête de recherche:
$ 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 ;Parfois, vous vous demandez pourquoi certains résultats sont ou ne sont pas retournés.
Voici un exemple de l'application de démonstration ElasticVision, mais pas avec une requête complexe:
class SearchController
{
public function __invoke ( SearchFormRequest $ request )
{
$ people = Cartographer :: search ( $ request -> get ( ' keywords ' ))-> get ();
return view ( ' search ' , [
' people ' => $ people ,
]);
}
} Pour déboguer cette requête de recherche, vous pouvez appeler la méthode debug statique sur le moteur élastique pour Laravel Scout:
use Hilsonxhero ElasticVision Infrastructure Scout ElasticEngine ;
$ debug = ElasticEngine :: debug ();La classe de débogage que cette méthode renvoie peut vous donner la dernière requête exécutée en tant que tableau ou JSON. Vous devriez être en mesure de copier-coller le JSON en tant que requête directe à Elasticsearch.
$ lastQueryAsArray = ElasticEngine :: debug ()-> array ();
$ lastQueryAsJson = ElasticEngine :: debug ()-> json ();Utilisez la commande Laravel Scout Import pour créer et mettre à jour les indices.
php artisan scout:import <model>
Par exemple, si votre modèle est "App Models Post", la commande serait comme ceci:
php artisan scout:import "AppModelsPost"
Si vous souhaitez recréer un index, assurez-vous d'abord qu'il est supprimé, puis créez-le. Suivi avec une importation Scout pour remplir l'index également.
Si vous utilisez des index aliasés, vous devez utiliser cette commande au lieu de scout:import
php artisan elastic:update <index?>
Vous pouvez spécifier un index ou choisir de l'omettre et la commande mettra à jour tous vos index. Par exemple, si votre modèle est "App Model Post" et que l'index est "Posts":
php artisan elastic:update posts
php artisan scout:delete-index <model>
Utilisez la commande Laravel Disping Delete-Index pour supprimer les indices.
Assurez-vous que vous avez d'abord configuré vos index dans config/elasticvision.php et exécuter les commandes scout.