@Test
  public void testMappedWorks() throws Exception {
    final List<SegmentAnalysis> results =
        getSegmentAnalysises(new QueryableIndexSegment("test_1", TestIndex.getMMappedTestIndex()));

    Assert.assertEquals(1, results.size());

    final SegmentAnalysis analysis = results.get(0);
    Assert.assertEquals("test_1", analysis.getId());

    final Map<String, ColumnAnalysis> columns = analysis.getColumns();
    Assert.assertEquals(
        TestIndex.COLUMNS.length - 1,
        columns.size()); // All columns including time and excluding empty/null column

    for (String dimension : TestIndex.DIMENSIONS) {
      final ColumnAnalysis columnAnalysis = columns.get(dimension);
      if (dimension.equals("null_column")) {
        Assert.assertNull(columnAnalysis);
      } else {
        Assert.assertEquals(dimension, ValueType.STRING.name(), columnAnalysis.getType());
        Assert.assertTrue(dimension, columnAnalysis.getSize() > 0);
        Assert.assertTrue(dimension, columnAnalysis.getCardinality() > 0);
      }
    }

    for (String metric : TestIndex.METRICS) {
      final ColumnAnalysis columnAnalysis = columns.get(metric);

      Assert.assertEquals(metric, ValueType.FLOAT.name(), columnAnalysis.getType());
      Assert.assertTrue(metric, columnAnalysis.getSize() > 0);
      Assert.assertNull(metric, columnAnalysis.getCardinality());
    }
  }
 @Override
 public SegmentAnalysis apply(SegmentAnalysis analysis) {
   return new SegmentAnalysis(
       analysis.getId(),
       analysis.getIntervals() != null
           ? JodaUtils.condenseIntervals(analysis.getIntervals())
           : null,
       analysis.getColumns(),
       analysis.getSize(),
       analysis.getNumRows());
 }
 @VisibleForTesting
 public static SegmentAnalysis finalizeAnalysis(SegmentAnalysis analysis) {
   return new SegmentAnalysis(
       analysis.getId(),
       analysis.getIntervals() != null
           ? JodaUtils.condenseIntervals(analysis.getIntervals())
           : null,
       analysis.getColumns(),
       analysis.getSize(),
       analysis.getNumRows(),
       analysis.getAggregators(),
       analysis.getQueryGranularity());
 }
  @VisibleForTesting
  public static SegmentAnalysis mergeAnalyses(
      final SegmentAnalysis arg1, final SegmentAnalysis arg2, boolean lenientAggregatorMerge) {
    if (arg1 == null) {
      return arg2;
    }

    if (arg2 == null) {
      return arg1;
    }

    List<Interval> newIntervals = null;
    if (arg1.getIntervals() != null) {
      newIntervals = Lists.newArrayList();
      newIntervals.addAll(arg1.getIntervals());
    }
    if (arg2.getIntervals() != null) {
      if (newIntervals == null) {
        newIntervals = Lists.newArrayList();
      }
      newIntervals.addAll(arg2.getIntervals());
    }

    final Map<String, ColumnAnalysis> leftColumns = arg1.getColumns();
    final Map<String, ColumnAnalysis> rightColumns = arg2.getColumns();
    Map<String, ColumnAnalysis> columns = Maps.newTreeMap();

    Set<String> rightColumnNames = Sets.newHashSet(rightColumns.keySet());
    for (Map.Entry<String, ColumnAnalysis> entry : leftColumns.entrySet()) {
      final String columnName = entry.getKey();
      columns.put(columnName, entry.getValue().fold(rightColumns.get(columnName)));
      rightColumnNames.remove(columnName);
    }

    for (String columnName : rightColumnNames) {
      columns.put(columnName, rightColumns.get(columnName));
    }

    final Map<String, AggregatorFactory> aggregators = Maps.newHashMap();

    if (lenientAggregatorMerge) {
      // Merge each aggregator individually, ignoring nulls
      for (SegmentAnalysis analysis : ImmutableList.of(arg1, arg2)) {
        if (analysis.getAggregators() != null) {
          for (AggregatorFactory aggregator : analysis.getAggregators().values()) {
            AggregatorFactory merged = aggregators.get(aggregator.getName());
            if (merged != null) {
              try {
                merged = merged.getMergingFactory(aggregator);
              } catch (AggregatorFactoryNotMergeableException e) {
                merged = null;
              }
            } else {
              merged = aggregator;
            }
            aggregators.put(aggregator.getName(), merged);
          }
        }
      }
    } else {
      final AggregatorFactory[] aggs1 =
          arg1.getAggregators() != null
              ? arg1.getAggregators()
                  .values()
                  .toArray(new AggregatorFactory[arg1.getAggregators().size()])
              : null;
      final AggregatorFactory[] aggs2 =
          arg2.getAggregators() != null
              ? arg2.getAggregators()
                  .values()
                  .toArray(new AggregatorFactory[arg2.getAggregators().size()])
              : null;
      final AggregatorFactory[] merged =
          AggregatorFactory.mergeAggregators(Arrays.asList(aggs1, aggs2));
      if (merged != null) {
        for (AggregatorFactory aggregator : merged) {
          aggregators.put(aggregator.getName(), aggregator);
        }
      }
    }

    final QueryGranularity queryGranularity =
        QueryGranularity.mergeQueryGranularities(
            Lists.newArrayList(arg1.getQueryGranularity(), arg2.getQueryGranularity()));

    final String mergedId;

    if (arg1.getId() != null && arg2.getId() != null && arg1.getId().equals(arg2.getId())) {
      mergedId = arg1.getId();
    } else {
      mergedId = "merged";
    }

    return new SegmentAnalysis(
        mergedId,
        newIntervals,
        columns,
        arg1.getSize() + arg2.getSize(),
        arg1.getNumRows() + arg2.getNumRows(),
        aggregators.isEmpty() ? null : aggregators,
        queryGranularity);
  }