FullText Search MySql - uniqcle/Yii2 GitHub Wiki
Поиск по всему содержимому, тем самым оценивается релевантность поиска.
Активно используется индекс. Т.е заранее прошлись по всем записям, по всем словам и составили словарь всех слов, которые встречаются.
Автомобиль - 73(6 раз), 266(4 раза), 49541(2раза)
Арбуз - 56475(4 раза), 73(3 раза), 899(1 раз)
Больше - 889(11), 87(2), 25756(1)
Вода - 6849(8), 882(1)
...
Слова составлены в алфавитном порядке.
Напр., Автомобиль встречается в записи 73 - 6 раз, в записи 266 - 4 раза, в записи 49541 - 2 раза и т.д.
Если слово нашлось, то мы берем все идентификаторы этих записи, ищем в БД и выводим записи этих идентификаторов. К тому же у нас есть мера релевантности.
Из минусов: индекс нужно постоянно поддерживать в актуальном состоянии.
Если из БД много читают, а добавляют немного, то индексы уместны. А если много добавляют, а мало читают, то индексы могут снизить производительность.
Добавляем индекс на то поле, по которому будет выполняться поиск.
Миграция console\migrations\m190909_062542_create_index_fulltext_content.php
Может занять некоторое время, если БД большая. К тому же на диске тратится место
use yii\db\Migration;
class m190909_062542_create_index_fulltext_content extends Migration
{
public function safeUp()
{
$this->execute("ALTER TABLE news ADD FULLTEXT INDEX idx_content (content)");
//$this->execute("ALTER TABLE news ADD FULLTEXT INDEX idx_title (title)");
}
public function safeDown()
{
$this->dropIndex('idx_content', 'news');
//$this->dropIndex('idx_title', 'news');
}
}
frontend\controllers\SearchController.php
namespace frontend\controllers;
use Yii;
use yii\web\Controller;
use frontend\models\forms\SearchForm;
class SearchController extends Controller
{
public function actionSimple(){
...
}
public function actionFulltext(){
$model = new SearchForm();
$result = null;
if( $model->load( Yii::$app->request->post() ) ){
$result = $model->fullTextSearch();
}
return $this->render('fulltext', [
'model' => $model,
'result' => $result
]);
}
}
frontend\models\forms\SearchForm.php
namespace frontend\models\forms;
use yii\base\Model;
use frontend\models\Search;
class SearchForm extends Model
{
public $keyword;
public function rules(){
return [
['keyword', 'required'],
['keyword', 'trim']
];
}
public function simpleSearch(){
...
}
public function fullTextSearch(){
if( $this->validate() ){
$model = new Search();
return $model->fullTextSearch( $this->keyword );
}
}
}
frontend\models\Search.php
namespace frontend\models;
use Yii;
//Занимается логикой поиска
class Search
{
public function simpleSearch($keyword){
...
}
public function fullTextSearch($keyword){
$params = [
':keyword' => $keyword
];
$sql = "SELECT * FROM news WHERE MATCH(content) AGAINST(:keyword) LIMIT 20";
return Yii::$app->db->createCommand($sql)->bindValues($params)->queryAll();
}
}
frontend\views\search\fulltext.php
/* @var $this yii\web\View */
/* @var $form yii\bootstrap\ActiveForm */
/* @var $model \frontend\models\LoginForm */
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
use frontend\components\HighlightHelper;
$this->title = 'Login User';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="site-signup">
<h1><?= Html::encode($this->title) ?></h1>
<p></p>
<!-- Форма ввода -->
<div class="row">
<div class="col-lg-5">
<?php $form = ActiveForm::begin(['id' => 'form-signup']); ?>
<?= $form->field($model, 'keyword')->textInput(['autofocus' => true]) ?>
<div class="form-group">
<?= Html::submitButton('Search', ['class' => 'btn btn-primary', 'name' => 'signup-button']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
</div>
<div class="row">
<!-- Область вывода -->
<?php if(is_array($result) && !empty($result)){ ?>
<table class="table">
<thead class="thead-dark">
<tr>
<th scope="col">#</th>
<th scope="col">Title</th>
<th scope="col">Content</th>
<th scope="col">Status</th>
</tr>
</thead>
<tbody>
<?php foreach($result as $item): ?>
<tr>
<th scope="row"><?=$item['id'] ?></th>
<td><?=$item['title']; ?></td>
<td><?php echo HighlightHelper::process($model->keyword, $item['content']); ?></td>
<td><?=$item['status']; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php } ?>
</div>
</div>