yii2.0框架中使用elasticsearch

common/Elasticseatch.php

复制代码
  1 <?php
  2 
  3 namespace app\common;
  4 
  5 use yii;
  6 use yii\elasticsearch\ActiveRecord;
  7 use yii\data\Pagination;
  8 
  9 
 10 class Elasticsearch extends ActiveRecord{
 11 
 12     /**
 13      * 使用的数据库连接
 14      */
 15     public static function getDb(){
 16         return \Yii::$app->get('elasticsearch');
 17     }
 18 
 19     /**
 20      * [mapConfig mapping配置]
 21      * 返回这个模型的映射
 22      *
 23      * properties配置说明
 24      *
 25      * 属性
 26      * 数据类型 string(已废弃) byte short integer long float double boolean date binary(二进制) ip(以字符换格式存IPv4地址) token_count(存储索引的字数信息)
 27      *
 28      * 公共属性
 29      * index            -> analyzed(被分析,编入索引,产生的token能被搜索到) not_analyzed(不被分析,使用原始值编入索引,在索引中作为单个词) no(不编入索引,无法搜索该字段)
 30      * store            -> 指定是否将字段的原始值写入索引,默认值是no,字段值被分析,能够被搜索,但是,字段值不会存储,这意味着,该字段能够被查询,但是不会存储字段的原始值  通常情况字段值已经是_source字段的一部分,可以只检索需要的
 31      * boost            -> 字段级别的助推,默认值是1,定义了字段在文档中的重要性/权重
 32      * include_in_all   -> 该属性指定当前字段是否包括在_all字段中,默认值是true
 33      * copy_to          -> 该属性指定一个字段名称,ElasticSearch引擎将当前字段的值复制到该属性指定的字段中
 34      * doc_values       -> 文档值是存储在硬盘上的索引时(indexing time)数据结构,对于not_analyzed字段,默认值是true,analyzed string字段不支持文档值;如果您确定不需要对字段进行排序或聚合,或者从脚本访问字段值,则可以禁用doc值以节省磁盘空间
 35      * fielddata        -> 字段数据是存储在内存中的查询时(querying time)数据结构,只支持analyzed string字段
 36      * null_value       -> 该属性指定一个值,当字段的值为NULL时,该字段使用null_value代替NULL值;在ElasticSearch中,NULL 值不能被索引和搜索,当一个字段设置为NULL值,ElasticSearch引擎认为该字段没有任何值,使用该属性为NULL字段设置一个指定的值,使该字段能够被索引和搜索。
 37      *
 38      * 常用其他属性
 39      * analyzer         -> 该属性定义用于建立索引和搜索的分析器名称,默认值是全局定义的分析器名称,该属性可以引用在配置结点(settings)中自定义的分析器
 40      * search_analyzer  -> 该属性定义的分析器,用于处理发送到特定字段的查询字符串
 41      * ignore_above     -> 该属性指定一个整数值,当字符串字段(analyzed string field)的字节数量大于该数值之后,超过长度的部分字符数据将不能被analyzer处理,不能被编入索引;对于 not analyzed string字段,超过长度的部分字符将被忽略,不会被编入索引。默认值是0,禁用该属性;
 42      * position_increment_gap   -> 该属性指定在相同词的位置上增加的gap,默认值是100;
 43      * index_options    -> 索引选项控制添加到倒排索引(Inverted Index)的信息
 44      * [
 45      *      docs        -> 只索引文档编号(Doc Number)
 46      *      freqs       -> 索引文档编号和词频率(term frequency)
 47      *      positions   -> 索引文档编号,词频率和词位置(序号)
 48      *      offsets     -> 索引文档编号,词频率,词偏移量(开始和结束位置)和词位置(序号)
 49      *      默认情况下,被分析的字符串(analyzed string)字段使用positions,其他字段使用docs
 50      * ]
 51      *
 52      * 数值类型的其他属性
 53      * precision_step   -> 该属性指定为数值字段每个值生成的term数量,值越低,产生的term数量越高,范围查询越快,索引越大,默认值是4
 54      * ignore_malformed -> 忽略格式错误的数值,默认值是false,不忽略错误格式,对整个文档不处理,并且抛出异常
 55      * coerce           -> 默认值是true,尝试将字符串转换为数值,如果字段类型是整数,那么将小数取整
 56      *
 57      * 日期类型的其他属性
 58      * format           -> 指定日期的格式,例如:“yyyy-MM-dd hh:mm:ss”
 59      * ignore_malformed -> 忽略错误格式,默认值是false,不忽略错误格式
 60      *
 61      * fields           -> 在fields属性中定义一个或多个字段,该字段的值和当前字段值相同,可以设置一个字段用于搜索,一个字段用于排序等
 62      *      "properties":
 63      *      {
 64      *          "id":{
 65      *              "type":"long",
 66      *              "fields":{
 67      *                  "id2":{"type":"long","index":"not_analyzed"}
 68      *              }
 69      *          }
 70      *      }
 71      * _all             -> 表示存储其他字段的数据以便搜索,默认情况下,_all字段是启用的,包含了索引中所有字段的数据,然而这一字段使索引变大,如果不需要,请禁用该字段,或排除某些字段
 72      * "_all":{"enabled":false}
 73      * _source          -> 表示在生成索引的过程中,存储发送到ElasticSearch的原始JSON文档,默认情况下,该字段会被启用,因为索引的局部更新功能依赖该字段。
 74      * _routing         -> 路由字段 公式:shard_num = hash(_routing) % num_primary_shards  使用的默认字段是_id,设置required为true,表示路由字段在进行索引的CRUD操作时必需显式赋值。
 75      *
 76      * 不可配置的元字段
 77      * _index           -> 返回文档所属的索引
 78      * _uid             -> 返回文档的type和id
 79      * _type            -> 返回文档类型(type)
 80      * _id              -> 返回文档的ID;
 81      * _size            -> 返回文档的_source字段中函数的字节数量;
 82      * _field_names     -> 返回文档中不包含null值的字段名称;
 83      *
 84      */
 85     public static function mapConfig(){
 86         return [
 87             // 根据type来获取model->attributes
 88             'member' => [
 89                 'properties' => [
 90                     'user_id'    => ['type' => 'integer'],
 91                     'user_name'     => ['type' => 'text'],
 92                     'email'         => ['type' => 'keyword'],
 93                     'qq'            => ['type' => 'keyword'],
 94                     'edit_time'     => ['type' => 'date', 'format' => 'yyyy-MM-dd HH:mm:ss||yyyy-MM-dd']
 95                 ]
 96             ],
 97             // 根据type来获取model->attributes
 98             'member2' => [
 99                 'properties' => [
100                      'user_id'      => ['type' => 'integer'],
101                      'user_name'        => ['type' => 'text'], 
               'edit_time'        => ['type' => 'date', 'format' => 'yyyy-MM-dd HH:mm:ss||yyyy-MM-dd']
102                 ]
103             ]
104         ];
105     }
106 
107     /**
108      * 获取群集中所有索引映射
109      */
110     public static function getMapping(){
111         $db = self::getDb();
112         $command = $db->createCommand();
113         return $command->getMapping();
114     }
115 
116     /**
117      * 根据索引主键获取相关信息
118      * 支持单个或多个
119      */
120     public function getByKey($id){
121         if(is_array($id)){
122             $res = self::mget($id);
123         }else{
124             $res = self::get($id);
125         }
126         return $res;
127     }
128 
129     /**
130      * 单个
131      * 默认返回object对象 返回数组 添加->asArray()
132      */
133     public function getOne($query = []){
134         $es_query = self::find();
135 
136         // 匹配查询
137         if($query && !empty($query)){
138             $es_query->query($query);
139         }
140         // 分组
141         $res = $es_query->one();
142 
143         return $res;
144     }
145 
146     /**
147      * 列表
148      * 默认返回object对象 返回数组 添加->asArray()
149      * search 与 all 区别在于 all是在search基础上处理再拿出结果
150      */
151     public function getList($query = [], $order = [], $offset = 0, $limit = 20){
152         $es_query = self::find();
153 
154         // 匹配查询
155         if($query && !empty($query)){
156             $es_query->query($query);
157         }
158         // 排序
159         if($order && !empty($order)){
160             $es_query->orderby($order);
161         }
162         // 分组
163         $res = $es_query->offset($offset)->limit($limit)->asArray()->all();
164         $list = array_column($res, '_source');
165 
166         return $list;
167     }
168 
169     /**
170      * 分页列表
171      * 默认返回object对象 返回数组 添加->asArray()
172      * search 与 all 区别在于 all是在search基础上处理再拿出结果
173      */
174     public function getPageList($query = [], $order = []){
175         $es_query = self::find();
176 
177         // 匹配查询
178         if($query && !empty($query)){
179             $es_query->query($query);
180         }
181         // 排序
182         if($order && !empty($order)){
183             $es_query->orderby($order);
184         }
185 
186         // 分组
187         $count = $es_query->search();
188         // 分页
189         $pages = new Pagination(['totalCount' => $count['hits']['total']]);
190         // 分组
191         $res = $es_query->offset($pages->offset)->limit($pages->limit)->asArray()->all();
192         $list = array_column($res, '_source');
193 
194         return ['list' => $list, 'pages' => $pages];
195     }
196 
197     /**
198      * 列表
199      * 默认返回object对象 返回数组 添加->asArray()
200      * search 与 all 区别在于 all是在search基础上处理再拿出结果
201      */
202     public function getFilterList($filter_arr = [], $query = [], $order = [], $offset = 0, $limit = 20){
203         $es_query = self::find();
204 
205         // 过滤器
206         if($filter_arr && !empty($filter_arr)){
207             $es_query->postFilter($filter_arr);
208         }
209         // 匹配查询
210         if($query && !empty($query)){
211             $es_query->query($query);
212         }
213         // 排序
214         if($order && !empty($order)){
215             $es_query->orderby($order);
216         }
217         // 分组
218         $res = $es_query->offset($offset)->limit($limit)->all();
219 
220         return $res;
221     }
222 
223     /**
224      * 获取高亮列表
225      * 默认返回object对象 返回数组 添加->asArray()
226      * search 与 all 区别在于 all是在search基础上处理再拿出结果
227      * 循环使用$v->highlight获取高亮列表
228      */
229     public function getHighLightList($highlight_arr = [], $query = [], $offset = 0, $limit = 20){
230         $es_query = self::find();
231 
232         // 高亮
233         if($highlight_arr && !empty($highlight_arr)){
234             $es_query->highlight($highlight_arr);
235         }
236         // 匹配查询
237         if($query && !empty($query)){
238             $es_query->query($query);
239         }
240         // 分组
241         $res = $es_query->offset($offset)->limit($limit)->all();
242 
243         return $res;
244     }
245 
246     /**
247      * 获取聚合列表
248      * 默认返回object对象 返回数组 添加->asArray()
249      * search 与 all 区别在于 all是在search基础上处理再拿出结果
250      */
251     public function getAggList($aggregate_name, $addAggregate_arr = [], $query = [], $offset = 0, $limit = 20){
252         $es_query = self::find();
253 
254         // 聚合
255         if($addAggregate_arr && !empty($addAggregate_arr)){
256             $es_query->addAggregate($aggregate_name, $addAggregate_arr);
257         }
258         // 匹配查询
259         if($query && !empty($query)){
260             $es_query->query($query);
261         }
262         // 分组
263         $res = $es_query->offset($offset)->limit($limit)->search();
264 
265         return ['list' => $res['hits']['hits'], $aggregate_name => $res['aggregations'][$aggregate_name]];
266     }
267 }
复制代码

 

