org.apache.solr.client.solrj.response.QueryResponse.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.solr.client.solrj.response.QueryResponse.java

Source

/*
 * 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.client.solrj.response;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.beans.DocumentObjectBinder;
import org.apache.solr.client.solrj.response.json.NestableJsonFacet;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.params.CursorMarkParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SimpleOrderedMap;

/**
 * 
 *
 * @since solr 1.3
 */
@SuppressWarnings("unchecked")
public class QueryResponse extends SolrResponseBase {
    // Direct pointers to known types
    private NamedList<Object> _header = null;
    private SolrDocumentList _results = null;
    private NamedList<ArrayList> _sortvalues = null;
    private NamedList<Object> _facetInfo = null;
    private NamedList<Object> _debugInfo = null;
    private NamedList<Object> _highlightingInfo = null;
    private NamedList<Object> _spellInfo = null;
    private List<NamedList<Object>> _clusterInfo = null;
    private NamedList<Object> _jsonFacetingInfo = null;
    private Map<String, NamedList<Object>> _suggestInfo = null;
    private NamedList<Object> _statsInfo = null;
    private NamedList<NamedList<Object>> _termsInfo = null;
    private NamedList<SolrDocumentList> _moreLikeThisInfo = null;
    private String _cursorMarkNext = null;

    // Grouping response
    private NamedList<Object> _groupedInfo = null;
    private GroupResponse _groupResponse = null;

    private NamedList<Object> _expandedInfo = null;
    private Map<String, SolrDocumentList> _expandedResults = null;

    // Facet stuff
    private Map<String, Integer> _facetQuery = null;
    private List<FacetField> _facetFields = null;
    private List<FacetField> _limitingFacets = null;
    private List<FacetField> _facetDates = null;
    private List<RangeFacet> _facetRanges = null;
    private NamedList<List<PivotField>> _facetPivot = null;
    private List<IntervalFacet> _intervalFacets = null;

    // Highlight Info
    private Map<String, Map<String, List<String>>> _highlighting = null;

    // SpellCheck Response
    private SpellCheckResponse _spellResponse = null;

    // Clustering Response
    private ClusteringResponse _clusterResponse = null;

    // Json Faceting Response
    private NestableJsonFacet _jsonFacetingResponse = null;

    // Suggester Response
    private SuggesterResponse _suggestResponse = null;

    // Terms Response
    private TermsResponse _termsResponse = null;

    // Field stats Response
    private Map<String, FieldStatsInfo> _fieldStatsInfo = null;

    // Debug Info
    private Map<String, Object> _debugMap = null;
    private Map<String, Object> _explainMap = null;

    // utility variable used for automatic binding -- it should not be serialized
    private transient final SolrClient solrClient;

    public QueryResponse() {
        solrClient = null;
    }

    /**
     * Utility constructor to set the solrServer and namedList
     */
    public QueryResponse(NamedList<Object> res, SolrClient solrClient) {
        this.setResponse(res);
        this.solrClient = solrClient;
    }

    public QueryResponse(SolrClient solrClient) {
        this.solrClient = solrClient;
    }

