Completed
Pull Request — master (#212)
by Alexandru
10:59
created

Analyzer::getIndex()   B

Complexity

Conditions 8
Paths 9

Size

Total Lines 30
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 8

Importance

Changes 0
Metric Value
eloc 16
dl 0
loc 30
c 0
b 0
f 0
ccs 17
cts 17
cp 1
rs 8.4444
cc 8
nc 9
nop 0
crap 8
1
<?php
2
3
namespace Rennokki\DynamoDb\ConditionAnalyzer;
4
5
use Illuminate\Support\Arr;
6
use Rennokki\DynamoDb\ComparisonOperator;
7
use Rennokki\DynamoDb\DynamoDbModel;
8
use Rennokki\DynamoDb\H;
9
10
/**
11
 * Class ConditionAnalyzer
12
 * @package Rennokki\DynamoDb\ConditionAnalyzer
13
 *
14
 * Usage:
15
 *
16
 * $analyzer = with(new Analyzer)
17
 *  ->on($model)
18
 *  ->withIndex($index)
19
 *  ->analyze($conditions);
20
 *
21
 * $analyzer->isExactSearch();
22
 * $analyzer->keyConditions();
23
 * $analyzer->filterConditions();
24
 * $analyzer->index();
25
 */
26
class Analyzer
27
{
28
    /**
29
     * @var DynamoDbModel
30
     */
31
    private $model;
32
33
    /**
34
     * @var array
35
     */
36
    private $conditions = [];
37
38
    /**
39
     * @var string
40
     */
41
    private $indexName;
42
43
    public function on(DynamoDbModel $model)
44
    {
45
        $this->model = $model;
46
47
        return $this;
48
    }
49
50
    public function withIndex($index)
51
    {
52
        $this->indexName = $index;
53
54
        return $this;
55
    }
56
57
    public function analyze($conditions)
58
    {
59
        $this->conditions = $conditions;
60
61
        return $this;
62
    }
63
64
    public function isExactSearch()
65
    {
66
        if (empty($this->conditions)) {
67
            return false;
68
        }
69
70
        if (empty($this->identifierConditions())) {
71
            return false;
72
        }
73
74
        foreach ($this->conditions as $condition) {
75
            if (Arr::get($condition, 'type') !== ComparisonOperator::EQ) {
76
                return false;
77
            }
78
        }
79
80
        return true;
81
    }
82
83
    /**
84
     * @return Index|null
85
     */
86
    public function index()
87
    {
88
        return $this->getIndex();
89
    }
90
91
    public function keyConditions()
92
    {
93
        $index = $this->getIndex();
94
95
        if ($index) {
96
            return $this->getConditions($index->columns());
97
        }
98
99
        return $this->identifierConditions();
100
    }
101
102
    public function filterConditions()
103
    {
104
        $keyConditions = $this->keyConditions() ?: [];
105
106
        return array_filter($this->conditions, function ($condition) use ($keyConditions) {
107
            return array_search($condition, $keyConditions) === false;
108
        });
109
    }
110
111
    public function identifierConditions()
112
    {
113
        $keyNames = $this->model->getKeyNames();
114
115
        $conditions = $this->getConditions($keyNames);
116
117
        if (!$this->hasValidQueryOperator(...$keyNames)) {
0 ignored issues
show
Bug introduced by
$keyNames is expanded, but the parameter $hash of Rennokki\DynamoDb\Condit...hasValidQueryOperator() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

117
        if (!$this->hasValidQueryOperator(/** @scrutinizer ignore-type */ ...$keyNames)) {
Loading history...
118
            return null;
119
        }
120
121
        return $conditions;
122
    }
123
124
    public function identifierConditionValues()
125
    {
126
        $idConditions = $this->identifierConditions();
127
128
        if (!$idConditions) {
129
            return [];
130
        }
131
132
        $values = [];
133
134
        foreach ($idConditions as $condition) {
135
            $values[$condition['column']] = $condition['value'];
136
        }
137
138
        return $values;
139
    }
140
141
    /**
142
     * @param $column
143
     *
144
     * @return array
145
     */
146
    private function getCondition($column)
147
    {
148
        return H::array_first($this->conditions, function ($condition) use ($column) {
149
            return $condition['column'] === $column;
150
        });
151
    }
152
153
    /**
154
     * @param $columns
155
     *
156
     * @return array
157
     */
158
    private function getConditions($columns)
159
    {
160
        return array_filter($this->conditions, function ($condition) use ($columns) {
161
            return in_array($condition['column'], $columns);
162
        });
163
    }
164
165
    /**
166
     * @return Index|null
167
     */
168
    private function getIndex()
169
    {
170
        if (empty($this->conditions)) {
171
            return null;
172
        }
173
174
        $index = null;
175
176
        foreach ($this->model->getDynamoDbIndexKeys() as $name => $keysInfo) {
177
            $conditionKeys = Arr::pluck($this->conditions, 'column');
178
            $keys = array_values($keysInfo);
179
180
            if (count(array_intersect($conditionKeys, $keys)) === count($keys)) {
181
                if (!isset($this->indexName) || $this->indexName === $name) {
182
                    $index = new Index(
183
                        $name,
184
                        Arr::get($keysInfo, 'hash'),
185
                        Arr::get($keysInfo, 'range')
186
                    );
187
188
                    break;
189
                }
190
            }
191
        }
192
193
        if ($index && !$this->hasValidQueryOperator($index->hash, $index->range)) {
0 ignored issues
show
introduced by
$index is of type Rennokki\DynamoDb\ConditionAnalyzer\Index|null, thus it always evaluated to false.
Loading history...
194
            $index = null;
195
        }
196
197
        return $index;
198
    }
199
200
    private function hasValidQueryOperator($hash, $range = null)
201
    {
202
        $hashCondition = $this->getCondition($hash);
203
204
        $validQueryOp = ComparisonOperator::isValidQueryDynamoDbOperator($hashCondition['type']);
205
206
        if ($validQueryOp && $range) {
207
            $rangeCondition = $this->getCondition($range);
208
209
            $validQueryOp = ComparisonOperator::isValidQueryDynamoDbOperator(
210
                $rangeCondition['type'],
211
                true
212
            );
213
        }
214
215
        return $validQueryOp;
216
    }
217
}
218