// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.search.query; import com.google.common.base.Splitter; import com.yahoo.collections.LazySet; import com.yahoo.component.ComponentSpecification; import com.yahoo.processing.request.CompoundName; import com.yahoo.prelude.query.*; import com.yahoo.search.Query; import com.yahoo.search.query.profile.types.FieldDescription; import com.yahoo.search.query.profile.types.QueryProfileType; import com.yahoo.search.rendering.RendererRegistry; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.ArrayList; import java.util.List; import java.util.Set; /** * Parameters deciding how the result of a query should be presented * * @author Arne Bergene Fossaa */ public class Presentation implements Cloneable { /** The type representing the property arguments consumed by this */ private static QueryProfileType argumentType; public static final String PRESENTATION = "presentation"; public static final String BOLDING = "bolding"; public static final String TIMING = "timing"; public static final String SUMMARY = "summary"; public static final String REPORT_COVERAGE = "reportCoverage"; public static final String SUMMARY_FIELDS = "summaryFields"; /** The (short) name of the parameter holding the name of the return format to use */ public static final String FORMAT = "format"; static { argumentType=new QueryProfileType(PRESENTATION); argumentType.setStrict(true); argumentType.setBuiltin(true); argumentType.addField(new FieldDescription(BOLDING, "boolean", "bolding")); argumentType.addField(new FieldDescription(TIMING, "boolean", "timing")); argumentType.addField(new FieldDescription(SUMMARY, "string", "summary")); argumentType.addField(new FieldDescription(REPORT_COVERAGE, "string", "reportcoverage")); argumentType.addField(new FieldDescription(FORMAT, "string", "format template")); argumentType.addField(new FieldDescription(SUMMARY_FIELDS, "string", "summaryFields")); argumentType.freeze(); } public static QueryProfileType getArgumentType() { return argumentType; } /** How the result should be highlighted */ private Highlight highlight= null; /** The terms to highlight in the result (only used by BoldingSearcher, may be removed later). */ private List boldingData = null; /** Whether or not to do highlighting */ private boolean bolding = true; /** The summary class to be shown */ private String summary = null; /** Whether coverage information (how much of the indices was searched should be included in the result */ private boolean reportCoverage=false; /** The name of the renderer to use for rendering the hits. */ private ComponentSpecification format = RendererRegistry.defaultRendererId.toSpecification(); /** Whether optional timing data should be rendered */ private boolean timing = false; /** Set of explicitly requested summary fields, instead of summary classes */ @NonNull private Set summaryFields = LazySet.newHashSet(); private static final Splitter COMMA_SPLITTER = Splitter.on(',').omitEmptyStrings().trimResults(); public Presentation(Query parent) { } /** Returns how terms in this result should be highlighted, or null if not set */ public Highlight getHighlight() { return highlight; } /** Sets how terms in this result should be highlighted. Set to null to turn highlighting off */ public void setHighlight(Highlight highlight) { this.highlight = highlight; } /** Returns the name of the summary class to be used to present hits from this query, or null if not set */ public String getSummary() { return summary; } /** Sets the name of the summary class to be used to present hits from this query */ public void setSummary(String summary) { this.summary = summary; } /** Returns whether matching query terms should be bolded in the result. Default is true. */ public boolean getBolding() { return bolding; } /** Sets whether matching query terms should be bolded in the result */ public void setBolding(boolean bolding) { this.bolding = bolding; } /** Returns whether coverage information should be returned in the result, if available. Default is false */ public boolean getReportCoverage() { return reportCoverage; } /** Sets whether coverage information should be returned in the result, if available */ public void setReportCoverage(boolean reportCoverage) { this.reportCoverage=reportCoverage; } /** Get the name of the format desired for result rendering. */ @NonNull public ComponentSpecification getRenderer() { return format; } /** Set the desired format for result rendering. If null, use the default renderer. */ public void setRenderer(@Nullable ComponentSpecification format) { this.format = (format != null) ? format : RendererRegistry.defaultRendererId.toSpecification(); } /** * Get the name of the format desired for result rendering. */ @NonNull public String getFormat() { return format.getName(); } /** * Set the desired format for result rendering. If null, use the default renderer. */ public void setFormat(@Nullable String format) { setRenderer(ComponentSpecification.fromString(format)); } @Override public Object clone() { try { Presentation clone = (Presentation)super.clone(); if (boldingData != null) clone.boldingData = new ArrayList<>(boldingData); if (highlight != null) clone.highlight = highlight.clone(); if (summaryFields != null) { clone.summaryFields = LazySet.newHashSet(); clone.summaryFields.addAll(this.summaryFields); } return clone; } catch (CloneNotSupportedException e) { throw new RuntimeException("Someone inserted a noncloneable superclass",e); } } @Override public boolean equals(Object o) { if (o == null || !(o instanceof Presentation)) return false; Presentation p = (Presentation) o; return QueryHelper.equals(bolding,p.bolding) && QueryHelper.equals(summary,p.summary); } @Override public int hashCode() { return QueryHelper.combineHash(bolding, summary); } /** * @return whether to add optional timing data to the rendered result */ public boolean getTiming() { return timing; } public void setTiming(boolean timing) { this.timing = timing; } /** * Return the set of explicitly requested fields. Returns an empty set if no * fields are specified outside of summary classes. The returned set is * mutable and fields may be added or removed before passing on the query. * * @return the set of names of requested fields, never null */ @NonNull public Set getSummaryFields() { return summaryFields; } /** Prepares this for binary serialization. For internal use - see {@link Query#prepare} */ public void prepare() { if (highlight != null) highlight.prepare(); } /** * Parse the given string as a comma delimited set of field names and * overwrite the set of summary fields. Whitespace will be trimmed. If you * want to add or remove fields programmatically, use * {@link #getSummaryFields()} and modify the returned set. * * @param asString * the summary fields requested, e.g. "price,author,title" */ public void setSummaryFields(String asString) { summaryFields.clear(); for (String field : COMMA_SPLITTER.split(asString)) { summaryFields.add(field); } } }