    @Override
    public void setResponse(NamedList<Object> res) {
        super.setResponse(res);

        // Look for known things
        for (int i = 0; i < res.size(); i++) {
            String n = res.getName(i);
            if ("responseHeader".equals(n)) {
                _header = (NamedList<Object>) res.getVal(i);
            } else if ("response".equals(n)) {
                _results = (SolrDocumentList) res.getVal(i);
            } else if ("sort_values".equals(n)) {
                _sortvalues = (NamedList<ArrayList>) res.getVal(i);
            } else if ("facet_counts".equals(n)) {
                _facetInfo = (NamedList<Object>) res.getVal(i);
                // extractFacetInfo inspects _results, so defer calling it
                // in case it hasn't been populated yet.
            } else if ("debug".equals(n)) {
                _debugInfo = (NamedList<Object>) res.getVal(i);
                extractDebugInfo(_debugInfo);
            } else if ("grouped".equals(n)) {
                _groupedInfo = (NamedList<Object>) res.getVal(i);
                extractGroupedInfo(_groupedInfo);
            } else if ("expanded".equals(n)) {
                NamedList map = (NamedList) res.getVal(i);
                _expandedResults = map.asMap(1);
            } else if ("highlighting".equals(n)) {
                _highlightingInfo = (NamedList<Object>) res.getVal(i);
                extractHighlightingInfo(_highlightingInfo);
            } else if ("spellcheck".equals(n)) {
                _spellInfo = (NamedList<Object>) res.getVal(i);
                extractSpellCheckInfo(_spellInfo);
            } else if ("clusters".equals(n)) {
                _clusterInfo = (ArrayList<NamedList<Object>>) res.getVal(i);
                extractClusteringInfo(_clusterInfo);
            } else if ("facets".equals(n)) {
                _jsonFacetingInfo = (NamedList<Object>) res.getVal(i);
                // Don't call extractJsonFacetingInfo(_jsonFacetingInfo) here in an effort to do it lazily
            } else if ("suggest".equals(n)) {
                _suggestInfo = (Map<String, NamedList<Object>>) res.getVal(i);
                extractSuggesterInfo(_suggestInfo);
            } else if ("stats".equals(n)) {
                _statsInfo = (NamedList<Object>) res.getVal(i);
                extractStatsInfo(_statsInfo);
            } else if ("terms".equals(n)) {
                _termsInfo = (NamedList<NamedList<Object>>) res.getVal(i);
                extractTermsInfo(_termsInfo);
            } else if ("moreLikeThis".equals(n)) {
                _moreLikeThisInfo = (NamedList<SolrDocumentList>) res.getVal(i);
            } else if (CursorMarkParams.CURSOR_MARK_NEXT.equals(n)) {
                _cursorMarkNext = (String) res.getVal(i);
            }
        }
        if (_facetInfo != null)
            extractFacetInfo(_facetInfo);
    }

    private void extractSpellCheckInfo(NamedList<Object> spellInfo) {
        _spellResponse = new SpellCheckResponse(spellInfo);
    }

    private void extractClusteringInfo(List<NamedList<Object>> clusterInfo) {
        _clusterResponse = new ClusteringResponse(clusterInfo);
    }

    private void extractJsonFacetingInfo(NamedList<Object> facetInfo) {
        _jsonFacetingResponse = new NestableJsonFacet(facetInfo);
    }

    private void extractSuggesterInfo(Map<String, NamedList<Object>> suggestInfo) {
        _suggestResponse = new SuggesterResponse(suggestInfo);
    }

    private void extractTermsInfo(NamedList<NamedList<Object>> termsInfo) {
        _termsResponse = new TermsResponse(termsInfo);
    }

    private void extractStatsInfo(NamedList<Object> info) {
        _fieldStatsInfo = extractFieldStatsInfo(info);
    }

    private Map<String, FieldStatsInfo> extractFieldStatsInfo(NamedList<Object> info) {
        if (info != null) {
            Map<String, FieldStatsInfo> fieldStatsInfoMap = new TreeMap<>();
            NamedList<NamedList<Object>> ff = (NamedList<NamedList<Object>>) info.get("stats_fields");
            if (ff != null) {
                for (Map.Entry<String, NamedList<Object>> entry : ff) {
                    NamedList<Object> v = entry.getValue();
                    if (v != null) {
                        fieldStatsInfoMap.put(entry.getKey(), new FieldStatsInfo(v, entry.getKey()));
                    }
                }
            }
            return fieldStatsInfoMap;
        }
        return null;
    }

    private void extractDebugInfo(NamedList<Object> debug) {
        _debugMap = new LinkedHashMap<>(); // keep the order
        for (Map.Entry<String, Object> info : debug) {
            _debugMap.put(info.getKey(), info.getValue());
        }

        // Parse out interesting bits from the debug info
        _explainMap = new HashMap<>();
        NamedList<Object> explain = (NamedList<Object>) _debugMap.get("explain");
        if (explain != null) {
            for (Map.Entry<String, Object> info : explain) {
                String key = info.getKey();
                _explainMap.put(key, info.getValue());
            }
        }
    }

