/**
   * Creates an instance of StatsValues which supports values from the specified {@link StatsField}
   *
   * @param statsField {@link StatsField} whose statistics will be created by the resulting {@link
   *     StatsValues}
   * @return Instance of {@link StatsValues} that will create statistics from values from the
   *     specified {@link StatsField}
   */
  public static StatsValues createStatsValues(StatsField statsField) {

    final SchemaField sf = statsField.getSchemaField();

    if (null == sf) {
      // function stats
      return new NumericStatsValues(statsField);
    }

    final FieldType fieldType = sf.getType(); // TODO: allow FieldType to provide impl.

    if (TrieDateField.class.isInstance(fieldType)) {
      return new DateStatsValues(statsField);
    } else if (TrieField.class.isInstance(fieldType)) {
      return new NumericStatsValues(statsField);
    } else if (StrField.class.isInstance(fieldType)) {
      return new StringStatsValues(statsField);
    } else if (sf.getType().getClass().equals(EnumField.class)) {
      return new EnumStatsValues(statsField);
    } else {
      throw new SolrException(
          SolrException.ErrorCode.BAD_REQUEST,
          "Field type " + fieldType + " is not currently supported");
    }
  }
  protected AbstractStatsValues(StatsField statsField) {
    this.statsField = statsField;
    this.computeCount = statsField.calculateStats(Stat.count);
    this.computeMissing = statsField.calculateStats(Stat.missing);
    this.computeCalcDistinct =
        statsField.calculateStats(Stat.countDistinct)
            || statsField.calculateStats(Stat.distinctValues);
    this.computeMin = statsField.calculateStats(Stat.min);
    this.computeMax = statsField.calculateStats(Stat.max);
    this.computeMinOrMax = computeMin || computeMax;

    this.distinctValues = computeCalcDistinct ? new TreeSet<T>() : null;

    this.computeCardinality = statsField.calculateStats(Stat.cardinality);
    if (computeCardinality) {

      hasher = statsField.getHllOptions().getHasher();
      hll = statsField.getHllOptions().newHLL();
      assert null != hll : "Cardinality requires an HLL";
    } else {
      hll = null;
      hasher = null;
    }

    // alternatively, we could refactor a common base class that doesn't know/care
    // about either SchemaField or ValueSource - but then there would be a lot of
    // duplicate code between "NumericSchemaFieldStatsValues" and
    // "NumericValueSourceStatsValues" which would have diff parent classes
    //
    // part of the complexity here being that the StatsValues API serves two
    // masters: collecting concrete Values from things like DocValuesStats and
    // the distributed aggregation logic, but also collecting docIds which it
    // then
    // uses to go out and pull concreate values from the ValueSource
    // (from a func, or single valued field)
    if (null != statsField.getSchemaField()) {
      assert null == statsField.getValueSource();
      this.sf = statsField.getSchemaField();
      this.ft = sf.getType();
    } else {
      assert null != statsField.getValueSource();
      assert null == statsField.getSchemaField();
      this.sf = null;
      this.ft = null;
    }
  }