aboutsummaryrefslogtreecommitdiffstats
path: root/predicate-search-core/src/main/java/com/yahoo/document/predicate/BinaryFormat.java
blob: c3e00bd939a588464645d74f8bca4014d4059e79 (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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.document.predicate;

import com.yahoo.slime.Cursor;
import com.yahoo.slime.Inspector;
import com.yahoo.slime.Slime;

import java.util.Objects;

/**
 * @author Simon Thoresen Hult
 */
public class BinaryFormat {

    final static String NODE_TYPE = "type";
    final static String KEY = "key";
    final static String SET = "feature_set";
    final static String RANGE_MIN = "range_min";
    final static String RANGE_MAX = "range_max";
    final static String CHILDREN = "children";
    final static String PARTITIONS = "partitions";
    final static String EDGE_PARTITIONS = "edge_partitions";
    final static String HASHED_PARTITIONS = "hashed_partitions";
    final static String HASHED_EDGE_PARTITIONS = "hashed_edge_partitions";
    final static String HASH = "hash";
    final static String PAYLOAD = "payload";
    final static String LABEL = "label";
    final static String VALUE = "value";
    final static String LOWER_BOUND = "lower_bound";
    final static String UPPER_BOUND = "upper_bound";

    final static int TYPE_CONJUNCTION = 1;
    final static int TYPE_DISJUNCTION = 2;
    final static int TYPE_NEGATION = 3;
    final static int TYPE_FEATURE_SET = 4;
    final static int TYPE_FEATURE_RANGE = 5;
    final static int TYPE_TRUE = 6;
    final static int TYPE_FALSE = 7;

    public static byte[] encode(Predicate predicate) {
        Objects.requireNonNull(predicate, "predicate");
        Slime slime = new Slime();
        encode(predicate, slime.setObject());
        return com.yahoo.slime.BinaryFormat.encode(slime);
    }

    public static Predicate decode(byte[] buf) {
        Objects.requireNonNull(buf, "buf");
        Slime slime = com.yahoo.slime.BinaryFormat.decode(buf);
        return decode(slime.get());
    }

    private static Predicate decode(Inspector in) {
        switch ((int)in.field(NODE_TYPE).asLong()) {
        case TYPE_CONJUNCTION:
            Conjunction conjunction = new Conjunction();
            in = in.field(CHILDREN);
            for (int i = 0, len = in.children(); i < len; ++i) {
                conjunction.addOperand(decode(in.entry(i)));
            }
            return conjunction;

        case TYPE_DISJUNCTION:
            Disjunction disjunction = new Disjunction();
            in = in.field(CHILDREN);
            for (int i = 0, len = in.children(); i < len; ++i) {
                disjunction.addOperand(decode(in.entry(i)));
            }
            return disjunction;

        case TYPE_NEGATION:
            return new Negation(decode(in.field(CHILDREN).entry(0)));

        case TYPE_FEATURE_RANGE:
            FeatureRange featureRange = new FeatureRange(in.field(KEY).asString());
            if (in.field(RANGE_MIN).valid()) {
                featureRange.setFromInclusive(in.field(RANGE_MIN).asLong());
            }
            if (in.field(RANGE_MAX).valid()) {
                featureRange.setToInclusive(in.field(RANGE_MAX).asLong());
            }
            Inspector p_in = in.field(PARTITIONS);
            for (int i = 0, len = p_in.children(); i < len; ++i) {
                featureRange.addPartition(new RangePartition(p_in.entry(i).asString()));
            }
            p_in = in.field(EDGE_PARTITIONS);
            for (int i = 0, len = p_in.children(); i < len; ++i) {
                Inspector obj = p_in.entry(i);
                featureRange.addPartition(new RangeEdgePartition(
                        obj.field(LABEL).asString(), obj.field(VALUE).asLong(),
                        (int)obj.field(LOWER_BOUND).asLong(), (int)obj.field(UPPER_BOUND).asLong()));
            }
            return featureRange;

        case TYPE_FEATURE_SET:
            FeatureSet featureSet = new FeatureSet(in.field(KEY).asString());
            in = in.field(SET);
            for (int i = 0, len = in.children(); i < len; ++i) {
                featureSet.addValue(in.entry(i).asString());
            }
            return featureSet;

        case TYPE_TRUE:
            return new BooleanPredicate(true);

        case TYPE_FALSE:
            return new BooleanPredicate(false);

            default:
            throw new UnsupportedOperationException(String.valueOf(in.field(NODE_TYPE).asLong()));
        }
    }

    private static void encode(Predicate predicate, Cursor out) {
        if (predicate instanceof Conjunction) {
            out.setLong(NODE_TYPE, TYPE_CONJUNCTION);
            out = out.setArray(CHILDREN);
            for (Predicate operand : ((Conjunction)predicate).getOperands()) {
                encode(operand, out.addObject());
            }
        } else if (predicate instanceof Disjunction) {
            out.setLong(NODE_TYPE, TYPE_DISJUNCTION);
            out = out.setArray(CHILDREN);
            for (Predicate operand : ((Disjunction)predicate).getOperands()) {
                encode(operand, out.addObject());
            }
        } else if (predicate instanceof FeatureRange) {
            FeatureRange range = (FeatureRange) predicate;
            out.setLong(NODE_TYPE, TYPE_FEATURE_RANGE);
            out.setString(KEY, range.getKey());
            Long from = range.getFromInclusive();
            if (from != null) {
                out.setLong(RANGE_MIN, from);
            }
            Long to = range.getToInclusive();
            if (to != null) {
                out.setLong(RANGE_MAX, to);
            }
            Cursor p_out = out.setArray(HASHED_PARTITIONS);
            for (RangePartition p : range.getPartitions()) {
                p_out.addLong(PredicateHash.hash64(p.getLabel()));
            }
            p_out = out.setArray(HASHED_EDGE_PARTITIONS);
            for (RangeEdgePartition p : range.getEdgePartitions()) {
                Cursor obj = p_out.addObject();
                obj.setLong(HASH, PredicateHash.hash64(p.getLabel()));
                obj.setLong(VALUE, p.getValue());
                obj.setLong(PAYLOAD, p.encodeBounds());
            }
        } else if (predicate instanceof FeatureSet) {
            out.setLong(NODE_TYPE, TYPE_FEATURE_SET);
            out.setString(KEY, ((FeatureSet)predicate).getKey());
            out = out.setArray(SET);
            for (String value : ((FeatureSet)predicate).getValues()) {
                out.addString(value);
            }
        } else if (predicate instanceof Negation) {
            out.setLong(NODE_TYPE, TYPE_NEGATION);
            out = out.setArray(CHILDREN);
            encode(((Negation)predicate).getOperand(), out.addObject());
        } else if (predicate instanceof BooleanPredicate) {
            out.setLong(NODE_TYPE, ((BooleanPredicate)predicate).getValue() ? TYPE_TRUE : TYPE_FALSE);
        } else {
            throw new UnsupportedOperationException(predicate.getClass().getName());
        }
    }
}