StorageMapper::read()   B
last analyzed

Complexity

Conditions 6
Paths 12

Size

Total Lines 37
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 20
nc 12
nop 2
dl 0
loc 37
rs 8.9777
c 0
b 0
f 0
1
<?php
2
/**
3
 * Analytics
4
 *
5
 * SPDX-FileCopyrightText: 2019-2022 Marcel Scherello
6
 * SPDX-License-Identifier: AGPL-3.0-or-later
7
 */
8
9
namespace OCA\Analytics\Db;
10
11
use OCP\DB\Exception;
0 ignored issues
show
Bug introduced by
The type OCP\DB\Exception was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
12
use OCP\DB\QueryBuilder\IQueryBuilder;
0 ignored issues
show
Bug introduced by
The type OCP\DB\QueryBuilder\IQueryBuilder was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
use OCP\IDBConnection;
0 ignored issues
show
Bug introduced by
The type OCP\IDBConnection was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
use OCP\IL10N;
0 ignored issues
show
Bug introduced by
The type OCP\IL10N was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
use Psr\Log\LoggerInterface;
0 ignored issues
show
Bug introduced by
The type Psr\Log\LoggerInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
17
class StorageMapper
18
{
19
    private $userId;
20
    private $l10n;
21
    private $db;
22
    private $logger;
23
    const TABLE_NAME = 'analytics_facts';
24
25
    public function __construct(
26
        $userId,
27
        IL10N $l10n,
28
        IDBConnection $db,
29
        LoggerInterface $logger
30
    )
31
    {
32
        $this->userId = $userId;
33
        $this->l10n = $l10n;
34
        $this->db = $db;
35
        $this->logger = $logger;
36
        self::TABLE_NAME;
37
    }
38
39
    /**
40
     * create data
41
     * @param int $datasetId
42
     * @param $dimension1
43
     * @param $dimension2
44
     * @param $value
45
     * @param string|null $user_id
46
     * @param null $timestamp
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $timestamp is correct as it would always require null to be passed?
Loading history...
47
     * @param $bulkInsert
48
     * @param $aggregation
49
     * @return string
50
     * @throws Exception
51
     */
52
    public function create(int $datasetId, $dimension1, $dimension2, $value, string $user_id = null, $timestamp = null, $bulkInsert = null, $aggregation = null)
53
    {
54
        $dimension1 = $dimension1 !== null ? str_replace('*', '', $dimension1) : null;
55
        $dimension2 = $dimension2 !== null ? str_replace('*', '', $dimension2) : null;
56
        $timestamp = $timestamp ?? time();
57
        $result = '';
58
59
        if ($user_id) $this->userId = $user_id;
60
61
        // if the data source option to delete all date before loading is "true"
62
        // in this case, bulkInsert is set to true. Then no further checks for existing records is needed
63
        if ($bulkInsert === null) {
64
            $sql = $this->db->getQueryBuilder();
65
            $sql->from(self::TABLE_NAME)
66
                ->addSelect('value')
67
                ->where($sql->expr()->eq('user_id', $sql->createNamedParameter($this->userId)))
68
                ->andWhere($sql->expr()->eq('dataset', $sql->createNamedParameter($datasetId)));
69
70
            // if the dimension value is null, a different where clause is required
71
            $expression1 = $dimension1 === null ? $sql->expr()->isNull('dimension1') : $sql->expr()->eq('dimension1', $sql->createNamedParameter($dimension1));
72
            $expression2 = $dimension2 === null ? $sql->expr()->isNull('dimension2') : $sql->expr()->eq('dimension2', $sql->createNamedParameter($dimension2));
73
74
            $sql->andWhere($expression1)
75
                ->andWhere($expression2);
76
77
            $statement = $sql->executeQuery();
78
            $result = $statement->fetch();
79
            $statement->closeCursor();
80
        }
81
82
        if ($result) {
83
            $sql = $this->db->getQueryBuilder();
84
            $sql->update(self::TABLE_NAME)
85
                ->set('timestamp', $sql->createNamedParameter($timestamp))
86
                ->where($sql->expr()->eq('user_id', $sql->createNamedParameter($this->userId)))
87
                ->andWhere($sql->expr()->eq('dataset', $sql->createNamedParameter($datasetId)))
88
                ->andWhere($sql->expr()->eq('dimension1', $sql->createNamedParameter($dimension1)))
89
                ->andWhere($sql->expr()->eq('dimension2', $sql->createNamedParameter($dimension2)));
90
91
            if ($aggregation === 'xxsummation') {
92
                // Feature not yet available
93
                // $this->logger->error('old value: ' . $result['value']);
94
                // $this->logger->error('new value: ' . $value + $result['value']);
95
                $sql->set('value', $sql->createNamedParameter($value + $result['value']));
96
            } else {
97
                $sql->set('value', $sql->createNamedParameter($value));
98
            }
99
100
            $sql->executeStatement();
101
            return 'update';
102
        } else {
103
            $sql = $this->db->getQueryBuilder();
104
            $sql->insert(self::TABLE_NAME)
105
                ->values([
106
                    'user_id' => $sql->createNamedParameter($this->userId),
107
                    'dataset' => $sql->createNamedParameter($datasetId),
108
                    'dimension1' => $sql->createNamedParameter($dimension1),
109
                    'dimension2' => $sql->createNamedParameter($dimension2),
110
                    'value' => $sql->createNamedParameter($value),
111
                    'timestamp' => $sql->createNamedParameter($timestamp),
112
                ]);
113
            $sql->executeStatement();
114
            return 'insert';
115
        }
116
    }
117
118
    /**
119
     * read data for dataset
120
     * @param int $dataset
121
     * @param array $options
122
     * @return array
123
     */
124
    public function read(int $dataset, $options = '')
125
    {
126
        $sql = $this->db->getQueryBuilder();
127
        $sql->from(self::TABLE_NAME)
128
            ->where($sql->expr()->eq('dataset', $sql->createNamedParameter($dataset)))
129
            ->addgroupBy('dataset');
130
131
        // loop the available dimensions and check if any is hidden by the drill down selection of the user
132
        // if the dimension is not part of the drill down filter, it is not hidden => to be displayed
133
        $availableDimensions = array('dimension1', 'dimension2');
134
        foreach ($availableDimensions as $dimension) {
135
            if (!isset($options['drilldown'][$dimension])) {
136
                $sql->addSelect($dimension)
137
                    ->addGroupBy($dimension)
138
                    ->addOrderBy($dimension, 'ASC');
139
            }
140
        }
141
142
        // value column deeds to be at the last position in the select. So it needs to be after the dynamic selects
143
        $sql->addSelect($sql->func()->sum('value'));
144
145
        // add the where clauses depending on the filter selection of the
146
        if (isset($options['filter'])) {
147
            foreach ($options['filter'] as $key => $value) {
148
                $this->sqlWhere($sql, $key, $value['option'], $value['value']);
149
            }
150
        }
151
152
        $statement = $sql->executeQuery();
153
        $rows = $statement->fetchAll();
154
        $statement->closeCursor();
155
156
        // reindex result to get rid of the column headers as the frontend works incremental
157
        foreach ($rows as &$row) {
158
            $row = array_values($row);
159
        }
160
        return $rows;
161
    }
162
163
    /**
164
     * delete data
165
     * @param int $datasetId
166
     * @param $dimension1
167
     * @param $dimension2
168
     * @param string|null $user_id
169
     * @return bool
170
     * @throws Exception
171
     */
172
    public function delete(int $datasetId, $dimension1, $dimension2, string $user_id = null)
173
    {
174
        if ($user_id) $this->userId = $user_id;
175
176
        $sql = $this->db->getQueryBuilder();
177
        $sql->delete(self::TABLE_NAME)
178
            ->where($sql->expr()->eq('user_id', $sql->createNamedParameter($this->userId)))
179
            ->andWhere($sql->expr()->eq('dataset', $sql->createNamedParameter($datasetId)));
180
181
        // a %(*) does not work for null columns. in case it is just %, don't apply the where clause at all
182
        if ($dimension1 !== '*') {
183
            $dimension1 = str_replace('*', '%', $dimension1);
184
            $sql->andWhere($sql->expr()->like('dimension1', $sql->createNamedParameter($dimension1)));
185
        }
186
        if ($dimension2 !== '*') {
187
            $dimension2 = str_replace('*', '%', $dimension2);
188
            $sql->andWhere($sql->expr()->like('dimension2', $sql->createNamedParameter($dimension2)));
189
        }
190
191
        $sql->executeStatement();
192
        return true;
193
    }
194
195
    /**
196
     * Simulate delete data
197
     * @param int $datasetId
198
     * @param $dimension1
199
     * @param $dimension2
200
     * @return array
201
     * @throws Exception
202
     */
203
    public function deleteSimulate(int $datasetId, $dimension1, $dimension2)
204
    {
205
        $sql = $this->db->getQueryBuilder();
206
        $sql->from(self::TABLE_NAME)
207
            ->selectAlias($sql->func()->count('*'), 'count')
208
            ->where($sql->expr()->eq('user_id', $sql->createNamedParameter($this->userId)))
209
            ->andWhere($sql->expr()->eq('dataset', $sql->createNamedParameter($datasetId)));
210
211
        if ($dimension1 !== '*') {
212
            $dimension1 = str_replace('*', '%', $dimension1);
213
            $sql->andWhere($sql->expr()->like('dimension1', $sql->createNamedParameter($dimension1)));
214
        }
215
216
        if ($dimension2 !== '*') {
217
            $dimension2 = str_replace('*', '%', $dimension2);
218
            $sql->andWhere($sql->expr()->like('dimension2', $sql->createNamedParameter($dimension2)));
219
        }
220
221
        $statement = $sql->executeQuery();
222
        $result = $statement->fetch();
223
        $statement->closeCursor();
224
225
        return $result;
226
    }
227
228
    /**
229
     * delete data
230
     * @param int $datasetId
231
     * @param $options
232
     * @return int
233
     * @throws Exception
234
     */
235
    public function deleteWithFilter(int $datasetId, $options)
236
    {
237
        $sql = $this->db->getQueryBuilder();
238
        $sql->delete(self::TABLE_NAME)
239
            ->where($sql->expr()->eq('dataset', $sql->createNamedParameter($datasetId)));
240
241
        // add the where clauses depending on the filter selection of the
242
        if (isset($options['filter'])) {
243
            foreach ($options['filter'] as $key => $value) {
244
                $this->sqlWhere($sql, $key, $value['option'], $value['value']);
245
            }
246
        }
247
        return $sql->executeStatement(); // number of deleted rows
248
    }
249
250
    /**
251
     * delete data
252
     * @param int $datasetId
253
     * @param $options
254
     * @return bool
255
     * @throws Exception
256
     */
257
    public function deleteWithFilterSimulate(int $datasetId, $options)
258
    {
259
        $sql = $this->db->getQueryBuilder();
260
        $sql->from(self::TABLE_NAME)
261
            ->selectAlias($sql->func()->count('*'), 'count')
262
            ->where($sql->expr()->eq('dataset', $sql->createNamedParameter($datasetId)));
263
264
        // add the where clauses depending on the filter selection of the
265
        if (isset($options['filter'])) {
266
            foreach ($options['filter'] as $key => $value) {
267
                $this->sqlWhere($sql, $key, $value['option'], $value['value']);
268
            }
269
        }
270
271
        $statement = $sql->executeQuery();
272
        $result = $statement->fetch();
273
        $statement->closeCursor();
274
        return $result;
275
    }
276
277
    /**
278
     * delete all data of a dataset
279
     * @param int $datasetId
280
     * @return bool
281
     */
282
    public function deleteByDataset(int $datasetId)
283
    {
284
        $sql = $this->db->getQueryBuilder();
285
        $sql->delete(self::TABLE_NAME)
286
            ->where($sql->expr()->eq('dataset', $sql->createNamedParameter($datasetId)));
287
        $sql->executeStatement();
288
        return true;
289
    }
290
291
    /**
292
     * Simulate delete data
293
     * @param int $datasetId
294
     * @param string|null $user_id
295
     * @return array
296
     * @throws Exception
297
     */
298
    public function getRecordCount(int $datasetId, string $user_id = null)
299
    {
300
        if ($user_id) $this->userId = $user_id;
301
        $sql = $this->db->getQueryBuilder();
302
        $sql->from(self::TABLE_NAME)
303
            ->selectAlias($sql->func()->count('*'), 'count')
304
            ->where($sql->expr()->eq('user_id', $sql->createNamedParameter($this->userId)))
305
            ->andWhere($sql->expr()->eq('dataset', $sql->createNamedParameter($datasetId)));
306
        $statement = $sql->executeQuery();
307
        $result = $statement->fetch();
308
        $statement->closeCursor();
309
        return $result;
310
    }
311
312
    /**
313
     * Add where statements to a query builder
314
     *
315
     * @param IQueryBuilder $sql
316
     * @param $column
317
     * @param $option
318
     * @param $value
319
     */
320
    protected function sqlWhere(IQueryBuilder $sql, $column, $option, $value)
321
    {
322
        if ($option === 'EQ') {
323
            $sql->andWhere($sql->expr()->eq($column, $sql->createNamedParameter($value)));
324
        } elseif ($option === 'GT') {
325
            $sql->andWhere($sql->expr()->gt($column, $sql->createNamedParameter($value)));
326
        } elseif ($option === 'LT') {
327
            $sql->andWhere($sql->expr()->lt($column, $sql->createNamedParameter($value)));
328
        } elseif ($option === 'IN') {
329
            $sql->andWhere($sql->expr()->in($column, $sql->createParameter('inValues')));
330
            $sql->setParameter('inValues', explode(',', $value), IQueryBuilder::PARAM_STR_ARRAY);
331
        } elseif ($option === 'LIKE') {
332
            $sql->andWhere($sql->expr()->like($column, $sql->createNamedParameter('%' . $value . '%')));
333
        }
334
    }
335
}