Статьи из рубрики data structure
Как создать индекс ElasticSearch
Полнотекстовый поиск
Один парень написал: "Нужно сделать поиск слов в таблице "articles" по столбцу "content". В результатах поиска сначала выводить те, которые содержат наибольшее количество слов в статье и далее по мере уменьшения."
Мысль летит) Но ясно, что нужен полнотекстовый поиск.
WordPress плагин для недвижимости
Без изменения структуры данных
Привлекательность WordPress в большом количестве тем оформления. Иллюзия легкости, с которой можно поменять тему, не дает покоя разработчикам. Сейчас на WordPress работают даже магазины.
Чтобы разобраться, как это возможно, я решил написать плагин для агенств недвижимости. Расширять структуру данных не хотелось - это уже будет не WordPress. Решил сохранять JSON-определения объектов недвижимости в поле post_content
и преобразовывать эти определения "на лету" - Demo, GitHub
Как устроена структура данных Nested Set
Как сделать рубрикатор
Рубрикатор, как средство поиска интересных статей, имеет свои недостатки - трудно придумать иерархию, названия рубрик, трудно потом решить к какой конкретно рубрике относится статья. "Но, так принято" - скажите вы и будете правы.
Цепочки статей в блоге
Можно ли сделать блог удобнее?
Для группировки статей существует возможность помечать статьи метками или прикреплять к наперед заданной рубрике. Кроме того, в конце статьи есть навигация - предыдущая, следующая статья, ну и меню конечно. Вроде все удобно.
Поиск
Метки
- 1. Как создать индекс ElasticSearch
- 2. Интерфейс для полнотекстового поиска
Один парень написал: "Нужно сделать поиск слов в таблице "articles" по столбцу "content". В результатах поиска сначала выводить те, которые содержат наибольшее количество слов в статье и далее по мере уменьшения."
Мысль летит) Но ясно, что нужен полнотекстовый поиск.
Что мы имеем? Есть таблица с заголовками, статьями и прочими полями. Нужен индекс, который позволит проводить поиск по содержимому. Искать надо слово или набор слов, причем в любом количестве и быстро. Для подобной задачи подходит ElasticSearch и начать нужно с установки Java, ElasticSearch (который требует версии PHP >= 7.0) и расширения yii2-elasticsearch.
Model
Индекс нужно создавать для модели, содержащей статьи. Допустим это модель Post
. В общем каталоге для моделей, то есть в папке common/models
можно создать подкаталог elastic
и определить в нем класс модели для индекса (yii2-elasticsearch doc, https://habr.com/post/280488/).
namespace common\models\elastic; use yii\elasticsearch\ActiveRecord; class Post extends ActiveRecord { // DB name public static function index() { return 'blog'; } // Table name public static function type() { return 'post'; } public function attributes() { return ['id', 'title', 'content']; } public function rules() { return [ [$this->attributes(), 'safe'] ]; } // Table definition public static function mapping() { return [ static::type() => [ 'properties' => [ 'id' => ['type' => 'long'], 'title' => ['type' => 'text'], 'content' => ['type' => 'text'], ] ], ]; } public static function updateMapping() { $db = static::getDb(); $command = $db->createCommand(); $command->setMapping(static::index(), static::type(), static::mapping()); } // Fill in by common/models/Post model public function fill($model, $setPrimaryKey = true) { if($setPrimaryKey) $this->primaryKey = $model->id; $this->attributes = [ 'title' => $model->title, 'content' => $model->content, ]; } public static function createIndex() { $db = static::getDb(); $command = $db->createCommand(); if($command->indexExists(static::index())) $command->deleteIndex(static::index()); $command->createIndex(static::index(), [ 'settings' => [ 'analysis' => [ 'filter' => [ 'ru_stop' => [ 'type' => 'stop', 'stopwords' => '_russian_', ], 'ru_stemmer' => [ 'type' => 'stemmer', 'language' => 'russian', ], ], 'analyzer' => [ 'default' => [ 'char_filter' => [ 'html_strip', ], 'tokenizer' => 'standard', 'filter' => [ 'lowercase', 'ru_stop', 'ru_stemmer', ], ], ], ], ], 'mappings' => static::mapping(), ]); } }
Полей в индексе может быть больше. Собственно, можно указать все поля, где целесообразно вести поиск. В "боевой" версии я добавил еще excerpt
.
При создании индекса используется анализатор. Я просто скопировал его готовым из примера. Очевидно, что он требует к себе большего внимания, но результата можно достичь и копипастом. А результат классный - поиск ведется не только по полному совпадению слова, но и по корню.
Controller
Для выполнения поиска нужно предварительно сформировать индекс. Делается это просто. Перебираются все записи основной модели и сохраняются в индексе.
Контроллер можно определить консольным - для начала работы удобно формировать индекс из консоли.
Кроме того, при изменении записей в основной модели, индекс требует периодического полного обновления. Эту процедуру тоже удобно проводить из консоли, через cron
.
namespace console\controllers; use Yii; use yii\console\Controller; use common\models\Post; use common\models\elastic\Post as ElasticPost; class ElasticController extends Controller { public function actionCreateIndex() { ElasticPost::createIndex(); foreach(Post::find()->where([ 'status' => Post::STATUS_PUBLISHED ])->all() as $post) { $elastic = new ElasticPost(); $elastic->fill($post); $elastic->save(); } Yii::info('The ElasticSearch index was created ('. ElasticPost::index() .'/'. ElasticPost::type() .').', __METHOD__); } }
Log message
Метод Yii::info()
записывает сообщение в лог. Лог может быть не только файлом, но и email ящиком. В этом случае, после формирования индекса, можно получать извещение на нужный ящик.
Параметр categories
в console/config.php
определяет, что на ящик нужно отправлять только сообщения начинающиеся на указанную строку. В свою очередь строка задается в __METHOD__
. Параметр logVars
нужно обязательно обнулить, чтобы не получать кучу не нужной информации.
<?php $params = array_merge( require(__DIR__ . '/../../common/config/params.php'), require(__DIR__ . '/../../common/config/params-local.php'), require(__DIR__ . '/params.php'), require(__DIR__ . '/params-local.php') ); $mailer = require(__DIR__ . '/../../common/config/mailer.php'); return [ 'id' => 'app-console', 'basePath' => dirname(__DIR__), 'bootstrap' => ['log'], 'controllerNamespace' => 'console\controllers', 'components' => [ 'urlManager' => [ 'class' => 'yii\web\UrlManager', 'enablePrettyUrl' => true, 'showScriptName' => false, 'baseUrl' => 'http://localhost/console', ], 'mailer' => $mailer, 'log' => [ 'targets' => [ [ 'class' => 'yii\log\FileTarget', 'levels' => ['error', 'warning'], ], [ 'class' => 'yii\log\EmailTarget', 'levels' => ['info'], 'categories' => ['console\controllers\ElasticController*'], 'logVars' => [], 'message' => [ 'from' => ['admin@sample.ru'], 'to' => ['admin@sample.ru'], 'subject' => 'ElasticSearch', ], ], ], ], ], 'params' => $params, ];
Создание индекса ElasticSearch
Переходим в корень приложения, запускаем и ждем письма. Не забудьте определить mailer
.
php yii elastic/create-index
Проверим, как работает индекс.
curl -XGET "localhost:9200/blog/post/_search?pretty" -d' { "_source": ["title"], "query": { "match": { "content": "history" } } }'
Заключение
Для использования сформированного индекса нужен интерфейс во frontend
, который позволит вводить слова поиска и учитывать полученное условие при выдаче результатов. Но об этом в следующей статье.
Оставьте комментарий
Только зарегистрированные пользователи могут оставлять комментарии. Пожалуйста войдите или пройдите регистрацию.
Можно авторизоваться, используя социальную сеть-
-
-