rennokki/elasticscout

此包已被放弃,不再维护。作者建议使用babenkoivan/elastic-scout-driver包代替。

Elasticsearch 驱动器 for Laravel Scout。

1.5.0 2020-07-27 15:25 UTC

README

CI Latest Stable Version Total Downloads Monthly Downloads codecov StyleCI License

ElasticScout 是一个 Laravel Scout 驱动器,它可以与任何 Elasticsearch 服务器交互,在模型中提供全文搜索的全部功能。

此包源自Babenko Ivan 的 Elasticscout Driver 仓库

🤝 支持

Renoki Co. 在 GitHub 上致力于将许多开源项目和有用的项目带给世界。每天开发和维护项目是一项艰巨的工作,尽管如此,我们仍然热爱它。

如果您在日常工作中使用您的应用程序,在演示演示中,爱好项目,甚至是学校项目中,请分享一些关于我们工作的好评或赞助我们的工作。好评会触动我们的心灵和情绪,而赞助将使开源项目保持活力。

ko-fi

🚀 安装

使用 Composer CLI 安装包

$ composer require rennokki/elasticscout

如果您的 Laravel 包不支持自动发现,请将以下内容添加到您的 config/app.php 文件中

'providers' => [
    ...
    Rennokki\ElasticScout\ElasticScoutServiceProvider::class,
    ...
];

发布配置文件

$ php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
$ php artisan vendor:publish --provider="Rennokki\ElasticScout\ElasticScoutServiceProvider"

如果您希望直接访问已配置为具有您自己的配置的 Elasticsearch 客户端,您可以通过将外观添加到 config/app.php 来这样做

'ElasticScout' => Rennokki\ElasticScout\Facades\ElasticClient::class,

然后您可以像平时一样访问它

// Get all indexes
ElasticScout::indices()->get(['index' => '*']);

配置 Scout

在您的 .env 文件中,将您的 SCOUT_DRIVER 设置为 elasticscout,并附带 Elasticsearch 配置

SCOUT_DRIVER=elasticscout

SCOUT_ELASTICSEARCH_HOST=localhost
SCOUT_ELASTICSEARCH_PORT=9200

AWS Elasticsearch 服务

Amazon Elasticsearch 服务无需任何额外设置即可在 VPC 集群中完美运行。然而,对于公共集群,它有点奇怪,因为它需要 IAM 认证。

您首先确保已经安装了正确的 elasticsearch/elasticsearch 包版本。例如,对于 7.4 集群,您应该安装 elasticsearch/elasticsearch:"7.4.*",否则您的应用程序将收到错误。

$ composer require elasticsearch/elasticsearch:"7.4.*"

要找到正确的包大小,请检查Elasticsearch 的版本矩阵

当从 AWS Elasticsearch 集群请求数据时,ElasticScout 确保通过附加处理程序将您的身份验证传递给后续请求。您只需启用设置,通过设置 SCOUT_ELASTICSCOUT_AWS_ENABLED 环境变量即可完成此操作

AWS_ACCESS_KEY_ID=my_key
AWS_SECRET_ACCESS_KEY=my_secret

SCOUT_ELASTICSCOUT_AWS_ENABLED=true
SCOUT_ELASTICSCOUT_AWS_REGION=us-east-1

SCOUT_ELASTICSEARCH_HOST=search-xxxxxx.es.amazonaws.com
SCOUT_ELASTICSEARCH_PORT=443
SCOUT_ELASTICSEARCH_SCHEME=https

请注意:您不需要用户名和密码即可访问 AWS Elasticsearch 服务集群。

📄 索引

创建索引

在 Elasticsearch 中,索引是 MySQL 中的表,或 MongoDB 中的集合的等效项。您可以使用 artisan 创建索引类

$ php artisan make:elasticscout:index PostIndex

你会在 app/Indexes/PostIndex.php 中看到类似的内容

namespace App\Indexes;

use Rennokki\ElasticScout\Index;
use Rennokki\ElasticScout\Migratable;

class PostIndex extends Index
{
    use Migratable;

    /**
     * The settings applied to this index.
     *
     * @var array
     */
    protected $settings = [
        //
    ];

    /**
     * The mapping for this index.
     *
     * @var array
     */
    protected $mapping = [
        //
    ];
}

这里的关键是你可以为每个索引设置设置和映射。你可以在Elasticsearch的文档网站上找到更多关于 映射设置 的信息。

以下是一个为地理点数据类型创建映射的示例。

class RestaurantIndex extends Index
{
    ...
    protected $mapping = [
        'properties' => [
            'location' => [
                'type' => 'geo_point',
            ],
        ],
    ];
}

以下是在 $settings 变量中为空白分词器创建新分析器的示例。

class PostIndex extends Index
{
    ...
    protected settings = [
        'analysis' => [
            'analyzer' => [
                'content' => [
                    'type' => 'custom',
                    'tokenizer' => 'whitespace',
                ],
            ],
        ],
    ];
}

如果你希望更改索引的名称,可以通过覆盖 $name 变量来实现。

