| /* |
| * 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.search.facet; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.solr.common.SolrException; |
| import org.apache.solr.common.params.CommonParams; |
| import org.apache.solr.common.params.FacetParams; |
| import org.apache.solr.common.params.RequiredSolrParams; |
| import org.apache.solr.common.params.SolrParams; |
| import org.apache.solr.common.util.StrUtils; |
| import org.apache.solr.search.QueryParsing; |
| import org.apache.solr.search.SolrReturnFields; |
| import org.apache.solr.search.StrParser; |
| import org.apache.solr.search.SyntaxError; |
| |
| import static org.apache.solr.common.params.CommonParams.SORT; |
| |
| public class LegacyFacet { |
| private SolrParams params; |
| private Map<String,Object> json; |
| private Map<String,Object> currentCommand = null; // always points to the current facet command |
| private Map<String,Object> currentSubs; // always points to the current facet:{} block |
| |
| String facetValue; |
| String key; |
| SolrParams localParams; |
| SolrParams orig; |
| SolrParams required; |
| |
| Map<String, List<Subfacet>> subFacets; // only parsed once |
| |
| public LegacyFacet(SolrParams params) { |
| this.params = params; |
| this.orig = params; |
| this.json = new LinkedHashMap<>(); |
| this.currentSubs = json; |
| } |
| |
| |
| Map<String,Object> getLegacy() { |
| subFacets = parseSubFacets(params); |
| String[] queries = params.getParams(FacetParams.FACET_QUERY); |
| if (queries != null) { |
| for (String q : queries) { |
| addQueryFacet(q); |
| } |
| } |
| String[] fields = params.getParams(FacetParams.FACET_FIELD); |
| if (fields != null) { |
| for (String field : fields) { |
| addFieldFacet(field); |
| } |
| } |
| String[] ranges = params.getParams(FacetParams.FACET_RANGE); |
| if (ranges != null) { |
| for (String range : ranges) { |
| addRangeFacet(range); |
| } |
| } |
| // SolrCore.log.error("###################### JSON FACET:" + json); |
| return json; |
| } |
| |
| |
| protected static class Subfacet { |
| public String parentKey; |
| public String type; // query, range, field |
| public String value; // the actual field or the query, including possible local params |
| } |
| |
| |
| protected static Map<String, List<Subfacet>> parseSubFacets(SolrParams params) { |
| Map<String,List<Subfacet>> map = new HashMap<>(); |
| Iterator<String> iter = params.getParameterNamesIterator(); |
| |
| String SUBFACET="subfacet."; |
| while (iter.hasNext()) { |
| String key = iter.next(); |
| |
| if (key.startsWith(SUBFACET)) { |
| List<String> parts = StrUtils.splitSmart(key, '.'); |
| if (parts.size() != 3) { |
| throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "expected subfacet parameter name of the form subfacet.mykey.field, got:" + key); |
| } |
| Subfacet sub = new Subfacet(); |
| sub.parentKey = parts.get(1); |
| sub.type = parts.get(2); |
| sub.value = params.get(key); |
| |
| List<Subfacet> subs = map.get(sub.parentKey); |
| if (subs == null) { |
| subs = new ArrayList<>(1); |
| } |
| subs.add(sub); |
| map.put(sub.parentKey, subs); |
| } |
| } |
| |
| return map; |
| } |
| |
| |
| protected void addQueryFacet(String q) { |
| parseParams(FacetParams.FACET_QUERY, q); |
| Map<String,Object> cmd = new HashMap<String,Object>(2); |
| Map<String,Object> type = new HashMap<String,Object>(1); |
| type.put("query", cmd); |
| cmd.put("q", q); |
| addSub(key, type); |
| handleSubs(cmd); |
| } |
| |
| protected void addRangeFacet(String field) { |
| parseParams(FacetParams.FACET_RANGE, field); |
| Map<String,Object> cmd = new HashMap<String,Object>(5); |
| Map<String,Object> type = new HashMap<String,Object>(1); |
| type.put("range", cmd); |
| |
| String f = key; |
| cmd.put("field", facetValue); |
| cmd.put("start", required.getFieldParam(f,FacetParams.FACET_RANGE_START)); |
| cmd.put("end", required.getFieldParam(f,FacetParams.FACET_RANGE_END)); |
| cmd.put("gap", required.getFieldParam(f, FacetParams.FACET_RANGE_GAP)); |
| String[] p = params.getFieldParams(f, FacetParams.FACET_RANGE_OTHER); |
| if (p != null) cmd.put("other", p.length==1 ? p[0] : Arrays.asList(p)); |
| p = params.getFieldParams(f, FacetParams.FACET_RANGE_INCLUDE); |
| if (p != null) cmd.put("include", p.length==1 ? p[0] : Arrays.asList(p)); |
| |
| final int mincount = params.getFieldInt(f,FacetParams.FACET_MINCOUNT, 0); |
| cmd.put("mincount", mincount); |
| |
| boolean hardend = params.getFieldBool(f,FacetParams.FACET_RANGE_HARD_END,false); |
| if (hardend) cmd.put("hardend", hardend); |
| |
| addSub(key, type); |
| handleSubs(cmd); |
| } |
| |
| protected void addFieldFacet(String field) { |
| parseParams(FacetParams.FACET_FIELD, field); |
| |
| String f = key; // the parameter to use for per-field parameters... f.key.facet.limit=10 |
| |
| int offset = params.getFieldInt(f, FacetParams.FACET_OFFSET, 0); |
| int limit = params.getFieldInt(f, FacetParams.FACET_LIMIT, 10); |
| |
| int mincount = params.getFieldInt(f, FacetParams.FACET_MINCOUNT, 1); |
| |
| boolean missing = params.getFieldBool(f, FacetParams.FACET_MISSING, false); |
| |
| // default to sorting if there is a limit. |
| String sort = params.getFieldParam(f, FacetParams.FACET_SORT, limit>0 ? FacetParams.FACET_SORT_COUNT : FacetParams.FACET_SORT_INDEX); |
| String prefix = params.getFieldParam(f, FacetParams.FACET_PREFIX); |
| |
| Map<String,Object> cmd = new HashMap<>(); |
| cmd.put("field", facetValue); |
| if (offset != 0) cmd.put("offset", offset); |
| if (limit != 10) cmd.put("limit", limit); |
| if (mincount != 1) cmd.put("mincount", mincount); |
| if (missing) cmd.put("missing", missing); |
| if (prefix != null) cmd.put("prefix", prefix); |
| if (sort.equals("count")) { |
| // our default |
| } else if (sort.equals("index")) { |
| cmd.put(SORT, "index asc"); |
| } else { |
| cmd.put(SORT, sort); // can be sort by one of our stats |
| } |
| |
| Map<String,Object> type = new HashMap<>(1); |
| type.put("terms", cmd); |
| |
| addSub(key, type); |
| handleSubs(cmd); |
| } |
| |
| private void handleSubs(Map<String,Object> cmd) { |
| Map<String,Object> savedCmd = currentCommand; |
| Map<String,Object> savedSubs = currentSubs; |
| try { |
| currentCommand = cmd; |
| currentSubs = null; |
| |
| // parse stats for this facet |
| String[] stats = params.getFieldParams(key, "facet.stat"); |
| if (stats != null) { |
| for (String stat : stats) { |
| addStat(stat); |
| } |
| } |
| |
| List<Subfacet> subs = subFacets.get(key); |
| if (subs != null) { |
| for (Subfacet subfacet : subs) { |
| if ("field".equals(subfacet.type)) { |
| addFieldFacet(subfacet.value); |
| } else if ("query".equals(subfacet.type)) { |
| addQueryFacet(subfacet.value); |
| } else if ("range".equals(subfacet.type)) { |
| addQueryFacet(subfacet.value); |
| } |
| } |
| } |
| |
| |
| } finally { |
| currentCommand = savedCmd; |
| currentSubs = savedSubs; |
| } |
| } |
| |
| |
| private void addStat(String val) { |
| StrParser sp = new StrParser(val); |
| int start = 0; |
| sp.eatws(); |
| if (sp.pos >= sp.end) addStat(val, val); |
| |
| // try key:func() format |
| String key = null; |
| String funcStr = val; |
| |
| if (key == null) { |
| key = SolrReturnFields.getFieldName(sp); |
| if (key != null && sp.opt(':')) { |
| // OK, we got the key |
| funcStr = val.substring(sp.pos); |
| } else { |
| // an invalid key... it must not be present. |
| sp.pos = start; |
| key = null; |
| } |
| } |
| |
| if (key == null) { |
| key = funcStr; // not really ideal |
| } |
| |
| addStat(key, funcStr); |
| } |
| |
| private void addStat(String key, String val) { |
| if ("count".equals(val) || "count()".equals(val)) return; // we no longer have a count function, we always return the count |
| getCurrentSubs().put(key, val); |
| } |
| |
| private void addSub(String key, Map<String,Object> sub) { |
| getCurrentSubs().put(key, sub); |
| } |
| |
| @SuppressWarnings({"unchecked", "rawtypes"}) |
| private Map<String,Object> getCurrentSubs() { |
| if (currentSubs == null) { |
| currentSubs = new LinkedHashMap(); |
| currentCommand.put("facet", currentSubs); |
| } |
| return currentSubs; |
| } |
| |
| |
| |
| protected void parseParams(String type, String param) { |
| facetValue = param; |
| key = param; |
| |
| try { |
| localParams = QueryParsing.getLocalParams(param, orig); |
| |
| if (localParams == null) { |
| params = orig; |
| required = new RequiredSolrParams(params); |
| // setupStats(); |
| return; |
| } |
| |
| params = SolrParams.wrapDefaults(localParams, orig); |
| required = new RequiredSolrParams(params); |
| |
| // remove local params unless it's a query |
| if (type != FacetParams.FACET_QUERY) { |
| facetValue = localParams.get(CommonParams.VALUE); |
| } |
| |
| // reset set the default key now that localParams have been removed |
| key = facetValue; |
| |
| // allow explicit set of the key |
| key = localParams.get(CommonParams.OUTPUT_KEY, key); |
| |
| // setupStats(); |
| } catch (SyntaxError e) { |
| throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e); |
| } |
| } |
| |
| |
| } |