aboutsummaryrefslogtreecommitdiffstats
path: root/searchlib/src/vespa/searchlib/predicate/predicate_range_term_expander.h
blob: 6a6b91e20f2f0768b79323965f624b5df185d343 (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
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#pragma once

#include <vespa/vespalib/stllike/string.h>
#include <vespa/vespalib/util/issue.h>
#include <climits>
#include <cinttypes>

namespace search::predicate {

/**
 * Helper class for expanding a point in a predicate range query to
 * the hashed labels. Used by PredicateBlueprint.
 */
class PredicateRangeTermExpander {
    int _arity;
    uint16_t _max_positive_levels;
    uint16_t _max_negative_levels;
    int64_t _lower_bound;
    int64_t _upper_bound;

public:
    PredicateRangeTermExpander(int arity,
                               int64_t lower_bound = LLONG_MIN,
                               int64_t upper_bound = LLONG_MAX)
        : _arity(arity),
          _max_positive_levels(1),
          _max_negative_levels(1),
          _lower_bound(lower_bound),
          _upper_bound(upper_bound) {
        uint64_t t = _upper_bound;
        while ((t /= _arity) > 0) ++_max_positive_levels;
        t = uint64_t(0)-_lower_bound;
        while ((t /= _arity) > 0) ++_max_negative_levels;
    }

    template <typename Handler>
    void expand(const vespalib::string &key, int64_t value, Handler &handler);
};


/**
 * Handler must implement handleRange(string) and handleEdge(string, uint64_t).
 */
template <typename Handler>
void PredicateRangeTermExpander::expand(const vespalib::string &key, int64_t signed_value, Handler &handler) {
    if (signed_value < _lower_bound || signed_value > _upper_bound) {
        vespalib::Issue::report("predicate_range_term_expander: Search outside bounds should have been rejected by ValidatePredicateSearcher.");
        return;
    }
    size_t buffer_size = 21 * 2 + 3 + key.size(); // 2 numbers + punctuation + key
    char buffer[buffer_size];
    int size;
    int prefix_size = snprintf(buffer, buffer_size, "%s=", key.c_str());
    bool negative = signed_value < 0;
    uint64_t value;
    int max_levels;
    if (negative) {
        value = uint64_t(0)-signed_value;
        buffer[prefix_size++] = '-';
        max_levels = _max_negative_levels;
    } else {
        value = signed_value;
        max_levels = _max_positive_levels;
    }

    int64_t edge_interval = (value / _arity) * _arity;
    size = snprintf(buffer + prefix_size, buffer_size - prefix_size, "%" PRIu64, edge_interval);
    handler.handleEdge(vespalib::stringref(buffer, prefix_size + size),
                       value - edge_interval);

    uint64_t level_size = _arity;
    for (int i = 0; i < max_levels; ++i) {
        uint64_t start = (value / level_size) * level_size;
        if (negative) {
            if (start + level_size - 1 > (uint64_t(0)-LLONG_MIN)) {
                break;
            }
            size = snprintf(buffer + prefix_size, buffer_size - prefix_size,
                            "%" PRIu64 "-%" PRIu64,
                            start + level_size - 1, start);
        } else {
            if (start + level_size - 1 > LLONG_MAX) {
                break;
            }
            size = snprintf(buffer + prefix_size, buffer_size - prefix_size,
                            "%" PRIu64 "-%" PRIu64,
                           start, start + level_size - 1);
        }
        handler.handleRange(vespalib::stringref(buffer, prefix_size + size));
        level_size *= _arity;
        if (!level_size) {  // overflow
            break;
        }
    }
}

}