class PostIndex extends Index
{
    protected $name = 'posts_index_2';
}

将索引附加到模型

所有可以搜索的模型都应该使用 Rennokki\ElasticScout\Searchable 特性和实现 Rennokki\ElasticScout\Index\HasElasticScoutIndex 接口。

use Rennokki\ElasticScout\Contracts\HasElasticScoutIndex;
use Rennokki\ElasticScout\Searchable;

class Post extends Model implements HasElasticScoutIndex
{
    use Searchable;
}

此外,模型还应指定索引类。

use App\Indexes\PostIndex;
use Rennokki\ElasticScout\Contracts\HasElasticScoutIndex;
use Rennokki\ElasticScout\Index;
use Rennokki\ElasticScout\Searchable;

class Post extends Model implements HasElasticScoutIndex
{
    use Searchable;

    /**
     * Get the index instance class for Elasticsearch.
     *
     * @return \Rennokki\ElasticScout\Index
     */
    public function getElasticScoutIndex(): Index
    {
        return new PostIndex($this);
    }
}

将索引发布到Elasticsearch

要将索引发布到Elasticsearch,你应该同步索引。

$ php artisan elasticscout:index:sync App\\Post

现在,每次你的模型创建、更新或删除新记录时,它们都将自动同步到Elasticsearch。

如果你想导入已存在的数据,请使用Scout文档中描述的 scout:import 命令

你可以在代码中同步索引。

$restaurant = Restaurant::first();

$restaurant->getIndex()->sync(); // returns true/false

🔍 搜索查询

要查询Elasticsearch中的数据,你可以使用 search() 方法。

Post::search('Laravel')
    ->take(30)
    ->from(10)
    ->get();

如果你只想获取文档的数量,你也可以这样做。

$posts = Post::search('Lumen')->count();

🔺 过滤查询

ElasticScout 允许你通过通过 elasticsearch() 方法使用内置方法创建自定义查询。

必须、必须不、应该、过滤

你可以在构建器中直接使用Elasticsearch的必须、必须不、应该和过滤键。请注意,你可以按需链式调用。

Post::elasticsearch()
    ->must(['term' => ['tag' => 'wow']])
    ->should(['term' => ['tag' => 'yay']])
    ->shouldNot(['term' => ['tag' => 'nah']])
    ->filter(['term' => ['tag' => 'wow']])
    ->get();

⚗️ 查询定制

你可以向body或query键追加数据。

// apend to the body payload
Post::elasticsearch()
    ->appendToBody('minimum_should_match', 1)
    ->appendToBody('some_field', ['array' => 'yes'])
    ->get();
// append to the query payload
Post::elasticsearch()
    ->appendToQuery('some_field', 'value')
    ->appendToQuery('some_other_field', ['array' => 'yes'])
    ->get();

Wheres

Post::elasticsearch()
    ->where('title.keyword', 'Elasticsearch')
    ->first();
Book::elasticsearch()
    ->whereBetween('price', [100, 200])
    ->first();
Book::elasticsearch()
    ->whereNotBetween('price', [100, 200])
    ->first();

Whens、Unless、动态Wheres

Book::elasticsearch()
    ->when(true, function ($query) {
        return $query->where('price', 100);
    })->get();
Book::elasticsearch()
    ->unless(false, function ($query) {
        return $query->where('price', 100);
    })->get();
Book::elasticsearch()
    ->wherePrice(100)
    ->get();

// This is the equivalent.
Book::elasticsearch()
    ->where('price', 100)
    ->get();

如果动态Where包含多个单词,它们将被 snake_case 分割。

Book::elasticsearch()
    ->whereFanVotes(10)
    ->get();

// This is the same.
Book::elasticsearch()
    ->where('fan_votes', 10)
    ->get();

正则表达式过滤器

Post::elasticsearch()
    ->whereRegexp('title.raw', 'A.+')
    ->get();

存在性检查

由于Elasticsearch具有NoSQL结构,你应该能够检查字段是否存在。

Post::elasticsearch()
    ->whereExists('meta')
    ->whereNotExists('new_meta')
    ->get();

地理类型搜索

Restaurant::whereGeoDistance('location', [-70, 40], '1000m')
    ->get();
Restaurant::whereGeoBoundingBox(
    'location',
    [
        'top_left' => [-74.1, 40.73],
        'bottom_right' => [-71.12, 40.01],
    ]
)->get();
Restaurant::whereGeoPolygon(
    'location',
    [
        [-70, 40], [-80, 30], [-90, 20],
    ]
)->get();
Restaurant::whereGeoShape(
    'shape',
    [
        'type' => 'circle',
        'radius' => '1km',
        'coordinates' => [4, 52],
    ],
    'WITHIN'
)->get();

使用作用域

Elasticscout 还与在主模型中定义的作用域一起工作。

class Restaurant extends Model
{
    public function scopeNearby($query, $lat, $lon, $meters)
    {
        return $query->whereGeoDistance('location', [$lat, $lon], $meters.'m');
    }
}

