Completed
Push — master ( f100fd...4450c4 )
by
unknown
02:19
created

NOSQLQuery::find()   B

Complexity

Conditions 9
Paths 32

Size

Total Lines 30
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

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