为什么要用?
由于mysql自带的like并不是很优秀,而且有可能要根据一定的”分词” 去匹配,所以才需要使用 elastic search。
安装扩展
composer require --prefer-dist yiisoft/yii2-elasticsearch -vvv
配置
<?php
// ......
'components' => [
'elasticsearch' => [
'class' => 'yii\elasticsearch\Connection',
'nodes' => [
[
'http_address' => '127.0.0.1:9200'
]
// configure more hosts if you have a cluster
],
// set autodetectCluster to false if you don't want to auto detect nodes
// 'autodetectCluster' => false,
'dslVersion' => 7 // default is 5
],
]
模拟场景
现在有一个文章表,需要做一个搜索功能,搜索的内容可能包含在博客标题和博客内容中。
博客sql
CREATE TABLE `post` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL COMMENT '文章标题',
`content` text COMMENT '文章内容',
`view_num` int(11) NOT NULL COMMENT '预览数',
`thumb` varchar(255) NOT NULL COMMENT '缩略图',
`add_time` int(11) NOT NULL COMMENT '添加时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
文章model
此 model 管理 数据库中的数据
在文章增删改查的时候对应操作elastic search的增删改查,可以使得mysql数据实时同步给elastic search,未全字段都交给 elastic search 管理,所以 es model 仅定义部分内容。
<?php
namespace app\models;
use yii\behaviors\TimestampBehavior;
/**
* This is the model class for table "{{%post}}".
*
* @property int $id
* @property string $title 文章标题
* @property string $content 文章内容
* @property int $view_num 预览数
* @property string $thumb 缩略图
* @property int $add_time 添加时间
*/
class Post extends \yii\db\ActiveRecord
{
public function behaviors ()
{
return [
[
'class' => TimestampBehavior::class,
'createdAtAttribute' => 'add_time',
'updatedAtAttribute' => false
]
];
}
// 添加或者修改索引
public function afterSave ($insert, $changedAttributes)
{
// 需要加入索引的内容
// 新增数据
if($insert)
{
// 将新增数据写入到 elastic search
$esPost = new EsPost();
$esPost->_id = $this->id;
$esPost->title = $this->title;
$esPost->content = $this->content;
$esPost->insert();
}
else
{
// 更新数据
$changeFields = array_keys($changedAttributes);
$fields = [
'title',
'content',
'add_time'
];
// 是否修改了es需要的字段内容
$interFields = array_intersect($changeFields, $fields);
if(! empty($interFields))
{
$esPost = EsPost::findOne($this->id);
if(! empty($esPost))
{
$attrs = [];
foreach($interFields as $field)
{
// 此方法修改最佳,比之 ->setAttributes 或者 ->attributes
$esPost->{$field} = $this->{$field};
}
$esPost->update(false);
}
}
}
}
// 删除数据
public function afterDelete ()
{
$esPost = EsPost::findOne($this->id);
if(! empty($esPost))
{
$esPost->delete();
}
}
/**
* @inheritdoc
*/
public static function tableName ()
{
return '{{%post}}';
}
/**
* @inheritdoc
*/
public function rules ()
{
return [
[
[
'title',
'content',
'thumb'
],
'required'
],
[
[
'content'
],
'string'
],
[
[
'title'
],
'string',
'max' => 100
],
[
[
'thumb'
],
'string',
'max' => 255
]
];
}
/**
* @inheritdoc
*/
public function attributeLabels ()
{
return [
'title' => '文章标题',
'content' => '文章内容',
'thumb' => '缩略图'
];
}
}
es的对应model
此model管理elastic search的内容
<?php
namespace app\models;
use yii\elasticsearch\ActiveRecord;
/**
* elastic search 文章
*
* @author vogin
* @property string title
* @property string content
* @property integer add_time
*/
class EsPost extends ActiveRecord
{
public static function index ()
{
// 名字不能以 _或者 - 或者 + 开始
return 'e_post';
}
public function attributes ()
{
return [
'title',
'content',
'add_time'
];
}
public static function mapping ()
{
return [
// Field types: https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html#field-datatypes
'properties' => [
'title' => [
'type' => 'text'
],
// text类型在存储数据的时候会默认进行分词,并生成索引。而keyword存储数据的时候,不会分词建立索引,显然,这样划分数据更加节省内存。
'content' => [
'type' => 'text'
],
'add_time' => [
'type' => 'integer'
]
]
];
}
/**
* Set (update) mappings for this model
*/
public static function updateMapping ()
{
$db = static::getDb();
$command = $db->createCommand();
$command->setMapping(static::index(), static::type(), static::mapping());
}
/**
* Create this model's index
*/
public static function createIndex ()
{
$db = static::getDb();
$command = $db->createCommand();
$command->createIndex(static::index(), [
// 'aliases' => [ /* ... */ ],
'mappings' => static::mapping()
// 'settings' => [ /* ... */ ],
]);
}
/**
* Delete this model's index
*/
public static function deleteIndex ()
{
$db = static::getDb();
$command = $db->createCommand();
$command->deleteIndex(static::index(), static::type());
}
public static function title ($title)
{
return [
'match' => [
'title' => $title
]
];
}
public static function content ($content)
{
return [
'match' => [
'content' => $content
]
];
}
}
基本使用
<?php
class PostController extends Controller{
public function actionAdd(){
// 增加数据,会触发 Post 中 afterSave钩子函数,进行数据增加
$post = new Post();
$post->title = '今天天气挺好的';
$post->content = '今天看到日出东方,天朗气清,捕捉一屋晨风,看来是个赶海的日子';
$post->thumb = 'http://www.aaa.com/a.jpg';
$post->insert();
// 修改数据,会触发 Post 中 afterSave钩子函数,进行数据修改
$post = Post::findOne(5);
$post->title = '修改了标题5';
$post->update(false);
// 删除数据,会触发 Post 中 afterDelete 钩子函数,进行数据删除
$post = Post::findOne(5);
$post->delete();
// 查找es数据
EsPost::findOne(6);
// 分页
$query = EsPost::find();
$provider = new \yii\elasticsearch\ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 10
]
]);
$models = $provider->getModels();
var_dump($models);
// 类mysql like %%
$posts = EsPost::find()->query([
// match相当于like查询
'match' => [
'title' => '今天天气挺好'
]
])->all();
// 类似mysql =
// 【注】:精准查找中,term => 内容是中文可能不好使
$posts = EsPost::find()->query([
'term' => 'zhangsan'
])->all();
}
}