blob: cf4719817f9b97b165bacb451b49c0a626633423 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.solr.handler.component;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TotalHits;
import org.apache.lucene.search.grouping.SearchGroup;
import org.apache.lucene.search.grouping.TopGroups;
import org.apache.lucene.util.BytesRef;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestInfo;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.search.CursorMark;
import org.apache.solr.search.DocListAndSet;
import org.apache.solr.search.DocSlice;
import org.apache.solr.search.QParser;
import org.apache.solr.search.QueryCommand;
import org.apache.solr.search.QueryResult;
import org.apache.solr.search.RankQuery;
import org.apache.solr.search.SortSpec;
import org.apache.solr.search.grouping.GroupingSpecification;
import org.apache.solr.search.grouping.distributed.command.QueryCommandResult;
import org.apache.solr.util.RTimer;
/**
* This class is experimental and will be changing in the future.
*
*
* @since solr 1.3
*/
public class ResponseBuilder
{
public SolrQueryRequest req;
public SolrQueryResponse rsp;
public boolean doHighlights;
public boolean doFacets;
public boolean doExpand;
public boolean doStats;
public boolean doTerms;
public boolean doAnalytics;
public MergeStrategy mergeFieldHandler;
private boolean needDocList = false;
private boolean needDocSet = false;
private int fieldFlags = 0;
//private boolean debug = false;
private boolean debugTimings, debugQuery, debugResults, debugTrack;
private QParser qparser = null;
private String queryString = null;
private Query query = null;
private List<Query> filters = null;
private SortSpec sortSpec = null;
private GroupingSpecification groupingSpec;
private CursorMark cursorMark;
private CursorMark nextCursorMark;
private List<MergeStrategy> mergeStrategies;
private RankQuery rankQuery;
private DocListAndSet results = null;
private NamedList<Object> debugInfo = null;
private RTimer timer = null;
private Query highlightQuery = null;
public List<SearchComponent> components;
SolrRequestInfo requestInfo;
public ResponseBuilder(SolrQueryRequest req, SolrQueryResponse rsp, List<SearchComponent> components)
{
this.req = req;
this.rsp = rsp;
this.components = components;
this.requestInfo = SolrRequestInfo.getRequestInfo();
}
//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
//// Distributed Search section
//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
public static final String FIELD_SORT_VALUES = "fsv";
public static final String SHARDS = "shards";
public static final String IDS = "ids";
/**
* public static final String NUMDOCS = "nd";
* public static final String DOCFREQS = "tdf";
* public static final String TERMS = "terms";
* public static final String EXTRACT_QUERY_TERMS = "eqt";
* public static final String LOCAL_SHARD = "local";
* public static final String DOC_QUERY = "dq";
* *
*/
public static int STAGE_START = 0;
public static int STAGE_PARSE_QUERY = 1000;
public static int STAGE_TOP_GROUPS = 1500;
public static int STAGE_EXECUTE_QUERY = 2000;
public static int STAGE_GET_FIELDS = 3000;
public static int STAGE_DONE = Integer.MAX_VALUE;
public int stage; // What stage is this current request at?
//The address of the Shard
boolean isDistrib; // is this a distributed search?
public String[] shards;
public String[] slices; // the optional logical ids of the shards
public int shards_rows = -1;
public int shards_start = -1;
public List<ShardRequest> outgoing; // requests to be sent
public List<ShardRequest> finished; // requests that have received responses from all shards
public String shortCircuitedURL;
/**
* This function will return true if this was a distributed search request.
*/
public boolean isDistributed() {
return this.isDistrib;
}
public int getShardNum(String shard) {
for (int i = 0; i < shards.length; i++) {
if (shards[i] == shard || shards[i].equals(shard)) return i;
}
return -1;
}
public void addRequest(SearchComponent me, ShardRequest sreq) {
outgoing.add(sreq);
if ((sreq.purpose & ShardRequest.PURPOSE_PRIVATE) == 0) {
// if this isn't a private request, let other components modify it.
for (SearchComponent component : components) {
if (component != me) {
component.modifyRequest(this, me, sreq);
}
}
}
}
public Map<Object, ShardDoc> resultIds;
// Maps uniqueKeyValue to ShardDoc, which may be used to
// determine order of the doc or uniqueKey in the final
// returned sequence.
// Only valid after STAGE_EXECUTE_QUERY has completed.
public boolean onePassDistributedQuery;
public FacetComponent.FacetInfo _facetInfo;
/* private... components that don't own these shouldn't use them */
SolrDocumentList _responseDocs;
StatsInfo _statsInfo;
TermsComponent.TermsHelper _termsHelper;
SimpleOrderedMap<List<NamedList<Object>>> _pivots;
Object _analyticsRequestManager;
boolean _isOlapAnalytics;
// Context fields for grouping
public final Map<String, Collection<SearchGroup<BytesRef>>> mergedSearchGroups = new HashMap<>();
public final Map<String, Integer> mergedGroupCounts = new HashMap<>();
public final Map<String, Map<SearchGroup<BytesRef>, Set<String>>> searchGroupToShards = new HashMap<>();
public final Map<String, TopGroups<BytesRef>> mergedTopGroups = new HashMap<>();
public final Map<String, QueryCommandResult> mergedQueryCommandResults = new HashMap<>();
public final Map<Object, SolrDocument> retrievedDocuments = new HashMap<>();
public int totalHitCount; // Hit count used when distributed grouping is performed.
// Used for timeAllowed parameter. First phase elapsed time is subtracted from the time allowed for the second phase.
public int firstPhaseElapsedTime;
/**
* Utility function to add debugging info. This will make sure a valid
* debugInfo exists before adding to it.
*/
public void addDebugInfo( String name, Object val )
{
if( debugInfo == null ) {
debugInfo = new SimpleOrderedMap<>();
}
debugInfo.add( name, val );
}
public void addDebug(Object val, String... path) {
if( debugInfo == null ) {
debugInfo = new SimpleOrderedMap<>();
}
NamedList<Object> target = debugInfo;
for (int i=0; i<path.length-1; i++) {
String elem = path[i];
@SuppressWarnings({"unchecked"})
NamedList<Object> newTarget = (NamedList<Object>)debugInfo.get(elem);
if (newTarget == null) {
newTarget = new SimpleOrderedMap<>();
target.add(elem, newTarget);
}
target = newTarget;
}
target.add(path[path.length-1], val);
}
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
public boolean isDebug() {
return debugQuery || debugTimings || debugResults || debugTrack;
}
/**
*
* @return true if all debugging options are on
*/
public boolean isDebugAll(){
return debugQuery && debugTimings && debugResults && debugTrack;
}
public void setDebug(boolean dbg){
debugQuery = dbg;
debugTimings = dbg;
debugResults = dbg;
debugTrack = dbg;
}
public void addMergeStrategy(MergeStrategy mergeStrategy) {
if(mergeStrategies == null) {
mergeStrategies = new ArrayList<>();
}
mergeStrategies.add(mergeStrategy);
}
public List<MergeStrategy> getMergeStrategies() {
return this.mergeStrategies;
}
public RankQuery getRankQuery() {
return rankQuery;
}
public void setRankQuery(RankQuery rankQuery) {
this.rankQuery = rankQuery;
}
public void setResponseDocs(SolrDocumentList _responseDocs) {
this._responseDocs = _responseDocs;
}
public SolrDocumentList getResponseDocs() {
return this._responseDocs;
}
public boolean isDebugTrack() {
return debugTrack;
}
public void setDebugTrack(boolean debugTrack) {
this.debugTrack = debugTrack;
}
public boolean isDebugTimings() {
return debugTimings;
}
public void setDebugTimings(boolean debugTimings) {
this.debugTimings = debugTimings;
}
public boolean isDebugQuery() {
return debugQuery;
}
public void setDebugQuery(boolean debugQuery) {
this.debugQuery = debugQuery;
}
public boolean isDebugResults() {
return debugResults;
}
public void setDebugResults(boolean debugResults) {
this.debugResults = debugResults;
}
public NamedList<Object> getDebugInfo() {
return debugInfo;
}
public void setDebugInfo(NamedList<Object> debugInfo) {
this.debugInfo = debugInfo;
}
public int getFieldFlags() {
return fieldFlags;
}
public void setFieldFlags(int fieldFlags) {
this.fieldFlags = fieldFlags;
}
public List<Query> getFilters() {
return filters;
}
public void setFilters(List<Query> filters) {
this.filters = filters;
}
public Query getHighlightQuery() {
return highlightQuery;
}
public void setHighlightQuery(Query highlightQuery) {
this.highlightQuery = highlightQuery;
}
public boolean isNeedDocList() {
return needDocList;
}
public void setNeedDocList(boolean needDocList) {
this.needDocList = needDocList;
}
public boolean isNeedDocSet() {
return needDocSet;
}
public void setNeedDocSet(boolean needDocSet) {
this.needDocSet = needDocSet;
}
public QParser getQparser() {
return qparser;
}
public void setQparser(QParser qparser) {
this.qparser = qparser;
}
public String getQueryString() {
return queryString;
}
public void setQueryString(String qstr) {
this.queryString = qstr;
}
public Query getQuery() {
return query;
}
public void setQuery(Query query) {
this.query = query;
}
public DocListAndSet getResults() {
return results;
}
public void setResults(DocListAndSet results) {
this.results = results;
}
public SortSpec getSortSpec() {
return sortSpec;
}
public void setSortSpec(SortSpec sortSpec) {
this.sortSpec = sortSpec;
}
public GroupingSpecification getGroupingSpec() {
return groupingSpec;
}
public void setGroupingSpec(GroupingSpecification groupingSpec) {
this.groupingSpec = groupingSpec;
}
public boolean grouping() {
return groupingSpec != null;
}
public RTimer getTimer() {
return timer;
}
public void setTimer(RTimer timer) {
this.timer = timer;
}
/**
* Creates a SolrIndexSearcher.QueryCommand from this
* ResponseBuilder. TimeAllowed is left unset.
*/
public QueryCommand createQueryCommand() {
QueryCommand cmd = new QueryCommand();
cmd.setQuery(wrap(getQuery()))
.setFilterList(getFilters())
.setSort(getSortSpec().getSort())
.setOffset(getSortSpec().getOffset())
.setLen(getSortSpec().getCount())
.setFlags(getFieldFlags())
.setNeedDocSet(isNeedDocSet())
.setCursorMark(getCursorMark());
return cmd;
}
/** Calls {@link RankQuery#wrap(Query)} if there's a rank query, otherwise just returns the query. */
public Query wrap(Query q) {
if(this.rankQuery != null) {
return this.rankQuery.wrap(q);
} else {
return q;
}
}
/**
* Sets results from a SolrIndexSearcher.QueryResult.
*/
public void setResult(QueryResult result) {
setResults(result.getDocListAndSet());
if (result.isPartialResults()) {
rsp.getResponseHeader().asShallowMap()
.put(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY, Boolean.TRUE);
if(getResults() != null && getResults().docList==null) {
getResults().docList = new DocSlice(0, 0, new int[] {}, new float[] {}, 0, 0, TotalHits.Relation.EQUAL_TO);
}
}
final Boolean segmentTerminatedEarly = result.getSegmentTerminatedEarly();
if (segmentTerminatedEarly != null) {
rsp.getResponseHeader().add(SolrQueryResponse.RESPONSE_HEADER_SEGMENT_TERMINATED_EARLY_KEY, segmentTerminatedEarly);
}
if (null != cursorMark) {
assert null != result.getNextCursorMark() : "using cursor but no next cursor set";
this.setNextCursorMark(result.getNextCursorMark());
}
}
public long getNumberDocumentsFound() {
if (_responseDocs == null) {
return 0;
}
return _responseDocs.getNumFound();
}
public CursorMark getCursorMark() {
return cursorMark;
}
public void setCursorMark(CursorMark cursorMark) {
this.cursorMark = cursorMark;
}
public CursorMark getNextCursorMark() {
return nextCursorMark;
}
public void setNextCursorMark(CursorMark nextCursorMark) {
this.nextCursorMark = nextCursorMark;
}
public void setAnalytics(boolean doAnalytics) {
this.doAnalytics = doAnalytics;
}
public boolean isAnalytics() {
return this.doAnalytics;
}
public void setAnalyticsRequestManager(Object analyticsRequestManager) {
this._analyticsRequestManager = analyticsRequestManager;
}
public Object getAnalyticsRequestManager() {
return this._analyticsRequestManager;
}
public void setOlapAnalytics(boolean isOlapAnalytics) {
this._isOlapAnalytics = isOlapAnalytics;
}
public boolean isOlapAnalytics() {
return this._isOlapAnalytics;
}
}