为什么要用?

由于mysql自带的like并不是很优秀,而且有可能要根据一定的”分词” 去匹配,所以才需要使用 elastic search。

安装扩展

  1. composer require --prefer-dist yiisoft/yii2-elasticsearch -vvv

配置

  1. <?php
  2. // ......
  3. 'components' => [
  4. 'elasticsearch' => [
  5. 'class' => 'yii\elasticsearch\Connection',
  6. 'nodes' => [
  7. [
  8. 'http_address' => '127.0.0.1:9200'
  9. ]
  10. // configure more hosts if you have a cluster
  11. ],
  12. // set autodetectCluster to false if you don't want to auto detect nodes
  13. // 'autodetectCluster' => false,
  14. 'dslVersion' => 7 // default is 5
  15. ],
  16. ]

模拟场景

现在有一个文章表,需要做一个搜索功能,搜索的内容可能包含在博客标题和博客内容中。

博客sql

  1. CREATE TABLE `post` (
  2. `id` int(11) NOT NULL AUTO_INCREMENT,
  3. `title` varchar(100) NOT NULL COMMENT '文章标题',
  4. `content` text COMMENT '文章内容',
  5. `view_num` int(11) NOT NULL COMMENT '预览数',
  6. `thumb` varchar(255) NOT NULL COMMENT '缩略图',
  7. `add_time` int(11) NOT NULL COMMENT '添加时间',
  8. PRIMARY KEY (`id`)
  9. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

文章model

此 model 管理 数据库中的数据

在文章增删改查的时候对应操作elastic search的增删改查,可以使得mysql数据实时同步给elastic search,未全字段都交给 elastic search 管理,所以 es model 仅定义部分内容。

  1. <?php
  2. namespace app\models;
  3. use yii\behaviors\TimestampBehavior;
  4. /**
  5. * This is the model class for table "{{%post}}".
  6. *
  7. * @property int $id
  8. * @property string $title 文章标题
  9. * @property string $content 文章内容
  10. * @property int $view_num 预览数
  11. * @property string $thumb 缩略图
  12. * @property int $add_time 添加时间
  13. */
  14. class Post extends \yii\db\ActiveRecord
  15. {
  16. public function behaviors ()
  17. {
  18. return [
  19. [
  20. 'class' => TimestampBehavior::class,
  21. 'createdAtAttribute' => 'add_time',
  22. 'updatedAtAttribute' => false
  23. ]
  24. ];
  25. }
  26. // 添加或者修改索引
  27. public function afterSave ($insert, $changedAttributes)
  28. {
  29. // 需要加入索引的内容
  30. // 新增数据
  31. if($insert)
  32. {
  33. // 将新增数据写入到 elastic search
  34. $esPost = new EsPost();
  35. $esPost->_id = $this->id;
  36. $esPost->title = $this->title;
  37. $esPost->content = $this->content;
  38. $esPost->insert();
  39. }
  40. else
  41. {
  42. // 更新数据
  43. $changeFields = array_keys($changedAttributes);
  44. $fields = [
  45. 'title',
  46. 'content',
  47. 'add_time'
  48. ];
  49. // 是否修改了es需要的字段内容
  50. $interFields = array_intersect($changeFields, $fields);
  51. if(! empty($interFields))
  52. {
  53. $esPost = EsPost::findOne($this->id);
  54. if(! empty($esPost))
  55. {
  56. $attrs = [];
  57. foreach($interFields as $field)
  58. {
  59. // 此方法修改最佳,比之 ->setAttributes 或者 ->attributes
  60. $esPost->{$field} = $this->{$field};
  61. }
  62. $esPost->update(false);
  63. }
  64. }
  65. }
  66. }
  67. // 删除数据
  68. public function afterDelete ()
  69. {
  70. $esPost = EsPost::findOne($this->id);
  71. if(! empty($esPost))
  72. {
  73. $esPost->delete();
  74. }
  75. }
  76. /**
  77. * @inheritdoc
  78. */
  79. public static function tableName ()
  80. {
  81. return '{{%post}}';
  82. }
  83. /**
  84. * @inheritdoc
  85. */
  86. public function rules ()
  87. {
  88. return [
  89. [
  90. [
  91. 'title',
  92. 'content',
  93. 'thumb'
  94. ],
  95. 'required'
  96. ],
  97. [
  98. [
  99. 'content'
  100. ],
  101. 'string'
  102. ],
  103. [
  104. [
  105. 'title'
  106. ],
  107. 'string',
  108. 'max' => 100
  109. ],
  110. [
  111. [
  112. 'thumb'
  113. ],
  114. 'string',
  115. 'max' => 255
  116. ]
  117. ];
  118. }
  119. /**
  120. * @inheritdoc
  121. */
  122. public function attributeLabels ()
  123. {
  124. return [
  125. 'title' => '文章标题',
  126. 'content' => '文章内容',
  127. 'thumb' => '缩略图'
  128. ];
  129. }
  130. }

es的对应model

此model管理elastic search的内容

  1. <?php
  2. namespace app\models;
  3. use yii\elasticsearch\ActiveRecord;
  4. /**
  5. * elastic search 文章
  6. *
  7. * @author vogin
  8. * @property string title
  9. * @property string content
  10. * @property integer add_time
  11. */
  12. class EsPost extends ActiveRecord
  13. {
  14. public static function index ()
  15. {
  16. // 名字不能以 _或者 - 或者 + 开始
  17. return 'e_post';
  18. }
  19. public function attributes ()
  20. {
  21. return [
  22. 'title',
  23. 'content',
  24. 'add_time'
  25. ];
  26. }
  27. public static function mapping ()
  28. {
  29. return [
  30. // Field types: https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html#field-datatypes
  31. 'properties' => [
  32. 'title' => [
  33. 'type' => 'text'
  34. ],
  35. // text类型在存储数据的时候会默认进行分词,并生成索引。而keyword存储数据的时候,不会分词建立索引,显然,这样划分数据更加节省内存。
  36. 'content' => [
  37. 'type' => 'text'
  38. ],
  39. 'add_time' => [
  40. 'type' => 'integer'
  41. ]
  42. ]
  43. ];
  44. }
  45. /**
  46. * Set (update) mappings for this model
  47. */
  48. public static function updateMapping ()
  49. {
  50. $db = static::getDb();
  51. $command = $db->createCommand();
  52. $command->setMapping(static::index(), static::type(), static::mapping());
  53. }
  54. /**
  55. * Create this model's index
  56. */
  57. public static function createIndex ()
  58. {
  59. $db = static::getDb();
  60. $command = $db->createCommand();
  61. $command->createIndex(static::index(), [
  62. // 'aliases' => [ /* ... */ ],
  63. 'mappings' => static::mapping()
  64. // 'settings' => [ /* ... */ ],
  65. ]);
  66. }
  67. /**
  68. * Delete this model's index
  69. */
  70. public static function deleteIndex ()
  71. {
  72. $db = static::getDb();
  73. $command = $db->createCommand();
  74. $command->deleteIndex(static::index(), static::type());
  75. }
  76. public static function title ($title)
  77. {
  78. return [
  79. 'match' => [
  80. 'title' => $title
  81. ]
  82. ];
  83. }
  84. public static function content ($content)
  85. {
  86. return [
  87. 'match' => [
  88. 'content' => $content
  89. ]
  90. ];
  91. }
  92. }

基本使用

  1. <?php
  2. class PostController extends Controller{
  3. public function actionAdd(){
  4. // 增加数据,会触发 Post 中 afterSave钩子函数,进行数据增加
  5. $post = new Post();
  6. $post->title = '今天天气挺好的';
  7. $post->content = '今天看到日出东方,天朗气清,捕捉一屋晨风,看来是个赶海的日子';
  8. $post->thumb = 'http://www.aaa.com/a.jpg';
  9. $post->insert();
  10. // 修改数据,会触发 Post 中 afterSave钩子函数,进行数据修改
  11. $post = Post::findOne(5);
  12. $post->title = '修改了标题5';
  13. $post->update(false);
  14. // 删除数据,会触发 Post 中 afterDelete 钩子函数,进行数据删除
  15. $post = Post::findOne(5);
  16. $post->delete();
  17. // 查找es数据
  18. EsPost::findOne(6);
  19. // 分页
  20. $query = EsPost::find();
  21. $provider = new \yii\elasticsearch\ActiveDataProvider([
  22. 'query' => $query,
  23. 'pagination' => [
  24. 'pageSize' => 10
  25. ]
  26. ]);
  27. $models = $provider->getModels();
  28. var_dump($models);
  29. // 类mysql like %%
  30. $posts = EsPost::find()->query([
  31. // match相当于like查询
  32. 'match' => [
  33. 'title' => '今天天气挺好'
  34. ]
  35. ])->all();
  36. // 类似mysql =
  37. // 【注】:精准查找中,term => 内容是中文可能不好使
  38. $posts = EsPost::find()->query([
  39. 'term' => 'zhangsan'
  40. ])->all();
  41. }
  42. }