1
|
|
|
<?php |
2
|
|
|
declare(strict_types=1); |
3
|
|
|
|
4
|
|
|
namespace Level23\Druid\Concerns; |
5
|
|
|
|
6
|
|
|
use Closure; |
7
|
|
|
use InvalidArgumentException; |
8
|
|
|
use Level23\Druid\Filters\InFilter; |
9
|
|
|
use Level23\Druid\Filters\OrFilter; |
10
|
|
|
use Level23\Druid\Filters\AndFilter; |
11
|
|
|
use Level23\Druid\Filters\NotFilter; |
12
|
|
|
use Level23\Druid\Interval\Interval; |
13
|
|
|
use Level23\Druid\Filters\LikeFilter; |
14
|
|
|
use Level23\Druid\Filters\BoundFilter; |
15
|
|
|
use Level23\Druid\Filters\RegexFilter; |
16
|
|
|
use Level23\Druid\Filters\SearchFilter; |
17
|
|
|
use Level23\Druid\Dimensions\Dimension; |
18
|
|
|
use Level23\Druid\Filters\FilterBuilder; |
19
|
|
|
use Level23\Druid\Filters\BetweenFilter; |
20
|
|
|
use Level23\Druid\Filters\IntervalFilter; |
21
|
|
|
use Level23\Druid\Filters\SelectorFilter; |
22
|
|
|
use Level23\Druid\Filters\FilterInterface; |
23
|
|
|
use Level23\Druid\Filters\JavascriptFilter; |
24
|
|
|
use Level23\Druid\Interval\IntervalInterface; |
25
|
|
|
use Level23\Druid\Dimensions\DimensionBuilder; |
26
|
|
|
use Level23\Druid\Extractions\ExtractionBuilder; |
27
|
|
|
use Level23\Druid\Dimensions\DimensionInterface; |
28
|
|
|
use Level23\Druid\Filters\ColumnComparisonFilter; |
29
|
|
|
use Level23\Druid\Extractions\ExtractionInterface; |
30
|
|
|
|
31
|
|
|
trait HasFilter |
32
|
|
|
{ |
33
|
|
|
/** |
34
|
|
|
* @var \Level23\Druid\Filters\FilterInterface|null |
35
|
|
|
*/ |
36
|
|
|
protected $filter; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* Filter our results where the given dimension matches the value based on the operator. |
40
|
|
|
* The operator can be '=', '>', '>=', '<', '<=', '<>', '!=', 'like', 'not like', 'regex', 'not regex', |
41
|
|
|
* 'javascript', 'not javascript', 'search' and 'not search' |
42
|
|
|
* |
43
|
|
|
* @param string|FilterInterface|\Closure $filterOrDimensionOrClosure The dimension which you want to filter. |
44
|
|
|
* @param string|null $operator The operator which you want to use to filter. |
45
|
|
|
* See below for a complete list of supported |
46
|
|
|
* operators. |
47
|
|
|
* @param mixed $value The value which you want to use in your |
48
|
|
|
* filter comparison |
49
|
|
|
* @param \Closure|null $extraction A closure which builds one or more extraction |
50
|
|
|
* function. These are applied before the filter |
51
|
|
|
* will be applied. So the filter will use the |
52
|
|
|
* value returned by the extraction function(s). |
53
|
|
|
* @param string $boolean This influences how this filter will be |
54
|
|
|
* joined with previous added filters. Should |
55
|
|
|
* both filters apply ("and") or one or the |
56
|
|
|
* other ("or") ? Default is "and". |
57
|
|
|
* |
58
|
|
|
* @return $this |
59
|
|
|
*/ |
60
|
50 |
|
public function where( |
61
|
|
|
$filterOrDimensionOrClosure, |
62
|
|
|
$operator = null, |
63
|
|
|
$value = null, |
64
|
|
|
Closure $extraction = null, |
65
|
|
|
string $boolean = 'and' |
66
|
|
|
) { |
67
|
50 |
|
$filter = null; |
68
|
50 |
|
if (is_string($filterOrDimensionOrClosure)) { |
69
|
34 |
|
if ($value === null && $operator !== null) { |
70
|
2 |
|
$value = $operator; |
71
|
2 |
|
$operator = '='; |
72
|
|
|
} |
73
|
|
|
|
74
|
34 |
|
if ($operator === null || $value === null) { |
75
|
2 |
|
throw new InvalidArgumentException('You have to supply an operator and an compare value when you supply a dimension as string'); |
76
|
|
|
} |
77
|
|
|
|
78
|
32 |
|
$operator = strtolower($operator); |
79
|
|
|
|
80
|
32 |
|
if ($operator == '=') { |
81
|
12 |
|
$filter = new SelectorFilter( |
82
|
12 |
|
$filterOrDimensionOrClosure, |
83
|
12 |
|
(string)$value, |
84
|
12 |
|
$this->getExtraction($extraction) |
85
|
|
|
); |
86
|
20 |
|
} elseif ($operator == '<>' || $operator == '!=') { |
87
|
5 |
|
$filter = new NotFilter( |
88
|
5 |
|
new SelectorFilter($filterOrDimensionOrClosure, (string)$value, $this->getExtraction($extraction)) |
89
|
|
|
); |
90
|
15 |
|
} elseif (in_array($operator, ['>', '>=', '<', '<='])) { |
91
|
4 |
|
$filter = new BoundFilter( |
92
|
4 |
|
$filterOrDimensionOrClosure, |
93
|
4 |
|
$operator, |
94
|
4 |
|
(string)$value, |
95
|
4 |
|
null, |
96
|
4 |
|
$this->getExtraction($extraction) |
97
|
|
|
); |
98
|
11 |
|
} elseif ($operator == 'like') { |
99
|
1 |
|
$filter = new LikeFilter( |
100
|
1 |
|
$filterOrDimensionOrClosure, $value, '\\', $this->getExtraction($extraction) |
101
|
|
|
); |
102
|
10 |
|
} elseif ($operator == 'not like') { |
103
|
1 |
|
$filter = new NotFilter( |
104
|
1 |
|
new LikeFilter($filterOrDimensionOrClosure, $value, '\\', $this->getExtraction($extraction)) |
105
|
|
|
); |
106
|
9 |
|
} elseif ($operator == 'javascript') { |
107
|
1 |
|
$filter = new JavascriptFilter($filterOrDimensionOrClosure, $value, $this->getExtraction($extraction)); |
108
|
8 |
|
} elseif ($operator == 'not javascript') { |
109
|
1 |
|
$filter = new NotFilter( |
110
|
1 |
|
new JavascriptFilter($filterOrDimensionOrClosure, $value, $this->getExtraction($extraction)) |
111
|
|
|
); |
112
|
7 |
|
} elseif ($operator == 'regex' || $operator == 'regexp') { |
113
|
2 |
|
$filter = new RegexFilter($filterOrDimensionOrClosure, $value, $this->getExtraction($extraction)); |
114
|
5 |
|
} elseif ($operator == 'not regex' || $operator == 'not regexp') { |
115
|
2 |
|
$filter = new NotFilter( |
116
|
2 |
|
new RegexFilter($filterOrDimensionOrClosure, $value, $this->getExtraction($extraction)) |
117
|
|
|
); |
118
|
3 |
|
} elseif ($operator == 'search') { |
119
|
1 |
|
$filter = new SearchFilter( |
120
|
1 |
|
$filterOrDimensionOrClosure, $value, false, $this->getExtraction($extraction) |
121
|
|
|
); |
122
|
2 |
|
} elseif ($operator == 'not search') { |
123
|
1 |
|
$filter = new NotFilter(new SearchFilter( |
124
|
1 |
|
$filterOrDimensionOrClosure, $value, false, $this->getExtraction($extraction) |
125
|
|
|
)); |
126
|
|
|
} else { |
127
|
32 |
|
$filter = null; |
128
|
|
|
} |
129
|
17 |
|
} elseif ($filterOrDimensionOrClosure instanceof FilterInterface) { |
130
|
12 |
|
$filter = $filterOrDimensionOrClosure; |
131
|
6 |
|
} elseif ($filterOrDimensionOrClosure instanceof Closure) { |
132
|
|
|
|
133
|
|
|
// lets create a bew builder object where the user can mess around with |
134
|
2 |
|
$builder = new FilterBuilder(); |
135
|
|
|
|
136
|
|
|
// call the user function |
137
|
2 |
|
call_user_func($filterOrDimensionOrClosure, $builder); |
138
|
|
|
|
139
|
|
|
// Now retrieve the filter which was created and add it to our current filter set. |
140
|
2 |
|
$filter = $builder->getFilter(); |
141
|
|
|
} |
142
|
|
|
|
143
|
48 |
|
if ($filter === null) { |
144
|
5 |
|
throw new InvalidArgumentException('The arguments which you have supplied cannot be parsed.'); |
145
|
|
|
} |
146
|
|
|
|
147
|
43 |
|
strtolower($boolean) == 'and' ? $this->addAndFilter($filter) : $this->addOrFilter($filter); |
148
|
|
|
|
149
|
43 |
|
return $this; |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* Build a where selection which is inverted |
154
|
|
|
* |
155
|
|
|
* @param \Closure $filterBuilder A closure which will receive a FilterBuilder instance. |
156
|
|
|
* @param string $boolean This influences how this filter will be joined with previous added filters. |
157
|
|
|
* Should both filters apply ("and") or one or the other ("or") ? Default is "and". |
158
|
|
|
* |
159
|
|
|
* @return $this |
160
|
|
|
*/ |
161
|
1 |
|
public function whereNot(Closure $filterBuilder, $boolean = 'and') |
162
|
|
|
{ |
163
|
|
|
// lets create a bew builder object where the user can mess around with |
164
|
1 |
|
$builder = new FilterBuilder(); |
165
|
|
|
|
166
|
|
|
// call the user function |
167
|
1 |
|
call_user_func($filterBuilder, $builder); |
168
|
|
|
|
169
|
|
|
// Now retrieve the filter which was created and add it to our current filter set. |
170
|
1 |
|
$filter = $builder->getFilter(); |
171
|
1 |
|
if ($filter) { |
172
|
1 |
|
return $this->where(new NotFilter($filter), null, null, null, $boolean); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
// Whe no filter was given, just return. |
176
|
|
|
return $this; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* Build a where selection which is inverted |
181
|
|
|
* |
182
|
|
|
* @param \Closure $filterBuilder A closure which will receive a FilterBuilder instance. |
183
|
|
|
* |
184
|
|
|
* @return $this |
185
|
|
|
*/ |
186
|
1 |
|
public function orWhereNot(Closure $filterBuilder) |
187
|
|
|
{ |
188
|
1 |
|
return $this->whereNot($filterBuilder, 'or'); |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* This applies a filter, only it will join previous added filters with an "or" instead of an "and". |
193
|
|
|
* See the documentation of the "where" method for more information |
194
|
|
|
* |
195
|
|
|
* @param string|FilterInterface $filterOrDimension |
196
|
|
|
* @param string|null $operator |
197
|
|
|
* @param mixed|null $value |
198
|
|
|
* @param \Closure|null $extraction |
199
|
|
|
* |
200
|
|
|
* @return $this |
201
|
|
|
* @see \Level23\Druid\Concerns\HasFilter::where() |
202
|
|
|
*/ |
203
|
2 |
|
public function orWhere($filterOrDimension, $operator = null, $value = null, Closure $extraction = null) |
204
|
|
|
{ |
205
|
2 |
|
return $this->where($filterOrDimension, $operator, $value, $extraction, 'or'); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Filter records where the given dimension exists in the given list of items |
210
|
|
|
* |
211
|
|
|
* @param string $dimension The dimension which you want to filte |
212
|
|
|
* @param array $items A list of values. We will return records where the dimension is in this list. |
213
|
|
|
* @param \Closure|null $extraction An extraction function to extract a different value from the dimension. |
214
|
|
|
* @param string $boolean This influences how this filter will be joined with previous added filters. |
215
|
|
|
* Should both filters apply ("and") or one or the other ("or") ? Default is |
216
|
|
|
* "and". |
217
|
|
|
* |
218
|
|
|
* @return $this |
219
|
|
|
*/ |
220
|
3 |
|
public function whereIn(string $dimension, array $items, Closure $extraction = null, $boolean = 'and') |
221
|
|
|
{ |
222
|
3 |
|
$filter = new InFilter($dimension, $items, $this->getExtraction($extraction)); |
223
|
|
|
|
224
|
3 |
|
return $this->where($filter, null, null, null, $boolean); |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
/** |
228
|
|
|
* Filter records where the given dimension exists in the given list of items. |
229
|
|
|
* |
230
|
|
|
* If there are previously defined filters, this filter will be joined with an "or". |
231
|
|
|
* |
232
|
|
|
* @param string $dimension The dimension which you want to filte |
233
|
|
|
* @param array $items A list of values. We will return records where the dimension is in this list. |
234
|
|
|
* @param \Closure|null $extraction An extraction function to extract a different value from the dimension. |
235
|
|
|
* |
236
|
|
|
* @return $this |
237
|
|
|
*/ |
238
|
1 |
|
public function orWhereIn(string $dimension, array $items, Closure $extraction = null) |
239
|
|
|
{ |
240
|
1 |
|
return $this->whereIn($dimension, $items, $extraction, 'or'); |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
/** |
244
|
|
|
* Filter records where dimensionA is equal to dimensionB. |
245
|
|
|
* You can either supply a string or a Closure. The Closure will receive a DimensionBuilder object, which allows |
246
|
|
|
* you to select a dimension and apply extraction functions if needed. |
247
|
|
|
* |
248
|
|
|
* Example: |
249
|
|
|
* ```php |
250
|
|
|
* $builder->whereColumn('initials', function(DimensionBuilder $dimensionBuilder) { |
251
|
|
|
* $dimensionBuilder->select('first_name', function(ExtractionBuilder $extractionBuilder) { |
252
|
|
|
* $extractionBuilder->substring(0, 1); |
253
|
|
|
* }); |
254
|
|
|
* }); |
255
|
|
|
* ``` |
256
|
|
|
* |
257
|
|
|
* @param string|Closure $dimensionA The dimension which you want to compare, or a Closure which will receive a |
258
|
|
|
* DimensionBuilder which allows you to select a dimension in a more advance way. |
259
|
|
|
* @param string|Closure $dimensionB The dimension which you want to compare, or a Closure which will receive a |
260
|
|
|
* DimensionBuilder which allows you to select a dimension in a more advance way. |
261
|
|
|
* @param string $boolean This influences how this filter will be joined with previous added filters. |
262
|
|
|
* Should both filters apply ("and") or one or the other ("or") ? Default is |
263
|
|
|
* "and". |
264
|
|
|
* |
265
|
|
|
* @return $this |
266
|
|
|
*/ |
267
|
1 |
|
public function whereColumn($dimensionA, $dimensionB, string $boolean = 'and') |
268
|
|
|
{ |
269
|
1 |
|
$filter = new ColumnComparisonFilter( |
270
|
1 |
|
$this->columnCompareDimension($dimensionA), |
271
|
1 |
|
$this->columnCompareDimension($dimensionB) |
272
|
|
|
); |
273
|
|
|
|
274
|
1 |
|
return $this->where($filter, null, null, null, $boolean); |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
/** |
278
|
|
|
* Filter records where dimensionA is equal to dimensionB. |
279
|
|
|
* You can either supply a string or a Closure. The Closure will receive a DimensionBuilder object, which allows |
280
|
|
|
* you to select a dimension and apply extraction functions if needed. |
281
|
|
|
* |
282
|
|
|
* Example: |
283
|
|
|
* ```php |
284
|
|
|
* $builder->orWhereColumn('initials', function(DimensionBuilder $dimensionBuilder) { |
285
|
|
|
* $dimensionBuilder->select('first_name', function(ExtractionBuilder $extractionBuilder) { |
286
|
|
|
* $extractionBuilder->substring(0, 1); |
287
|
|
|
* }); |
288
|
|
|
* }); |
289
|
|
|
* ``` |
290
|
|
|
* |
291
|
|
|
* @param string|Closure $dimensionA The dimension which you want to compare, or a Closure which will receive a |
292
|
|
|
* DimensionBuilder which allows you to select a dimension in a more advance way. |
293
|
|
|
* @param string|Closure $dimensionB The dimension which you want to compare, or a Closure which will receive a |
294
|
|
|
* DimensionBuilder which allows you to select a dimension in a more advance way. |
295
|
|
|
* |
296
|
|
|
* @return $this |
297
|
|
|
*/ |
298
|
1 |
|
public function orWhereColumn($dimensionA, $dimensionB) |
299
|
|
|
{ |
300
|
1 |
|
return $this->whereColumn($dimensionA, $dimensionB, 'or'); |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
|
304
|
|
|
|
305
|
|
|
/** |
306
|
|
|
* Filter records where dimensionA is NOT equal to dimensionB. |
307
|
|
|
* You can either supply a string or a Closure. The Closure will receive a DimensionBuilder object, which allows |
308
|
|
|
* you to select a dimension and apply extraction functions if needed. |
309
|
|
|
* |
310
|
|
|
* Example: |
311
|
|
|
* ```php |
312
|
|
|
* $builder->whereNotColumn('initials', function(DimensionBuilder $dimensionBuilder) { |
313
|
|
|
* $dimensionBuilder->select('first_name', function(ExtractionBuilder $extractionBuilder) { |
314
|
|
|
* $extractionBuilder->substring(0, 1); |
315
|
|
|
* }); |
316
|
|
|
* }); |
317
|
|
|
* ``` |
318
|
|
|
* |
319
|
|
|
* @param string|Closure $dimensionA The dimension which you want to compare, or a Closure which will receive a |
320
|
|
|
* DimensionBuilder which allows you to select a dimension in a more advance way. |
321
|
|
|
* @param string|Closure $dimensionB The dimension which you want to compare, or a Closure which will receive a |
322
|
|
|
* DimensionBuilder which allows you to select a dimension in a more advance way. |
323
|
|
|
* @param string $boolean This influences how this filter will be joined with previous added filters. |
324
|
|
|
* Should both filters apply ("and") or one or the other ("or") ? Default is |
325
|
|
|
* "and". |
326
|
|
|
* |
327
|
|
|
* @return $this |
328
|
|
|
* @deprecated Use the whereNot() method combined with a whereColumn instead. |
329
|
|
|
*/ |
330
|
1 |
|
public function whereNotColumn($dimensionA, $dimensionB, string $boolean = 'and') |
331
|
|
|
{ |
332
|
1 |
|
$filter = new ColumnComparisonFilter( |
333
|
1 |
|
$this->columnCompareDimension($dimensionA), |
334
|
1 |
|
$this->columnCompareDimension($dimensionB) |
335
|
|
|
); |
336
|
|
|
|
337
|
1 |
|
return $this->where(new NotFilter($filter), null, null, null, $boolean); |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* Filter records where dimensionA is NOT equal to dimensionB. |
342
|
|
|
* You can either supply a string or a Closure. The Closure will receive a DimensionBuilder object, which allows |
343
|
|
|
* you to select a dimension and apply extraction functions if needed. |
344
|
|
|
* |
345
|
|
|
* Example: |
346
|
|
|
* ```php |
347
|
|
|
* $builder->orWhereNotColumn('initials', function(DimensionBuilder $dimensionBuilder) { |
348
|
|
|
* $dimensionBuilder->select('first_name', function(ExtractionBuilder $extractionBuilder) { |
349
|
|
|
* $extractionBuilder->substring(0, 1); |
350
|
|
|
* }); |
351
|
|
|
* }); |
352
|
|
|
* ``` |
353
|
|
|
* |
354
|
|
|
* @param string|Closure $dimensionA The dimension which you want to compare, or a Closure which will receive a |
355
|
|
|
* DimensionBuilder which allows you to select a dimension in a more advance way. |
356
|
|
|
* @param string|Closure $dimensionB The dimension which you want to compare, or a Closure which will receive a |
357
|
|
|
* DimensionBuilder which allows you to select a dimension in a more advance way. |
358
|
|
|
* |
359
|
|
|
* @return $this |
360
|
|
|
*/ |
361
|
1 |
|
public function orWhereNotColumn($dimensionA, $dimensionB) |
362
|
|
|
{ |
363
|
1 |
|
return $this->whereNotColumn($dimensionA, $dimensionB, 'or'); |
|
|
|
|
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
/** |
367
|
|
|
* This filter will select records where the given dimension is greater than or equal to the given minValue, and |
368
|
|
|
* less than or equal to the given $maxValue. |
369
|
|
|
* |
370
|
|
|
* So in SQL syntax, this would be: |
371
|
|
|
* ``` |
372
|
|
|
* WHERE dimension => $minValue AND dimension <= $maxValue |
373
|
|
|
* ``` |
374
|
|
|
* |
375
|
|
|
* @param string $dimension The dimension which you want to filter |
376
|
|
|
* @param string|int $minValue The minimum value where the dimension should match. It should be equal or |
377
|
|
|
* greater than this value. |
378
|
|
|
* @param string|int $maxValue The maximum value where the dimension should match. It should be less than |
379
|
|
|
* this value. |
380
|
|
|
* @param \Closure|null $extraction Extraction function to extract a different value from the dimension. |
381
|
|
|
* @param null|string $ordering Specifies the sorting order to use when comparing values against the |
382
|
|
|
* between filter. Can be one of the following values: "lexicographic", |
383
|
|
|
* "alphanumeric", "numeric", "strlen", "version". See Sorting Orders for |
384
|
|
|
* more details. By default it will be "numeric" if the values are |
385
|
|
|
* numeric, otherwise it will be "lexicographic" |
386
|
|
|
* @param string $boolean This influences how this filter will be joined with previous added filters. |
387
|
|
|
* Should both filters apply ("and") or one or the other ("or") ? Default is |
388
|
|
|
* "and". |
389
|
|
|
* |
390
|
|
|
* @return $this |
391
|
|
|
*/ |
392
|
1 |
|
public function whereBetween( |
393
|
|
|
string $dimension, |
394
|
|
|
$minValue, |
395
|
|
|
$maxValue, |
396
|
|
|
Closure $extraction = null, |
397
|
|
|
string $ordering = null, |
398
|
|
|
string $boolean = 'and' |
399
|
|
|
) { |
400
|
1 |
|
$filter = new BetweenFilter($dimension, $minValue, $maxValue, $ordering, $this->getExtraction($extraction)); |
401
|
|
|
|
402
|
1 |
|
return $this->where($filter, null, null, null, $boolean); |
403
|
|
|
} |
404
|
|
|
|
405
|
|
|
/** |
406
|
|
|
* This filter will select records where the given dimension is greater than or equal to the given minValue, and |
407
|
|
|
* less than or equal to the given $maxValue. |
408
|
|
|
* |
409
|
|
|
* This method will join previous added filters with an "or" instead of an "and". |
410
|
|
|
* |
411
|
|
|
* So in SQL syntax, this would be: |
412
|
|
|
* ``` |
413
|
|
|
* WHERE (dimension => $minValue AND dimension <= $maxValue) or .... (other filters here) |
414
|
|
|
* ``` |
415
|
|
|
* |
416
|
|
|
* @param string $dimension The dimension which you want to filter |
417
|
|
|
* @param string|int $minValue The minimum value where the dimension should match. It should be equal or |
418
|
|
|
* greater than this value. |
419
|
|
|
* @param string|int $maxValue The maximum value where the dimension should match. It should be less than |
420
|
|
|
* this value. |
421
|
|
|
* @param \Closure|null $extraction Extraction function to extract a different value from the dimension. |
422
|
|
|
* @param null|string $ordering Specifies the sorting order to use when comparing values against the |
423
|
|
|
* between filter. Can be one of the following values: "lexicographic", |
424
|
|
|
* "alphanumeric", "numeric", "strlen", "version". See Sorting Orders for |
425
|
|
|
* more details. By default it will be "numeric" if the values are |
426
|
|
|
* numeric, otherwise it will be "lexicographic" |
427
|
|
|
* |
428
|
|
|
* @return $this |
429
|
|
|
*/ |
430
|
1 |
|
public function orWhereBetween( |
431
|
|
|
string $dimension, |
432
|
|
|
$minValue, |
433
|
|
|
$maxValue, |
434
|
|
|
Closure $extraction = null, |
435
|
|
|
string $ordering = null |
436
|
|
|
) { |
437
|
1 |
|
return $this->whereBetween($dimension, $minValue, $maxValue, $extraction, $ordering, 'or'); |
438
|
|
|
} |
439
|
|
|
|
440
|
|
|
/** |
441
|
|
|
* This filter will select records where the given dimension is NOT between the given min and max value. |
442
|
|
|
* |
443
|
|
|
* So in SQL syntax, this would be: |
444
|
|
|
* ``` |
445
|
|
|
* WHERE dimension < $minValue AND dimension > $maxValue |
446
|
|
|
* ``` |
447
|
|
|
* |
448
|
|
|
* @param string $dimension The dimension which you want to filter |
449
|
|
|
* @param string|int $minValue The minimum value where the dimension should NOT match. It should be equal or |
450
|
|
|
* greater than this value. |
451
|
|
|
* @param string|int $maxValue The maximum value where the dimension should NOT match. It should be less than |
452
|
|
|
* this value. |
453
|
|
|
* @param \Closure|null $extraction Extraction function to extract a different value from the dimension. |
454
|
|
|
* @param null|string $ordering Specifies the sorting order to use when comparing values against the |
455
|
|
|
* between filter. Can be one of the following values: "lexicographic", |
456
|
|
|
* "alphanumeric", "numeric", "strlen", "version". See Sorting Orders for |
457
|
|
|
* more details. By default it will be "numeric" if the values are |
458
|
|
|
* numeric, otherwise it will be "lexicographic" |
459
|
|
|
* @param string $boolean This influences how this filter will be joined with previous added filters. |
460
|
|
|
* Should both filters apply ("and") or one or the other ("or") ? Default is |
461
|
|
|
* "and". |
462
|
|
|
* |
463
|
|
|
* @return $this |
464
|
|
|
* @deprecated Use the whereNot() method combined with a whereBetween instead. |
465
|
|
|
*/ |
466
|
1 |
|
public function whereNotBetween( |
467
|
|
|
string $dimension, |
468
|
|
|
$minValue, |
469
|
|
|
$maxValue, |
470
|
|
|
Closure $extraction = null, |
471
|
|
|
string $ordering = null, |
472
|
|
|
string $boolean = 'and' |
473
|
|
|
) { |
474
|
1 |
|
$filter = new BetweenFilter($dimension, $minValue, $maxValue, $ordering, $this->getExtraction($extraction)); |
475
|
|
|
|
476
|
1 |
|
return $this->where(new NotFilter($filter), null, null, null, $boolean); |
477
|
|
|
} |
478
|
|
|
|
479
|
|
|
/** |
480
|
|
|
* This filter will select records where the given dimension is NOT between the given min and max value. |
481
|
|
|
* |
482
|
|
|
* So in SQL syntax, this would be: |
483
|
|
|
* ``` |
484
|
|
|
* WHERE (dimension < $minValue AND dimension > $maxValue) or .... (other filters here) |
485
|
|
|
* ``` |
486
|
|
|
* |
487
|
|
|
* @param string $dimension The dimension which you want to filter |
488
|
|
|
* @param string|int $minValue The minimum value where the dimension should NOT match. It should be equal or |
489
|
|
|
* greater than this value. |
490
|
|
|
* @param string|int $maxValue The maximum value where the dimension should NOT match. It should be less than |
491
|
|
|
* this value. |
492
|
|
|
* @param \Closure|null $extraction Extraction function to extract a different value from the dimension. |
493
|
|
|
* @param null|string $ordering Specifies the sorting order to use when comparing values against the |
494
|
|
|
* between filter. Can be one of the following values: "lexicographic", |
495
|
|
|
* "alphanumeric", "numeric", "strlen", "version". See Sorting Orders for |
496
|
|
|
* more details. By default it will be "numeric" if the values are |
497
|
|
|
* numeric, otherwise it will be "lexicographic" |
498
|
|
|
* |
499
|
|
|
* @return $this |
500
|
|
|
* @deprecated Use the whereNot() method combined with a whereBetween instead. |
501
|
|
|
*/ |
502
|
1 |
|
public function orWhereNotBetween( |
503
|
|
|
string $dimension, |
504
|
|
|
$minValue, |
505
|
|
|
$maxValue, |
506
|
|
|
Closure $extraction = null, |
507
|
|
|
string $ordering = null |
508
|
|
|
) { |
509
|
1 |
|
return $this->whereNotBetween($dimension, $minValue, $maxValue, $extraction, $ordering, 'or'); |
|
|
|
|
510
|
|
|
} |
511
|
|
|
|
512
|
|
|
/** |
513
|
|
|
* Filter records where the given dimension NOT exists in the given list of items |
514
|
|
|
* |
515
|
|
|
* @param string $dimension The dimension which you want to filter |
516
|
|
|
* @param array $items A list of values. We will return records where the dimension is NOT in this |
517
|
|
|
* list. |
518
|
|
|
* @param \Closure|null $extraction An extraction function to extract a different value from the dimension. |
519
|
|
|
* @param string $boolean This influences how this filter will be joined with previous added filters. |
520
|
|
|
* Should both filters apply ("and") or one or the other ("or") ? Default is |
521
|
|
|
* "and". |
522
|
|
|
* |
523
|
|
|
* @return $this |
524
|
|
|
* @deprecated Use the whereNot() method combined with a whereIn instead. |
525
|
|
|
*/ |
526
|
1 |
|
public function whereNotIn(string $dimension, array $items, Closure $extraction = null, string $boolean = 'and') |
527
|
|
|
{ |
528
|
1 |
|
$filter = new NotFilter(new InFilter($dimension, $items, $this->getExtraction($extraction))); |
529
|
|
|
|
530
|
1 |
|
return $this->where($filter, null, null, null, $boolean); |
531
|
|
|
} |
532
|
|
|
|
533
|
|
|
/** |
534
|
|
|
* Filter records where the given dimension NOT exists in the given list of items |
535
|
|
|
* |
536
|
|
|
* @param string $dimension The dimension which you want to filter |
537
|
|
|
* @param array $items A list of values. We will return records where the dimension is NOT in this |
538
|
|
|
* list. |
539
|
|
|
* @param \Closure|null $extraction An extraction function to extract a different value from the dimension. |
540
|
|
|
* |
541
|
|
|
* @return $this |
542
|
|
|
* @deprecated Use the orWhereNot() method combined with a whereIn instead. |
543
|
|
|
*/ |
544
|
1 |
|
public function orWhereNotIn(string $dimension, array $items, Closure $extraction = null) |
545
|
|
|
{ |
546
|
1 |
|
return $this->whereNotIn($dimension, $items, $extraction, 'or'); |
|
|
|
|
547
|
|
|
} |
548
|
|
|
|
549
|
|
|
/** |
550
|
|
|
* Filter on an dimension where the value does NOT exists in the given intervals array. |
551
|
|
|
* |
552
|
|
|
* The intervals array can contain the following: |
553
|
|
|
* - Only 2 elements, start and stop. |
554
|
|
|
* - an Interval object |
555
|
|
|
* - an raw interval string as used in druid. For example: 2019-04-15T08:00:00.000Z/2019-04-15T09:00:00.000Z |
556
|
|
|
* - an array which each contain 2 elements, a start and stop date. These can be an DateTime object, a unix |
557
|
|
|
* timestamp or anything which can be parsed by DateTime::__construct |
558
|
|
|
* |
559
|
|
|
* So valid are: |
560
|
|
|
* ['now', 'tomorrow'] |
561
|
|
|
* [['now', 'now + 1 hour'], ['tomorrow', 'tomorrow + 1 hour']] |
562
|
|
|
* ['2019-04-15T08:00:00.000Z/2019-04-15T09:00:00.000Z'] |
563
|
|
|
* |
564
|
|
|
* @param string $dimension The dimension which you want to filter |
565
|
|
|
* @param array $intervals The interval which you do not want to match. See above for more info. |
566
|
|
|
* @param \Closure|null $extraction Extraction function to extract a different value from the dimension. |
567
|
|
|
* @param string $boolean This influences how this filter will be joined with previous added filters. |
568
|
|
|
* Should both filters apply ("and") or one or the other ("or") ? Default is |
569
|
|
|
* "and". |
570
|
|
|
* |
571
|
|
|
* @return $this |
572
|
|
|
* @deprecated Use the whereNot() method combined with a whereInterval instead. |
573
|
|
|
*/ |
574
|
1 |
|
public function whereNotInterval( |
575
|
|
|
string $dimension, |
576
|
|
|
array $intervals, |
577
|
|
|
Closure $extraction = null, |
578
|
|
|
string $boolean = 'and' |
579
|
|
|
) { |
580
|
1 |
|
$filter = new IntervalFilter( |
581
|
1 |
|
$dimension, |
582
|
1 |
|
$this->normalizeIntervals($intervals), |
583
|
1 |
|
$this->getExtraction($extraction) |
584
|
|
|
); |
585
|
|
|
|
586
|
1 |
|
return $this->where(new NotFilter($filter), null, null, null, $boolean); |
587
|
|
|
} |
588
|
|
|
|
589
|
|
|
/** |
590
|
|
|
* Filter on an dimension where the value does NOT exists in the given intervals array. |
591
|
|
|
* |
592
|
|
|
* The intervals array can contain the following: |
593
|
|
|
* - Only 2 elements, start and stop. |
594
|
|
|
* - an Interval object |
595
|
|
|
* - an raw interval string as used in druid. For example: 2019-04-15T08:00:00.000Z/2019-04-15T09:00:00.000Z |
596
|
|
|
* - an array which each contain 2 elements, a start and stop date. These can be an DateTime object, a unix |
597
|
|
|
* timestamp or anything which can be parsed by DateTime::__construct |
598
|
|
|
* |
599
|
|
|
* So valid are: |
600
|
|
|
* ['now', 'tomorrow'] |
601
|
|
|
* [['now', 'now + 1 hour'], ['tomorrow', 'tomorrow + 1 hour']] |
602
|
|
|
* ['2019-04-15T08:00:00.000Z/2019-04-15T09:00:00.000Z'] |
603
|
|
|
* |
604
|
|
|
* @param string $dimension The dimension which you want to filter |
605
|
|
|
* @param array $intervals The interval which you do not want to match. See above for more info. |
606
|
|
|
* @param \Closure|null $extraction Extraction function to extract a different value from the dimension. |
607
|
|
|
* |
608
|
|
|
* @return $this |
609
|
|
|
* @deprecated Use the orWhereNot() method combined with a whereInterval instead. |
610
|
|
|
*/ |
611
|
1 |
|
public function orWhereNotInterval(string $dimension, array $intervals, Closure $extraction = null) |
612
|
|
|
{ |
613
|
1 |
|
return $this->whereNotInterval($dimension, $intervals, $extraction, 'or'); |
|
|
|
|
614
|
|
|
} |
615
|
|
|
|
616
|
|
|
/** |
617
|
|
|
* Filter on an dimension where the value exists in the given intervals array. |
618
|
|
|
* |
619
|
|
|
* The intervals array can contain the following: |
620
|
|
|
* - an Interval object |
621
|
|
|
* - an raw interval string as used in druid. For example: 2019-04-15T08:00:00.000Z/2019-04-15T09:00:00.000Z |
622
|
|
|
* - an array which contains 2 elements, a start and stop date. These can be an DateTime object, a unix timestamp |
623
|
|
|
* or anything which can be parsed by DateTime::__construct |
624
|
|
|
* |
625
|
|
|
* @param string $dimension The dimension which you want to filter |
626
|
|
|
* @param array $intervals The interval which you want to match. See above for more info. |
627
|
|
|
* @param \Closure|null $extraction Extraction function to extract a different value from the dimension. |
628
|
|
|
* @param string $boolean This influences how this filter will be joined with previous added filters. |
629
|
|
|
* Should both filters apply ("and") or one or the other ("or") ? Default is |
630
|
|
|
* "and". |
631
|
|
|
* |
632
|
|
|
* @return $this |
633
|
|
|
*/ |
634
|
1 |
|
public function whereInterval( |
635
|
|
|
string $dimension, |
636
|
|
|
array $intervals, |
637
|
|
|
Closure $extraction = null, |
638
|
|
|
string $boolean = 'and' |
639
|
|
|
) { |
640
|
1 |
|
$filter = new IntervalFilter( |
641
|
1 |
|
$dimension, |
642
|
1 |
|
$this->normalizeIntervals($intervals), |
643
|
1 |
|
$this->getExtraction($extraction) |
644
|
|
|
); |
645
|
|
|
|
646
|
1 |
|
return $this->where($filter, null, null, null, $boolean); |
647
|
|
|
} |
648
|
|
|
|
649
|
|
|
/** |
650
|
|
|
* Filter on an dimension where the value exists in the given intervals array. |
651
|
|
|
* |
652
|
|
|
* The intervals array can contain the following: |
653
|
|
|
* - an Interval object |
654
|
|
|
* - an raw interval string as used in druid. For example: 2019-04-15T08:00:00.000Z/2019-04-15T09:00:00.000Z |
655
|
|
|
* - an array which contains 2 elements, a start and stop date. These can be an DateTime object, a unix timestamp |
656
|
|
|
* or anything which can be parsed by DateTime::__construct |
657
|
|
|
* |
658
|
|
|
* @param string $dimension The dimension which you want to filter |
659
|
|
|
* @param array $intervals The interval which you want to match. See above for more info. |
660
|
|
|
* @param \Closure|null $extraction Extraction function to extract a different value from the dimension. |
661
|
|
|
* |
662
|
|
|
* @return $this |
663
|
|
|
*/ |
664
|
1 |
|
public function orWhereInterval(string $dimension, array $intervals, Closure $extraction = null) |
665
|
|
|
{ |
666
|
1 |
|
return $this->whereInterval($dimension, $intervals, $extraction, 'or'); |
667
|
|
|
} |
668
|
|
|
|
669
|
|
|
/** |
670
|
|
|
* Normalize the given dimension to a DimensionInterface object. |
671
|
|
|
* |
672
|
|
|
* @param string|Closure $dimension |
673
|
|
|
* |
674
|
|
|
* @return \Level23\Druid\Dimensions\DimensionInterface |
675
|
|
|
* @throws InvalidArgumentException |
676
|
|
|
*/ |
677
|
5 |
|
protected function columnCompareDimension($dimension): DimensionInterface |
678
|
|
|
{ |
679
|
5 |
|
if ($dimension instanceof Closure) { |
680
|
2 |
|
$builder = new DimensionBuilder(); |
681
|
2 |
|
call_user_func($dimension, $builder); |
682
|
2 |
|
$dimensions = $builder->getDimensions(); |
683
|
|
|
|
684
|
2 |
|
if (count($dimensions) != 1) { |
685
|
1 |
|
throw new InvalidArgumentException('Your dimension builder should select 1 dimension'); |
686
|
|
|
} |
687
|
|
|
|
688
|
1 |
|
return $dimensions[0]; |
689
|
|
|
} |
690
|
|
|
|
691
|
3 |
|
return new Dimension($dimension); |
692
|
|
|
} |
693
|
|
|
|
694
|
|
|
/** |
695
|
|
|
* Normalize the given intervals into Interval objects. |
696
|
|
|
* |
697
|
|
|
* @param array $intervals |
698
|
|
|
* |
699
|
|
|
* @return array |
700
|
|
|
*/ |
701
|
11 |
|
protected function normalizeIntervals(array $intervals): array |
702
|
|
|
{ |
703
|
11 |
|
$first = reset($intervals); |
704
|
|
|
|
705
|
|
|
// If first is an array or already a druid interval string or object we do not wrap it in an array |
706
|
11 |
|
if (!is_array($first) && !$this->isDruidInterval($first)) { |
707
|
8 |
|
$intervals = [$intervals]; |
708
|
|
|
} |
709
|
|
|
|
710
|
|
|
return array_map(function ($interval) { |
711
|
|
|
|
712
|
11 |
|
if ($interval instanceof IntervalInterface) { |
713
|
1 |
|
return $interval; |
714
|
|
|
} |
715
|
|
|
|
716
|
|
|
// If it is a string we explode it into to elements |
717
|
10 |
|
if (is_string($interval)) { |
718
|
1 |
|
$interval = explode('/', $interval); |
719
|
|
|
} |
720
|
|
|
|
721
|
|
|
// If the value is an array and is not empty and has either one or 2 values its an interval array |
722
|
10 |
|
if (is_array($interval) && !empty(array_filter($interval)) && count($interval) < 3) { |
723
|
|
|
/** @scrutinizer ignore-type */ |
724
|
7 |
|
return new Interval(...$interval); |
725
|
|
|
} |
726
|
|
|
|
727
|
3 |
|
throw new InvalidArgumentException( |
728
|
|
|
'Invalid type given in the interval array. We cannot process ' . |
729
|
3 |
|
var_export($interval, true) |
730
|
|
|
); |
731
|
11 |
|
}, $intervals); |
732
|
|
|
} |
733
|
|
|
|
734
|
|
|
/** |
735
|
|
|
* Returns true if the argument provided is a druid interval string or interface |
736
|
|
|
* |
737
|
|
|
* @param string|IntervalInterface $interval |
738
|
|
|
* |
739
|
|
|
* @return bool |
740
|
|
|
*/ |
741
|
10 |
|
protected function isDruidInterval($interval) |
742
|
|
|
{ |
743
|
10 |
|
if ($interval instanceof IntervalInterface) { |
744
|
1 |
|
return true; |
745
|
|
|
} |
746
|
|
|
|
747
|
9 |
|
return is_string($interval) && strpos($interval, '/') !== false; |
748
|
|
|
} |
749
|
|
|
|
750
|
|
|
/** |
751
|
|
|
* Helper method to add an OR filter |
752
|
|
|
* |
753
|
|
|
* @param FilterInterface $filter |
754
|
|
|
*/ |
755
|
5 |
|
protected function addOrFilter(FilterInterface $filter): void |
756
|
|
|
{ |
757
|
5 |
|
if (!$this->filter instanceof FilterInterface) { |
758
|
5 |
|
$this->filter = $filter; |
759
|
|
|
|
760
|
5 |
|
return; |
761
|
|
|
} |
762
|
|
|
|
763
|
5 |
|
if ($this->filter instanceof OrFilter) { |
764
|
1 |
|
$this->filter->addFilter($filter); |
765
|
|
|
|
766
|
1 |
|
return; |
767
|
|
|
} |
768
|
|
|
|
769
|
5 |
|
$this->filter = new OrFilter([$this->filter, $filter]); |
770
|
5 |
|
} |
771
|
|
|
|
772
|
|
|
/** |
773
|
|
|
* Helper method to add an AND filter |
774
|
|
|
* |
775
|
|
|
* @param FilterInterface $filter |
776
|
|
|
*/ |
777
|
39 |
|
protected function addAndFilter(FilterInterface $filter): void |
778
|
|
|
{ |
779
|
39 |
|
if (!$this->filter instanceof FilterInterface) { |
780
|
39 |
|
$this->filter = $filter; |
781
|
|
|
|
782
|
39 |
|
return; |
783
|
|
|
} |
784
|
|
|
|
785
|
21 |
|
if ($this->filter instanceof AndFilter) { |
786
|
2 |
|
$this->filter->addFilter($filter); |
787
|
|
|
|
788
|
2 |
|
return; |
789
|
|
|
} |
790
|
|
|
|
791
|
21 |
|
$this->filter = new AndFilter([$this->filter, $filter]); |
792
|
21 |
|
} |
793
|
|
|
|
794
|
|
|
/** |
795
|
|
|
* @return \Level23\Druid\Filters\FilterInterface|null |
796
|
|
|
*/ |
797
|
30 |
|
public function getFilter(): ?FilterInterface |
798
|
|
|
{ |
799
|
30 |
|
return $this->filter; |
800
|
|
|
} |
801
|
|
|
|
802
|
|
|
/** |
803
|
|
|
* @param \Closure|null $extraction |
804
|
|
|
* |
805
|
|
|
* @return \Level23\Druid\Extractions\ExtractionInterface|null |
806
|
|
|
*/ |
807
|
39 |
|
private function getExtraction(?Closure $extraction): ?ExtractionInterface |
808
|
|
|
{ |
809
|
39 |
|
if (empty($extraction)) { |
810
|
38 |
|
return null; |
811
|
|
|
} |
812
|
|
|
|
813
|
1 |
|
$builder = new ExtractionBuilder(); |
814
|
1 |
|
call_user_func($extraction, $builder); |
815
|
|
|
|
816
|
1 |
|
return $builder->getExtraction(); |
817
|
|
|
} |
818
|
|
|
} |
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.