fadion / bouncy
将 Elasticsearch 结果映射到 Eloquent 模型
Requires
- php: >=5.4.0
- elasticsearch/elasticsearch: ~1.0
- illuminate/database: 4.2.*
- illuminate/support: 4.2.*
This package is auto-updated.
Last update: 2024-09-21 20:07:49 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 客户端外观
最后,当你需要它时,你可以使用门面以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