1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* A Searcher for a MongoDB backend. |
5
|
|
|
*/ |
6
|
|
|
class Xhgui_Searcher_Mongo implements Xhgui_Searcher_Interface |
7
|
|
|
{ |
8
|
|
|
protected $_collection; |
9
|
|
|
|
10
|
|
|
protected $_watches; |
11
|
|
|
|
12
|
|
|
protected $_mapper; |
13
|
|
|
|
14
|
|
|
public function __construct(MongoDb $db) |
15
|
|
|
{ |
16
|
|
|
$this->_collection = $db->results; |
|
|
|
|
17
|
|
|
$this->_watches = $db->watches; |
|
|
|
|
18
|
|
|
$this->_mapper = new Xhgui_Db_Mapper(); |
19
|
|
|
} |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* {@inheritdoc} |
23
|
|
|
*/ |
24
|
|
|
public function latest() |
25
|
|
|
{ |
26
|
|
|
$cursor = $this->_collection->find() |
27
|
|
|
->sort(array('meta.request_date' => -1)) |
28
|
|
|
->limit(1); |
29
|
|
|
$result = $cursor->getNext(); |
30
|
|
|
return $this->_wrap($result); |
|
|
|
|
31
|
|
|
} |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* {@inheritdoc} |
35
|
|
|
*/ |
36
|
|
|
public function query($conditions, $limit, $fields = []) |
37
|
|
|
{ |
38
|
|
|
$result = $this->_collection->find($conditions, $fields) |
39
|
|
|
->limit($limit); |
40
|
|
|
|
41
|
|
|
return iterator_to_array($result); |
42
|
|
|
} |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* {@inheritdoc} |
46
|
|
|
*/ |
47
|
|
|
public function get($id) |
48
|
|
|
{ |
49
|
|
|
return $this->_wrap($this->_collection->findOne(array( |
|
|
|
|
50
|
|
|
'_id' => new MongoId($id) |
51
|
|
|
))); |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* {@inheritdoc} |
56
|
|
|
*/ |
57
|
|
|
public function getForUrl($url, $options, $conditions = []) |
58
|
|
|
{ |
59
|
|
|
$conditions = array_merge( |
60
|
|
|
(array)$conditions, |
61
|
|
|
array('simple_url' => $url) |
62
|
|
|
); |
63
|
|
|
$options = array_merge($options, array( |
64
|
|
|
'conditions' => $conditions, |
65
|
|
|
)); |
66
|
|
|
return $this->paginate($options); |
|
|
|
|
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* {@inheritdoc} |
71
|
|
|
*/ |
72
|
|
|
public function getPercentileForUrl($percentile, $url, $search = []) |
73
|
|
|
{ |
74
|
|
|
$result = $this->_mapper->convert(array( |
75
|
|
|
'conditions' => $search + array('simple_url' => $url) |
76
|
|
|
)); |
77
|
|
|
$match = $result['conditions']; |
78
|
|
|
|
79
|
|
|
$col = '$meta.request_date'; |
80
|
|
View Code Duplication |
if (!empty($search['limit']) && $search['limit'][0] == "P") { |
|
|
|
|
81
|
|
|
$col = '$meta.request_ts'; |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
$results = $this->_collection->aggregate(array( |
85
|
|
|
array('$match' => $match), |
86
|
|
|
array( |
87
|
|
|
'$project' => array( |
88
|
|
|
'date' => $col, |
89
|
|
|
'profile.main()' => 1 |
90
|
|
|
) |
91
|
|
|
), |
92
|
|
|
array( |
93
|
|
|
'$group' => array( |
94
|
|
|
'_id' => '$date', |
95
|
|
|
'row_count' => array('$sum' => 1), |
96
|
|
|
'wall_times' => array('$push' => '$profile.main().wt'), |
97
|
|
|
'cpu_times' => array('$push' => '$profile.main().cpu'), |
98
|
|
|
'mu_times' => array('$push' => '$profile.main().mu'), |
99
|
|
|
'pmu_times' => array('$push' => '$profile.main().pmu'), |
100
|
|
|
) |
101
|
|
|
), |
102
|
|
|
array( |
103
|
|
|
'$project' => array( |
104
|
|
|
'date' => '$date', |
105
|
|
|
'row_count' => '$row_count', |
106
|
|
|
'raw_index' => array( |
107
|
|
|
'$multiply' => array( |
108
|
|
|
'$row_count', |
109
|
|
|
$percentile / 100 |
110
|
|
|
) |
111
|
|
|
), |
112
|
|
|
'wall_times' => '$wall_times', |
113
|
|
|
'cpu_times' => '$cpu_times', |
114
|
|
|
'mu_times' => '$mu_times', |
115
|
|
|
'pmu_times' => '$pmu_times', |
116
|
|
|
) |
117
|
|
|
), |
118
|
|
|
array('$sort' => array('_id' => 1)), |
119
|
|
|
), |
120
|
|
|
array('cursor' => array('batchSize' => 0)) |
121
|
|
|
); |
122
|
|
|
|
123
|
|
|
if (empty($results['result'])) { |
124
|
|
|
return []; |
125
|
|
|
} |
126
|
|
|
$keys = array( |
127
|
|
|
'wall_times' => 'wt', |
128
|
|
|
'cpu_times' => 'cpu', |
129
|
|
|
'mu_times' => 'mu', |
130
|
|
|
'pmu_times' => 'pmu' |
131
|
|
|
); |
132
|
|
|
foreach ($results['result'] as &$result) { |
133
|
|
|
$result['date'] = ($result['_id'] instanceof MongoDate) ? date('Y-m-d H:i:s', $result['_id']->sec) : $result['_id']; |
134
|
|
|
unset($result['_id']); |
135
|
|
|
$index = max(round($result['raw_index']) - 1, 0); |
136
|
|
|
foreach ($keys as $key => $out) { |
137
|
|
|
sort($result[$key]); |
138
|
|
|
$result[$out] = isset($result[$key][$index]) ? $result[$key][$index] : null; |
139
|
|
|
unset($result[$key]); |
140
|
|
|
} |
141
|
|
|
} |
142
|
|
|
return $results['result']; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* {@inheritdoc} |
147
|
|
|
*/ |
148
|
|
|
public function getAvgsForUrl($url, $search = []) |
149
|
|
|
{ |
150
|
|
|
$match = array('meta.simple_url' => $url); |
151
|
|
|
if (isset($search['date_start'])) { |
152
|
|
|
$match['meta.request_date']['$gte'] = (string)$search['date_start']; |
153
|
|
|
} |
154
|
|
|
if (isset($search['date_end'])) { |
155
|
|
|
$match['meta.request_date']['$lte'] = (string)$search['date_end']; |
156
|
|
|
} |
157
|
|
|
$results = $this->_collection->aggregate(array( |
158
|
|
|
array('$match' => $match), |
159
|
|
|
array( |
160
|
|
|
'$project' => array( |
161
|
|
|
'date' => '$meta.request_date', |
162
|
|
|
'profile.main()' => 1, |
163
|
|
|
) |
164
|
|
|
), |
165
|
|
|
array( |
166
|
|
|
'$group' => array( |
167
|
|
|
'_id' => '$date', |
168
|
|
|
'avg_wt' => array('$avg' => '$profile.main().wt'), |
169
|
|
|
'avg_cpu' => array('$avg' => '$profile.main().cpu'), |
170
|
|
|
'avg_mu' => array('$avg' => '$profile.main().mu'), |
171
|
|
|
'avg_pmu' => array('$avg' => '$profile.main().pmu'), |
172
|
|
|
) |
173
|
|
|
), |
174
|
|
|
array('$sort' => array('_id' => 1)) |
175
|
|
|
), |
176
|
|
|
array('cursor' => array('batchSize' => 0)) |
177
|
|
|
); |
178
|
|
|
if (empty($results['result'])) { |
179
|
|
|
return []; |
180
|
|
|
} |
181
|
|
|
foreach ($results['result'] as $i => $result) { |
182
|
|
|
$results['result'][$i]['date'] = $result['_id']; |
183
|
|
|
unset($results['result'][$i]['_id']); |
184
|
|
|
} |
185
|
|
|
return $results['result']; |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* {@inheritdoc} |
190
|
|
|
*/ |
191
|
|
|
public function getAll($options = []) |
192
|
|
|
{ |
193
|
|
|
return $this->paginate($options); |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* {@inheritdoc} |
198
|
|
|
*/ |
199
|
|
|
public function delete($id) |
200
|
|
|
{ |
201
|
|
|
$this->_collection->remove(array('_id' => new MongoId($id)), []); |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* {@inheritdoc} |
206
|
|
|
*/ |
207
|
|
|
public function truncate() |
208
|
|
|
{ |
209
|
|
|
return $this->_collection->drop(); |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* {@inheritdoc} |
214
|
|
|
*/ |
215
|
|
|
public function saveWatch(array $data) |
216
|
|
|
{ |
217
|
|
|
if (empty($data['name'])) { |
218
|
|
|
return false; |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
if (!empty($data['removed']) && isset($data['_id'])) { |
222
|
|
|
$this->_watches->remove( |
223
|
|
|
array('_id' => new MongoId($data['_id'])), |
224
|
|
|
array('w' => 1) |
225
|
|
|
); |
226
|
|
|
return true; |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
if (empty($data['_id'])) { |
230
|
|
|
$this->_watches->insert( |
231
|
|
|
$data, |
232
|
|
|
array('w' => 1) |
233
|
|
|
); |
234
|
|
|
return true; |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
$data['_id'] = new MongoId($data['_id']); |
238
|
|
|
$this->_watches->update( |
239
|
|
|
array('_id' => $data['_id']), |
240
|
|
|
$data, |
241
|
|
|
array('w' => 1) |
242
|
|
|
); |
243
|
|
|
return true; |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
/** |
247
|
|
|
* {@inheritdoc} |
248
|
|
|
*/ |
249
|
|
|
public function getAllWatches() |
250
|
|
|
{ |
251
|
|
|
$cursor = $this->_watches->find(); |
252
|
|
|
return array_values(iterator_to_array($cursor)); |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
/** |
256
|
|
|
* {@inheritdoc} |
257
|
|
|
*/ |
258
|
|
|
public function truncateWatches() |
259
|
|
|
{ |
260
|
|
|
$this->_watches->drop(); |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
/** |
264
|
|
|
* {@inheritdoc} |
265
|
|
|
*/ |
266
|
|
|
private function paginate($options) |
267
|
|
|
{ |
268
|
|
|
$opts = $this->_mapper->convert($options); |
269
|
|
|
|
270
|
|
|
$totalRows = $this->_collection->find( |
271
|
|
|
$opts['conditions'], |
272
|
|
|
array('_id' => 1))->count(); |
273
|
|
|
|
274
|
|
|
$totalPages = max(ceil($totalRows / $opts['perPage']), 1); |
275
|
|
|
$page = 1; |
276
|
|
|
if (isset($options['page'])) { |
277
|
|
|
$page = min(max($options['page'], 1), $totalPages); |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
$projection = false; |
281
|
|
|
if (isset($options['projection'])) { |
282
|
|
|
if ($options['projection'] === true) { |
283
|
|
|
$projection = array('meta' => 1, 'profile.main()' => 1); |
284
|
|
|
} else { |
285
|
|
|
$projection = $options['projection']; |
286
|
|
|
} |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
if ($projection === false) { |
290
|
|
|
$cursor = $this->_collection->find($opts['conditions']) |
291
|
|
|
->sort($opts['sort']) |
292
|
|
|
->skip((int)($page - 1) * $opts['perPage']) |
293
|
|
|
->limit($opts['perPage']); |
294
|
|
|
} else { |
295
|
|
|
$cursor = $this->_collection->find($opts['conditions'], $projection) |
296
|
|
|
->sort($opts['sort']) |
297
|
|
|
->skip((int)($page - 1) * $opts['perPage']) |
298
|
|
|
->limit($opts['perPage']); |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
return array( |
302
|
|
|
'results' => $this->_wrap($cursor), |
303
|
|
|
'sort' => $opts['sort'], |
304
|
|
|
'direction' => $opts['direction'], |
305
|
|
|
'page' => $page, |
306
|
|
|
'perPage' => $opts['perPage'], |
307
|
|
|
'totalPages' => $totalPages |
308
|
|
|
); |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
/** |
312
|
|
|
* Converts arrays + MongoCursors into Xhgui_Profile instances. |
313
|
|
|
* |
314
|
|
|
* @param array|MongoCursor $data The data to transform. |
315
|
|
|
* @return Xhgui_Profile|Xhgui_Profile[] The transformed/wrapped results. |
316
|
|
|
*/ |
317
|
|
|
private function _wrap($data) |
318
|
|
|
{ |
319
|
|
|
if ($data === null) { |
320
|
|
|
throw new Exception('No profile data found.'); |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
if (is_array($data)) { |
324
|
|
|
return new Xhgui_Profile($data); |
325
|
|
|
} |
326
|
|
|
$results = []; |
327
|
|
|
foreach ($data as $row) { |
328
|
|
|
$results[] = new Xhgui_Profile($row); |
329
|
|
|
} |
330
|
|
|
return $results; |
331
|
|
|
} |
332
|
|
|
} |
333
|
|
|
|
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.