aboutsummaryrefslogtreecommitdiffstats
path: root/container-search/src/main/java/com/yahoo/search/grouping/request/BucketResolver.java
blob: 8e14d827fb56b38d238e3034533be3fa75e04381 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.grouping.request;

import java.util.LinkedList;
import java.util.List;

/**
 * This is a helper class for resolving buckets to a list of
 * {@link GroupingExpression} objects. To resolve a list simply
 * {@link #push(ConstantValue, boolean)} onto it, before calling
 * {@link #resolve(GroupingExpression)} to retrieve the list of corresponding
 * grouping expression object.
 *
 * @author Simon Thoresen Hult
 */
public class BucketResolver {

    private final List<BucketValue> buckets = new LinkedList<>();
    private ConstantValue<?> prev = null;
    private boolean previnclusive = false;
    private int idx = 0;

    /**
     * Pushes the given expression onto this bucket resolver. Once all buckets have been pushed using this method, call
     * {@link #resolve(GroupingExpression)} to retrieve to combined grouping expression.
     *
     * @param val the expression to push
     * @param inclusive whether or not the value is inclusive or not
     * @throws IllegalArgumentException thrown if the expression is incompatible
     */
    public BucketResolver push(ConstantValue<?> val, boolean inclusive) {
        if (prev == null) {
            prev = val;
        } else if (!(prev instanceof InfiniteValue || val instanceof InfiniteValue)
                   && !prev.getClass().equals(val.getClass())) {
            throw new IllegalArgumentException("Bucket type mismatch, expected '" + prev.getClass().getSimpleName() +
                                               "' got '" + val.getClass().getSimpleName() + "'.");
        } else if (prev instanceof InfiniteValue && val instanceof InfiniteValue) {
            throw new IllegalArgumentException("Bucket type mismatch, cannot both be infinity.");
        }
        if ((++idx % 2) == 0) {
            ConstantValue<?> begin = previnclusive ? prev : nextValue(prev);
            ConstantValue<?> end = inclusive ? nextValue(val) : val;
            if (begin instanceof DoubleValue || end instanceof DoubleValue) {
                buckets.add(new DoubleBucket(begin, end));
            } else if (begin instanceof LongValue || end instanceof LongValue) {
                buckets.add(new LongBucket(begin, end));
            } else if (begin instanceof StringValue || end instanceof StringValue) {
                buckets.add(new StringBucket(begin, end));
            } else if (begin instanceof RawValue || end instanceof RawValue) {
                buckets.add(new RawBucket(begin, end));
            } else {
                throw new UnsupportedOperationException("Bucket type '" + val.getClass() + "' not supported.");
            }
        }
        prev = val;
        previnclusive = inclusive;
        return this;
    }

    /**
     * Resolves and returns the list of grouping expressions that correspond to the previously pushed buckets.
     *
     * @param exp The expression to assign to the function.
     * @return The list corresponding to the pushed buckets.
     */
    public PredefinedFunction resolve(GroupingExpression exp) {
        if ((idx % 2) == 1) {
            throw new IllegalStateException("Missing to-limit of last bucket.");
        }
        int len = buckets.size();
        if (len == 0) {
            throw new IllegalStateException("Expected at least one bucket, got none.");
        }
        ConstantValue<?> begin = buckets.get(0).getFrom();
        ConstantValue<?> end = buckets.get(0).getTo();
        if (begin instanceof DoubleValue || end instanceof DoubleValue) {
            if (len == 1) {
                return new DoublePredefined(exp, (DoubleBucket)buckets.get(0));
            } else {
                return new DoublePredefined(exp, (DoubleBucket)buckets.get(0),
                                            buckets.subList(1, len).toArray(new DoubleBucket[len - 1]));
            }
        } else if (begin instanceof LongValue || end instanceof LongValue) {
            if (len == 1) {
                return new LongPredefined(exp, (LongBucket)buckets.get(0));
            } else {
                return new LongPredefined(exp, (LongBucket)buckets.get(0),
                                          buckets.subList(1, len).toArray(new LongBucket[len - 1]));
            }
        } else if (begin instanceof StringValue || end instanceof StringValue) {
            if (len == 1) {
                return new StringPredefined(exp, (StringBucket)buckets.get(0));
            } else {
                return new StringPredefined(exp, (StringBucket)buckets.get(0),
                                            buckets.subList(1, len).toArray(new StringBucket[len - 1]));
            }
        } else if (begin instanceof RawValue || end instanceof RawValue) {
            if (len == 1) {
                return new RawPredefined(exp, (RawBucket)buckets.get(0));
            } else {
                return new RawPredefined(exp, (RawBucket)buckets.get(0),
                                            buckets.subList(1, len).toArray(new RawBucket[len - 1]));
            }
        }
        throw new UnsupportedOperationException("Bucket type '" + begin.getClass() + "' not supported.");
    }

    private ConstantValue<?> nextValue(ConstantValue<?> value) {
        if (value instanceof LongValue) {
            return LongBucket.nextValue((LongValue)value);
        } else if (value instanceof DoubleValue) {
            return DoubleBucket.nextValue((DoubleValue)value);
        } else if (value instanceof StringValue) {
            return StringBucket.nextValue((StringValue)value);
        } else if (value instanceof RawValue) {
            return RawBucket.nextValue((RawValue)value);
        }
        return value;
    }
}