Completed
Push — master ( ce09a3...d3e9f0 )
by Mehmet
04:41
created

ElasticSearch::mergeFilters()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 12
rs 9.2
cc 4
eloc 8
nc 3
nop 2
1
<?php
2
3
namespace Soupmix;
4
/*
5
Elasticsearch Adapter
6
*/
7
Use Elasticsearch as ElasticsearchLib;
8
9
class ElasticSearch implements Base
10
{
11
    public $conn = null;
12
    private $index = null;
13
14
    private static $operators = [
15
        'range'     => ['gt', 'gte', 'lt', 'lte'],
16
        'standart'  => ['prefix', 'regexp', 'wildchard'],
17
        'BooleanQueryLevel' => ['not'],
18
        'special'   => ['in']
19
    ];
20
21
22
    public function __construct($config)
23
    {
24
        $this->index = $config['db_name'];
25
        $this->connect($config);
26
    }
27
28
    public function connect($config)
29
    {
30
        $this->conn = ElasticsearchLib\ClientBuilder::create()->setHosts($config['hosts'])->build();
31
    }
32
33
    public function create($collection)
34
    {
35
    }
36
37
    public function drop($collection)
38
    {
39
        $params = ['index' => $this->index];
40
        try {
41
            $this->conn->indices()->delete($params);
42
        } catch (\Exception $e) {
43
            // This ignore the error
44
            return false;
45
        }
46
        return true;
47
    }
48
49
    public function truncate($collection)
50
    {
51
    }
52
53
    public function createIndexes($collection, $indexes)
54
    {
55
    }
56
57
    public function insert($collection, $values)
58
    {
59
        $params = [];
60
        $params['body'] = $values;
61
        $params['index'] = $this->index;
62
        $params['type'] = $collection;
63
        try {
64
            $result = $this->conn->index($params);
65
            if ($result['created']) {
66
                return $result['_id'];
67
            }
68
            return null;
69
        } catch (\Exception $e) {
70
            return;
71
        }
72
    }
73
74
    public function get($collection, $docId)
75
    {
76
        $params = [];
77
        $params['index'] = $this->index;
78
        $params['type'] = $collection;
79
80
        try {
81
            if (gettype($docId) == "array") {
82
                $params['body'] = [
83
                    'query' => [
84
                        'filtered' => [
85
                            'filter' => [
86
                                'ids' => ['values'=>$docId]
87
                            ],
88
                        ],
89
                    ],
90
                ];
91
                $results = $this->conn->search($params);
92
                if ($results['hits']['total'] == 0) {
93
                    return;
94
                }
95
                $result = [];
96
                foreach ($results['hits']['hits'] as $item){
97
                    $result[$item['_id']]=$item['_source'];
98
                }
99
                return $result;
100
            } else {
101
                $params['id'] = $docId;
102
                $result = $this->conn->get($params);
103
            }
104
            if ($result['found']) {
105
                $result['_source']['_id'] = $result['_id'];
106
                return $result['_source'];
107
            } else {
108
                return;
109
            }
110
        } catch (\Exception $e) {
111
            return;
112
        }
113
    }
114
115
    public function update($collection, $filter, $values)
116
    {
117
        $docs = $this->find($collection, $filter, ['_id']);
118
        if ($docs['total']===0) {
119
            return 0;
120
        }
121
        $params = [];
122
        $params['index'] = $this->index;
123
        $params['type'] = $collection;
124
        $modified_count = 0;
125
        foreach ($docs['data'] as $doc) {
126
            $params['id'] = $doc['_id'];
127
            $params['body']['doc'] = $values;
128
            try {
129
                $return = $this->conn->update($params);
130
                if ($return['_shards']['successful']==1) {
131
                    ++$modified_count;
132
                }
133
            } catch (\Exception $e) {
134
                // should we throw exception? Probably not.
135
            }
136
        }
137
138
        return $modified_count;
139
    }
140
141
    public function delete($collection, $filter)
142
    {
143
        if (isset($filter['_id'])) {
144
            $params = [];
145
            $params['index'] = $this->index;
146
            $params['type'] = $collection;
147
            $params['id'] = $filter['_id'];
148
            try {
149
                $result = $this->conn->delete($params);
150
            } catch (\Exception $e) {
151
                return 0;
152
            }
153
            if ($result['found']) {
154
                return 1;
155
            }
156
        } else {
157
            $params = [];
158
            $params['index'] = $this->index;
159
            $params['type'] = $collection;
160
            $params['fields'] = '_id';
161
            $result = $this->find('users', $filter, ['_id'], null, 0, 1);
162
            if ($result['total']==1) {
163
                $params = [];
164
                $params['index'] = $this->index;
165
                $params['type'] = $collection;
166
                $params['id'] = $result['data']['_id'];
167
                try {
168
                    $result = $this->conn->delete($params);
169
                } catch (\Exception $e) {
170
                    return 0;
171
                }
172
                if ($result['found']) {
173
                    return 1;
174
                }
175
            }
176
        }
177
178
        return 0;
179
    }
180
181
    public function find($collection, $filters, $fields = null, $sort = null, $start = 0, $limit = 25, $debug = false)
182
    {
183
        $return_type = '_source';
184
        $params = [];
185
        $params['index'] = $this->index;
186
        $params['type'] = $collection;
187
        if ($filters!==null) {
188
            $filters = self::buildFilter($filters);
189
            $params['body'] = [
190
                'query' => [
191
                    'filtered' => [
192
                        'filter' => [
193
                            'bool' => $filters,
194
                        ],
195
                    ],
196
                ],
197
            ];
198
        }
199
        $count = $this->conn->count($params);
200
        if ($fields!==null) {
201
            $params['fields'] = implode(',', $fields);
202
            $return_type = 'fields';
203
        }
204
        if ($sort!==null) {
205
            $params['sort'] = '';
206
            foreach ($sort as $sort_key => $sort_dir) {
207
                if ($params['sort']!='') {
208
                    $params['sort'] .= ',';
209
                }
210
                $params['sort'] .= $sort_key.':'.$sort_dir;
211
            }
212
        }
213
        if ($fields != null) {
214
            $params['fields'] = $fields;
215
            $return_type = 'fields';
216
        }
217
        $params['from'] = (int) $start;
218
        $params['size'] = (int) $limit;
219
        if ($debug) {
220
           return $params;
221
        }
222
        $return = $this->conn->search($params);
223
        if ($return['hits']['total']==0) {
224
            return ['total' => 0, 'data' => null];
225
        }
226
        elseif ($limit==1) {
227
            $return['hits']['hits'][0][$return_type]['_id'] = $return['hits']['hits'][0]['_id'];
228
            return ['total' => 1, 'data' => $return['hits']['hits'][0][$return_type]];
229
        }
230
        $result = [];
231
        foreach ($return['hits']['hits'] as $item) {
232
            if (($return_type == 'fields') && ($fields != ['_id'])) {
233
                foreach ($item[$return_type]as $k => $v) {
234
                    $item[$return_type][$k] = $v[0];
235
                }
236
            }
237
            $item[$return_type]['_id'] = $item['_id'];
238
            $result[] = $item[$return_type];
239
        }
240
        return ['total' => $count['count'], 'data' => $result];
241
    }
242
243
    public function query($query)
244
    {
245
        // reserved        
246
    }
247
248
    public static function buildFilter($filter)
249
    {
250
        $filters = [];
251
        foreach ($filter as $key => $value) {
252
            $is_not = '';
253
            if (strpos($key, '__')!==false) {
254
                $tmpFilters = self::buildFilterForKeys($key, $value, $is_not);
255
                $filters = self::mergeFilters($filters, $tmpFilters);
256
            } elseif ((strpos($key, '__') === false) && (is_array($value))) {
257
                $filters['should'] = self::buildFilterForOR($value);
258
            } else {
259
                $filters['must'][] = ['term' => [$key => $value]];
260
            }
261
        }
262
        return $filters;
263
    }
264
265
    private static function mergeFilters ($filters, $tmpFilters){
266
        foreach ($tmpFilters as $fKey => $fVals) {
267
            if (isset($filters[$fKey])) {
268
                foreach ($fVals as $fVal) {
269
                    $filters[$fKey][] = $fVal;
270
                }
271
            } else {
272
                $filters[$fKey] = $fVals;
273
            }
274
        }
275
        return $filters;
276
    }
277
278
    private static function buildFilterForKeys($key, $value, $is_not)
279
    {
280
        $filters = [];
281
        preg_match('/__(.*?)$/i', $key, $matches);
282
        $operator = $matches[1];
283
        if (strpos($operator, '!')===0) {
284
            $operator = str_replace('!', '', $operator);
285
            $is_not = '_not';
286
        }
287
        $key = str_replace($matches[0], '', $key);
288
        foreach (self::$operators as $type => $possibilities) {
289
            if (in_array($operator, $possibilities)) {
290
                switch ($type) {
291
                    case 'range':
292
                        $filters['must'.$is_not][] = ['range' => [$key => [$operator => $value]]];
293
                        break;
294
                    case 'standart':
295
                        $filters['must'.$is_not][] = [$type => [$key => $value]];
296
                        break;
297
                    case 'BooleanQueryLevel':
298
                        switch ($operator) {
299
                            case 'not':
300
                                $filters['must_not'][] = ['term' => [$key => $value]];
301
                                break;
302
                        }
303
                        break;
304
                    case 'special':
305
                        switch ($operator) {
306
                            case 'in':
307
                                $filters['must'.$is_not][] = ['terms' => [$key => $value]];
308
                                break;
309
                        }
310
                        break;
311
                }
312
            }
313
        }
314
        return $filters;
315
    }
316
317
    private static function buildFilterForOR($orValues)
318
    {
319
        $filters = [];
320
        foreach ($orValues as $orValue) {
321
            $subKey = array_keys($orValue)[0];
322
            $subValue = $orValue[$subKey];
323
            if (strpos($subKey, '__') !== false) {
324
                preg_match('/__(.*?)$/i', $subKey, $subMatches);
325
                $subOperator = $subMatches[1];
326
                if (strpos($subOperator, '!')===0) {
327
                    $subOperator = str_replace('!', '', $subOperator);
328
                }
329
                $subKey = str_replace($subMatches[0], '', $subKey);
330
                foreach (self::$operators as $type => $possibilities) {
331
                    if (in_array($subOperator, $possibilities)) {
332
                        switch ($type) {
333
                            case 'range':
334
                                $filters[] = ['range' => [$subKey => [$subOperator => $subValue]]];
335
                                break;
336
                            case 'standart':
337
                                $filters[] = [$type => [$subKey => $subValue]];
338
                                break;
339
                            case 'special':
340
                                switch ($subOperator) {
341
                                    case 'in':
342
                                        $filters[] = ['terms' => [$subKey => $subValue]];
343
                                        break;
344
                                }
345
                                break;
346
                        }
347
                    }
348
                }
349
            } else {
350
                $filters[] = ['term' => [$subKey => $subValue]];
351
            }
352
        }
353
        return $filters;
354
    }
355
}
356