Java tutorial
/* * 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; } }