An extension provide simple full-text search with ideas get from laravel/scout and base on teamtnt/tntsearch wrapper for Yii2 Active Record.
Require Yii2 Searchable using Composer:
composer require vxm/yii2-searchable
Finally, add the \vxm\searchable\SearchableTrait
trait and attach vxm\searchable\SearchableBehavior
behavior to the Active Record you would like to make searchable.
This will help sync the model with index data.
use vxm\searchable\SearchableBehavior;
use vxm\searchable\SearchableTrait;
class Article extends ActiveRecord
{
use SearchableTrait;
/**
* @inheritDoc
*/
public function behaviors()
{
return [
'searchable' => SearchableBehavior::class
];
}
}
While not strictly required to use this extension, you should strongly consider configuring a yii2-queue before using an extension. Running a queue worker will allow it to queue all operations that sync your model information to your search indexes, providing much better response times for your application's web interface.
Once you have configured a queue component, set the value of the queue option in your application configuration file to component id of it or an array config of it.
'components' => [
'searchable' => [
'class' => 'vxm\searchable\Searchable',
'queue' => 'queueComponentId'
]
]
By default a component will be add to your application components via bootstrapping with id searchable
. If you need to config it you can manual config in your application config file:
'components' => [
'searchable' => [
'class' => 'vxm\searchable\Searchable',
'storagePath' => '@runtime/vxm/search',
'queue' => null, // an optional not required
'defaultSearchMode' => \vxm\searchable\Searchable::FUZZY_SEARCH,
'asYouType' => false,
'fuzziness' => false,
'fuzzyPrefixLength' => 2,
'fuzzyMaxExpansions' => 50,
'fuzzyMaxExpansions' => 50,
'fuzzyDistance' => 50
]
]
Each Active Record model is synced with a given search index
, which contains all of the searchable records for that model.
In other words, you can think of each index like a MySQL table. By default, each model will be persisted to an index matching the model's typical table
name.
Typically, this is the plural form of the model name; however, you are free to customize the index
name by overriding the searchableIndex
static method on the Active Record model class:
use vxm\searchable\SearchableBehavior;
use vxm\searchable\SearchableTrait;
class Article extends ActiveRecord
{
use SearchableTrait;
/**
* @inheritDoc
*/
public function behaviors()
{
return [
'searchable' => SearchableBehavior::class
];
}
/**
* Get the index name for the model class.
*
* @return string
*/
public static function searchableIndex(): string
{
return 'articles_index';
}
}
By default, the entire toArray
form of a given model will be persisted to its search index.
If you would like to customize the data that is synchronized to the search index,
you may override the toSearchableArray
method on the model:
use vxm\searchable\SearchableBehavior;
use vxm\searchable\SearchableTrait;
class Article extends ActiveRecord
{
use SearchableTrait;
/**
* @inheritDoc
*/
public function behaviors()
{
return [
'searchable' => SearchableBehavior::class
];
}
/**
* Get the indexable data array for the model.
*
* @return array
*/
public function toSearchableArray(): array
{
$array = $this->toArray();
// Customize array...
return $array;
}
}
By default, the primary key name of the model as the unique ID stored in the search index.
If you need to customize this behavior, you may override the searchableKey
static method on the model:
use vxm\searchable\SearchableBehavior;
use vxm\searchable\SearchableTrait;
class Article extends ActiveRecord
{
use SearchableTrait;
/**
* @inheritDoc
*/
public function behaviors()
{
return [
'searchable' => SearchableBehavior::class
];
}
/**
* Get searchable key by default primary key will be use.
*
* @return string key name.
*/
public static function searchableKey(): string
{
return 'id';
}
}
If you are installing an extension into an existing project, you may already have database records you need to import into your search driver. This extension provides an import action that you may use to import all of your existing records into your search indexes:
php yii searchable/import --models="app\models\Post"
You can import multi model classes by separator ,
:
php yii searchable/import --models="app\models\Post, app\models\Category"
Once you have added the vxm\searchable\SearchableTrait
and attached the vxm\searchable\SearchableBehavior
behavior to a model,
all you need to do is save a model instance and it will automatically be added to your search index.
If you have configured queue this operation will be performed in the background by your queue worker:
$post = new \app\models\Post;
// ...
$post->save();
If you would like to add a Active Query results to your search index, you may use makeSearchable
method onto an Active Query result.
The makeSearchable
method will chunk the results of the query and add the records to your search index.
Again, if you have configured queue, all of the chunks will be added in the background by your queue workers:
// Adding via Active Query result...
$models = \app\models\Post::find()->where(['author_id' => 1])->all();
\app\models\Post::makeSearchable($models);
The makeSearchable
method can be considered an upsert
operation. In other words, if the model record is already in your index, it will be updated.
If it does not exist in the search index, it will be added to the index.
To update a searchable model, you only need to update the model instance's properties and save the model to your database. This extension will automatically persist the changes to your search index:
$post = \app\models\Post::findOne(1);
// Update the post...
$post->save();
You may also use the makeSearchable
method on an Active Record class to update instance.
If the models do not exist in your search index, they will be created:
// Updating via Active Query result...
$models = \app\models\Post::find()->where(['author_id' => 1])->all();
\app\models\Post::makeSearchable($models);
To delete a record from your index, delete the model from the database:
$post = \app\models\Post::findOne(1);
$post->delete();
If you would like to delete a Active Query result from your search index, you may use the deleteSearchable
method on an Active Record class:
// Deleting via Active Query result...
$models = \app\models\Post::find()->where(['author_id' => 1])->all();
\app\models\Post::deleteSearchable($models);
Sometimes you may need to perform a batch of Active Record operations on a model without syncing the model data to your search index.
You may do this using the withoutSyncingToSearch
method. This method accepts a single callback which will be immediately executed.
Any model operations that occur within the callback will not be synced to the model's index:
\app\models\Post::withoutSyncingToSearch(function () {
$post = \app\models\Post::findOne(1);
$post->save(); // will not syncing with index data
});
Sometimes you may need to only make a model searchable under certain conditions. For example, imagine you have app\models\Article
model that may be in one of two states: draft
and published
.
You may only want to allow published
posts to be searchable. To accomplish this, you may define a shouldBeSearchable
method on your model:
use vxm\searchable\SearchableBehavior;
use vxm\searchable\SearchableTrait;
class Article extends ActiveRecord
{
use SearchableTrait;
/**
* @inheritDoc
*/
public function behaviors()
{
return [
'searchable' => SearchableBehavior::class
];
}
/**
* Determine if the model should be searchable.
*
* @return bool
*/
public static function shouldBeSearchable()
{
return $this->is_published;
}
}
The shouldBeSearchable
method is only applied when manipulating models through the save method.
Directly making models using the searchable
or makeSearchable
method will override the result of the shouldBeSearchable
method:
// Will respect "shouldBeSearchable"...
$post = \app\models\Post::findOne(1);
$post->save();
// Will override "shouldBeSearchable"...
$post->searchable();
$models = \app\models\Post::find()->where(['author_id' => 1])->all();
\app\models\Post::makeSearchable($models);
You may begin searching a model using the search
method. The search method accepts a single string that will be used to search your models.
This method return an ActiveQuery
you can add more condition or relationship like an origin query.
Note when add more query condition you must not be use
where
method useandWhere
ororWhere
instead because it will override search ids condition result.
$posts = \app\models\Post::search('vxm')->all();
$posts = \app\models\Post::search('vxm')->andWhere(['author_id' => 1])->all();
// not use
$posts = \app\models\Post::search('vxm')->where(['author_id' => 1])->all();
You can joining relations on search query with relations support searchable
:
$posts = \app\models\Post::search('vxm')->joinWith('category')->andWhere(Category::search('vxm category'));
You can choice a boolean
or fuzzy
search mode as a second parameter if not set defaultSearchMode
of Searchable
component will be use:
$posts = \app\models\Post::search('vxm', 'fuzzy', ['fuzziness' => true])->all();
$posts = \app\models\Post::search('vxm', 'boolean')->all();
For more detail of search mode please refer to teamtnt/tntsearch to see full document.