    private void extractGroupedInfo(NamedList<Object> info) {
        if (info != null) {
            _groupResponse = new GroupResponse();
            int size = info.size();
            for (int i = 0; i < size; i++) {
                String fieldName = info.getName(i);
                Object fieldGroups = info.getVal(i);
                SimpleOrderedMap<Object> simpleOrderedMap = (SimpleOrderedMap<Object>) fieldGroups;

                Object oMatches = simpleOrderedMap.get("matches");
                Object oNGroups = simpleOrderedMap.get("ngroups");
                Object oGroups = simpleOrderedMap.get("groups");
                Object queryCommand = simpleOrderedMap.get("doclist");
                if (oMatches == null) {
                    continue;
                }

                if (oGroups != null) {
                    Integer iMatches = (Integer) oMatches;
                    ArrayList<Object> groupsArr = (ArrayList<Object>) oGroups;
                    GroupCommand groupedCommand;
                    if (oNGroups != null) {
                        Integer iNGroups = (Integer) oNGroups;
                        groupedCommand = new GroupCommand(fieldName, iMatches, iNGroups);
                    } else {
                        groupedCommand = new GroupCommand(fieldName, iMatches);
                    }

                    for (Object oGrp : groupsArr) {
                        SimpleOrderedMap grpMap = (SimpleOrderedMap) oGrp;
                        Object sGroupValue = grpMap.get("groupValue");
                        SolrDocumentList doclist = (SolrDocumentList) grpMap.get("doclist");
                        Group group = new Group(sGroupValue != null ? sGroupValue.toString() : null, doclist);
                        groupedCommand.add(group);
                    }

                    _groupResponse.add(groupedCommand);
                } else if (queryCommand != null) {
                    Integer iMatches = (Integer) oMatches;
                    GroupCommand groupCommand;
                    if (oNGroups != null) {
                        Integer iNGroups = (Integer) oNGroups;
                        groupCommand = new GroupCommand(fieldName, iMatches, iNGroups);
                    } else {
                        groupCommand = new GroupCommand(fieldName, iMatches);
                    }
                    SolrDocumentList docList = (SolrDocumentList) queryCommand;
                    groupCommand.add(new Group(fieldName, docList));
                    _groupResponse.add(groupCommand);
                }
            }
        }
    }

    private void extractHighlightingInfo(NamedList<Object> info) {
        _highlighting = new HashMap<>();
        for (Map.Entry<String, Object> doc : info) {
            Map<String, List<String>> fieldMap = new HashMap<>();
            _highlighting.put(doc.getKey(), fieldMap);

            NamedList<List<String>> fnl = (NamedList<List<String>>) doc.getValue();
            for (Map.Entry<String, List<String>> field : fnl) {
                fieldMap.put(field.getKey(), field.getValue());
            }
        }
    }

    private void extractFacetInfo(NamedList<Object> info) {
        // Parse the queries
        _facetQuery = new LinkedHashMap<>();
        NamedList<Integer> fq = (NamedList<Integer>) info.get("facet_queries");
        if (fq != null) {
            for (Map.Entry<String, Integer> entry : fq) {
                _facetQuery.put(entry.getKey(), entry.getValue());
            }
        }

        // Parse the facet info into fields
        // TODO?? The list could be <int> or <long>?  If always <long> then we can switch to <Long>
        NamedList<NamedList<Number>> ff = (NamedList<NamedList<Number>>) info.get("facet_fields");
        if (ff != null) {
            _facetFields = new ArrayList<>(ff.size());
            _limitingFacets = new ArrayList<>(ff.size());

            long minsize = _results == null ? Long.MAX_VALUE : _results.getNumFound();
            for (Map.Entry<String, NamedList<Number>> facet : ff) {
                FacetField f = new FacetField(facet.getKey());
                for (Map.Entry<String, Number> entry : facet.getValue()) {
                    f.add(entry.getKey(), entry.getValue().longValue());
                }

                _facetFields.add(f);
                FacetField nl = f.getLimitingFields(minsize);
                if (nl.getValueCount() > 0) {
                    _limitingFacets.add(nl);
                }
            }
        }

        //Parse range facets
        NamedList<NamedList<Object>> rf = (NamedList<NamedList<Object>>) info.get("facet_ranges");
        if (rf != null) {
            _facetRanges = extractRangeFacets(rf);
        }

        //Parse pivot facets
        NamedList pf = (NamedList) info.get("facet_pivot");
        if (pf != null) {
            _facetPivot = new NamedList<>();
            for (int i = 0; i < pf.size(); i++) {
                _facetPivot.add(pf.getName(i), readPivots((List<NamedList>) pf.getVal(i)));
            }
        }

        //Parse interval facets
        NamedList<NamedList<Object>> intervalsNL = (NamedList<NamedList<Object>>) info.get("facet_intervals");
        if (intervalsNL != null) {
            _intervalFacets = new ArrayList<>(intervalsNL.size());
            for (Map.Entry<String, NamedList<Object>> intervalField : intervalsNL) {
                String field = intervalField.getKey();
                List<IntervalFacet.Count> counts = new ArrayList<IntervalFacet.Count>(
                        intervalField.getValue().size());
                for (Map.Entry<String, Object> interval : intervalField.getValue()) {
                    counts.add(new IntervalFacet.Count(interval.getKey(), (Integer) interval.getValue()));
                }
                _intervalFacets.add(new IntervalFacet(field, counts));
            }
        }
    }

