hyperf/docs/en/scout.md
2022-10-31 00:47:12 +07:00

11 KiB

Model full text search

Preface

hyperf/scout is derived from laravel/scout, we have made some coroutine transformation to it , but maintains the same API. I would like to thank the Laravel development team for implementing such a powerful and easy-to-use component. This document is partially excerpted from the official Laravel documentation translated by the Laravel China community organization.

Hyperf/Scout provides a simple, driver-based solution for full-text search of models. Using model watchers, Scout automatically synchronizes your search index and model records.

Currently, Scout comes with an Elasticsearch driver; writing a custom driver is simple, and you are free to extend Scout with your own search implementation.

Install

Introduce component package and Elasticsearch driver

composer require hyperf/scout
composer require hyperf/elasticsearch

After Scout is installed, use the vendor:publish command to generate the Scout configuration file. This command will generate a scout.php configuration file in your config directory.

php bin/hyperf.php vendor:publish hyperf/scout

Finally, add the Hyperf\Scout\Searchable trait to the model you want to search. This trait registers a model observer to keep the model in sync with all drivers:

<?php

namespace App;

use Hyperf\Database\Model\Model;
use Hyperf\Scout\Searchable;

class Post extends Model
{
    use Searchable;
}

Configure

Config file

Generate configuration file

php bin/hyperf.php vendor:publish hyperf/scout

Configuration file

<?php

declare(strict_types=1);

return [
    'default' => env('SCOUT_ENGINE', 'elasticsearch'),
    'chunk' => [
        'searchable' => 500,
        'unsearchable' => 500,
    ],
    'prefix' => env('SCOUT_PREFIX', ''),
    'soft_delete' => false,
    'concurrency' => 100,
    'engine' => [
        'elasticsearch' => [
            'driver' => Hyperf\Scout\Provider\ElasticsearchProvider::class,
            // If index is set to null, each model corresponds to an index, otherwise each model corresponds to a type
            'index' => null,
            'hosts' => [
                env('ELASTICSEARCH_HOST', 'http://127.0.0.1:9200'),
            ],
        ],
    ],
];

Configure model index

Each model is synchronized with a given search "index" that contains all searchable records for that model. In other words, you can think of each "index" as a MySQL table. By default, each model is persisted to an index that matches the model's "table" name (usually the plural of the model name). You can also customize the model's index by overriding the searchableAs method on the model:

    <?php

    namespace App;

    use Hyperf\Scout\Searchable;
    use Hyperf\Database\Model\Model;

    class Post extends Model
    {
        use Searchable;

        /**
         * Get the index name for the model.
         *
         * @return string
         */
        public function searchableAs()
        {
            return 'posts_index';
        }
    }

Configure searchable data

By default, "index" will read data from the model's toArray method for persistence. If you want to customize the data synced to the search index, you can override the toSearchableArray method on the model:

    <?php

    namespace App;

    use Hyperf\Scout\Searchable;
    use Hyperf\Database\Model\Model;

    class Post extends Model
    {
        use Searchable;

        /**
         * Get the indexable data array for the model.
         *
         * @return array
         */
        public function toSearchableArray()
        {
            $array = $this->toArray();

            // Customize array...

            return $array;
        }
    }

index

Batch Import

If you want to install Scout into an existing project, you probably already have database records that you want to import into search-driven. Import all existing records into the search index using the import command provided by Scout:

    php bin/hyperf.php scout:import "App\Post"

Add record

When you add the Trait Hyperf\Scout\Searchable to a model, all you need to do is save a model instance and it will be automatically added to your search index. The update index operation will be done at the end of the coroutine and will not block the request.

    $order = new App\Order;

    // ...

    $order->save();

Bulk add

If you want to add a collection of models to the search index via the model query builder, you can also chain the searchable method on the model query builder. searchable will chunk the query result of the constructor and add the record to your search index.

    // Use the Model Query Builder to add...
    App\Order::where('price', '>', 100)->searchable();

    // Adding records using model relationships...
    $user->orders()->searchable();

    // Adding records using collections...
    $orders->searchable();

The searchable method can be thought of as an "upsert" operation. In other words, if the model record is already in your index, it will be updated. If it doesn't exist in the search index, add it to the index.

update record

