diff options
Diffstat (limited to 'container-search/src/main/java/com/yahoo/fs4/GetDocSumsPacket.java')
-rw-r--r-- | container-search/src/main/java/com/yahoo/fs4/GetDocSumsPacket.java | 202 |
1 files changed, 201 insertions, 1 deletions
diff --git a/container-search/src/main/java/com/yahoo/fs4/GetDocSumsPacket.java b/container-search/src/main/java/com/yahoo/fs4/GetDocSumsPacket.java index 6a808e17b5c..7353d0730a4 100644 --- a/container-search/src/main/java/com/yahoo/fs4/GetDocSumsPacket.java +++ b/container-search/src/main/java/com/yahoo/fs4/GetDocSumsPacket.java @@ -1,15 +1,215 @@ // 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.document.GlobalId; +import com.yahoo.log.LogLevel; +import com.yahoo.prelude.fastsearch.FastHit; +import com.yahoo.prelude.query.Item; +import com.yahoo.search.Query; +import com.yahoo.search.Result; +import com.yahoo.search.result.Hit; +import com.yahoo.text.Utf8; + +import java.nio.ByteBuffer; +import java.util.Iterator; +import java.util.logging.Logger; + /** * <p>A packet for requesting a list of document summaries. * This packet can be encoded only.</p> * * @author bratseth */ -public class GetDocSumsPacket { +public class GetDocSumsPacket extends Packet { /** Session id key. Yep, putting this here is ugly as hell */ public static final String sessionIdKey = "sessionId"; + private static final Logger log = Logger.getLogger(GetDocSumsPacket.class.getName()); + private final Result result; + private final Query query; + private final String summaryClass; + private QueryPacketData queryPacketData = null; + private int flags = 0; + + /** + * True if we should send the query with this docsum, false otherwise. + * Sending the query is necessary if we need to return summary features or generate a dynamic summary + */ + private final boolean sendQuery; + + private GetDocSumsPacket(Result result, String summaryClass, boolean sendQuery) { + this.result = result; + this.query = result.getQuery(); + this.summaryClass = summaryClass; + this.sendQuery = sendQuery; + } + + /** + * Creates a get docsums packet for a certain result + */ + public static GetDocSumsPacket create(Result result, String summaryClass, boolean sendQuery) { + return new GetDocSumsPacket(result, summaryClass, sendQuery); + } + + /** + * features bits, as given in searchlib/src/searchlib/common/packets.h + * definition of enum getdocsums_features + */ + public static final int GDF_MLD = 0x00000001; + public static final int GDF_QUERYSTACK = 0x00000004; + public static final int GDF_RANKP_QFLAGS = 0x00000010; + public static final int GDF_LOCATION = 0x00000080; + public static final int GDF_RESCLASSNAME = 0x00000800; + public static final int GDF_PROPERTIES = 0x00001000; + public static final int GDF_FLAGS = 0x00002000; + + public void encodeBody(ByteBuffer buffer) { + setFieldsFromHits(); + + boolean useQueryCache = query.getRanking().getQueryCache(); + // If feature cache is used we need to include the sessionId as key. + if (useQueryCache) { // TODO: Move this decision (and the key) to ranking + query.getRanking().getProperties().put(sessionIdKey, query.getSessionId().toString()); + } + + // set the default features + long features = GDF_MLD; + if (sendQuery) + features |= GDF_QUERYSTACK; + features |= GDF_RANKP_QFLAGS; + + // do we want a specific result class? + if (summaryClass != null) + features |= GDF_RESCLASSNAME; + if (query.getRanking().getLocation() != null) + features |= GDF_LOCATION; + if (query.hasEncodableProperties()) + features |= GDF_PROPERTIES; + if (flags != 0) { + features |= GDF_FLAGS; + } + buffer.putInt((int)features); + buffer.putInt(0); //Unused, was docstamp + long timeLeft = query.getTimeLeft(); + buffer.putInt(Math.max(1, (int)timeLeft)); // Safety to avoid sending down 0 or negative number + if (log.isLoggable(LogLevel.DEBUG)) { + log.log(LogLevel.DEBUG, "Timeout from query(" + query.getTimeout() + "), sent to backend: " + timeLeft); + } + + if (queryPacketData != null) + encodeQueryFromPacketData(buffer, useQueryCache); + else + encodeQuery(buffer); + + if (flags != 0) + buffer.putInt(flags); + encodeDocIds(buffer); + } + + private void setFieldsFromHits() { + for (Iterator<Hit> i = result.hits().unorderedDeepIterator(); i.hasNext(); ) { + Hit h = i.next(); + if (h instanceof FastHit) { + FastHit hit = (FastHit)h; + QueryPacketData tag = hit.getQueryPacketData(); + if (tag != null) { + this.queryPacketData = tag; + break; + } + } + } + } + + private void encodeQueryFromPacketData(ByteBuffer buffer, boolean reencodePropertyMaps) { + queryPacketData.encodeRankProfile(buffer); + queryPacketData.encodeQueryFlags(buffer); + + encodeSummaryClass(buffer); + + if (reencodePropertyMaps || ! sendQuery) // re-encode when we're not sending query, to avoid resending all encoded properties + query.encodeAsProperties(buffer, sendQuery); + else + queryPacketData.encodePropertyMaps(buffer); + + if (sendQuery) + queryPacketData.encodeQueryStack(buffer); + queryPacketData.encodeLocation(buffer); + } + + private void encodeSummaryClass(ByteBuffer buffer) { + if (summaryClass != null) { + byte[] tmp = Utf8.toBytes(summaryClass); + buffer.putInt(tmp.length); + buffer.put(tmp); + } + } + + private void encodeQuery(ByteBuffer buffer) { + Item.putString(query.getRanking().getProfile(), buffer); + buffer.putInt(QueryPacket.getQueryFlags(query)); + + encodeSummaryClass(buffer); + + query.encodeAsProperties(buffer, sendQuery); + + if (sendQuery) { + // The stack must be resubmitted to generate dynamic docsums + int itemCountPosition = buffer.position(); + buffer.putInt(0); + int dumpLengthPosition = buffer.position(); + buffer.putInt(0); + int count = query.encode(buffer); + buffer.putInt(itemCountPosition, count); + buffer.putInt(dumpLengthPosition, buffer.position() - dumpLengthPosition - 4); + } + + if (query.getRanking().getLocation() != null) { + int locationLengthPosition = buffer.position(); + buffer.putInt(0); + int locationLength = query.getRanking().getLocation().encode(buffer); + buffer.putInt(locationLengthPosition, locationLength); + } + } + + private void encodeDocIds(ByteBuffer buffer) { + byte[] emptyGid = new byte[GlobalId.LENGTH]; + for (Iterator<Hit> i = result.hits().unorderedDeepIterator(); i.hasNext(); ) { + Hit hit = i.next(); + if (hit instanceof FastHit && !hit.isFilled(summaryClass)) { + FastHit fastHit = (FastHit)hit; + buffer.put(fastHit.getGlobalId() != null ? fastHit.getRawGlobalId() : emptyGid); + buffer.putInt(fastHit.getPartId()); + buffer.putInt(0); //Unused, was docstamp + } + } + } + + public int getCode() { + return 219; + } + + public String toString() { + return "Get docsums x packet fetching " + getNumDocsums() + " docsums and packet length of " + getLength() + " bytes."; + } + + public int getNumDocsums() { + int num = 0; + for (Iterator<Hit> i = result.hits().unorderedDeepIterator(); i.hasNext(); ) { + Hit hit = i.next(); + if (hit instanceof FastHit && !hit.isFilled(summaryClass)) { + num++; + } + } + return num; + } + + /** + * Return the document summary class we want the fdispatch + * to use when replying to us + */ + @SuppressWarnings("UnusedDeclaration") + public String getSummaryClass() { + return summaryClass; + } } |