ไดรเวอร์ Elasticsearch สำหรับ Laravel Scout ด้วยพลังของการสืบค้นของ Elasticsearch
ผ่านนักแต่งเพลง
composer require hilsonxhero/elasticvisionคุณจะต้องใช้ไฟล์กำหนดค่าเพื่อกำหนดดัชนีของคุณ:
php artisan vendor:publish --tag=elasticvision-config อย่าลืมทำตามคำแนะนำการติดตั้งสำหรับ Laravel Scout และในการกำหนดค่า Laravel Scout ของคุณตั้งค่าไดรเวอร์ให้ elastic
คุณสามารถกำหนดการแมปสำหรับดัชนีของคุณในไฟล์ config eLasticVision:
return [
' indexes ' => [
App Models Product ::class,
],
];php artisan scout:index productsphp artisan scout:import " AppModelsProduct " ในกรณีสุดท้ายคุณอาจใช้อินเทอร์เฟซ Explored และเขียนทับการแมปด้วยฟังก์ชัน mappableAs()
โดยพื้นฐานแล้วนี่หมายความว่ามันขึ้นอยู่กับคุณไม่ว่าคุณจะมีทั้งหมดเข้าด้วยกันในโมเดลหรือแยกกันในไฟล์กำหนดค่า
ส่งคืนเอกสารที่มีคำที่แน่นอนในฟิลด์ที่ให้ไว้
คุณสามารถใช้คำค้นหาคำเพื่อค้นหาเอกสารตามมูลค่าที่แม่นยำเช่นราคารหัสผลิตภัณฑ์หรือชื่อผู้ใช้
use App Models Post ;
$ posts = Post :: search ( ' lorem ' )
-> filter ( new Term ( ' published ' , true ))
-> get ();ส่งคืนเอกสารที่มีข้อกำหนดที่แน่นอนอย่างน้อยหนึ่งคำในฟิลด์ที่ให้ไว้
แบบสอบถามคำศัพท์เหมือนกับแบบสอบถามคำยกเว้นคุณสามารถค้นหาค่าหลายค่า เอกสารจะตรงกันหากมีอย่างน้อยหนึ่งคำ ในการค้นหาเอกสารที่มีคำที่ตรงกันมากกว่าหนึ่งคำ
use App Models Post ;
$ posts = Post :: search ( ' lorem ' )
-> should ( new Terms ( ' tags ' , [ ' featured ' ], 2 ))
-> get (); field
(ไม่บังคับวัตถุ) ฟิลด์ที่คุณต้องการค้นหา
boost
(ไม่บังคับ, ลอย) หมายเลขจุดลอยตัวที่ใช้ในการลดหรือเพิ่มคะแนนความเกี่ยวข้องของแบบสอบถาม ค่าเริ่มต้นเป็น 1.0
โดยค่าเริ่มต้นผลการค้นหาของคุณจะถูกเรียงตามคะแนนตาม Elasticsearch หากคุณต้องการก้าวเข้ามาและมีอิทธิพลต่อการเรียงลำดับที่คุณสามารถทำได้โดยใช้ฟังก์ชั่น orderBy() จาก Laravel Scout
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 ();ส่งคืนเอกสารที่มีคำศัพท์ที่ตรงกับนิพจน์ทั่วไป
นิพจน์ทั่วไปเป็นวิธีการจับคู่รูปแบบในข้อมูลโดยใช้อักขระตัวยึดที่เรียกว่าตัวดำเนินการ สำหรับรายชื่อผู้ให้บริการที่ได้รับการสนับสนุนโดย Query RegeXP ให้ดูไวยากรณ์นิพจน์ทั่วไป
use App Models User ;
$ results = User :: search ( ' fugiat ' )-> must ( new RegExp ( ' username ' , ' k.*y ' , ' ALL ' , false ))-> get (); field
(ไม่บังคับวัตถุ) ฟิลด์ที่คุณต้องการค้นหา
value
(จำเป็น, สตริง) นิพจน์ทั่วไปสำหรับคำศัพท์ที่คุณต้องการค้นหาใน <field> ที่ให้มา สำหรับรายชื่อผู้ให้บริการที่รองรับดูไวยากรณ์นิพจน์ทั่วไป
flags
(ไม่บังคับสตริง) เปิดใช้งานตัวดำเนินการเสริมสำหรับนิพจน์ทั่วไป สำหรับค่าที่ถูกต้องและข้อมูลเพิ่มเติมดูไวยากรณ์นิพจน์ทั่วไป
case_insensitive
(ตัวเลือกบูลีน) อนุญาตให้ทำการจับคู่แบบไม่รู้สึกตัวของค่านิพจน์ปกติกับค่าฟิลด์ที่จัดทำดัชนีเมื่อตั้งค่าเป็น TRUE ค่าเริ่มต้นเป็นเท็จซึ่งหมายถึงความไวของกรณีของการจับคู่ขึ้นอยู่กับการทำแผนที่ของฟิลด์พื้นฐาน
boost
(ไม่บังคับ, ลอย) หมายเลขจุดลอยตัวที่ใช้ในการลดหรือเพิ่มคะแนนความเกี่ยวข้องของแบบสอบถาม ค่าเริ่มต้นเป็น 1.0
ส่งคืนเอกสารที่มีคำศัพท์ที่ตรงกับรูปแบบของไวด์การ์ด
ผู้ให้บริการ Wildcard เป็นตัวยึดตำแหน่งที่ตรงกับตัวละครหนึ่งตัวหรือมากกว่า ตัวอย่างเช่นผู้ให้บริการ * Wildcard ตรงกับอักขระเป็นศูนย์หรือมากกว่า คุณสามารถรวมตัวดำเนินการไวด์การ์ดกับตัวละครอื่น ๆ เพื่อสร้างรูปแบบไวด์การ์ด
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 จะส่งคืนเอกสารผู้ปกครองรูท
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 );แบบสอบถามวลีตรงกับคำศัพท์กับ SLOP ที่กำหนดค่าได้ (ซึ่งค่าเริ่มต้นเป็น 0) ในลำดับใด ๆ ข้อกำหนดที่เปลี่ยนไปมี slop เป็น 2
เครื่องวิเคราะห์สามารถตั้งค่าเป็นตัววิเคราะห์ว่าตัววิเคราะห์จะดำเนินการวิเคราะห์ข้อความบนข้อความ เป็นค่าเริ่มต้นไปยังคำจำกัดความการแมปที่ชัดเจนของฟิลด์หรือตัววิเคราะห์การค้นหาเริ่มต้นตัวอย่างเช่น:
การกำหนดค่าส่วนใหญ่ที่คุณจะทำผ่านการแมปดัชนีของคุณ อย่างไรก็ตามหากตัวอย่างเช่นคุณต้องการกำหนดการตั้งค่า Elasticsearch ขั้นสูงเช่นเครื่องวิเคราะห์หรือโทเคนิเซอร์ที่คุณต้องทำโดยใช้การตั้งค่าดัชนี
โปรดทราบว่าเมื่อใดก็ตามที่คุณเปลี่ยนการตั้งค่าดัชนีคุณต้องสร้างดัชนีใหม่
ในการเริ่มต้นใช้การตั้งค่าดัชนีเราจะขยายในโมเดลโพสต์ด้วยฟังก์ชั่น indexSettings เพื่อตั้งค่าตัววิเคราะห์
<?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' คุณ (เช่น) จะได้ผลลัพธ์สำหรับ 'ตอบสนอง' เพื่อให้แน่ใจว่าคำพ้องความหมายตรงกับทุกกรณีตัวกรอง 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 ();
}
}การรวมเป็นส่วนหนึ่งของคำค้นหาของคุณและสามารถสรุปข้อมูลของคุณได้ คุณสามารถอ่านเพิ่มเติมเกี่ยวกับการรวมตัวใน 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 ;บางครั้งคุณอาจสงสัยว่าทำไมผลลัพธ์บางอย่างจึงไม่ถูกส่งคืน
นี่คือตัวอย่างจากแอป Demo Elasticvision แม้ว่าจะไม่ได้อยู่กับการสืบค้นที่ซับซ้อน:
class SearchController
{
public function __invoke ( SearchFormRequest $ request )
{
$ people = Cartographer :: search ( $ request -> get ( ' keywords ' ))-> get ();
return view ( ' search ' , [
' people ' => $ people ,
]);
}
} ในการดีบักคำค้นหาการค้นหานี้คุณสามารถเรียกวิธี debug แบบคงที่บนเอ็นจิ้นยืดหยุ่นสำหรับ Laravel Scout:
use Hilsonxhero ElasticVision Infrastructure Scout ElasticEngine ;
$ debug = ElasticEngine :: debug ();คลาสการดีบักที่วิธีนี้ส่งคืนสามารถให้การสืบค้นที่ดำเนินการล่าสุดแก่คุณเป็นอาร์เรย์หรือเป็น JSON คุณควรจะคัดลอก JSON เป็นแบบสอบถามโดยตรงไปยัง Elasticsearch
$ lastQueryAsArray = ElasticEngine :: debug ()-> array ();
$ lastQueryAsJson = ElasticEngine :: debug ()-> json ();ใช้คำสั่งนำเข้า Laravel Scout เพื่อสร้างและอัปเดตดัชนี
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 และเรียกใช้คำสั่ง Scout