All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.solr.search.facet.LegacyFacet Maven / Gradle / Ivy

There is a newer version: 9.7.0
Show newest version
/*
 * 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 static org.apache.solr.common.params.CommonParams.SORT;

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 java.util.Objects;
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.CollectionUtil;
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;

public class LegacyFacet {
  private SolrParams params;
  private Map json;
  private Map currentCommand = null; // always points to the current facet command
  private Map currentSubs; // always points to the current facet:{} block

  String facetValue;
  String key;
  SolrParams localParams;
  SolrParams orig;
  SolrParams required;

  Map> subFacets; // only parsed once

  public LegacyFacet(SolrParams params) {
    this.params = params;
    this.orig = params;
    this.json = new LinkedHashMap<>();
    this.currentSubs = json;
  }

  Map 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> parseSubFacets(SolrParams params) {
    Map> map = new HashMap<>();
    Iterator iter = params.getParameterNamesIterator();

    String SUBFACET = "subfacet.";
    while (iter.hasNext()) {
      String key = iter.next();

      if (key.startsWith(SUBFACET)) {
        List 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 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 cmd = CollectionUtil.newHashMap(2);
    Map type = CollectionUtil.newHashMap(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 cmd = CollectionUtil.newHashMap(5);
    Map type = CollectionUtil.newHashMap(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 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 type = CollectionUtil.newHashMap(1);
    type.put("terms", cmd);

    addSub(key, type);
    handleSubs(cmd);
  }

  private void handleSubs(Map cmd) {
    Map savedCmd = currentCommand;
    Map 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 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 sub) {
    getCurrentSubs().put(key, sub);
  }

  private Map 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 (!Objects.equals(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);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy