mammutgroup / bouncy
将 Elasticsearch 结果映射到 Eloquent 模型
Requires
- php: >=5.4.0
- elasticsearch/elasticsearch: ~1.0
- illuminate/database: 5.*
- illuminate/support: 5.*
This package is not auto-updated.
Last update: 2024-09-14 18:53:14 UTC
README
要在 Laravel 5 中使用它,请参阅
l5
分支。
Bouncy
Elasticsearch 是一款优秀的搜索引擎,但将其结果转换为易于使用的数据集需要一些工作。Bouncy 正好可以做到这一点:它将 Elasticsearch 结果映射到 Eloquent 模型,因此您可以继续使用相同的逻辑,同时增加一些特殊功能。此外,它还处理索引,无论是手动还是自动在模型创建、更新或删除时进行。
此包是为一个个人项目创建的,它仍在开发中。然而,我不期望它的 API 发生变化。
我受到了启发,大部分实现基于 Elasticquent。基本上,它是对那个包的重写分支。向开发者致敬。
目录
安装
- 将包添加到您的
composer.json
文件中,并运行composer update
{ "require": { "fadion/bouncy": "~1.0" } }
-
将服务提供者添加到您的
app/config/app.php
文件中,在providers
数组内:'Fadion\Bouncy\BouncyServiceProvider'
-
通过在终端中运行以下命令发布配置文件:
php artisan config:publish fadion/bouncy
-
编辑配置文件(位于
app/config/packages/bouncy/
),并设置 Elasticsearch 索引名称、服务器配置等。
设置
让您的模型知道它们应该使用 Bouncy,只需添加一个特质即可!我将使用一个虚构的 Product
模型进行示例。
use Fadion\Bouncy\BouncyTrait; class Product extends Eloquent { use BouncyTrait; // ...other Eloquent attributes // or methods. }
索引和类型名称
索引可以在配置文件中设置,而类型名称会自动从模型的表名中获取。这通常是组织文档的好方法,因为您只需配置一次,就可以忽略它。
当您需要专门设置索引或类型名称时,只需将以下属性添加到您的模型中
class Product extends Eloquent { protected $indexName = 'awesome_index'; protected $typeName = 'cool_type'; }
索引
在进行任何搜索查询之前,Elasticsearch 需要一个索引来工作。通常这是一个繁琐的任务,但 Bouncy 使其变得非常简单。
索引所有记录
Product::all()->index();
索引一组模型
Product::where('sold', true)->get()->index();
索引单个模型
$product = Product::find(10); $product->index();
集合索引将批量添加,Elasticsearch 处理得相当快。但是,请注意,索引大型集合是一个耗尽资源的过程。击中 SQL 数据库并迭代每一行需要时间和资源,因此请尽量保持集合相对较小。您将不得不根据您的服务器资源和配置实验一次可以索引的数据量。
更新索引
更新是安全地重新索引现有文档的一种方式,在版本冲突方面。当模型存在且其任何属性已更改时,它的索引将被更新。否则,它将作为调用 index()
方法一样添加到索引中。
更新模型的索引
$product = Product::find(10); $product->price = 100; $product->updateIndex();
使用自定义属性更新模型的索引。这种用法很少,因为最好是保持模型和索引同步,但在需要时它就在那里。
$product = Product::find(10); $product->updateIndex([ 'price' => 120, 'sold' => false ]);
删除索引
当您完成一个模型的索引时,显然您可以删除它。
移除集合的索引
Product::where('quantity', '<', 25)->get()->removeIndex();
移除单个模型的索引
$product = Product::find(10); $product->removeIndex();
该方法故意命名为'remove'而不是'delete',以免与Eloquent的delete()方法混淆。
重新索引
一个便捷方法,实际上会移除索引后再添加。这在您希望文档获得一个全新的索引、重置版本信息时非常有用。
重新索引集合
Product::where('condition', 'new')->get()->reindex();
重新索引单个模型
$product = Product::find(10); $product->reindex();
并发控制
Elasticsearch假设在索引过程中不会出现文档冲突,因此它不提供自动并发控制。然而,它确实提供了文档的版本信息,可用于确保旧文档不会覆盖新文档。这是手册中描述的推荐技术。
Bouncy提供了一个通过检查版本进行索引的单个方法。它只会在指定的版本与文档中的版本匹配时更新索引,否则返回false。显然,它是并发安全的。
$product = Product::find(10); $product->indexWithVersion(3);
自动索引
Bouncy知道何时创建、保存或删除模型,并将这些更改反映到索引中。除了现有数据库的初始索引创建外,通常您不需要使用上述方法来操作索引。任何新的模型索引都将自动添加,在保存时更新,在模型被删除时移除。
您可以通过将auto_index
配置选项设置为false
来完全禁用自动索引。这样做后,您需要自己将数据库同步到索引。
Bouncy无法更新或删除索引的唯一情况是在执行大量更新或删除时。这些查询直接在查询构建器上运行,无法覆盖它们。我正在寻找一种更好的方法来做这件事,但到目前为止,以下查询不会反映索引上的更改
Product::where('price', 100)->update(['price' => 110]); // or Product::where('price', 100)->delete();
您仍然可以手动调用索引方法并解决限制。这会增加一个数据库查询,但至少可以保持您的数据同步。
Product::where('price', 100)->get()->updateIndex(['price' => 110]); Product::where('price', 100)->update(['price' => 110]); // or Product::where('price', 100)->get()->removeIndex(); Product::where('price', 100)->delete();
映射
映射可以像您之前看到的任何内容一样轻松创建。它们定义为模型的类属性,并使用一些简单的方法处理。
向模型添加映射属性
use Fadion\Bouncy\BouncyTrait; class Product extends Eloquent { use BouncyTrait; protected $mappingProperties = [ 'title' => [ 'type' => 'string', 'store' => true ], 'description' => [ 'type' => 'string', 'index' => 'analyzed' ] ] }
放置这些映射
Product::putMapping();
获取映射
Product::getMapping();
删除映射
Product::deleteMapping();
重建(删除并再次放置)映射
Product::rebuildMapping();
检查映射是否存在
if (Product::hasMapping()) { // do something }
搜索
现在让我们谈谈真正的重点!搜索是Elasticsearch的强项,也是您为什么要使用它的原因。Bouncy不会干扰您,允许您以与Elasticsearch客户端完全相同的方式构建任何搜索查询。这提供了极大的灵活性,同时将Eloquent模型集合的结果提供给您。
一个匹配查询的示例
$params = [ 'query' => [ 'match' => [ 'title' => 'github' ] ], 'size' => 20 ]; $products = Product::search($params); foreach ($products as $product) { echo $product->title; }
$params
数组完全符合Elasticsearch的期望,以构建JSON请求。这里没有新内容!您可以轻松构建任何搜索查询,无论是匹配、多匹配、more_like_this等。
分页
分页结果在应用程序中非常重要,而原始Elasticsearch结果通常很难进行分页。这也是使用Bouncy的一个好理由!它以与Eloquent相同的方式分页结果,因此您不需要学习任何新内容。
每页显示15个模型(默认值)
$products = Product::search($params)->paginate();
分页到任意数量
$products = Product::search($params)->paginate(30);
在您的视图中,您可以像以前一样显示分页链接
$products->links();
限制
为了性能,应该在Elasticsearch参数列表中使用size
关键字对搜索结果进行限制。然而,为了便于限制,Bouncy提供了这项功能。
限制到50个结果
$products = Product::search($params)->limit(50);
结果信息
Elasticsearch提供了有关查询的一些信息,例如总命中数或所需时间。Bouncy的结果集合有方法可以轻松访问这些信息。
$products = Product::search($params); $products->total(); // Total number of hits $products->maxScore(); // Maximum score of the results $products->took(); // Time in ms it took to run the query $products->timedOut(); // Wheather the query timed out, or not. $products->shards(); // Array of shards information $products->shards($key); // Information on specific shard
文档信息
Elasticsearch文档有一些信息,如得分和版本。您可以使用以下方法访问这些数据
$products = Product::search($params); foreach ($products as $product) { $product->isDocument(); // Checks if it's an Elasticsearch document $product->documentScore(); // Score set in search results $product->documentVersion(); // Document version if present }
高亮显示
高亮是一个很好的视觉功能,可以增强您的搜索结果。Bouncy让访问高亮字段变得非常简单。
$params = [ 'query' => [ 'match' => [ 'title' => 'github' ] ], 'highlight' => [ 'fields' => [ 'title' => new \stdClass ] ] ]; $products = Product::search($params); foreach ($products as $product) { echo $product->highlight('title'); }
highlight()
方法将访问具有提供名称的任何高亮字段,如果找不到则静默失败(实际上,返回 false)。
搜索简写
灵活性和所有这些都是很棒的,但在某些情况下,您可能只需要运行一个简单的匹配查询并完成任务,而不必编写完整的参数数组。Bouncy提供了一些简写方法来处理最常见的搜索查询。它们与 search()
方法的工作方式和处理结果的方式相同,因此所有上述内容也适用于它们。
匹配查询
$products = Product::match($title, $query)
多匹配查询
$products = Product::multiMatch(Array $fields, $query)
模糊查询
$products = Product::fuzzy($field, $value, $fuzziness = 'AUTO')
地理形状查询
$products = Product::geoshape($field, Array $coordinates, $type = 'envelope')
ids 查询
$products = Product::ids(Array $values)
更多类似查询
$products = Product::moreLikeThis(Array $fields, Array $ids, $minTermFreq = 1, $percentTermsToMatch = 0.5, $minWordLength = 3)
自定义文档字段
Bouncy在索引时会使用您模型的属性,这在大多数情况下应该没问题。然而,如果您想控制Elasticsearch文档的结构,您可以通过在模型中添加一个 $documentFields
方法来自定义字段。
use Fadion\Bouncy\BouncyTrait; class Product extends Eloquent { use BouncyTrait; public function documentFields() { return [ 'id' => $this->id, 'price' => $this->price, 'rating' => 'perfect' ]; }; }
自定义集合
如果您正在使用Eloquent的自定义集合,您仍然可以使用Bouncy的方法。您只需要为您的集合类添加一个特质。
use Illuminate\Database\Eloquent\Collection; use Fadion\Bouncy\BouncyCollectionTrait; class MyAwesomeCollection extends Collection { use BouncyCollectionTrait; }
Elasticsearch 客户端外观
最后,当您需要时,您可以使用Facade以Laravel的方式访问Elasticsearch的本地客户端。为了使此步骤正常工作,您需要在app/config/app.php
中的别名字符串数组中添加一个别名:'Elastic' => 'Fadion\Bouncy\Facades\Elastic'
。
Elastic::index(); Elastic::get(); Elastic::search(); Elastic::indices()->create(); // and any other method it provides