public static RangeEndpointCalculator<? extends Comparable<?>> create(RangeFacetRequest request) {
   final SchemaField sf = request.getField();
   final FieldType ft = sf.getType();
   final RangeEndpointCalculator<?> calc;
   if (ft instanceof TrieField) {
     final TrieField trie = (TrieField) ft;
     switch (trie.getType()) {
       case FLOAT:
         calc = new FloatRangeEndpointCalculator(request);
         break;
       case DOUBLE:
         calc = new DoubleRangeEndpointCalculator(request);
         break;
       case INTEGER:
         calc = new IntegerRangeEndpointCalculator(request);
         break;
       case LONG:
         calc = new LongRangeEndpointCalculator(request);
         break;
       default:
         throw new SolrException(
             SolrException.ErrorCode.BAD_REQUEST,
             "Unable to range facet on tried field of unexpected type:" + sf.getName());
     }
   } else if (ft instanceof DateField) {
     calc = new DateRangeEndpointCalculator(request, null);
   } else {
     throw new SolrException(
         SolrException.ErrorCode.BAD_REQUEST, "Unable to range facet on field:" + sf);
   }
   return calc;
 }
  public void inform(IndexSchema schema) {
    FieldType doubleType = schema.getFieldTypeByName(doubleFieldName);
    if (doubleType == null) {
      throw new RuntimeException("Can not find double: " + doubleFieldName);
    }
    if (!(doubleType instanceof TrieDoubleField)) {
      throw new RuntimeException("double must be TrieDoubleField: " + doubleType);
    }

    // Just set these, delegate everything else to the field type
    int p = (INDEXED | TOKENIZED | OMIT_NORMS | OMIT_TF_POSITIONS);
    List<SchemaField> fields = new ArrayList<SchemaField>(schema.getFields().values());
    for (SchemaField sf : fields) {
      if (sf.getType() == this) {
        String name = sf.getName();
        schema
            .getFields()
            .put(
                name + TwoDoublesFieldInfo.SUFFIX_X,
                new SchemaField(name + TwoDoublesFieldInfo.SUFFIX_X, doubleType, p, null));
        schema
            .getFields()
            .put(
                name + TwoDoublesFieldInfo.SUFFIX_Y,
                new SchemaField(name + TwoDoublesFieldInfo.SUFFIX_Y, doubleType, p, null));
      }
    }

    TrieField df = (TrieField) doubleType;
    NumericFieldInfo info = new NumericFieldInfo();
    info.setPrecisionStep(df.getPrecisionStep());
    info.store = true; // TODO properties &...

    spatialStrategy = new TwoDoublesStrategy(ctx, info, FieldCache.NUMERIC_UTILS_DOUBLE_PARSER);
    spatialStrategy.setIgnoreIncompatibleGeometry(ignoreIncompatibleGeometry);
  }
  /**
   * Term counts for use in field faceting that resepcts the specified mincount - if mincount is
   * null, the "zeros" param is consulted for the appropriate backcompat default
   *
   * @see FacetParams#FACET_ZEROS
   */
  private NamedList<Integer> getTermCounts(String field, Integer mincount, ParsedParams parsed)
      throws IOException {
    final SolrParams params = parsed.params;
    final DocSet docs = parsed.docs;
    final int threads = parsed.threads;
    int offset = params.getFieldInt(field, FacetParams.FACET_OFFSET, 0);
    int limit = params.getFieldInt(field, FacetParams.FACET_LIMIT, 100);
    if (limit == 0) return new NamedList<>();
    if (mincount == null) {
      Boolean zeros = params.getFieldBool(field, FacetParams.FACET_ZEROS);
      // mincount = (zeros!=null && zeros) ? 0 : 1;
      mincount = (zeros != null && !zeros) ? 1 : 0;
      // current default is to include zeros.
    }
    boolean missing = params.getFieldBool(field, FacetParams.FACET_MISSING, false);
    // default to sorting if there is a limit.
    String sort =
        params.getFieldParam(
            field,
            FacetParams.FACET_SORT,
            limit > 0 ? FacetParams.FACET_SORT_COUNT : FacetParams.FACET_SORT_INDEX);
    String prefix = params.getFieldParam(field, FacetParams.FACET_PREFIX);
    String contains = params.getFieldParam(field, FacetParams.FACET_CONTAINS);
    boolean ignoreCase = params.getFieldBool(field, FacetParams.FACET_CONTAINS_IGNORE_CASE, false);

    NamedList<Integer> counts;
    SchemaField sf = searcher.getSchema().getField(field);
    FieldType ft = sf.getType();

    // determine what type of faceting method to use
    final String methodStr = params.getFieldParam(field, FacetParams.FACET_METHOD);
    FacetMethod method = null;
    if (FacetParams.FACET_METHOD_enum.equals(methodStr)) {
      method = FacetMethod.ENUM;
    } else if (FacetParams.FACET_METHOD_fcs.equals(methodStr)) {
      method = FacetMethod.FCS;
    } else if (FacetParams.FACET_METHOD_fc.equals(methodStr)) {
      method = FacetMethod.FC;
    }

    if (method == FacetMethod.ENUM && TrieField.getMainValuePrefix(ft) != null) {
      // enum can't deal with trie fields that index several terms per value
      method = sf.multiValued() ? FacetMethod.FC : FacetMethod.FCS;
    }

    if (method == null && ft instanceof BoolField) {
      // Always use filters for booleans... we know the number of values is very small.
      method = FacetMethod.ENUM;
    }

    final boolean multiToken = sf.multiValued() || ft.multiValuedFieldCache();

    if (ft.getNumericType() != null && !sf.multiValued()) {
      // the per-segment approach is optimal for numeric field types since there
      // are no global ords to merge and no need to create an expensive
      // top-level reader
      method = FacetMethod.FCS;
    }

    if (method == null) {
      // TODO: default to per-segment or not?
      method = FacetMethod.FC;
    }

    if (method == FacetMethod.FCS && multiToken) {
      // only fc knows how to deal with multi-token fields
      method = FacetMethod.FC;
    }

    if (method == FacetMethod.ENUM && sf.hasDocValues()) {
      // only fc can handle docvalues types
      method = FacetMethod.FC;
    }

    if (params.getFieldBool(field, GroupParams.GROUP_FACET, false)) {
      counts =
          getGroupedCounts(
              searcher,
              docs,
              field,
              multiToken,
              offset,
              limit,
              mincount,
              missing,
              sort,
              prefix,
              contains,
              ignoreCase);
    } else {
      assert method != null;
      switch (method) {
        case ENUM:
          assert TrieField.getMainValuePrefix(ft) == null;
          counts =
              getFacetTermEnumCounts(
                  searcher,
                  docs,
                  field,
                  offset,
                  limit,
                  mincount,
                  missing,
                  sort,
                  prefix,
                  contains,
                  ignoreCase,
                  params);
          break;
        case FCS:
          assert !multiToken;
          if (ft.getNumericType() != null && !sf.multiValued()) {
            // force numeric faceting
            if (prefix != null && !prefix.isEmpty()) {
              throw new SolrException(
                  ErrorCode.BAD_REQUEST,
                  FacetParams.FACET_PREFIX + " is not supported on numeric types");
            }
            if (contains != null && !contains.isEmpty()) {
              throw new SolrException(
                  ErrorCode.BAD_REQUEST,
                  FacetParams.FACET_CONTAINS + " is not supported on numeric types");
            }
            counts =
                NumericFacets.getCounts(
                    searcher, docs, field, offset, limit, mincount, missing, sort);
          } else {
            PerSegmentSingleValuedFaceting ps =
                new PerSegmentSingleValuedFaceting(
                    searcher,
                    docs,
                    field,
                    offset,
                    limit,
                    mincount,
                    missing,
                    sort,
                    prefix,
                    contains,
                    ignoreCase);
            Executor executor = threads == 0 ? directExecutor : facetExecutor;
            ps.setNumThreads(threads);
            counts = ps.getFacetCounts(executor);
          }
          break;
        case FC:
          counts =
              DocValuesFacets.getCounts(
                  searcher,
                  docs,
                  field,
                  offset,
                  limit,
                  mincount,
                  missing,
                  sort,
                  prefix,
                  contains,
                  ignoreCase);
          break;
        default:
          throw new AssertionError();
      }
    }

    return counts;
  }