aboutsummaryrefslogtreecommitdiffstats
path: root/container-search/src/main/java/com/yahoo/fs4/QueryResultPacket.java
blob: 17f3ca2655f99d15ee1e4c2832833f713e5c7fc6 (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
// 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 com.yahoo.search.Query;

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


/**
 * An "extended query result" packet. This is the query result packets used today,
 * they allow more flexible sets of parameters to be shipped with query results.
 * This packet can be decoded only.
 *
 * @author bratseth
 */
public class QueryResultPacket extends Packet {

    /** This may have code 202, 208, 214 or 217 of historical reasons */
    private int code;

    /** Whether mld stuff, whatever that is, is included in this result */
    private boolean mldFeature=false;

    /** A feature of no apparent utility */
    private boolean datasetFeature=false;

    /** Whether coverage information is included in this result */
    private boolean coverageFeature = false;
    private boolean coverageExtendedFeature = false;
    private long coverageDocs = 0;
    private long activeDocs = 0;
    private long soonActiveDocs = 0;
    private int  degradedReason = 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 int dataset=-1;

    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; }

    /** Returns whether this result has the dataset feature */
    public boolean getDatasetFeature() { return datasetFeature; }

    public boolean getCoverageFeature() {
        return coverageFeature;
    }

    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();
        if (datasetFeature) dataset=ints.get();
        buffer.position(buffer.position() + ints.position()*4);
        if (groupDataFeature) {
            int len = buffer.getInt();
            groupData = new byte[len];
            buffer.get(groupData);
        }
        if (coverageFeature) {
            coverageDocs = buffer.getLong();
            activeDocs = buffer.getLong();
        }
        if (coverageExtendedFeature) {
            soonActiveDocs = buffer.getLong();
            degradedReason = buffer.getInt();
        } else {
            soonActiveDocs = activeDocs;
            degradedReason = 0;
        }
        decodeDocuments(buffer,documentCount);
        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 Number decodeMaxRank(ByteBuffer buffer) {
        return Double.valueOf(buffer.getDouble());
    }

    /**
     * feature bits
     */
    public static final int QRF_MLD               = 0x00000001;
    public static final int QRF_SORTDATA          = 0x00000010;
    public static final int QRF_EXTENDED_COVERAGE = 0x00000020;
    public static final int QRF_COVERAGE          = 0x00000040;
    public static final int QRF_GROUPDATA         = 0x00000200;
    public static final int QRF_PROPERTIES        = 0x00000400;

    /**
     * Sets the features of this package.
     * Features are either encoded by different package codes
     * or by a feature int, for reasons not easily comprehended.
     */
    private void decodeFeatures(IntBuffer buffer) {
        switch (getCode()) {
        case 217:
                int features=buffer.get();
                mldFeature       = (QRF_MLD & features) != 0;
                datasetFeature   = (0x002 & features) != 0;
                // Data given by sortFeature not currently used by QRS:
                // sortFeature   = (QRF_SORTDATA & features) != 0;
                coverageExtendedFeature  = (QRF_EXTENDED_COVERAGE & features) != 0;
                coverageFeature  = (QRF_COVERAGE & features) != 0;
                groupDataFeature = (QRF_GROUPDATA & features) != 0;
                propsFeature     = (QRF_PROPERTIES & features) != 0;
                break;
            default:
                throw new RuntimeException("Programming error");
        }
    }

    private void decodeDocuments(ByteBuffer buffer, int documentCount) {
        for (int i=0; i<documentCount; i++) {
            documents.add(new DocumentInfo(buffer, this));
        }
    }

    public int getCode() { return code; }

    protected void codeDecodedHook(int code) { this.code=code; }

    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 int getDataset() { return dataset; }

}