To update a searchable model, simply update the properties of the model instance and save the model to the database. Scout will automatically sync updates to your search index:

    $order = App\Order::find(1);

    // 更新 order...

    $order->save();

You can also use the searchable method on a model query statement to update a collection of models. If the model doesn't exist in the index you're retrieving, it will be created:

    // Update with model query statement...
    App\Order::where('price', '>', 100)->searchable();

    // You can also use model relational updates...
    $user->orders()->searchable();

    // You can also use collection update...
    $orders->searchable();

Delete Record

Simply delete the model from the database using delete to remove the record in the index. This form of deletion is even compatible with the soft-deleted model:

    $order = App\Order::find(1);

    $order->delete();

If you don't want to retrieve the model before deleting the record, you can use the unsearchable method on the model query instance or collection:

    // Delete via model query...
    App\Order::where('price', '>', 100)->unsearchable();

    // Delete via model relationship...
    $user->orders()->unsearchable();

    // Delete by Collection...
    $orders->unsearchable();

Pause indexing

You may need to perform a batch of model operations without syncing model data to the search index. At this point you can use the coroutine-safe withoutSyncingToSearch method to do this. This method accepts a callback that executes immediately. All operations in this callback will not be synchronized to the model's index:

    App\Order::withoutSyncingToSearch(function () {
        // Execute model actions...
    });

You can use the search method to search for models. The search method accepts a string to search for the model. You also need to chain the get method on the search query to query the matching model with a given search statement:

    $orders = App\Order::search('Star Trek')->get();

Scout searches return collections of model models, so you can return results directly from routes or controllers, and they will be automatically converted to JSON:

    Route::get('/search', function () {
        return App\Order::search([])->get();
    });

If you want raw results before they are returned to the model, you should use the raw method:

    $orders = App\Order::search('Star Trek')->raw();

Search queries are usually executed on the indexes specified by the model's searchableAs method. Of course, you can also use the within method to specify a custom index that should be searched:

    $orders = App\Order::search('Star Trek')
        ->within('tv_shows_popularity_desc')
        ->get();

Where Statement

Scout allows you to add simple "where" clauses to your search queries. Currently, these statements only support basic numerical equality checks, and are primarily used for range search queries based on the owner's ID. Since search indexes are not relational databases, more advanced "where" statements are currently not supported:

    $orders = App\Order::search('Star Trek')->where('user_id', 1)->get();

Pagination

In addition to retrieving a collection of models, you can also use the paginate method to paginate search results. This method will return a Paginator instance like traditional model query pagination:

    $orders = App\Order::search('Star Trek')->paginate();

You can specify how many models to retrieve per page by passing the number as the first argument to the paginate method:

    $orders = App\Order::search('Star Trek')->paginate(15);

After obtaining the retrieval results, you can use your favorite template engine to render the pagination links to display the results, just like traditional model query pagination:

    <div class="container">
        @foreach ($orders as $order)
            {{ $order->price }}
        @endforeach
    </div>

    {{ $orders->links() }}

custom engine

write engine

If the built-in Scout search engine does not meet your needs, you can write a custom engine and register it with Scout. Your engine needs to inherit the Hyperf\Scout\Engine\Engine abstract class, which contains five methods that your custom engine must implement:

    use Hyperf\Scout\Builder;

    abstract public function update($models);
    abstract public function delete($models);
    abstract public function search(Builder $builder);
    abstract public function paginate(Builder $builder, $perPage, $page);
    abstract public function map($results, $model);

It will be helpful to see these methods in the Hyperf\Scout\Engine\ElasticsearchEngine class. This class will provide a good starting point for you to learn how to implement these methods in your custom engine.

Registration engine

Once you have written your custom engine, you can specify the engine in the configuration file. For example, if you have written a MySqlSearchEngine, you can write this in the configuration file:

<?php
return [
    'default' => 'mysql',
    'engine' => [
        'mysql' => [
            'driver' => MySqlSearchEngine::class,
        ],
        'elasticsearch' => [
            'driver' => \Hyperf\Scout\Provider\ElasticsearchProvider::class,
        ],
    ],
];

Differences from laravel/scout

  • Hyperf/Scout uses coroutines to efficiently synchronize search indexes and model records without relying on queue mechanisms.
  • Hyperf/Scout provides the open source Elasticsearch engine by default instead of the closed source Algolia.