/** * 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 org.apache.solr.client.solrj.SolrServer; import org.apache.solr.client.solrj.beans.DocumentObjectBinder; import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SimpleOrderedMap; import java.util.*; /** * * @version $Id: QueryResponse.java 1180378 2011-10-08 14:14:08Z mvg $ * @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 Object _mdrillData = null; public Object get_mdrillData() { return _mdrillData; } private NamedList<Object> _debugInfo = null; private NamedList<Object> _highlightingInfo = null; private NamedList<Object> _spellInfo = null; private NamedList<Object> _statsInfo = null; private NamedList<Object> _termsInfo = null; // Grouping response private NamedList<Object> _groupedInfo = null; private GroupResponse _groupResponse = 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; // Highlight Info private Map<String,Map<String,List<String>>> _highlighting = null; // SpellCheck Response private SpellCheckResponse _spellResponse = 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,String> _explainMap = null; // utility variable used for automatic binding -- it should not be serialized private transient final SolrServer solrServer; public QueryResponse(){ solrServer = null; } /** * Utility constructor to set the solrServer and namedList */ public QueryResponse( NamedList<Object> res , SolrServer solrServer){ this.setResponse( res ); this.solrServer = solrServer; } Map<String,String> timetaken=new LinkedHashMap<String,String>(); public ArrayList<String> getTimetaken(int n) { Map<String,ArrayList<String>> rtn=new HashMap<String,ArrayList<String>>(); for(Map.Entry<String,String> e:timetaken.entrySet()) { String key=e.getKey(); String val=e.getValue(); String[] arr=key.split("@"); ArrayList<String> list=rtn.get(arr[0]); if(list==null) { list=new ArrayList<String>(); rtn.put(arr[0], list); } list.add(key+"@"+val); } ArrayList<String> rtntop=new ArrayList<String>(); for(Map.Entry<String,ArrayList<String>> e:rtn.entrySet()) { ArrayList<String> list=new ArrayList<String>(e.getValue()); Collections.sort(list,new Comparator<String>() { @Override public int compare(String o1, String o2) { String[] arr1=o1.split("@"); String[] arr2=o2.split("@"); int cmp1=0; if(arr1.length>3&&arr2.length>3) { cmp1=arr2[3].compareTo(arr1[3]); } if(cmp1==0) { cmp1=o2.compareTo(o1); } return cmp1; } }); rtntop.addAll(list.subList(0, Math.min(list.size(), n))); } return rtntop; } @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( "mdrill_data".equals( n ) ) { _mdrillData = res.getVal( i ); } else if( "mdrill_shard_time".equals( n ) ) { timetaken = (Map<String,String>) res.getVal( i ); } 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( "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 ( "stats".equals( n ) ) { _statsInfo = (NamedList<Object>) res.getVal( i ); extractStatsInfo(_statsInfo); } else if ( "terms".equals( n ) ) { _termsInfo = (NamedList<Object>) res.getVal( i ); extractTermsInfo( _termsInfo ); } } if(_facetInfo != null) extractFacetInfo( _facetInfo ); } private void extractSpellCheckInfo(NamedList<Object> spellInfo) { _spellResponse = new SpellCheckResponse(spellInfo); } private void extractTermsInfo(NamedList<Object> termsInfo) { _termsResponse = new TermsResponse(termsInfo); } private void extractStatsInfo(NamedList<Object> info) { if( info != null ) { _fieldStatsInfo = new HashMap<String, FieldStatsInfo>(); 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 ) { _fieldStatsInfo.put( entry.getKey(), new FieldStatsInfo( v, entry.getKey() ) ); } } } } } private void extractDebugInfo( NamedList<Object> debug ) { _debugMap = new LinkedHashMap<String, Object>(); // 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<String, String>(); NamedList<String> explain = (NamedList<String>)_debugMap.get( "explain" ); if( explain != null ) { for( Map.Entry<String, String> 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 = 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<String,Map<String,List<String>>>(); for( Map.Entry<String, Object> doc : info ) { Map<String,List<String>> fieldMap = new HashMap<String, List<String>>(); _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<String, Integer>(); 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<Object>> ff = (NamedList<NamedList<Object>>) info.get( "facet_fields" ); if( ff != null ) { _facetFields = new ArrayList<FacetField>( ff.size() ); _limitingFacets = new ArrayList<FacetField>( ff.size() ); long minsize = _results == null ? Long.MAX_VALUE :_results.getNumFound(); for( Map.Entry<String,NamedList<Object>> facet : ff ) {//遍历“facet_fields”,如果用了cross这里面只有一个值就是solrCorssFields_s。如果没用cross,就是普通情况 FacetField f = new FacetField( facet.getKey() );//写"solrCorssFields_s"或者普通的facet field name到FacetField if(facet.getKey().equals("solrCorssFields_s")){ //cross也有两种情况,一种是计算fl的sum,max,min。另一种是计算fl的进一步分组的组数(distinct count) int count1=0; for( Map.Entry<String, Object> entry : facet.getValue() ) {//遍历"solrCorssFields_s" NamedList<Object> nl = (NamedList<Object>)entry.getValue();//key是每个具体的cross串,value是具体信息的list if(count1==0){//entry.getKey().equals("recordcount")){//第一行的名字是recordcount,那么这一行就是分组的组数。 if(nl.get("recordcount") != null){//第一行下面有count(很大的数)和recordcount(真实值)两部分 long c = ((Number)nl.get("recordcount")).longValue(); f.setTotal(c); } }else{ int count2=0; long count=0; ArrayList<String> ext = new ArrayList<String>(); for( Map.Entry<String, Object> entry2 : nl) {//遍历每个"交叉分组串"的list单元,提取count,以及fl,dist等信息 if(count2==0){//每个组的第一行总是count count = ((Number)(nl.get("count"))).longValue();//得到count }else{ //在其他行得到信息 if(entry2.getValue() instanceof Number){//计算distinct的情况 ext.add(entry2.getKey()+","+((Number)(entry2.getValue())).intValue()); }else if(entry2.getValue() instanceof NamedList){//计算fl的情况(sum等) String name = entry2.getKey(); NamedList<Number> nl3 = (NamedList<Number>)entry2.getValue(); double sum = ((Number)(nl3.get("sum",0d))).doubleValue(); double max = ((Number)(nl3.get("max",0d))).doubleValue(); double min = ((Number)(nl3.get("min",0d))).doubleValue(); double dist = ((Number)(nl3.get("dist",0d))).doubleValue(); double cnt = ((Number)(nl3.get("cnt",0d))).doubleValue(); ext.add(name+","+sum+","+max+","+min+","+dist+","+cnt); } } count2++; } f.add( entry.getKey(), count, ext);//写入key(cross串)和value } count1++; } }else{//不用cross,普通情况 for( Map.Entry<String, Object> entry : facet.getValue() ) { Number value2 = (Number)entry.getValue(); f.add( entry.getKey(), value2.longValue() , null); } } _facetFields.add( f ); FacetField nl = f.getLimitingFields( minsize ); if( nl.getValueCount() > 0 ) { _limitingFacets.add( nl ); } } } //Parse date facets NamedList<NamedList<Object>> df = (NamedList<NamedList<Object>>) info.get("facet_dates"); if (df != null) { // System.out.println(df); _facetDates = new ArrayList<FacetField>( df.size() ); for (Map.Entry<String, NamedList<Object>> facet : df) { // System.out.println("Key: " + facet.getKey() + " Value: " + facet.getValue()); NamedList<Object> values = facet.getValue(); String gap = (String) values.get("gap"); Date end = (Date) values.get("end"); FacetField f = new FacetField(facet.getKey(), gap, end); for (Map.Entry<String, Object> entry : values) { try { f.add(entry.getKey(), Long.parseLong(entry.getValue().toString()), null); } catch (NumberFormatException e) { //Ignore for non-number responses which are already handled above } } _facetDates.add(f); } } //Parse range facets NamedList<NamedList<Object>> rf = (NamedList<NamedList<Object>>) info.get("facet_ranges"); if (rf != null) { _facetRanges = new ArrayList<RangeFacet>( 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"); rangeFacet = new RangeFacet.Numeric(facet.getKey(), start, end, gap, before, after); } else { 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"); rangeFacet = new RangeFacet.Date(facet.getKey(), start, end, gap, before, after); } NamedList<Integer> counts = (NamedList<Integer>) values.get("counts"); for (Map.Entry<String, Integer> entry : counts) { rangeFacet.addCount(entry.getKey(), entry.getValue()); } _facetRanges.add(rangeFacet); } } } //------------------------------------------------------ //------------------------------------------------------ /** * Remove the field facet info */ public void removeFacets() { _facetFields = new ArrayList<FacetField>(); } //------------------------------------------------------ //------------------------------------------------------ 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, String> getExplainMap() { return _explainMap; } public Map<String,Integer> getFacetQuery() { return _facetQuery; } /** * 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 TermsResponse getTermsResponse() { return _termsResponse; } /** * See also: {@link #getLimitingFacets()} */ public List<FacetField> getFacetFields() { return _facetFields; } public List<FacetField> getFacetDates() { return _facetDates; } public List<RangeFacet> getFacetRanges() { return _facetRanges; } /** 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 solrServer == null ? new DocumentObjectBinder().getBeans(type,_results): solrServer.getBinder().getBeans(type, _results); } public Map<String, FieldStatsInfo> getFieldStatsInfo() { return _fieldStatsInfo; } }