Passed
Pull Request — master (#35)
by Teye
04:28
created

HasAggregations::doublesSketch()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 6
c 0
b 0
f 0
dl 0
loc 14
ccs 4
cts 4
cp 1
rs 10
cc 2
nc 1
nop 4
crap 2
1
<?php
2
declare(strict_types=1);
3
4
namespace Level23\Druid\Concerns;
5
6
use Closure;
7
use InvalidArgumentException;
8
use Level23\Druid\Types\DataType;
9
use Level23\Druid\Dimensions\Dimension;
10
use Level23\Druid\Filters\FilterBuilder;
11
use Level23\Druid\Filters\FilterInterface;
12
use Level23\Druid\Aggregations\MaxAggregator;
13
use Level23\Druid\Aggregations\MinAggregator;
14
use Level23\Druid\Aggregations\SumAggregator;
15
use Level23\Druid\Aggregations\AnyAggregator;
16
use Level23\Druid\Aggregations\LastAggregator;
17
use Level23\Druid\Dimensions\DimensionBuilder;
18
use Level23\Druid\Aggregations\CountAggregator;
19
use Level23\Druid\Aggregations\FirstAggregator;
20
use Level23\Druid\Aggregations\FilteredAggregator;
21
use Level23\Druid\Collections\DimensionCollection;
22
use Level23\Druid\Aggregations\AggregatorInterface;
23
use Level23\Druid\Aggregations\JavascriptAggregator;
24
use Level23\Druid\Aggregations\HyperUniqueAggregator;
25
use Level23\Druid\Aggregations\CardinalityAggregator;
26
use Level23\Druid\Aggregations\DoublesSketchAggregator;
27
use Level23\Druid\Aggregations\DistinctCountAggregator;
28
29
trait HasAggregations
30
{
31
    /**
32
     * @var \Level23\Druid\DruidClient
33
     */
34
    protected $client;
35
36
    /**
37
     * @var null|\Level23\Druid\Queries\QueryBuilder
38
     */
39
    protected $query;
40
41
    /**
42
     * @var array|\Level23\Druid\Aggregations\AggregatorInterface[]
43
     */
44
    protected $aggregations = [];
45
46
    /**
47
     * @return array|\Level23\Druid\Aggregations\AggregatorInterface[]
48
     */
49 1
    public function getAggregations(): array
50
    {
51 1
        return $this->aggregations;
52
    }
53
54
    /**
55
     * Sum the given metric
56
     *
57
     * @param string        $metric
58
     * @param string        $as
59
     * @param string        $type
60
     * @param \Closure|null $filterBuilder   A closure which receives a FilterBuilder. When given, we will only apply
61
     *                                       the "sum" function to the records which match with the given filter.
62
     *
63
     * @return $this
64
     */
65 15
    public function sum(string $metric, string $as = '', string $type = DataType::LONG, Closure $filterBuilder = null)
66
    {
67 15
        $this->aggregations[] = $this->buildFilteredAggregation(
68 15
            new SumAggregator($metric, $as, $type),
69
            $filterBuilder
70
        );
71
72 14
        return $this;
73
    }
74
75
    /**
76
     * DoublesSketch is a mergeable streaming algorithm to estimate the distribution of values, and approximately
77
     * answer queries about the rank of a value, probability mass function of the distribution (PMF) or histogram,
78
     * cumulative distribution function (CDF), and quantiles (median, min, max, 95th percentile and such). See
79
     * Quantiles Sketch Overview.
80
     *s
81
     * To make use of this aggregator you have to have the datasketches module enabled in your druid server:
82
     * druid.extensions.loadList=["druid-datasketches"]
83
     *
84
     *
85
     * @param string   $metric          A String for the name of the input field (can contain sketches or raw numeric
86
     *                                  values).
87
     * @param string   $as              A String for the output (result) name of the calculation.
88
     * @param int|null $sizeAndAccuracy Parameter that determines the accuracy and size of the sketch. Higher k means
89
     *                                  higher accuracy but more space to store sketches. Must be a power of 2 from 2
90
     *                                  to 32768. See accuracy information in the DataSketches documentation for
91
     *                                  details.
92
     * @param int|null $maxStreamLength This parameter is a temporary solution to avoid a known issue. It may be
93
     *                                  removed in a future release after the bug is fixed. This parameter defines the
94
     *                                  maximum number of items to store in each sketch. If a sketch reaches the limit,
95
     *                                  the query can throw IllegalStateException. To workaround this issue, increase
96
     *                                  the maximum stream length. See accuracy information in the DataSketches
97
     *                                  documentation for how many bytes are required per stream length.
98
     *
99
     * @return $this
100
     */
101 5
    public function doublesSketch(
102
        string $metric,
103
        string $as = '',
104
        ?int $sizeAndAccuracy = null,
105
        ?int $maxStreamLength = null
106
    ): self {
107 5
        $this->aggregations[] = new DoublesSketchAggregator(
108
            $metric,
109 5
            $as ?: $metric,
110
            $sizeAndAccuracy,
111
            $maxStreamLength
112
        );
113
114 5
        return $this;
115
    }
116
117
    /**
118
     * Shorthand for summing long's
119
     *
120
     * @param string        $metric
121
     * @param string        $as
122
     * @param \Closure|null $filterBuilder   A closure which receives a FilterBuilder. When given, we will only apply
123
     *                                       the "sum" function to the records which match with the given filter.
124
     *
125
     * @return $this
126
     */
127 7
    public function longSum(string $metric, string $as = '', Closure $filterBuilder = null)
128
    {
129 7
        return $this->sum($metric, $as, DataType::LONG, $filterBuilder);
130
    }
131
132
    /**
133
     * Shorthand for summing doubles
134
     *
135
     * @param string        $metric
136
     * @param string        $as
137
     * @param \Closure|null $filterBuilder   A closure which receives a FilterBuilder. When given, we will only apply
138
     *                                       the "sum" function to the records which match with the given filter.
139
     *
140
     * @return $this
141
     */
142 1
    public function doubleSum(string $metric, string $as = '', Closure $filterBuilder = null)
143
    {
144 1
        return $this->sum($metric, $as, DataType::DOUBLE, $filterBuilder);
145
    }
146
147
    /**
148
     * Shorthand for summing floats
149
     *
150
     * @param string        $metric
151
     * @param string        $as
152
     * @param \Closure|null $filterBuilder   A closure which receives a FilterBuilder. When given, we will only apply
153
     *                                       the "sum" function to the records which match with the given filter.
154
     *
155
     * @return $this
156
     */
157 1
    public function floatSum(string $metric, string $as = '', Closure $filterBuilder = null)
158
    {
159 1
        return $this->sum($metric, $as, DataType::FLOAT, $filterBuilder);
160
    }
161
162
    /**
163
     * Uses HyperLogLog to compute the estimated cardinality of a dimension that has been aggregated as a "hyperUnique"
164
     * metric at indexing time.
165
     *
166
     * @see http://algo.inria.fr/flajolet/Publications/FlFuGaMe07.pdf
167
     *
168
     * @param string $metric
169
     * @param string $as
170
     * @param bool   $round              Only affects query-time behavior, and is ignored at ingestion-time. The
171
     *                                   HyperLogLog algorithm generates decimal estimates with some error. "round" can
172
     *                                   be set to true to round off estimated values to whole numbers. Note that even
173
     *                                   with rounding, the cardinality is still an estimate.
174
     * @param bool   $isInputHyperUnique Only affects ingestion-time behavior, and is ignored at query-time. Set to
175
     *                                   true to index pre-computed HLL (Base64 encoded output from druid-hll is
176
     *                                   expected).
177
     *
178
     * @return $this
179
     */
180 5
    public function hyperUnique(string $metric, string $as, bool $round = false, bool $isInputHyperUnique = false)
181
    {
182 5
        $this->aggregations[] = new HyperUniqueAggregator(
183
            $as, $metric, $isInputHyperUnique, $round
184
        );
185
186 5
        return $this;
187
    }
188
189
    /**
190
     * Computes the cardinality of a set of Apache Druid (incubating) dimensions, using HyperLogLog to estimate the
191
     * cardinality. Please note that this aggregator will be much slower than indexing a column with the hyperUnique
192
     * aggregator. This aggregator also runs over a dimension column, which means the string dimension cannot be
193
     * removed from the dataset to improve rollup. In general, we strongly recommend using the hyperUnique aggregator
194
     * instead of the cardinality aggregator if you do not care about the individual values of a dimension.
195
     *
196
     * The HyperLogLog algorithm generates decimal estimates with some error. "round" can be set to true to round off
197
     * estimated values to whole numbers. Note that even with rounding, the cardinality is still an estimate. The
198
     * "round" field only affects query-time behavior, and is ignored at ingestion-time.
199
     *
200
     * When setting byRow to false (the default) it computes the cardinality of the set composed of the union of all
201
     * dimension values for all the given dimensions. For a single dimension, this is equivalent to:
202
     * ```
203
     * SELECT COUNT(DISTINCT(dimension)) FROM <datasource>
204
     * ```
205
     *
206
     * For multiple dimensions, this is equivalent to something akin to
207
     * ```
208
     * SELECT COUNT(DISTINCT(value)) FROM (
209
     * SELECT dim_1 as value FROM <datasource>
210
     * UNION
211
     * SELECT dim_2 as value FROM <datasource>
212
     * UNION
213
     * SELECT dim_3 as value FROM <datasource>
214
     * )
215
     * ```
216
     *
217
     * When setting byRow to true it computes the cardinality by row, i.e. the cardinality of distinct dimension
218
     * combinations. This is equivalent to something akin to
219
     *
220
     * ```
221
     * SELECT COUNT(*) FROM ( SELECT DIM1, DIM2, DIM3 FROM <datasource> GROUP BY DIM1, DIM2, DIM3 )
222
     * ```
223
     *
224
     * @see https://druid.apache.org/docs/latest/querying/hll-old.html
225
     *
226
     * @param string         $as                           The output name which is used for the result.
227
     * @param \Closure|array $dimensionsOrDimensionBuilder An array with the dimensions which you want to calculate the
228
     *                                                     cardinality over, or a closure which will receive a
229
     *                                                     DimensionBuilder. You should build the dimensions which are
230
     *                                                     used to calculate the cardinality over.
231
     * @param bool           $byRow                        For more details see method description.
232
     * @param bool           $round                        Only affects query-time behavior, and is ignored at
233
     *                                                     ingestion-time. The HyperLogLog algorithm generates decimal
234
     *                                                     estimates with some error. "round" can be set to true to
235
     *                                                     round off estimated values to whole numbers. Note that even
236
     *                                                     with rounding, the cardinality is still an estimate.
237
     *
238
     * @return $this
239
     */
240 7
    public function cardinality(string $as, $dimensionsOrDimensionBuilder, bool $byRow = false, bool $round = false)
241
    {
242 7
        if ($dimensionsOrDimensionBuilder instanceof Closure) {
243 5
            $builder = new DimensionBuilder();
244 5
            call_user_func($dimensionsOrDimensionBuilder, $builder);
245 5
            $dimensions = $builder->getDimensions();
246 2
        } elseif (is_array($dimensionsOrDimensionBuilder)) {
247 1
            $dimensions = [];
248
249 1
            foreach ($dimensionsOrDimensionBuilder as $dimension) {
250 1
                $dimensions[] = new Dimension($dimension);
251
            }
252
        } else {
253 1
            throw new InvalidArgumentException('You should supply a Closure function or an array.');
254
        }
255
256 6
        $this->aggregations[] = new CardinalityAggregator(
257
            $as,
258 6
            new DimensionCollection(...$dimensions),
259
            $byRow,
260
            $round
261
        );
262
263 6
        return $this;
264
    }
265
266
    /**
267
     * When a closure is given, we will call the given function which is responsible for building a filter.
268
     * We will then only apply the given aggregator for the records where the filter matches.
269
     *
270
     * @param \Level23\Druid\Aggregations\AggregatorInterface $aggregator
271
     * @param \Closure|null                                   $filterBuilder
272
     *
273
     * @return \Level23\Druid\Aggregations\AggregatorInterface
274
     */
275 16
    protected function buildFilteredAggregation(
276
        AggregatorInterface $aggregator,
277
        Closure $filterBuilder = null
278
    ): AggregatorInterface {
279 16
        if (!$filterBuilder) {
280 13
            return $aggregator;
281
        }
282
283 3
        $builder = new FilterBuilder($this->client, $this->query);
284 3
        call_user_func($filterBuilder, $builder);
285 2
        $filter = $builder->getFilter();
286
287 2
        if ($filter instanceof FilterInterface) {
288 1
            return new FilteredAggregator($filter, $aggregator);
289
        }
290
291 1
        return $aggregator;
292
    }
293
294
    /**
295
     * Count the number of results and put it in a dimension with the given name.
296
     *
297
     * @param string        $as
298
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only count the
299
     *                                     records which match with the given filter.
300
     *
301
     * @return $this
302
     */
303 1
    public function count(string $as, Closure $filterBuilder = null)
304
    {
305 1
        $this->aggregations[] = $this->buildFilteredAggregation(
306 1
            new CountAggregator($as),
307
            $filterBuilder
308
        );
309
310 1
        return $this;
311
    }
312
313
    /**
314
     * Count the number of distinct values of a specific dimension.
315
     * NOTE: The DataSketches Theta Sketch extension is required to run this aggregation.
316
     *
317
     * @param string        $dimension
318
     * @param string        $as
319
     * @param int           $size          Must be a power of 2. Internally, size refers to the maximum number of
320
     *                                     entries sketch object will retain. Higher size means higher accuracy but
321
     *                                     more space to store sketches. Note that after you index with a particular
322
     *                                     size, druid will persist sketch in segments and you will use size greater or
323
     *                                     equal to that at query time. See the DataSketches site for details. In
324
     *                                     general, We recommend just sticking to default size.
325
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only count the
326
     *                                     records which match with the given filter.
327
     *
328
     * @return $this
329
     */
330 2
    public function distinctCount(string $dimension, string $as = '', $size = 16384, Closure $filterBuilder = null)
331
    {
332 2
        $this->aggregations[] = $this->buildFilteredAggregation(
333 2
            new DistinctCountAggregator($dimension, ($as ?: $dimension), $size),
334
            $filterBuilder
335
        );
336
337 2
        return $this;
338
    }
339
340
    /**
341
     * Get the minimum value for the given metric
342
     *
343
     * @param string        $metric
344
     * @param string        $as
345
     * @param string        $type
346
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
347
     *                                     min function to the records which match with the given filter.
348
     *
349
     * @return $this
350
     */
351 2
    public function min(string $metric, string $as = '', $type = DataType::LONG, Closure $filterBuilder = null)
352
    {
353 2
        $this->aggregations[] = $this->buildFilteredAggregation(
354 2
            new MinAggregator($metric, $as, $type),
355
            $filterBuilder
356
        );
357
358 2
        return $this;
359
    }
360
361
    /**
362
     * Get the minimum value for the given metric using long as type
363
     *
364
     * @param string        $metric
365
     * @param string        $as
366
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
367
     *                                     min function to the records which match with the given filter.
368
     *
369
     * @return $this
370
     */
371 1
    public function longMin(string $metric, string $as = '', Closure $filterBuilder = null)
372
    {
373 1
        return $this->min($metric, $as, DataType::LONG, $filterBuilder);
374
    }
375
376
    /**
377
     * Get the minimum value for the given metric using double as type
378
     *
379
     * @param string        $metric
380
     * @param string        $as
381
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
382
     *                                     min function to the records which match with the given filter.
383
     *
384
     * @return $this
385
     */
386 1
    public function doubleMin(string $metric, string $as = '', Closure $filterBuilder = null)
387
    {
388 1
        return $this->min($metric, $as, DataType::DOUBLE, $filterBuilder);
389
    }
390
391
    /**
392
     * Get the minimum value for the given metric using float as type
393
     *
394
     * @param string        $metric
395
     * @param string        $as
396
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
397
     *                                     min function to the records which match with the given filter.
398
     *
399
     * @return $this
400
     */
401 1
    public function floatMin(string $metric, string $as = '', Closure $filterBuilder = null)
402
    {
403 1
        return $this->min($metric, $as, DataType::FLOAT, $filterBuilder);
404
    }
405
406
    /**
407
     * Get the maximum value for the given metric
408
     *
409
     * @param string        $metric
410
     * @param string        $as
411
     * @param string        $type
412
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
413
     *                                     max function to the records which match with the given filter.
414
     *
415
     * @return $this
416
     */
417 2
    public function max(string $metric, string $as = '', $type = DataType::LONG, Closure $filterBuilder = null)
418
    {
419 2
        $this->aggregations[] = $this->buildFilteredAggregation(
420 2
            new MaxAggregator($metric, $as, $type),
421
            $filterBuilder
422
        );
423
424 2
        return $this;
425
    }
426
427
    /**
428
     * Get the maximum value for the given metric using long as type
429
     *
430
     * @param string        $metric
431
     * @param string        $as
432
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
433
     *                                     max function to the records which match with the given filter.
434
     *
435
     * @return $this
436
     */
437 1
    public function longMax(string $metric, string $as = '', Closure $filterBuilder = null)
438
    {
439 1
        return $this->max($metric, $as, DataType::LONG, $filterBuilder);
440
    }
441
442
    /**
443
     * Get the maximum value for the given metric using float as type
444
     *
445
     * @param string        $metric
446
     * @param string        $as
447
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
448
     *                                     max function to the records which match with the given filter.
449
     *
450
     * @return $this
451
     */
452 1
    public function floatMax(string $metric, string $as = '', Closure $filterBuilder = null)
453
    {
454 1
        return $this->max($metric, $as, DataType::FLOAT, $filterBuilder);
455
    }
456
457
    /**
458
     * Get the maximum value for the given metric using double as type
459
     *
460
     * @param string        $metric
461
     * @param string        $as
462
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
463
     *                                     max function to the records which match with the given filter.
464
     *
465
     * @return $this
466
     */
467 1
    public function doubleMax(string $metric, string $as = '', Closure $filterBuilder = null)
468
    {
469 1
        return $this->max($metric, $as, DataType::DOUBLE, $filterBuilder);
470
    }
471
472
    /**
473
     * Get the any metric found based on the applied group-by filters.
474
     * Returns any value including null. This aggregator can simplify and
475
     * optimize the performance by returning the first encountered value (including null)
476
     *
477
     * ANY aggregator cannot be used in ingestion spec, and should only be specified as part of queries.
478
     *
479
     * @param string        $metric
480
     * @param string        $as
481
     * @param string        $type
482
     * @param int|null      $maxStringBytes For string types only. Optional, defaults to 1024.
483
     * @param \Closure|null $filterBuilder  A closure which receives a FilterBuilder. When given, we will only apply the
484
     *                                      "any" function to the records which match with the given filter.
485
     *
486
     * @return $this
487
     */
488 2
    public function any(
489
        string $metric,
490
        string $as = '',
491
        $type = DataType::LONG,
492
        int $maxStringBytes = null,
493
        Closure $filterBuilder = null
494
    ) {
495 2
        $this->aggregations[] = $this->buildFilteredAggregation(
496 2
            new AnyAggregator($metric, $as, $type, $maxStringBytes),
497
            $filterBuilder
498
        );
499
500 2
        return $this;
501
    }
502
503
    /**
504
     * Get the any metric found based on the applied group-by filters.
505
     * Returns any value including null. This aggregator can simplify and
506
     * optimize the performance by returning the first encountered value (including null)
507
     *
508
     * ANY aggregator cannot be used in ingestion spec, and should only be specified as part of queries.
509
     *
510
     * @param string        $metric
511
     * @param string        $as
512
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
513
     *                                     "any" function to the records which match with the given filter.
514
     *
515
     * @return $this
516
     */
517 1
    public function doubleAny(string $metric, string $as = '', Closure $filterBuilder = null)
518
    {
519 1
        return $this->any($metric, $as, DataType::DOUBLE, null, $filterBuilder);
520
    }
521
522
    /**
523
     * Get the any metric found based on the applied group-by filters.
524
     * Returns any value including null. This aggregator can simplify and
525
     * optimize the performance by returning the first encountered value (including null)
526
     *
527
     * ANY aggregator cannot be used in ingestion spec, and should only be specified as part of queries.
528
     *
529
     * @param string        $metric
530
     * @param string        $as
531
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
532
     *                                     "any" function to the records which match with the given filter.
533
     *
534
     * @return $this
535
     */
536 1
    public function floatAny(string $metric, string $as = '', Closure $filterBuilder = null)
537
    {
538 1
        return $this->any($metric, $as, DataType::FLOAT, null, $filterBuilder);
539
    }
540
541
    /**
542
     * Get the any metric found based on the applied group-by filters.
543
     * Returns any value including null. This aggregator can simplify and
544
     * optimize the performance by returning the first encountered value (including null)
545
     *
546
     * ANY aggregator cannot be used in ingestion spec, and should only be specified as part of queries.
547
     *
548
     * @param string        $metric
549
     * @param string        $as
550
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
551
     *                                     "any" function to the records which match with the given filter.
552
     *
553
     * @return $this
554
     */
555 1
    public function longAny(string $metric, string $as = '', Closure $filterBuilder = null)
556
    {
557 1
        return $this->any($metric, $as, DataType::LONG, null, $filterBuilder);
558
    }
559
560
    /**
561
     * Get the any metric found based on the applied group-by filters.
562
     * Returns any value including null. This aggregator can simplify and
563
     * optimize the performance by returning the first encountered value (including null)
564
     *
565
     * ANY aggregator cannot be used in ingestion spec, and should only be specified as part of queries.
566
     *
567
     * @param string        $metric
568
     * @param string        $as
569
     * @param int|null      $maxStringBytes Optional, defaults to 1024
570
     * @param \Closure|null $filterBuilder  A closure which receives a FilterBuilder. When given, we will only apply the
571
     *                                      "any" function to the records which match with the given filter.
572
     *
573
     * @return $this
574
     */
575 1
    public function stringAny(
576
        string $metric,
577
        string $as = '',
578
        int $maxStringBytes = null,
579
        Closure $filterBuilder = null
580
    ) {
581 1
        return $this->any($metric, $as, DataType::STRING, $maxStringBytes, $filterBuilder);
582
    }
583
584
    /**
585
     * Get the first metric found based on the applied group-by filters.
586
     * So if you group by the dimension "countries", you can get the first "metric" per country.
587
     *
588
     * NOTE: This is different from the Laravel ELOQUENT first() method!
589
     *
590
     * @param string        $metric
591
     * @param string        $as
592
     * @param string        $type
593
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
594
     *                                     "first" function to the records which match with the given filter.
595
     *
596
     * @return $this
597
     */
598 3
    public function first(string $metric, string $as = '', $type = DataType::LONG, Closure $filterBuilder = null)
599
    {
600 3
        $this->aggregations[] = $this->buildFilteredAggregation(
601 3
            new FirstAggregator($metric, $as, $type),
602
            $filterBuilder
603
        );
604
605 3
        return $this;
606
    }
607
608
    /**
609
     * Get the first metric found based on the applied group-by filters.
610
     * So if you group by the dimension "countries", you can get the first "metric" per country.
611
     *
612
     * NOTE: This is different from the Laravel ELOQUENT first() method!
613
     *
614
     * @param string        $metric
615
     * @param string        $as
616
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
617
     *                                     "first" function to the records which match with the given filter.
618
     *
619
     * @return $this
620
     */
621 1
    public function longFirst(string $metric, string $as = '', Closure $filterBuilder = null)
622
    {
623 1
        return $this->first($metric, $as, DataType::LONG, $filterBuilder);
624
    }
625
626
    /**
627
     * Get the first metric found based on the applied group-by filters.
628
     * So if you group by the dimension "countries", you can get the first "metric" per country.
629
     *
630
     * NOTE: This is different from the Laravel ELOQUENT first() method!
631
     *
632
     * @param string        $metric
633
     * @param string        $as
634
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
635
     *                                     "first" function to the records which match with the given filter.
636
     *
637
     * @return $this
638
     */
639 1
    public function floatFirst(string $metric, string $as = '', Closure $filterBuilder = null)
640
    {
641 1
        return $this->first($metric, $as, DataType::FLOAT, $filterBuilder);
642
    }
643
644
    /**
645
     * Get the first metric found based on the applied group-by filters.
646
     * So if you group by the dimension "countries", you can get the first "metric" per country.
647
     *
648
     * NOTE: This is different from the Laravel ELOQUENT first() method!
649
     *
650
     * @param string        $metric
651
     * @param string        $as
652
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
653
     *                                     "first" function to the records which match with the given filter.
654
     *
655
     * @return $this
656
     */
657 1
    public function doubleFirst(string $metric, string $as = '', Closure $filterBuilder = null)
658
    {
659 1
        return $this->first($metric, $as, DataType::DOUBLE, $filterBuilder);
660
    }
661
662
    /**
663
     * Get the first metric found based on the applied group-by filters.
664
     * So if you group by the dimension "countries", you can get the first "metric" per country.
665
     *
666
     * NOTE: This is different from the Laravel ELOQUENT first() method!
667
     *
668
     * @param string        $metric
669
     * @param string        $as
670
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
671
     *                                     "first" function to the records which match with the given filter.
672
     *
673
     * @return $this
674
     */
675 1
    public function stringFirst(string $metric, string $as = '', Closure $filterBuilder = null)
676
    {
677 1
        return $this->first($metric, $as, DataType::STRING, $filterBuilder);
678
    }
679
680
    /**
681
     * Get the last metric found
682
     *
683
     * @param string        $metric
684
     * @param string        $as
685
     * @param string        $type
686
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
687
     *                                     "last" function to the records which match with the given filter.
688
     *
689
     * @return $this
690
     */
691 2
    public function last(string $metric, string $as = '', $type = DataType::LONG, Closure $filterBuilder = null)
692
    {
693 2
        $this->aggregations[] = $this->buildFilteredAggregation(
694 2
            new LastAggregator($metric, $as, $type),
695
            $filterBuilder
696
        );
697
698 2
        return $this;
699
    }
700
701
    /**
702
     * Get the last metric found based on the applied group-by filters.
703
     * So if you group by the dimension "countries", you can get the last "metric" per country.
704
     *
705
     * NOTE: This is different then the ELOQUENT last() method!
706
     *
707
     * @param string        $metric
708
     * @param string        $as
709
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
710
     *                                     "last" function to the records which match with the given filter.
711
     *
712
     * @return $this
713
     */
714 1
    public function longLast(string $metric, string $as = '', Closure $filterBuilder = null)
715
    {
716 1
        return $this->last($metric, $as, DataType::LONG, $filterBuilder);
717
    }
718
719
    /**
720
     * Get the last metric found based on the applied group-by filters.
721
     * So if you group by the dimension "countries", you can get the last "metric" per country.
722
     *
723
     * NOTE: This is different then the ELOQUENT last() method!
724
     *
725
     * @param string        $metric
726
     * @param string        $as
727
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
728
     *                                     "last" function to the records which match with the given filter.
729
     *
730
     * @return $this
731
     */
732 1
    public function floatLast(string $metric, string $as = '', Closure $filterBuilder = null)
733
    {
734 1
        return $this->last($metric, $as, DataType::FLOAT, $filterBuilder);
735
    }
736
737
    /**
738
     * Get the last metric found based on the applied group-by filters.
739
     * So if you group by the dimension "countries", you can get the last "metric" per country.
740
     *
741
     * NOTE: This is different then the ELOQUENT last() method!
742
     *
743
     * @param string        $metric
744
     * @param string        $as
745
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
746
     *                                     "last" function to the records which match with the given filter.
747
     *
748
     * @return $this
749
     */
750 1
    public function doubleLast(string $metric, string $as = '', Closure $filterBuilder = null)
751
    {
752 1
        return $this->last($metric, $as, DataType::DOUBLE, $filterBuilder);
753
    }
754
755
    /**
756
     * Get the last metric found based on the applied group-by filters.
757
     * So if you group by the dimension "countries", you can get the last "metric" per country.
758
     *
759
     * NOTE: This is different then the ELOQUENT last() method!
760
     *
761
     * @param string        $metric
762
     * @param string        $as
763
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
764
     *                                     "last" function to the records which match with the given filter.
765
     *
766
     * @return $this
767
     */
768 1
    public function stringLast(string $metric, string $as = '', Closure $filterBuilder = null)
769
    {
770 1
        return $this->last($metric, $as, DataType::STRING, $filterBuilder);
771
    }
772
773
    /**
774
     * Computes an arbitrary JavaScript function over a set of columns (both metrics and dimensions are allowed). Your
775
     * JavaScript functions are expected to return floating-point values.
776
     *
777
     * Note: JavaScript-based functionality is disabled by default. Please refer to the Druid JavaScript programming
778
     * guide for guidelines about using Druid's JavaScript functionality, including instructions on how to enable it.
779
     *
780
     * @param string        $as            The output name as the result will be available
781
     * @param array         $fieldNames    The columns which will be given to the fnAggregate function. Both metrics
782
     *                                     and dimensions are allowed.
783
     * @param string        $fnAggregate   A javascript function which does the aggregation. This function will receive
784
     *                                     the "current" value as first parameter. The other parameters will be the
785
     *                                     values of the columns as given in the $fieldNames parameter.
786
     * @param string        $fnCombine     A function which can combine two aggregation results.
787
     * @param string        $fnReset       A function which will reset a value.
788
     * @param \Closure|null $filterBuilder A closure which receives a FilterBuilder. When given, we will only apply the
789
     *                                     javascript function to the records which match with the given filter.
790
     *
791
     * @return $this
792
     */
793 1
    public function javascript(
794
        string $as,
795
        array $fieldNames,
796
        string $fnAggregate,
797
        string $fnCombine,
798
        string $fnReset,
799
        Closure $filterBuilder = null
800
    ) {
801 1
        $this->aggregations[] = $this->buildFilteredAggregation(
802 1
            new JavascriptAggregator($fieldNames, $as, $fnAggregate, $fnCombine, $fnReset),
803
            $filterBuilder
804
        );
805
806 1
        return $this;
807
    }
808
}