Completed
Push — master ( 0e37b1...688672 )
by Mehmet
02:47
created

ElasticSearch::getConnection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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