Completed
Pull Request — master (#268)
by
unknown
01:15
created

Xhgui_Storage_Mongo::getConditions()   B

Complexity

Conditions 6
Paths 24

Size

Total Lines 35

Duplication

Lines 10
Ratio 28.57 %

Importance

Changes 0
Metric Value
dl 10
loc 35
rs 8.7377
c 0
b 0
f 0
cc 6
nc 24
nop 1
1
<?php
2
3
/**
4
 * Class Xhgui_Storage_Mongo
5
 */
6
class Xhgui_Storage_Mongo extends Xhgui_Storage_Abstract implements
7
    \Xhgui_StorageInterface,
8
    \Xhgui_WatchedFunctionsStorageInterface
9
{
10
11
    protected $config;
12
13
    /**
14
     * @var \MongoDB
15
     */
16
    protected $connection;
17
18
    /**
19
     * @var int
20
     */
21
    protected $defaultPerPage = 25;
22
23
    /**
24
     * @var string
25
     */
26
    protected $collectionName;
27
28
    /**
29
     * @var \MongoClient
30
     */
31
    protected $mongoClient;
32
33
    /**
34
     * Mongo constructor.
35
     * @param $config
36
     * @throws \MongoConnectionException
37
     * @throws \MongoException
38
     */
39
    public function __construct($config, $collection = 'results')
40
    {
41
        $this->config = $config;
42
        // set default number of rows for all results. This can be changed
43
        // for each query
44
        $this->defaultPerPage = $config['page.limit'];
45
46
        $this->collectionName = $collection;
47
48
        // make sure options is an array
49
        if (empty($config['db.options'])) {
50
            $config['db.options'] = array();
51
        }
52
53
        $config['db.options']['connect'] = true;
54
    }
55
56
    /**
57
     * @inheritDoc
58
     * @param $options
59
     * @param bool $projections
60
     * @return Xhgui_Storage_ResultSet
61
     * @throws \MongoCursorException
62
     */
63
    public function find(\Xhgui_Storage_Filter $filter, $projections = false)
64
    {
65
        $sort = [];
66
        switch ($filter->getSort()) {
67
            case 'ct':
68
            case 'wt':
69
            case 'cpu':
70
            case 'mu':
71
            case 'pmu':
72
                $sort['profile.main().' . $filter->getSort()] = $filter->getDirection() === 'asc' ? 1 : -1;
73
                break;
74
            case 'time':
75
                $sort['meta.request_ts'] = $filter->getDirection() === 'asc' ? 1 : -1;
76
                break;
77
        }
78
79
        $conditions = $this->getConditions($filter);
80
81
        $ret = $this->getCollection()
82
                    ->find($conditions)
83
                    ->sort($sort)
84
                    ->skip((int)($filter->getPage() - 1) * $filter->getPerPage())
85
                    ->limit($filter->getPerPage());
86
87
88
        $result = new \Xhgui_Storage_ResultSet(iterator_to_array($ret));
89
        return $result;
90
    }
91
92
    /**
93
     * @inheritDoc
94
     * @param $options
95
     * @return int
96
     * @throws \MongoCursorTimeoutException
97
     * @throws \MongoException
98
     */
99
    public function count(\Xhgui_Storage_Filter $filter)
100
    {
101
        $conditions = $this->getConditions($filter);
102
103
        $ret = $this->getCollection()->find($conditions, array('_id' => 1))->count();
104
        return $ret;
105
    }
106
107
    /**
108
     * @inheritDoc
109
     * @param $id
110
     * @return array|null
111
     * @throws \MongoException
112
     */
113
    public function findOne($id)
114
    {
115
        $ret = $this->getCollection()
116
                    ->findOne(['_id' => new \MongoId($id)]);
117
        return $ret;
118
    }
119
120
    /**
121
     * @inheritDoc
122
     * @param $id
123
     * @return array|bool
124
     * @throws MongoCursorException
125
     * @throws MongoCursorTimeoutException
126
     * @throws MongoException
127
     */
128
    public function remove($id)
129
    {
130
        return $this->getCollection()->remove(
131
            array('_id' => new MongoId($id)),
132
            array('w' => 1)
133
        );
134
    }
135
136
    /**
137
     * @inheritDoc
138
     */
139
    public function drop()
140
    {
141
        // TODO: Implement drop() method.
142
    }
143
144
    /**
145
     * @inheritDoc
146
     * @param $match
147
     * @param $col
148
     * @param int $percentile
149
     * @codeCoverageIgnore despite appearances this is very simple function and there is nothing to test here.
150
     * @return array
151
     * @throws \MongoException
152
     */
153
    public function aggregate(\Xhgui_Storage_Filter $filter, $col, $percentile = 1)
154
    {
155
156
        $conditions = $this->getConditions($filter);
157
        $param = [
158
            ['$match' => $conditions],
159
            [
160
                '$project' => [
161
                    'date'           => '$meta.request_ts',
162
                    'profile.main()' => 1
163
                ]
164
            ],
165
            [
166
                '$group' => [
167
                    '_id'        => '$date',
168
                    'row_count'  => ['$sum' => 1],
169
                    'wall_times' => ['$push' => '$profile.main().wt'],
170
                    'cpu_times'  => ['$push' => '$profile.main().cpu'],
171
                    'mu_times'   => ['$push' => '$profile.main().mu'],
172
                    'pmu_times'  => ['$push' => '$profile.main().pmu'],
173
                ]
174
            ],
175
            [
176
                '$project' => [
177
                    'date'       => '$date',
178
                    'row_count'  => '$row_count',
179
                    'raw_index'  => [
180
                        '$multiply' => [
181
                            '$row_count',
182
                            $percentile / 100
183
                        ]
184
                    ],
185
                    'wall_times' => '$wall_times',
186
                    'cpu_times'  => '$cpu_times',
187
                    'mu_times'   => '$mu_times',
188
                    'pmu_times'  => '$pmu_times',
189
                ]
190
            ],
191
            [
192
                '$sort' => ['_id' => 1]
193
            ],
194
        ];
195
        $ret = $this->getCollection()->aggregate(
196
            $param,
197
            [
198
                'cursor' => ['batchSize' => 0]
199
            ]
200
        );
201
202
        return $ret;
203
    }
204
205
    /**
206
     * @inheritDoc
207
     * @return array
208
     */
209
    public function getWatchedFunctions()
210
    {
211
        $ret = [];
212
        try {
213
            $cursor = $this->getConnection()->watches->find()->sort(['name' => 1]);
214
            $ret = [];
215
            foreach ($cursor as $row) {
216
                $ret[] = ['id' => $row['_id']->__toString(), 'name' => $row['name']];
217
            }
218
        } catch (\Exception $e) {
219
            // if something goes wrong just return empty array
220
            // @todo add exception
221
        }
222
        return $ret;
223
    }
224
225
    /**
226
     * @inheritDoc
227
     * @param $name
228
     * @return bool
229
     */
230 View Code Duplication
    public function addWatchedFunction($name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
231
    {
232
233
        $name = trim($name);
234
        if (empty($name)) {
235
            return false;
236
        }
237
238
        try {
239
            $id = new \MongoId();
240
241
            $data = [
242
                '_id'  => $id,
243
                'name' => $name
244
            ];
245
            $this->getConnection()->watches->insert($data);
246
247
            return true;
248
        } catch (\Exception $e) {
249
            // if something goes wrong just ignore for now
250
            // @todo add exception
251
        }
252
        return false;
253
    }
254
255
    /**
256
     * @inheritDoc
257
     * @param $id
258
     * @param $name
259
     * @return bool
260
     */
261 View Code Duplication
    public function updateWatchedFunction($id, $name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
262
    {
263
        $name = trim($name);
264
        if (empty($name)) {
265
            return false;
266
        }
267
268
        try {
269
            $id = new \MongoId($id);
270
            $data = [
271
                '_id'  => $id,
272
                'name' => $name
273
            ];
274
            $this->getConnection()->watches->save($data);
275
276
            return true;
277
        } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
278
        }
279
280
        return false;
281
    }
282
283
    /**
284
     * @inheritDoc
285
     * @param $id
286
     * @return bool
287
     */
288
    public function removeWatchedFunction($id)
289
    {
290
291
        try {
292
            $id = new \MongoId($id);
293
294
            $this->getConnection()->watches->remove(['_id' => $id]);
295
296
            return true;
297
        } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
298
        }
299
        return false;
300
    }
301
302
    /**
303
     * Convert filter into mongo condition
304
     *
305
     * @param \Xhgui_Storage_Filter $filter
306
     * @return array
307
     */
308
    protected function getConditions(\Xhgui_Storage_Filter $filter)
309
    {
310
        $conditions = [];
311 View Code Duplication
        if (null !== $filter->getStartDate()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
312
            $conditions['meta.request_ts']['$gte'] = new \MongoDate(
313
                $this->getDateTimeFromString($filter->getStartDate(), 'start')->format('U')
0 ignored issues
show
Documentation introduced by
$filter->getStartDate() is of type object<DateTime>, but the function expects a string|integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
314
            );
315
        }
316
317 View Code Duplication
        if (null !== $filter->getEndDate()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
318
            $conditions['meta.request_ts']['$lte'] = new \MongoDate(
319
                $this->getDateTimeFromString($filter->getEndDate(), 'end')->format('U')
0 ignored issues
show
Documentation introduced by
$filter->getEndDate() is of type object<DateTime>, but the function expects a string|integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
320
            );
321
        }
322
323
        if (null !== $filter->getUrl()) {
324
            $conditions['meta.simple_url'] = $filter->getUrl();
325
        }
326
327
        foreach ([
328
                     'method'      => 'method',
329
                     'application' => 'application',
330
                     'version'     => 'version',
331
                     'branch'      => 'branch',
332
                     'controller'  => 'controller',
333
                     'action'      => 'action',
334
                 ] as $dbField => $field) {
335
            $method = 'get' . ucfirst($field);
336
            if ($filter->{$method}()) {
337
                $conditions['meta.' . $dbField] = $filter->{$method}();
338
            }
339
        }
340
341
        return $conditions;
342
    }
343
344
    /**
345
     * Get mongo client from config
346
     *
347
     * @return MongoClient
348
     * @throws MongoConnectionException
349
     */
350
    public function getMongoClient()
351
    {
352
        if (empty($this->mongoClient)) {
353
            $this->mongoClient = new \MongoClient($this->config['db.host'], $this->config['db.options']);
354
        }
355
        return $this->mongoClient;
356
    }
357
358
    /**
359
     * Set prepared mongo client.
360
     *
361
     * @param MongoClient $mongoClient
362
     */
363
    public function setMongoClient($mongoClient)
364
    {
365
        $this->mongoClient = $mongoClient;
366
    }
367
368
    /**
369
     * Get connection.
370
     *
371
     * @return MongoDB
372
     * @throws MongoConnectionException
373
     */
374
    public function getConnection()
375
    {
376
        if (empty($this->connection)) {
377
            $this->connection = $this->getMongoClient()->{$this->config['db.db']};
378
        }
379
380
        return $this->connection;
381
    }
382
383
    /**
384
     * Set existing connection
385
     *
386
     * @param MongoDB $connection
387
     */
388
    public function setConnection($connection)
389
    {
390
        $this->connection = $connection;
391
    }
392
393
    /**
394
     * Select specific connection
395
     *
396
     * @return MongoCollection
397
     * @throws Exception
398
     */
399
    public function getCollection()
400
    {
401
        return $this->getConnection()->selectCollection($this->collectionName);
402
    }
403
}
404