|
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 $_mapper; |
|
11
|
|
|
|
|
12
|
|
|
public function __construct(MongoDb $db) |
|
13
|
|
|
{ |
|
14
|
|
|
$this->_collection = $db->results; |
|
|
|
|
|
|
15
|
|
|
$this->_mapper = new Xhgui_Db_Mapper(); |
|
16
|
|
|
} |
|
17
|
|
|
|
|
18
|
|
|
/** |
|
19
|
|
|
* {@inheritdoc} |
|
20
|
|
|
*/ |
|
21
|
|
|
public function latest() |
|
22
|
|
|
{ |
|
23
|
|
|
$cursor = $this->_collection->find() |
|
24
|
|
|
->sort(array('meta.request_date' => -1)) |
|
25
|
|
|
->limit(1); |
|
26
|
|
|
$result = $cursor->getNext(); |
|
27
|
|
|
return $this->_wrap($result); |
|
|
|
|
|
|
28
|
|
|
} |
|
29
|
|
|
|
|
30
|
|
|
/** |
|
31
|
|
|
* {@inheritdoc} |
|
32
|
|
|
*/ |
|
33
|
|
|
public function query($conditions, $limit, $fields = []) |
|
34
|
|
|
{ |
|
35
|
|
|
$result = $this->_collection->find($conditions, $fields) |
|
36
|
|
|
->limit($limit); |
|
37
|
|
|
|
|
38
|
|
|
return iterator_to_array($result); |
|
39
|
|
|
} |
|
40
|
|
|
|
|
41
|
|
|
/** |
|
42
|
|
|
* {@inheritdoc} |
|
43
|
|
|
*/ |
|
44
|
|
|
public function get($id) |
|
45
|
|
|
{ |
|
46
|
|
|
return $this->_wrap($this->_collection->findOne(array( |
|
|
|
|
|
|
47
|
|
|
'_id' => new MongoId($id) |
|
48
|
|
|
))); |
|
49
|
|
|
} |
|
50
|
|
|
|
|
51
|
|
|
/** |
|
52
|
|
|
* {@inheritdoc} |
|
53
|
|
|
*/ |
|
54
|
|
|
public function getForUrl($url, $options, $conditions = array()) |
|
55
|
|
|
{ |
|
56
|
|
|
$conditions = array_merge( |
|
57
|
|
|
(array)$conditions, |
|
58
|
|
|
array('simple_url' => $url) |
|
59
|
|
|
); |
|
60
|
|
|
$options = array_merge($options, array( |
|
61
|
|
|
'conditions' => $conditions, |
|
62
|
|
|
)); |
|
63
|
|
|
return $this->paginate($options); |
|
|
|
|
|
|
64
|
|
|
} |
|
65
|
|
|
|
|
66
|
|
|
/** |
|
67
|
|
|
* {@inheritdoc} |
|
68
|
|
|
*/ |
|
69
|
|
|
public function getPercentileForUrl($percentile, $url, $search = array()) |
|
70
|
|
|
{ |
|
71
|
|
|
$result = $this->_mapper->convert(array( |
|
72
|
|
|
'conditions' => $search + array('simple_url' => $url) |
|
73
|
|
|
)); |
|
74
|
|
|
$match = $result['conditions']; |
|
75
|
|
|
|
|
76
|
|
|
$col = '$meta.request_date'; |
|
77
|
|
View Code Duplication |
if (!empty($search['limit']) && $search['limit'][0] == "P") { |
|
|
|
|
|
|
78
|
|
|
$col = '$meta.request_ts'; |
|
79
|
|
|
} |
|
80
|
|
|
|
|
81
|
|
|
$results = $this->_collection->aggregate(array( |
|
82
|
|
|
array('$match' => $match), |
|
83
|
|
|
array( |
|
84
|
|
|
'$project' => array( |
|
85
|
|
|
'date' => $col, |
|
86
|
|
|
'profile.main()' => 1 |
|
87
|
|
|
) |
|
88
|
|
|
), |
|
89
|
|
|
array( |
|
90
|
|
|
'$group' => array( |
|
91
|
|
|
'_id' => '$date', |
|
92
|
|
|
'row_count' => array('$sum' => 1), |
|
93
|
|
|
'wall_times' => array('$push' => '$profile.main().wt'), |
|
94
|
|
|
'cpu_times' => array('$push' => '$profile.main().cpu'), |
|
95
|
|
|
'mu_times' => array('$push' => '$profile.main().mu'), |
|
96
|
|
|
'pmu_times' => array('$push' => '$profile.main().pmu'), |
|
97
|
|
|
) |
|
98
|
|
|
), |
|
99
|
|
|
array( |
|
100
|
|
|
'$project' => array( |
|
101
|
|
|
'date' => '$date', |
|
102
|
|
|
'row_count' => '$row_count', |
|
103
|
|
|
'raw_index' => array( |
|
104
|
|
|
'$multiply' => array( |
|
105
|
|
|
'$row_count', |
|
106
|
|
|
$percentile / 100 |
|
107
|
|
|
) |
|
108
|
|
|
), |
|
109
|
|
|
'wall_times' => '$wall_times', |
|
110
|
|
|
'cpu_times' => '$cpu_times', |
|
111
|
|
|
'mu_times' => '$mu_times', |
|
112
|
|
|
'pmu_times' => '$pmu_times', |
|
113
|
|
|
) |
|
114
|
|
|
), |
|
115
|
|
|
array('$sort' => array('_id' => 1)), |
|
116
|
|
|
), |
|
117
|
|
|
array('cursor' => array('batchSize' => 0)) |
|
118
|
|
|
); |
|
119
|
|
|
|
|
120
|
|
|
if (empty($results['result'])) { |
|
121
|
|
|
return array(); |
|
122
|
|
|
} |
|
123
|
|
|
$keys = array( |
|
124
|
|
|
'wall_times' => 'wt', |
|
125
|
|
|
'cpu_times' => 'cpu', |
|
126
|
|
|
'mu_times' => 'mu', |
|
127
|
|
|
'pmu_times' => 'pmu' |
|
128
|
|
|
); |
|
129
|
|
|
foreach ($results['result'] as &$result) { |
|
130
|
|
|
$result['date'] = ($result['_id'] instanceof MongoDate) ? date('Y-m-d H:i:s', $result['_id']->sec) : $result['_id']; |
|
131
|
|
|
unset($result['_id']); |
|
132
|
|
|
$index = max(round($result['raw_index']) - 1, 0); |
|
133
|
|
|
foreach ($keys as $key => $out) { |
|
134
|
|
|
sort($result[$key]); |
|
135
|
|
|
$result[$out] = isset($result[$key][$index]) ? $result[$key][$index] : null; |
|
136
|
|
|
unset($result[$key]); |
|
137
|
|
|
} |
|
138
|
|
|
} |
|
139
|
|
|
return $results['result']; |
|
140
|
|
|
} |
|
141
|
|
|
|
|
142
|
|
|
/** |
|
143
|
|
|
* {@inheritdoc} |
|
144
|
|
|
*/ |
|
145
|
|
|
public function getAvgsForUrl($url, $search = array()) |
|
146
|
|
|
{ |
|
147
|
|
|
$match = array('meta.simple_url' => $url); |
|
148
|
|
|
if (isset($search['date_start'])) { |
|
149
|
|
|
$match['meta.request_date']['$gte'] = (string)$search['date_start']; |
|
150
|
|
|
} |
|
151
|
|
|
if (isset($search['date_end'])) { |
|
152
|
|
|
$match['meta.request_date']['$lte'] = (string)$search['date_end']; |
|
153
|
|
|
} |
|
154
|
|
|
$results = $this->_collection->aggregate(array( |
|
155
|
|
|
array('$match' => $match), |
|
156
|
|
|
array( |
|
157
|
|
|
'$project' => array( |
|
158
|
|
|
'date' => '$meta.request_date', |
|
159
|
|
|
'profile.main()' => 1, |
|
160
|
|
|
) |
|
161
|
|
|
), |
|
162
|
|
|
array( |
|
163
|
|
|
'$group' => array( |
|
164
|
|
|
'_id' => '$date', |
|
165
|
|
|
'avg_wt' => array('$avg' => '$profile.main().wt'), |
|
166
|
|
|
'avg_cpu' => array('$avg' => '$profile.main().cpu'), |
|
167
|
|
|
'avg_mu' => array('$avg' => '$profile.main().mu'), |
|
168
|
|
|
'avg_pmu' => array('$avg' => '$profile.main().pmu'), |
|
169
|
|
|
) |
|
170
|
|
|
), |
|
171
|
|
|
array('$sort' => array('_id' => 1)) |
|
172
|
|
|
), |
|
173
|
|
|
array('cursor' => array('batchSize' => 0)) |
|
174
|
|
|
); |
|
175
|
|
|
if (empty($results['result'])) { |
|
176
|
|
|
return array(); |
|
177
|
|
|
} |
|
178
|
|
|
foreach ($results['result'] as $i => $result) { |
|
179
|
|
|
$results['result'][$i]['date'] = $result['_id']; |
|
180
|
|
|
unset($results['result'][$i]['_id']); |
|
181
|
|
|
} |
|
182
|
|
|
return $results['result']; |
|
183
|
|
|
} |
|
184
|
|
|
|
|
185
|
|
|
/** |
|
186
|
|
|
* {@inheritdoc} |
|
187
|
|
|
*/ |
|
188
|
|
|
public function getAll($options = array()) |
|
189
|
|
|
{ |
|
190
|
|
|
return $this->paginate($options); |
|
191
|
|
|
} |
|
192
|
|
|
|
|
193
|
|
|
/** |
|
194
|
|
|
* {@inheritdoc} |
|
195
|
|
|
*/ |
|
196
|
|
|
public function delete($id) |
|
197
|
|
|
{ |
|
198
|
|
|
$this->_collection->remove(array('_id' => new MongoId($id)), array()); |
|
199
|
|
|
} |
|
200
|
|
|
|
|
201
|
|
|
/** |
|
202
|
|
|
* {@inheritdoc} |
|
203
|
|
|
*/ |
|
204
|
|
|
public function truncate() |
|
205
|
|
|
{ |
|
206
|
|
|
return $this->_collection->drop(); |
|
207
|
|
|
} |
|
208
|
|
|
|
|
209
|
|
|
/** |
|
210
|
|
|
* {@inheritdoc} |
|
211
|
|
|
*/ |
|
212
|
|
|
private function paginate($options) |
|
213
|
|
|
{ |
|
214
|
|
|
$opts = $this->_mapper->convert($options); |
|
215
|
|
|
|
|
216
|
|
|
$totalRows = $this->_collection->find( |
|
217
|
|
|
$opts['conditions'], |
|
218
|
|
|
array('_id' => 1))->count(); |
|
219
|
|
|
|
|
220
|
|
|
$totalPages = max(ceil($totalRows / $opts['perPage']), 1); |
|
221
|
|
|
$page = 1; |
|
222
|
|
|
if (isset($options['page'])) { |
|
223
|
|
|
$page = min(max($options['page'], 1), $totalPages); |
|
224
|
|
|
} |
|
225
|
|
|
|
|
226
|
|
|
$projection = false; |
|
227
|
|
|
if (isset($options['projection'])) { |
|
228
|
|
|
if ($options['projection'] === true) { |
|
229
|
|
|
$projection = array('meta' => 1, 'profile.main()' => 1); |
|
230
|
|
|
} else { |
|
231
|
|
|
$projection = $options['projection']; |
|
232
|
|
|
} |
|
233
|
|
|
} |
|
234
|
|
|
|
|
235
|
|
|
if ($projection === false) { |
|
236
|
|
|
$cursor = $this->_collection->find($opts['conditions']) |
|
237
|
|
|
->sort($opts['sort']) |
|
238
|
|
|
->skip((int)($page - 1) * $opts['perPage']) |
|
239
|
|
|
->limit($opts['perPage']); |
|
240
|
|
|
} else { |
|
241
|
|
|
$cursor = $this->_collection->find($opts['conditions'], $projection) |
|
242
|
|
|
->sort($opts['sort']) |
|
243
|
|
|
->skip((int)($page - 1) * $opts['perPage']) |
|
244
|
|
|
->limit($opts['perPage']); |
|
245
|
|
|
} |
|
246
|
|
|
|
|
247
|
|
|
return array( |
|
248
|
|
|
'results' => $this->_wrap($cursor), |
|
249
|
|
|
'sort' => $opts['sort'], |
|
250
|
|
|
'direction' => $opts['direction'], |
|
251
|
|
|
'page' => $page, |
|
252
|
|
|
'perPage' => $opts['perPage'], |
|
253
|
|
|
'totalPages' => $totalPages |
|
254
|
|
|
); |
|
255
|
|
|
} |
|
256
|
|
|
|
|
257
|
|
|
/** |
|
258
|
|
|
* Converts arrays + MongoCursors into Xhgui_Profile instances. |
|
259
|
|
|
* |
|
260
|
|
|
* @param array|MongoCursor $data The data to transform. |
|
261
|
|
|
* @return Xhgui_Profile|Xhgui_Profile[] The transformed/wrapped results. |
|
262
|
|
|
*/ |
|
263
|
|
|
protected function _wrap($data) |
|
264
|
|
|
{ |
|
265
|
|
|
if ($data === null) { |
|
266
|
|
|
throw new Exception('No profile data found.'); |
|
267
|
|
|
} |
|
268
|
|
|
|
|
269
|
|
|
if (is_array($data)) { |
|
270
|
|
|
return new Xhgui_Profile($data); |
|
271
|
|
|
} |
|
272
|
|
|
$results = array(); |
|
273
|
|
|
foreach ($data as $row) { |
|
274
|
|
|
$results[] = new Xhgui_Profile($row); |
|
275
|
|
|
} |
|
276
|
|
|
return $results; |
|
277
|
|
|
} |
|
278
|
|
|
} |
|
279
|
|
|
|
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.