$nearbyRestaurants =
    Restaurant::search('Dominos')->nearby(45, 35, 1000)->get();

查询缓存

使用 rennokki/laravel-eloquent-query-cache 可以实现查询缓存。您只需要检查代码库了解如何使用它。

基本上,您可以这样缓存请求

$booksByJohnGreen =
    Book::elasticsearch()
        ->cacheFor(now()->addMinutes(60))
        ->where('author', 'John Green')
        ->get();

Elasticsearch 规则

搜索规则是一个可以在多个查询中使用的类,帮助您一次性定义自定义的有效负载。这仅适用于搜索查询构建器。

要创建规则,使用 artisan 命令

$ php artisan make:elasticscout:rule NameRule

您会得到类似这样的结果

namespace App\SearchRules;

use Rennokki\ElasticScout\Builders\SearchQueryBuilder;
use Rennokki\ElasticScout\SearchRule;

class NameRule extends SearchRule
{
    /**
     * Initialize the rule.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Build the highlight payload.
     *
     * @param  SearchQueryBuilder  $builder
     * @return array
     */
    public function buildHighlightPayload(SearchQueryBuilder $builder)
    {
        return [
            //
        ];
    }

    /**
     * Build the query payload.
     *
     * @param  SearchQueryBuilder  $builder
     * @return array
     */
    public function buildQueryPayload(SearchQueryBuilder $builder)
    {
        return [
            //
        ];
    }
}

查询有效负载

buildQueryPayload() 中,您应该定义查询过程中将发生的查询有效负载。

例如,您可以从一些 bool 查询开始。有关 bool 查询的详细信息,您可以在Elasticsearch 文档中找到

class NameRule extends SearchRule
{
    public function buildQueryPayload(SearchQueryBuilder $builder)
    {
        return [
            'must' => [
                'match' => [
                    // access the search phrase from the $builder
                    'name' => $builder->query,
                ],
            ],
        ];
    }
}

要默认应用于所有搜索查询,在您的模型中定义一个 getElasticScoutSearchRules() 方法

use App\SearchRules\NameRule;

class Restaurant extends Model
{
    /**
     * Get the search rules for Elasticsearch.
     *
     * @return array
     */
    public function getElasticScoutSearchRules(): array
    {
        return [
            new NameRule,
        ];
    }
}

要查询级别应用规则,您可以调用 ->addRule() 方法

use App\SearchRules\NameRule;

Restaurant::search('Dominos')
    ->addRule(new NameRule)
    ->get();

您可以添加多个规则或将规则设置为特定值

use App\SearchRules\NameRule;
use App\SearchRules\LocationRule;

Restaurant::search('Dominos')
    ->addRules([
        new NameRule,
        new LocationRule($lat, $lon),
    ])->get();

// The rule that will be aplied will be only LocationRule
Restaurant::search('Dominos')
    ->addRule(new NameRule)
    ->setRules([
        new LocationRule($lat, $lon),
    ])->get();

高亮有效负载

在构建高亮有效负载时,您可以将数组传递给 buildHighlightPayload() 方法。有关高亮的更多详细信息,您可以在Elasticsearch 文档中找到

class NameRule extends SearchRule
{
    public function buildHighlightPayload(SearchQueryBuilder $builder)
    {
        return [
            'fields' => [
                'name' => [
                    'type' => 'plain',
                ],
            ],
        ];
    }

    public function buildQueryPayload(SearchQueryBuilder $builder)
    {
        return [
            'should' => [
                'match' => [
                    'name' => $builder->query,
                ],
            ],
        ];
    }
}

要访问有效负载,您可以使用模型中的 $highlight 属性(或最终集合中的每个模型的属性)。

use App\SearchRules\NameRule;

$restaurant = Restaurant::search('Dominos')->addRule(new NameRule)->first();

$name = $restaurant->elasticsearch_highlights->name;
$nameAsString = $restaurant->elasticsearch_highlights->nameAsString;

如果您需要向规则传递参数,可以通过添加您的构造方法来实现。

class NameRule extends SearchRule
{
    protected $name;

    public function __construct($name = null)
    {
        $this->name = $name;
    }

    public function buildQueryPayload(SearchQueryBuilder $builder)
    {
        // Override the name from the rule construct.
        $name = $this->name ?: $builder->query;

        return [
            'must' => [
                'match' => [
                    'name' => $name,
                ],
            ],
        ];
    }
}

Restaurant::search('Dominos')
    ->addRule(new NameRule('Pizza Hut'))
    ->get();

🐛 查询调试

您可以通过解释查询来进行调试。

Restaurant::search('Dominos')->explain();

您可以通过调用 getPayload() 来查看有效负载的外观。

Restaurant::search('Dominos')->getPayload();

🐛 测试

vendor/bin/phpunit

🤝 贡献

请参阅CONTRIBUTING 获取详细信息。

🔒 安全性

如果您发现任何与安全相关的问题,请通过电子邮件 alex@renoki.org 而不是使用问题跟踪器。

🎉 致谢