Passed
Push — master ( 97cd4f...984395 )
by
unknown
02:08
created

NOSQLQuery::parseCriteria()   B

Complexity

Conditions 9
Paths 24

Size

Total Lines 32
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 19
dl 0
loc 32
rs 8.0555
c 0
b 0
f 0
cc 9
nc 24
nop 3
1
<?php
2
namespace NOSQL\Models;
3
4
use MongoDB\BSON\ObjectId;
5
use MongoDB\Collection;
6
use MongoDB\Database;
7
use NOSQL\Services\Base\NOSQLBase;
8
use NOSQL\Dto\Model\ResultsetDto;
9
use NOSQL\Models\base\NOSQLParserTrait;
10
use PSFS\base\config\Config;
11
use PSFS\base\exception\ApiException;
12
13
final class NOSQLQuery {
14
    const NOSQL_ORDER_FIELD = '__order';
15
    const NOSQL_SORT_FIELD = '__sort';
16
    const NOSQL_PAGE_FIELD = '__page';
17
    const NOSQL_LIMIT_FIELD = '__limit';
18
    const NOSQL_COLLATION_FIELD = '__collation';
19
20
    /**
21
     * @param $pk
22
     * @param Database|null $con
23
     * @return mixed
24
     * @throws ApiException
25
     */
26
    public static function findPk($modelName, $pk, Database $con = null) {
27
        $model = new $modelName();
28
        $con = NOSQLParserTrait::initConnection($model, $con);
29
        $collection = $con->selectCollection($model->getSchema()->name);
30
        $result = $collection->findOne(['_id' => new ObjectId($pk)]);
31
        if(null !== $result) {
32
            $model->feed($result->getArrayCopy());
33
        } else {
34
            throw new ApiException(t('Document not found'), 404);
35
        }
36
        return $model;
37
    }
38
39
    /**
40
     * @param string $modelName
41
     * @param array $criteria
42
     * @param Database|null $con
43
     * @return ResultsetDto
44
     * @throws \NOSQL\Exceptions\NOSQLValidationException
45
     * @throws \PSFS\base\exception\GeneratorException
46
     */
47
    public static function find($modelName, array $criteria, Database $con = null) {
48
        /** @var NOSQLActiveRecord $model */
49
        $model = new $modelName();
50
        $con = NOSQLParserTrait::initConnection($model, $con);
51
        $collection = $con->selectCollection($model->getSchema()->name);
52
        $resultSet = new ResultsetDto(false);
53
        // TODO create Query model for it
54
        [$filters, $nosqlOptions] = self::parseCriteria($criteria, $model, $collection);
55
56
        $resultSet->count = $collection->countDocuments($filters, $nosqlOptions);
57
58
        $nosqlOptions["limit"] = (integer)(array_key_exists(self::NOSQL_LIMIT_FIELD, $criteria) ? $criteria[self::NOSQL_LIMIT_FIELD] : Config::getParam('pagination.limit', 50));
59
        $nosqlOptions["skip"] = (integer)(array_key_exists(self::NOSQL_PAGE_FIELD, $criteria) ? $criteria[self::NOSQL_PAGE_FIELD] : 0);
60
61
        if (array_key_exists(self::NOSQL_SORT_FIELD, $criteria)) {
62
            $nosqlOptions["sort"] = [];
63
            $nosqlOptions["sort"][$criteria[self::NOSQL_SORT_FIELD]] = (array_key_exists(self::NOSQL_ORDER_FIELD, $criteria)) ?
64
                                                                        $criteria[self::NOSQL_ORDER_FIELD] : 1;
65
        }
66
67
        $results = $collection->find($filters, $nosqlOptions);
68
        /** @var  $result */
69
        $items = $results->toArray();
70
        foreach($items as $item) {
71
            $model->feed($item->getArrayCopy(), true);
72
            $resultSet->items[] = $model->getDtoCopy(true);
73
        }
74
        return $resultSet;
75
    }
76
77
    /**
78
     * @param array $criteria
79
     * @param NOSQLActiveRecord $model
80
     * @return array
81
     */
82
    private static function parseCriteria(array $criteria, NOSQLActiveRecord $model, Collection $collection)
83
    {
84
        $filters = [];
85
        foreach ($model->getSchema()->properties as $property) {
86
            if (array_key_exists($property->name, $criteria)) {
87
                $filterValue = self::composeFilter($criteria, $property);
88
                $filters[$property->name] = $filterValue;
89
            }
90
        }
91
92
        // Check index collation
93
        $options = [];
94
        $indexes = $collection->listIndexes();
95
        foreach($indexes as $index) {
96
            $indexInfo = $index->__debugInfo();
97
            if (empty(array_diff(array_keys($index["key"]), array_keys($filters)))) {
98
                if (array_key_exists("collation", $indexInfo)) {
99
                    $collation = $indexInfo["collation"];
100
                    $options["collation"] = ["locale" => $collation["locale"], "strength" => $collation["strength"]];
101
                    break;
102
                }
103
            }
104
        }
105
106
        if (array_key_exists("collation", $options)) {
107
            foreach($filters as $key=>$filter) {
108
                if (is_string($criteria[$key])) {
109
                    $filters[$key] = $criteria[$key];
110
                }
111
            }
112
        }
113
        return [$filters, $options];
114
    }
115
116
    /**
117
     * @param array $criteria
118
     * @param \NOSQL\Dto\PropertyDto $property
119
     * @return array|bool|float|int|mixed
120
     */
121
    private static function composeFilter(array $criteria, \NOSQL\Dto\PropertyDto $property)
122
    {
123
        $filterValue = $criteria[$property->name];
124
        if (is_array($filterValue)) {
125
            $filterValue = [
126
                '$in' => $filterValue,
127
            ];
128
        } elseif (in_array($property->type, [
129
            NOSQLBase::NOSQL_TYPE_BOOLEAN,
130
            NOSQLBase::NOSQL_TYPE_INTEGER,
131
            NOSQLBase::NOSQL_TYPE_DOUBLE,
132
            NOSQLBase::NOSQL_TYPE_LONG])) {
133
            if ($property->type === NOSQLBase::NOSQL_TYPE_BOOLEAN) {
134
                switch ($filterValue) {
135
                    case '1':
136
                    case 1:
137
                    case 'true':
138
                    case true:
139
                        $filterValue = true;
140
                        break;
141
                    default:
142
                        $filterValue = false;
143
                        break;
144
                }
145
            } elseif (NOSQLBase::NOSQL_TYPE_INTEGER === $property->type) {
146
                $filterValue = (integer)$filterValue;
147
            } else {
148
                $filterValue = (float)$filterValue;
149
            }
150
            $filterValue = [
151
                '$eq' => $filterValue,
152
            ];
153
        } else {
154
            $filterValue = [
155
                '$regex' => '^' . $filterValue . '$',
156
                '$options' => 'i'
157
            ];
158
        }
159
        return $filterValue;
160
    }
161
}