    private List<RangeFacet> extractRangeFacets(NamedList<NamedList<Object>> rf) {
        List<RangeFacet> facetRanges = new ArrayList<>(rf.size());

        for (Map.Entry<String, NamedList<Object>> facet : rf) {
            NamedList<Object> values = facet.getValue();
            Object rawGap = values.get("gap");

            RangeFacet rangeFacet;
            if (rawGap instanceof Number) {
                Number gap = (Number) rawGap;
                Number start = (Number) values.get("start");
                Number end = (Number) values.get("end");

                Number before = (Number) values.get("before");
                Number after = (Number) values.get("after");
                Number between = (Number) values.get("between");

                rangeFacet = new RangeFacet.Numeric(facet.getKey(), start, end, gap, before, after, between);
            } else if (rawGap instanceof String && values.get("start") instanceof Date) {
                String gap = (String) rawGap;
                Date start = (Date) values.get("start");
                Date end = (Date) values.get("end");

                Number before = (Number) values.get("before");
                Number after = (Number) values.get("after");
                Number between = (Number) values.get("between");

                rangeFacet = new RangeFacet.Date(facet.getKey(), start, end, gap, before, after, between);
            } else {
                String gap = (String) rawGap;
                String start = (String) values.get("start");
                String end = (String) values.get("end");

                Number before = (Number) values.get("before");
                Number after = (Number) values.get("after");
                Number between = (Number) values.get("between");

                rangeFacet = new RangeFacet.Currency(facet.getKey(), start, end, gap, before, after, between);
            }

            NamedList<Integer> counts = (NamedList<Integer>) values.get("counts");
            for (Map.Entry<String, Integer> entry : counts) {
                rangeFacet.addCount(entry.getKey(), entry.getValue());
            }

            facetRanges.add(rangeFacet);
        }
        return facetRanges;
    }

    protected List<PivotField> readPivots(List<NamedList> list) {
        ArrayList<PivotField> values = new ArrayList<>(list.size());
        for (NamedList nl : list) {
            // NOTE, this is cheating, but we know the order they are written in, so no need to check
            assert "field".equals(nl.getName(0));
            String f = (String) nl.getVal(0);
            assert "value".equals(nl.getName(1));
            Object v = nl.getVal(1);
            assert "count".equals(nl.getName(2));
            int cnt = ((Integer) nl.getVal(2)).intValue();

            List<PivotField> subPivots = null;
            Map<String, FieldStatsInfo> fieldStatsInfos = null;
            Map<String, Integer> queryCounts = null;
            List<RangeFacet> ranges = null;

            if (4 <= nl.size()) {
                for (int index = 3; index < nl.size(); index++) {
                    final String key = nl.getName(index);
                    final Object val = nl.getVal(index);
                    switch (key) {

                    case "pivot": {
                        assert null != val : "Server sent back 'null' for sub pivots?";
                        assert val instanceof List : "Server sent non-List for sub pivots?";

                        subPivots = readPivots((List<NamedList>) val);
                        break;
                    }
                    case "stats": {
                        assert null != val : "Server sent back 'null' for stats?";
                        assert val instanceof NamedList : "Server sent non-NamedList for stats?";

                        fieldStatsInfos = extractFieldStatsInfo((NamedList<Object>) val);
                        break;
                    }
                    case "queries": {
                        // Parse the queries
                        queryCounts = new LinkedHashMap<>();
                        NamedList<Integer> fq = (NamedList<Integer>) val;
                        if (fq != null) {
                            for (Map.Entry<String, Integer> entry : fq) {
                                queryCounts.put(entry.getKey(), entry.getValue());
                            }
                        }
                        break;
                    }
                    case "ranges": {
                        ranges = extractRangeFacets((NamedList<NamedList<Object>>) val);
                        break;
                    }
                    default:
                        throw new RuntimeException("unknown key in pivot: " + key + " [" + val + "]");

                    }
                }
            }

            values.add(new PivotField(f, v, cnt, subPivots, fieldStatsInfos, queryCounts, ranges));
        }
        return values;
    }