model/EsModel

复制代码
 1 <?php
 2 
 3 namespace app\models;
 4 
 5 use app\common\Elasticsearch;
 6 
 7 class EsModel extends Elasticsearch{
 8 
 9     // _index _type _id 定义
10     // https://www.elastic.co/guide/cn/elasticsearch/guide/current/_Document_Metadata.html
11     public static $index_db_name = '';
12     public static $type_tb_name = '';
13 
14     // 索引名相当于库名
15     public static function index(){
16         return self::$index_db_name;
17     }
18 
19     // 类别名相当于表名
20     public static function type(){
21         return self::$type_tb_name;
22     }
23 
24     // 属性
25     public function attributes(){
26         $mapConfig = self::mapConfig();
27         return array_keys($mapConfig[self::$type_tb_name]['properties']);
28     }
29 
30     // 映射
31     public static function mapping(){
32         $mapConfig = self::mapConfig();
33         return [
34             static::type() => $mapConfig[self::$type_tb_name]
35         ];
36     }
37 
38     // 更新映射
39     public static function updateMapping(){
40         $db = self::getDb();
41         $command = $db->createCommand();
42         if(!$command->indexExists(self::index())){
43             $command->createIndex(self::index());
44         }
45         $res = $command->setMapping(self::index(), self::type(), self::mapping());
46         return $res;
47     }
48 
49     // 创建索引
50     public static function createIndex(){
51         $db = static::getDb();
52         $command = $db->createCommand();
53         $command->createIndex(static::index(), [
54             'settings' => [
55                 'index' => [
56                     // 指定新索引的时候不会立即生效,1s之后刷新生效  默认是1s
57                     'refresh_interval'      => '1s',
58                     // 索引分片个数  默认5片
59                     'number_of_shards'      => 5,
60                     // 每个分片副本个数  默认1个
61                     'number_of_replicas'    => 1
62                 ]
63             ],
64             'mappings' => static::mapping(),
65             //'warmers' => [ /* ... */ ],
66             //'aliases' => [ /* ... */ ],
67             //'creation_date' => '...'
68         ]);
69     }
70 
71     // 删除索引
72     public static function deleteIndex(){
73         $db = static::getDb();
74         $command = $db->createCommand();
75         $res = $command->deleteIndex(static::index(), static::type());
76         return $res;
77     }
78 }
复制代码

 

controller调用

1 // 调用ES
2 $model = new \app\models\EsModel();
3 $model::$index_db_name = 'index_name';
4 $model::$type_tb_name = 'type_name';