Elasticsearch Driver para Laravel Scout con el poder de las consultas de Elasticsearch.
A través del compositor
composer require hilsonxhero/elasticvisionNecesitará el archivo de configuración para definir sus índices:
php artisan vendor:publish --tag=elasticvision-config Tampoco olvide seguir las instrucciones de instalación para Laravel Scout, y en su configuración de Laravel Scout, configure el controlador en elastic .
Puede definir la asignación para su índice en el archivo de configuración ElasticVision:
return [
' indexes ' => [
App Models Product ::class,
],
];php artisan scout:index productsphp artisan scout:import " AppModelsProduct " En el último caso, puede implementar la interfaz Explored y sobrescribir el mapeo con la función mappableAs() .
Esencialmente, esto significa que depende de usted si le gusta tenerlo todo junto en el modelo o por separado en el archivo de configuración.
Devuelve documentos que contienen un término exacto en un campo proporcionado.
Puede usar el término consulta para encontrar documentos basados en un valor preciso, como un precio, una identificación de producto o un nombre de usuario.
use App Models Post ;
$ posts = Post :: search ( ' lorem ' )
-> filter ( new Term ( ' published ' , true ))
-> get ();Devuelve documentos que contienen uno o más términos exactos en un campo proporcionado.
La consulta de los términos es la misma que la consulta del término, excepto que puede buscar múltiples valores. Un documento coincidirá si contiene al menos uno de los términos. Para buscar documentos que contengan más de un término coincidente.
use App Models Post ;
$ posts = Post :: search ( ' lorem ' )
-> should ( new Terms ( ' tags ' , [ ' featured ' ], 2 ))
-> get (); field
(Opcional, Objeto) Campo que desea buscar.
boost
(Opcional, flotante) Número de punto flotante utilizado para disminuir o aumentar las puntuaciones de relevancia de una consulta. El valor predeterminado a 1.0.
Por defecto, sus resultados de búsqueda serán ordenados por su puntaje según Elasticsearch. Si desea intervenir e influir en la clasificación, puede hacerlo utilizando la función orderBy() de Laravel Scout.
use App Models Post ;
$ results = Post :: search ( ' Self-steering ' )
-> orderBy ( ' published_at ' , ' desc ' )
-> get ();Devuelve documentos que contienen términos dentro de un rango proporcionado.
use App Models User ;
$ results = User :: search ( ' fugiat ' )-> must ( new Range ( ' age ' ,[ ' gte ' => 18 , ' lte ' => 35 ]))-> get ();Devuelve documentos que contienen términos que coincidan con una expresión regular.
Una expresión regular es una forma de hacer coincidir los patrones en los datos utilizando caracteres de marcador de posición, llamados operadores. Para una lista de operadores compatibles con la consulta RegExp, consulte la sintaxis de expresión regular.
use App Models User ;
$ results = User :: search ( ' fugiat ' )-> must ( new RegExp ( ' username ' , ' k.*y ' , ' ALL ' , false ))-> get (); field
(Opcional, Objeto) Campo que desea buscar.
value
(Requerido, cadena) Expresión regular para los términos que desea encontrar en el <field> provisto. Para una lista de operadores compatibles, consulte la sintaxis de expresión regular.
flags
(Opcional, String) Habilita operadores opcionales para la expresión regular. Para valores válidos y más información, consulte la sintaxis de expresión regular.
case_insensitive
(Opcional, booleano) permite una coincidencia insensible en el caso del valor de expresión regular con los valores de campo indexados cuando se establece en verdadero. El valor predeterminado es falso, lo que significa que la sensibilidad del caso de la coincidencia depende de la asignación del campo subyacente.
boost
(Opcional, flotante) Número de punto flotante utilizado para disminuir o aumentar las puntuaciones de relevancia de una consulta. El valor predeterminado a 1.0.
Devuelve documentos que contienen términos que coinciden con un patrón comodín.
Un operador comodín es un marcador de posición que coincide con uno o más personajes. Por ejemplo, el * operador comodín coincide con cero o más caracteres. Puede combinar operadores comodín con otros personajes para crear un patrón comodín.
use App Models User ;
$ users = User :: search ()
-> should ( new Wildcard ( ' username ' , ' ki*y ' ))
-> get ();Devuelve documentos que coincidan con un texto, número, fecha o valor booleano proporcionado. El texto proporcionado se analiza antes de coincidir.
La consulta de coincidencia es la consulta estándar para realizar una búsqueda de texto completo, incluidas las opciones para la coincidencia difusa.
use App Models Article ;
$ articles = Article :: search ()
-> must ( new Matching ( ' title ' , ' ipsum ' ))
-> get ();Devuelve documentos que contienen las palabras de un texto proporcionado, en el mismo orden según lo dispuesto. El último término del texto proporcionado se trata como un prefijo, que coincide con cualquier palabra que comience con ese término.
use App Models User ;
$ users = User :: search ()
-> should ( new MatchPhrasePrefix ( ' message ' , ' quick brown f ' ))
-> get (); La consulta match_phrase analiza el texto y crea una consulta phrase fuera del texto analizado. Por ejemplo:
use App Models User ;
$ users = User :: search ()
-> should ( new MatchPhrase ( ' message ' , ' this is a test ' ))
-> get ();Envuelve otra consulta para buscar campos anidados.
La consulta nested busca objetos de campo anidados como si estuvieran indexados como documentos separados. Si un objeto coincide con la búsqueda, la consulta nested devuelve el documento principal principal.
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 );Una consulta de frase coincide con los términos hasta una SLOP configurable (que vale la pena a 0) en cualquier orden. Los términos transponidos tienen una pendiente de 2.
El analizador se puede configurar para controlar qué analizador realizará el proceso de análisis en el texto. Es predeterminado a la definición de mapeo explícito de campo, o al analizador de búsqueda predeterminado, por ejemplo:
La mayor parte de la configuración que hará a través de la asignación de su índice. Sin embargo, si, por ejemplo, desea definir la configuración de Elasticsearch más avanzada, como analizadores o tokenizadores, debe hacerlo utilizando la configuración del índice.
Tenga en cuenta que cada vez que cambie la configuración del índice, debe recrear el índice.
Para comenzar a usar la configuración del índice, ampliaremos el modelo de publicación con una función indexSettings para establecer un analizador.
<?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 ' ],
],
],
],
];
}
}El análisis de texto se establece como parte de la configuración del índice.
El siguiente ejemplo crea un analizador de sinónimo, el resultado final sería que cuando busca 'vue' usted (también) obtiene los resultados para 'reaccionar'. Para asegurarse de que los sinónimos coincidan con todos los casos, el filtro lowercase también se ejecuta.
<?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 ();
}
}Las agregaciones son parte de su consulta de búsqueda y pueden resumir sus datos. Puede leer más sobre las agregaciones en Elasticsearch en la documentación oficial. En este momento, no se incorporan todos los tipos de agregación, pero la creación de los faltantes debe ser factible (y estas adiciones al paquete son muy bienvenidas).
Agregar agregaciones hace que su consulta de búsqueda sea más avanzada. Aquí hay un ejemplo de la aplicación de demostración:
$ search = Cartographer :: search ();
$ search -> aggregation ( ' places ' , new TermsAggregation ( ' place ' ));
$ results = $ search -> raw ();
$ aggregations = $ results -> aggregations ();Esto devolverá una variedad de métricas sobre cuántas veces cada lugar está presente en el índice Elasticsearch.
Es muy fácil crear filtros y analizadores de sinónimos, pero tenga en cuenta que son "caros" para ejecutarse para ElasticSearch. Antes de recurrir a los sinónimos, vea si puede usar comodines o consultas difusas.
La documentación de Laravel Scout afirma que "más avanzado" donde "las cláusulas no son compatibles actualmente". Solo es posible una comprobación simple para ID además de la búsqueda estándar de término difuso:
$ categories = Category :: search ( ' lorem ipsum ' )-> filter ( new MatchPhrase ( ' status ' , ' enable ' ))-> take ( 15 )-> get ();ElasticVision amplía sus posibilidades utilizando constructores de consultas para escribir consultas más complejas.
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
);
}
}
Por ejemplo, para obtener todas las publicaciones que:
Puede ejecutar esta consulta de búsqueda:
$ 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 ;A veces te preguntarás por qué ciertos resultados se devuelven o no.
Aquí hay un ejemplo de la aplicación de demostración ElasticVision, aunque no con una consulta compleja:
class SearchController
{
public function __invoke ( SearchFormRequest $ request )
{
$ people = Cartographer :: search ( $ request -> get ( ' keywords ' ))-> get ();
return view ( ' search ' , [
' people ' => $ people ,
]);
}
} Para depurar esta consulta de búsqueda, puede llamar al método debug estática en el motor elástico para Laravel Scout:
use Hilsonxhero ElasticVision Infrastructure Scout ElasticEngine ;
$ debug = ElasticEngine :: debug ();La clase de depuración que devuelve este método puede darle la última consulta ejecutada como una matriz o como JSON. Debería poder copiar pete el JSON como una consulta directa a Elasticsearch.
$ lastQueryAsArray = ElasticEngine :: debug ()-> array ();
$ lastQueryAsJson = ElasticEngine :: debug ()-> json ();Use el comando de importación Laravel Scout para crear y actualizar índices.
php artisan scout:import <model>
Por ejemplo, si su modelo es "App Models Post", entonces el comando sería así:
php artisan scout:import "AppModelsPost"
Si desea recrear un índice, primero asegúrese de que esté eliminado y luego cree. Haga un seguimiento con una importación de Scout para rellenar el índice también.
Si está utilizando índices aliased, debe usar este comando en lugar de scout:import
php artisan elastic:update <index?>
Puede especificar un índice o elegir omitirlo y el comando actualizará todos sus índices. Por ejemplo, si su modelo es "App Model Post" y el índice es "Publicaciones":
php artisan elastic:update posts
php artisan scout:delete-index <model>
Use el comando Laravel Scount Delete-Index para eliminar los índices.
Asegúrese de haber configurado sus índices primero en config/elasticvision.php y ejecute los comandos Scout.