    //------------------------------------------------------
    //------------------------------------------------------

    /**
     * Remove the field facet info
     */
    public void removeFacets() {
        _facetFields = new ArrayList<>();
    }

    //------------------------------------------------------
    //------------------------------------------------------

    public NamedList<Object> getHeader() {
        return _header;
    }

    public SolrDocumentList getResults() {
        return _results;
    }

    public NamedList<ArrayList> getSortValues() {
        return _sortvalues;
    }

    public Map<String, Object> getDebugMap() {
        return _debugMap;
    }

    public Map<String, Object> getExplainMap() {
        return _explainMap;
    }

    public Map<String, Integer> getFacetQuery() {
        return _facetQuery;
    }

    /**
     *
     * @return map with each group value as key and the expanded documents that belong to the group as value.
     * There is no guarantee on the order of the keys obtained via an iterator.
     *
     */
    public Map<String, SolrDocumentList> getExpandedResults() {
        return this._expandedResults;
    }

    /**
     * Returns the {@link GroupResponse} containing the group commands.
     * A group command can be the result of one of the following parameters:
     * <ul>
     *   <li>group.field
     *   <li>group.func
     *   <li>group.query
     * </ul>
     *
     * @return the {@link GroupResponse} containing the group commands
     */
    public GroupResponse getGroupResponse() {
        return _groupResponse;
    }

    public Map<String, Map<String, List<String>>> getHighlighting() {
        return _highlighting;
    }

    public SpellCheckResponse getSpellCheckResponse() {
        return _spellResponse;
    }

    public ClusteringResponse getClusteringResponse() {
        return _clusterResponse;
    }

    public NestableJsonFacet getJsonFacetingResponse() {
        if (_jsonFacetingInfo != null && _jsonFacetingResponse == null)
            extractJsonFacetingInfo(_jsonFacetingInfo);
        return _jsonFacetingResponse;
    }

    public SuggesterResponse getSuggesterResponse() {
        return _suggestResponse;
    }

    public TermsResponse getTermsResponse() {
        return _termsResponse;
    }

    public NamedList<SolrDocumentList> getMoreLikeThis() {
        return _moreLikeThisInfo;
    }

    /**
     * See also: {@link #getLimitingFacets()}
     */
    public List<FacetField> getFacetFields() {
        return _facetFields;
    }

    public List<FacetField> getFacetDates() {
        return _facetDates;
    }

    public List<RangeFacet> getFacetRanges() {
        return _facetRanges;
    }

    public NamedList<List<PivotField>> getFacetPivot() {
        return _facetPivot;
    }

    public List<IntervalFacet> getIntervalFacets() {
        return _intervalFacets;
    }

    /** get
     * 
     * @param name the name of the
     * @return the FacetField by name or null if it does not exist
     */
    public FacetField getFacetField(String name) {
        if (_facetFields == null)
            return null;
        for (FacetField f : _facetFields) {
            if (f.getName().equals(name))
                return f;
        }
        return null;
    }

    public FacetField getFacetDate(String name) {
        if (_facetDates == null)
            return null;
        for (FacetField f : _facetDates)
            if (f.getName().equals(name))
                return f;
        return null;
    }

    /**
     * @return a list of FacetFields where the count is less then
     * then #getResults() {@link SolrDocumentList#getNumFound()}
     * 
     * If you want all results exactly as returned by solr, use:
     * {@link #getFacetFields()}
     */
    public List<FacetField> getLimitingFacets() {
        return _limitingFacets;
    }

    public <T> List<T> getBeans(Class<T> type) {
        return solrClient == null ? new DocumentObjectBinder().getBeans(type, _results)
                : solrClient.getBinder().getBeans(type, _results);
    }

    public Map<String, FieldStatsInfo> getFieldStatsInfo() {
        return _fieldStatsInfo;
    }

    public String getNextCursorMark() {
        return _cursorMarkNext;
    }
}