aboutsummaryrefslogtreecommitdiffstats
path: root/container-search/src/main/java/com/yahoo/fs4/QueryResultPacket.java
blob: 6a27beefb5e3509293af6b9d6707222c626c67e6 (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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.fs4;

import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * A query result packet (code 217). This packet can be decoded only.
 *
 * @author bratseth
 */
public class QueryResultPacket extends Packet {

    /** The code of this type of package */
    private static final int code = 217;

    /** Whether mld data is included in this result */
    private boolean mldFeature = false;

    /** Whether sort data is included in this result */
    private boolean sortData = false;

    /** Whether coverage information is included in this result */
    private boolean coverageNodes = false;
    private long  coverageDocs = 0;
    private long  activeDocs = 0;
    private long  soonActiveDocs = 0;
    private int   degradedReason = 0;
    private short nodesQueried = 0;
    private short nodesReplied = 0;

    /** Whether the result contains grouping results **/
    private boolean groupDataFeature = false;

    /** Whether the result contains properties **/
    private boolean propsFeature = false;

    private long totalDocumentCount;

    private Number maxRank;

    private int docstamp;

    private byte[] groupData = null;

    private List<DocumentInfo> documents=new ArrayList<>(10);

    public FS4Properties[] propsArray;

    private int offset;

    private QueryResultPacket() { }

    public static QueryResultPacket create() {
        return new QueryResultPacket();
    }

    public void setDocstamp(int docstamp){ this.docstamp=docstamp; }

    public int getDocstamp() { return docstamp; }

    /** Returns whether this has the mysterious mld feature */
    public boolean getMldFeature() { return mldFeature; }

    public boolean getCoverageFeature() { return true; }

    public long getCoverageDocs() { return coverageDocs; }

    public long getActiveDocs() { return activeDocs; }

    public long getSoonActiveDocs() { return soonActiveDocs; }

    public int getDegradedReason() { return degradedReason; }

    public boolean getCoverageFull() {
        return coverageDocs == activeDocs;
    }


    /** @return offset returned by backend */
    public int getOffset() { return offset; }

    /** Only for testing. */
    public void setOffset(int offset) {
        this.offset = offset;
    }

    @Override
    public void decodeBody(ByteBuffer buffer) {
        IntBuffer ints = buffer.asIntBuffer();
        decodeFeatures(ints);
        offset = ints.get();
        int documentCount = ints.get();
        buffer.position(buffer.position() + ints.position() * 4);
        totalDocumentCount = buffer.getLong();
        maxRank = decodeMaxRank(buffer);
        ints = buffer.asIntBuffer();
        docstamp = ints.get();
        buffer.position(buffer.position() + ints.position() * 4);
        // do not access "ints" below here!

        if (coverageNodes) {
            nodesQueried = buffer.getShort();
            nodesReplied = buffer.getShort();
        }

        byte[][] documentSortData = null;
        if (sortData && documentCount > 0) {
            documentSortData = decodeSortData(buffer, documentCount);
        }

        if (groupDataFeature) {
            int len = buffer.getInt();
            groupData = new byte[len];
            buffer.get(groupData);
        }

        coverageDocs = buffer.getLong();
        activeDocs = buffer.getLong();
        soonActiveDocs = buffer.getLong();
        degradedReason = buffer.getInt();

        decodeDocuments(buffer, documentCount, documentSortData);
        if (propsFeature) {
            int numMaps = buffer.getInt();
            propsArray = new FS4Properties[numMaps];
            for (int i = 0; i < numMaps; i++) {
                propsArray[i] = new FS4Properties();
                propsArray[i].decode(buffer);
            }
        }
    }

    private byte[][] decodeSortData(ByteBuffer buffer, int documentCount) {
        int[] indexes = new int[documentCount];
        indexes[0] = 0;
        for (int i = 1; i < documentCount; i++) {
            indexes[i] = buffer.getInt();
        }

        int sortDataLengthInBytes = buffer.getInt();
        byte[][] ret = new byte[indexes.length][];

        for (int i = 0; i < indexes.length; i++) {
            int end = i + 1 >= indexes.length ? sortDataLengthInBytes : indexes[i + 1];
            int len = end - indexes[i];
            ret[i] = new byte[len];
            buffer.get(ret[i], 0, len);
        }
        return ret;
    }

    private Number decodeMaxRank(ByteBuffer buffer) {
        return Double.valueOf(buffer.getDouble());
    }

    /**
     * feature bits
     */
    private static final int QRF_MLD             = 0x00000001;
    private static final int QRF_COVERAGE_NODES  = 0x00000002;
    private static final int QRF_SORTDATA        = 0x00000010;
    private static final int QRF_UNUSED_1        = 0x00000020;
    private static final int QRF_UNUSED_2        = 0x00000040;
    private static final int QRF_GROUPDATA       = 0x00000200;
    private static final int QRF_PROPERTIES      = 0x00000400;

    /** Decodes the feature int of this package data into boolean feature fields */
    private void decodeFeatures(IntBuffer buffer) {
        int features = buffer.get();
        mldFeature       = (QRF_MLD & features) != 0;
        sortData = (QRF_SORTDATA & features) != 0;
        coverageNodes    = (QRF_COVERAGE_NODES & features) != 0;
        groupDataFeature = (QRF_GROUPDATA & features) != 0;
        propsFeature     = (QRF_PROPERTIES & features) != 0;
    }

    private void decodeDocuments(ByteBuffer buffer, int documentCount, byte[][] documentSortData) {
        for (int i = 0; i < documentCount; i++) {
            byte[] sort = documentSortData == null ? null : documentSortData[i];
            documents.add(new DocumentInfo(buffer, this, sort));
        }
    }

    public int getCode() { return code; }

    protected void codeDecodedHook(int code) {
        if ( code != QueryResultPacket.code)
            throw new RuntimeException("Programming error, packet " + getCode() + "Not expected.");
    }

    public int getDocumentCount() { return documents.size(); }

    public String toString() {
        return "Query result x packet [" + getDocumentCount() + " documents]";
    }

    /** Returns the opaque grouping results **/
    public byte[] getGroupData() { return groupData; }


    /** Returns the total number of documents avalable for this query */
    public long getTotalDocumentCount() { return totalDocumentCount; }

    /** Only for testing. */
    public void setTotalDocumentCount(long totalDocumentCount) {
        this.totalDocumentCount = totalDocumentCount;
    }

    /** Returns a read-only list containing the DocumentInfo objects of this result */
    public List<DocumentInfo> getDocuments() {
        return Collections.unmodifiableList(documents);
    }

    public void addDocument(DocumentInfo document) {
        documents.add(document);
    }

    // TODO: Handle new maxRank intelligently
    public int getMaxRank() { return maxRank.intValue(); }

    public short getNodesQueried() { return nodesQueried; }
    public short getNodesReplied() { return nodesReplied; }

}