@Test
  public void testNoBuckets() throws Exception {
    SearchResponse response =
        client()
            .prepareSearch("idx")
            .addAggregation(
                terms("terms")
                    .field("tag")
                    .exclude("tag.*")
                    .subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)))
            .addAggregation(maxBucket("max_bucket").setBucketsPaths("terms>sum"))
            .execute()
            .actionGet();

    assertSearchResponse(response);

    Terms terms = response.getAggregations().get("terms");
    assertThat(terms, notNullValue());
    assertThat(terms.getName(), equalTo("terms"));
    List<Terms.Bucket> buckets = terms.getBuckets();
    assertThat(buckets.size(), equalTo(0));

    InternalBucketMetricValue maxBucketValue = response.getAggregations().get("max_bucket");
    assertThat(maxBucketValue, notNullValue());
    assertThat(maxBucketValue.getName(), equalTo("max_bucket"));
    assertThat(maxBucketValue.value(), equalTo(Double.NEGATIVE_INFINITY));
    assertThat(maxBucketValue.keys(), equalTo(new String[0]));
  }
  @Test
  public void singleValueField_WithMaxSize() throws Exception {
    SearchResponse response =
        client()
            .prepareSearch("idx")
            .setTypes("high_card_type")
            .addAggregation(
                terms("terms")
                    .executionHint(randomExecutionHint())
                    .field(SINGLE_VALUED_FIELD_NAME)
                    .size(20)
                    .order(
                        Terms.Order.term(
                            true))) // we need to sort by terms cause we're checking the first 20
                                    // values
            .execute()
            .actionGet();

    assertSearchResponse(response);

    Terms terms = response.getAggregations().get("terms");
    assertThat(terms, notNullValue());
    assertThat(terms.getName(), equalTo("terms"));
    assertThat(terms.getBuckets().size(), equalTo(20));

    for (int i = 0; i < 20; i++) {
      Terms.Bucket bucket = terms.getBucketByKey("val" + Strings.padStart(i + "", 3, '0'));
      assertThat(bucket, notNullValue());
      assertThat(key(bucket), equalTo("val" + Strings.padStart(i + "", 3, '0')));
      assertThat(bucket.getDocCount(), equalTo(1l));
    }
  }
  @Test
  public void singleValuedField_OrderedBySingleValueSubAggregationAsc() throws Exception {
    boolean asc = true;
    SearchResponse response =
        client()
            .prepareSearch("idx")
            .setTypes("type")
            .addAggregation(
                terms("terms")
                    .executionHint(randomExecutionHint())
                    .field(SINGLE_VALUED_FIELD_NAME)
                    .order(Terms.Order.aggregation("avg_i", asc))
                    .subAggregation(avg("avg_i").field("i")))
            .execute()
            .actionGet();

    assertSearchResponse(response);

    Terms terms = response.getAggregations().get("terms");
    assertThat(terms, notNullValue());
    assertThat(terms.getName(), equalTo("terms"));
    assertThat(terms.getBuckets().size(), equalTo(5));

    int i = 0;
    for (Terms.Bucket bucket : terms.getBuckets()) {
      assertThat(bucket, notNullValue());
      assertThat(key(bucket), equalTo("val" + i));
      assertThat(bucket.getDocCount(), equalTo(1l));
      Avg avg = bucket.getAggregations().get("avg_i");
      assertThat(avg, notNullValue());
      assertThat(avg.getValue(), equalTo((double) i));
      i++;
    }
  }
  @Test
  public void script_MultiValued() throws Exception {
    SearchResponse response =
        client()
            .prepareSearch("idx")
            .setTypes("type")
            .addAggregation(
                terms("terms")
                    .executionHint(randomExecutionHint())
                    .script("doc['" + MULTI_VALUED_FIELD_NAME + "'].values"))
            .execute()
            .actionGet();

    assertSearchResponse(response);

    Terms terms = response.getAggregations().get("terms");
    assertThat(terms, notNullValue());
    assertThat(terms.getName(), equalTo("terms"));
    assertThat(terms.getBuckets().size(), equalTo(6));

    for (int i = 0; i < 6; i++) {
      Terms.Bucket bucket = terms.getBucketByKey("val" + i);
      assertThat(bucket, notNullValue());
      assertThat(key(bucket), equalTo("val" + i));
      if (i == 0 || i == 5) {
        assertThat(bucket.getDocCount(), equalTo(1l));
      } else {
        assertThat(bucket.getDocCount(), equalTo(2l));
      }
    }
  }
  @Test
  public void script_SingleValue_WithSubAggregator_Inherited() throws Exception {
    SearchResponse response =
        client()
            .prepareSearch("idx")
            .setTypes("type")
            .addAggregation(
                terms("terms")
                    .executionHint(randomExecutionHint())
                    .script("doc['" + SINGLE_VALUED_FIELD_NAME + "'].value")
                    .subAggregation(count("count")))
            .execute()
            .actionGet();

    assertSearchResponse(response);

    Terms terms = response.getAggregations().get("terms");
    assertThat(terms, notNullValue());
    assertThat(terms.getName(), equalTo("terms"));
    assertThat(terms.getBuckets().size(), equalTo(5));

    for (int i = 0; i < 5; i++) {
      Terms.Bucket bucket = terms.getBucketByKey("val" + i);
      assertThat(bucket, notNullValue());
      assertThat(key(bucket), equalTo("val" + i));
      assertThat(bucket.getDocCount(), equalTo(1l));
      ValueCount valueCount = bucket.getAggregations().get("count");
      assertThat(valueCount, notNullValue());
      assertThat(valueCount.getValue(), equalTo(1l));
    }
  }
  @Test
  // the main purpose of this test is to make sure we're not allocating 2GB of memory per shard
  public void sizeIsZero() {
    final int minDocCount = randomInt(1);
    SearchResponse response =
        client()
            .prepareSearch("idx")
            .setTypes("high_card_type")
            .addAggregation(
                terms("terms")
                    .executionHint(randomExecutionHint())
                    .field(SINGLE_VALUED_FIELD_NAME)
                    .minDocCount(minDocCount)
                    .size(0))
            .execute()
            .actionGet();

    assertSearchResponse(response);

    Terms terms = response.getAggregations().get("terms");
    assertThat(terms, notNullValue());
    assertThat(terms.getName(), equalTo("terms"));
    assertThat(
        terms.getBuckets().size(),
        equalTo(minDocCount == 0 ? 105 : 100)); // 105 because of the other type
  }
  @Test
  public void partiallyUnmapped() throws Exception {
    SearchResponse response =
        client()
            .prepareSearch("idx", "idx_unmapped")
            .setTypes("type")
            .addAggregation(
                terms("terms").executionHint(randomExecutionHint()).field(SINGLE_VALUED_FIELD_NAME))
            .execute()
            .actionGet();

    assertSearchResponse(response);

    Terms terms = response.getAggregations().get("terms");
    assertThat(terms, notNullValue());
    assertThat(terms.getName(), equalTo("terms"));
    assertThat(terms.getBuckets().size(), equalTo(5));

    for (int i = 0; i < 5; i++) {
      Terms.Bucket bucket = terms.getBucketByKey("val" + i);
      assertThat(bucket, notNullValue());
      assertThat(key(bucket), equalTo("val" + i));
      assertThat(bucket.getDocCount(), equalTo(1l));
    }
  }
  @Test
  public void stringTermsNestedIntoPerBucketAggregator() throws Exception {
    // no execution hint so that the logic that decides whether or not to use ordinals is executed
    SearchResponse response =
        client()
            .prepareSearch("idx")
            .setTypes("type")
            .addAggregation(
                filter("filter")
                    .filter(termFilter(MULTI_VALUED_FIELD_NAME, "val3"))
                    .subAggregation(terms("terms").field(MULTI_VALUED_FIELD_NAME)))
            .execute()
            .actionGet();

    assertThat(response.getFailedShards(), equalTo(0));

    Filter filter = response.getAggregations().get("filter");

    Terms terms = filter.getAggregations().get("terms");
    assertThat(terms, notNullValue());
    assertThat(terms.getName(), equalTo("terms"));
    assertThat(terms.getBuckets().size(), equalTo(3));

    for (int i = 2; i <= 4; i++) {
      Terms.Bucket bucket = terms.getBucketByKey("val" + i);
      assertThat(bucket, notNullValue());
      assertThat(key(bucket), equalTo("val" + i));
      assertThat(bucket.getDocCount(), equalTo(i == 3 ? 2L : 1L));
    }
  }
  @Test
  public void singleValueField_OrderedByTermDesc() throws Exception {
    SearchResponse response =
        client()
            .prepareSearch("idx")
            .setTypes("type")
            .addAggregation(
                terms("terms")
                    .executionHint(randomExecutionHint())
                    .field(SINGLE_VALUED_FIELD_NAME)
                    .order(Terms.Order.term(false)))
            .execute()
            .actionGet();

    assertSearchResponse(response);

    Terms terms = response.getAggregations().get("terms");
    assertThat(terms, notNullValue());
    assertThat(terms.getName(), equalTo("terms"));
    assertThat(terms.getBuckets().size(), equalTo(5));

    int i = 4;
    for (Terms.Bucket bucket : terms.getBuckets()) {
      assertThat(bucket, notNullValue());
      assertThat(key(bucket), equalTo("val" + i));
      assertThat(bucket.getDocCount(), equalTo(1l));
      i--;
    }
  }
  @Test
  public void multiValuedField_WithValueScript_NotUnique() throws Exception {
    SearchResponse response =
        client()
            .prepareSearch("idx")
            .setTypes("type")
            .addAggregation(
                terms("terms")
                    .executionHint(randomExecutionHint())
                    .field(MULTI_VALUED_FIELD_NAME)
                    .script("_value.substring(0,3)"))
            .execute()
            .actionGet();

    assertSearchResponse(response);

    Terms terms = response.getAggregations().get("terms");
    assertThat(terms, notNullValue());
    assertThat(terms.getName(), equalTo("terms"));
    assertThat(terms.getBuckets().size(), equalTo(1));

    Terms.Bucket bucket = terms.getBucketByKey("val");
    assertThat(bucket, notNullValue());
    assertThat(key(bucket), equalTo("val"));
    assertThat(bucket.getDocCount(), equalTo(5l));
  }
  @Test
  public void testDocCount_asSubAgg() throws Exception {
    SearchResponse response =
        client()
            .prepareSearch("idx")
            .addAggregation(
                terms("terms")
                    .field("tag")
                    .order(Order.term(true))
                    .subAggregation(
                        histogram("histo")
                            .field(SINGLE_VALUED_FIELD_NAME)
                            .interval(interval)
                            .extendedBounds((long) minRandomValue, (long) maxRandomValue))
                    .subAggregation(maxBucket("max_bucket").setBucketsPaths("histo>_count")))
            .execute()
            .actionGet();

    assertSearchResponse(response);

    Terms terms = response.getAggregations().get("terms");
    assertThat(terms, notNullValue());
    assertThat(terms.getName(), equalTo("terms"));
    List<Terms.Bucket> termsBuckets = terms.getBuckets();
    assertThat(termsBuckets.size(), equalTo(interval));

    for (int i = 0; i < interval; ++i) {
      Terms.Bucket termsBucket = termsBuckets.get(i);
      assertThat(termsBucket, notNullValue());
      assertThat((String) termsBucket.getKey(), equalTo("tag" + (i % interval)));

      Histogram histo = termsBucket.getAggregations().get("histo");
      assertThat(histo, notNullValue());
      assertThat(histo.getName(), equalTo("histo"));
      List<? extends Bucket> buckets = histo.getBuckets();

      List<String> maxKeys = new ArrayList<>();
      double maxValue = Double.NEGATIVE_INFINITY;
      for (int j = 0; j < numValueBuckets; ++j) {
        Histogram.Bucket bucket = buckets.get(j);
        assertThat(bucket, notNullValue());
        assertThat(((Number) bucket.getKey()).longValue(), equalTo((long) j * interval));
        if (bucket.getDocCount() > maxValue) {
          maxValue = bucket.getDocCount();
          maxKeys = new ArrayList<>();
          maxKeys.add(bucket.getKeyAsString());
        } else if (bucket.getDocCount() == maxValue) {
          maxKeys.add(bucket.getKeyAsString());
        }
      }

      InternalBucketMetricValue maxBucketValue = termsBucket.getAggregations().get("max_bucket");
      assertThat(maxBucketValue, notNullValue());
      assertThat(maxBucketValue.getName(), equalTo("max_bucket"));
      assertThat(maxBucketValue.value(), equalTo(maxValue));
      assertThat(maxBucketValue.keys(), equalTo(maxKeys.toArray(new String[maxKeys.size()])));
    }
  }
  private void assertNoDocCountErrorSingleResponse(int size, SearchResponse testResponse) {
    Terms testTerms = testResponse.getAggregations().get("terms");
    assertThat(testTerms, notNullValue());
    assertThat(testTerms.getName(), equalTo("terms"));
    assertThat(testTerms.getDocCountError(), equalTo(0L));
    Collection<Bucket> testBuckets = testTerms.getBuckets();
    assertThat(testBuckets.size(), lessThanOrEqualTo(size));

    for (Terms.Bucket testBucket : testBuckets) {
      assertThat(testBucket, notNullValue());
      assertThat(testBucket.getDocCountError(), equalTo(0L));
    }
  }
  private void assertUnboundedDocCountError(
      int size, SearchResponse accurateResponse, SearchResponse testResponse) {
    Terms accurateTerms = accurateResponse.getAggregations().get("terms");
    assertThat(accurateTerms, notNullValue());
    assertThat(accurateTerms.getName(), equalTo("terms"));
    assertThat(accurateTerms.getDocCountError(), equalTo(0L));

    Terms testTerms = testResponse.getAggregations().get("terms");
    assertThat(testTerms, notNullValue());
    assertThat(testTerms.getName(), equalTo("terms"));
    assertThat(testTerms.getDocCountError(), anyOf(equalTo(-1L), equalTo(0L)));
    Collection<Bucket> testBuckets = testTerms.getBuckets();
    assertThat(testBuckets.size(), lessThanOrEqualTo(size));
    assertThat(accurateTerms.getBuckets().size(), greaterThanOrEqualTo(testBuckets.size()));

    for (Terms.Bucket testBucket : testBuckets) {
      assertThat(testBucket, notNullValue());
      Terms.Bucket accurateBucket = accurateTerms.getBucketByKey(testBucket.getKeyAsString());
      assertThat(accurateBucket, notNullValue());
      assertThat(accurateBucket.getDocCountError(), equalTo(0L));
      assertThat(testBucket.getDocCountError(), anyOf(equalTo(-1L), equalTo(0L)));
    }
  }
  @Test
  public void testMetric_topLevel() throws Exception {
    SearchResponse response =
        client()
            .prepareSearch("idx")
            .addAggregation(
                terms("terms")
                    .field("tag")
                    .subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)))
            .addAggregation(maxBucket("max_bucket").setBucketsPaths("terms>sum"))
            .execute()
            .actionGet();

    assertSearchResponse(response);

    Terms terms = response.getAggregations().get("terms");
    assertThat(terms, notNullValue());
    assertThat(terms.getName(), equalTo("terms"));
    List<Terms.Bucket> buckets = terms.getBuckets();
    assertThat(buckets.size(), equalTo(interval));

    List<String> maxKeys = new ArrayList<>();
    double maxValue = Double.NEGATIVE_INFINITY;
    for (int i = 0; i < interval; ++i) {
      Terms.Bucket bucket = buckets.get(i);
      assertThat(bucket, notNullValue());
      assertThat((String) bucket.getKey(), equalTo("tag" + (i % interval)));
      assertThat(bucket.getDocCount(), greaterThan(0l));
      Sum sum = bucket.getAggregations().get("sum");
      assertThat(sum, notNullValue());
      if (sum.value() > maxValue) {
        maxValue = sum.value();
        maxKeys = new ArrayList<>();
        maxKeys.add(bucket.getKeyAsString());
      } else if (sum.value() == maxValue) {
        maxKeys.add(bucket.getKeyAsString());
      }
    }

    InternalBucketMetricValue maxBucketValue = response.getAggregations().get("max_bucket");
    assertThat(maxBucketValue, notNullValue());
    assertThat(maxBucketValue.getName(), equalTo("max_bucket"));
    assertThat(maxBucketValue.value(), equalTo(maxValue));
    assertThat(maxBucketValue.keys(), equalTo(maxKeys.toArray(new String[maxKeys.size()])));
  }
  @Test
  public void singleValuedField_OrderedBySingleBucketSubAggregationAsc() throws Exception {
    boolean asc = randomBoolean();
    SearchResponse response =
        client()
            .prepareSearch("idx")
            .setTypes("type")
            .addAggregation(
                terms("tags")
                    .executionHint(randomExecutionHint())
                    .field("tag")
                    .order(Terms.Order.aggregation("filter", asc))
                    .subAggregation(filter("filter").filter(FilterBuilders.matchAllFilter())))
            .execute()
            .actionGet();

    assertSearchResponse(response);

    Terms tags = response.getAggregations().get("tags");
    assertThat(tags, notNullValue());
    assertThat(tags.getName(), equalTo("tags"));
    assertThat(tags.getBuckets().size(), equalTo(2));

    Iterator<Terms.Bucket> iters = tags.getBuckets().iterator();

    Terms.Bucket tag = iters.next();
    assertThat(tag, notNullValue());
    assertThat(key(tag), equalTo(asc ? "less" : "more"));
    assertThat(tag.getDocCount(), equalTo(asc ? 2l : 3l));
    Filter filter = tag.getAggregations().get("filter");
    assertThat(filter, notNullValue());
    assertThat(filter.getDocCount(), equalTo(asc ? 2l : 3l));

    tag = iters.next();
    assertThat(tag, notNullValue());
    assertThat(key(tag), equalTo(asc ? "more" : "less"));
    assertThat(tag.getDocCount(), equalTo(asc ? 3l : 2l));
    filter = tag.getAggregations().get("filter");
    assertThat(filter, notNullValue());
    assertThat(filter.getDocCount(), equalTo(asc ? 3l : 2l));
  }
  @Test
  public void unmapped() throws Exception {
    SearchResponse response =
        client()
            .prepareSearch("idx_unmapped")
            .setTypes("type")
            .addAggregation(
                terms("terms")
                    .executionHint(randomExecutionHint())
                    .size(randomInt(5))
                    .field(SINGLE_VALUED_FIELD_NAME))
            .execute()
            .actionGet();

    assertSearchResponse(response);

    Terms terms = response.getAggregations().get("terms");
    assertThat(terms, notNullValue());
    assertThat(terms.getName(), equalTo("terms"));
    assertThat(terms.getBuckets().size(), equalTo(0));
  }
  @Test
  public void emptyAggregation() throws Exception {
    prepareCreate("empty_bucket_idx")
        .addMapping("type", SINGLE_VALUED_FIELD_NAME, "type=integer")
        .execute()
        .actionGet();
    List<IndexRequestBuilder> builders = new ArrayList<IndexRequestBuilder>();
    for (int i = 0; i < 2; i++) {
      builders.add(
          client()
              .prepareIndex("empty_bucket_idx", "type", "" + i)
              .setSource(
                  jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, i * 2).endObject()));
    }
    indexRandom(true, builders.toArray(new IndexRequestBuilder[builders.size()]));

    SearchResponse searchResponse =
        client()
            .prepareSearch("empty_bucket_idx")
            .setQuery(matchAllQuery())
            .addAggregation(
                histogram("histo")
                    .field(SINGLE_VALUED_FIELD_NAME)
                    .interval(1l)
                    .minDocCount(0)
                    .subAggregation(terms("terms")))
            .execute()
            .actionGet();

    assertThat(searchResponse.getHits().getTotalHits(), equalTo(2l));
    Histogram histo = searchResponse.getAggregations().get("histo");
    assertThat(histo, Matchers.notNullValue());
    Histogram.Bucket bucket = histo.getBucketByKey(1l);
    assertThat(bucket, Matchers.notNullValue());

    Terms terms = bucket.getAggregations().get("terms");
    assertThat(terms, Matchers.notNullValue());
    assertThat(terms.getName(), equalTo("terms"));
    assertThat(terms.getBuckets().isEmpty(), is(true));
  }
  @Test
  public void multiValuedField_WithValueScript_WithInheritedSubAggregator() throws Exception {
    SearchResponse response =
        client()
            .prepareSearch("idx")
            .setTypes("type")
            .addAggregation(
                terms("terms")
                    .executionHint(randomExecutionHint())
                    .field(MULTI_VALUED_FIELD_NAME)
                    .script("'foo_' + _value")
                    .subAggregation(count("count")))
            .execute()
            .actionGet();

    assertSearchResponse(response);

    Terms terms = response.getAggregations().get("terms");
    assertThat(terms, notNullValue());
    assertThat(terms.getName(), equalTo("terms"));
    assertThat(terms.getBuckets().size(), equalTo(6));

    for (int i = 0; i < 6; i++) {
      Terms.Bucket bucket = terms.getBucketByKey("foo_val" + i);
      assertThat(bucket, notNullValue());
      assertThat(key(bucket), equalTo("foo_val" + i));
      if (i == 0 | i == 5) {
        assertThat(bucket.getDocCount(), equalTo(1l));
        ValueCount valueCount = bucket.getAggregations().get("count");
        assertThat(valueCount, notNullValue());
        assertThat(valueCount.getValue(), equalTo(2l));
      } else {
        assertThat(bucket.getDocCount(), equalTo(2l));
        ValueCount valueCount = bucket.getAggregations().get("count");
        assertThat(valueCount, notNullValue());
        assertThat("term[" + key(bucket) + "]", valueCount.getValue(), equalTo(4l));
      }
    }
  }
  @Test
  public void singleValuedField_OrderedBySubAggregationAsc_MultiHierarchyLevels() throws Exception {
    boolean asc = randomBoolean();
    SearchResponse response =
        client()
            .prepareSearch("idx")
            .setTypes("type")
            .addAggregation(
                terms("tags")
                    .executionHint(randomExecutionHint())
                    .field("tag")
                    .order(Terms.Order.aggregation("filter1>filter2>stats.max", asc))
                    .subAggregation(
                        filter("filter1")
                            .filter(FilterBuilders.matchAllFilter())
                            .subAggregation(
                                filter("filter2")
                                    .filter(FilterBuilders.matchAllFilter())
                                    .subAggregation(stats("stats").field("i")))))
            .execute()
            .actionGet();

    assertSearchResponse(response);

    Terms tags = response.getAggregations().get("tags");
    assertThat(tags, notNullValue());
    assertThat(tags.getName(), equalTo("tags"));
    assertThat(tags.getBuckets().size(), equalTo(2));

    Iterator<Terms.Bucket> iters = tags.getBuckets().iterator();

    // the max for "more" is 2
    // the max for "less" is 4

    Terms.Bucket tag = iters.next();
    assertThat(tag, notNullValue());
    assertThat(key(tag), equalTo(asc ? "more" : "less"));
    assertThat(tag.getDocCount(), equalTo(asc ? 3l : 2l));
    Filter filter1 = tag.getAggregations().get("filter1");
    assertThat(filter1, notNullValue());
    assertThat(filter1.getDocCount(), equalTo(asc ? 3l : 2l));
    Filter filter2 = filter1.getAggregations().get("filter2");
    assertThat(filter2, notNullValue());
    assertThat(filter2.getDocCount(), equalTo(asc ? 3l : 2l));
    Stats stats = filter2.getAggregations().get("stats");
    assertThat(stats, notNullValue());
    assertThat(stats.getMax(), equalTo(asc ? 2.0 : 4.0));

    tag = iters.next();
    assertThat(tag, notNullValue());
    assertThat(key(tag), equalTo(asc ? "less" : "more"));
    assertThat(tag.getDocCount(), equalTo(asc ? 2l : 3l));
    filter1 = tag.getAggregations().get("filter1");
    assertThat(filter1, notNullValue());
    assertThat(filter1.getDocCount(), equalTo(asc ? 2l : 3l));
    filter2 = filter1.getAggregations().get("filter2");
    assertThat(filter2, notNullValue());
    assertThat(filter2.getDocCount(), equalTo(asc ? 2l : 3l));
    stats = filter2.getAggregations().get("stats");
    assertThat(stats, notNullValue());
    assertThat(stats.getMax(), equalTo(asc ? 4.0 : 2.0));
  }
  @Test
  public void singleValueField_WithRegexFiltering() throws Exception {

    // include without exclude
    // we should be left with: val000, val001, val002, val003, val004, val005, val006, val007,
    // val008, val009

    SearchResponse response =
        client()
            .prepareSearch("idx")
            .setTypes("high_card_type")
            .addAggregation(terms("terms").field(SINGLE_VALUED_FIELD_NAME).include("val00.+"))
            .execute()
            .actionGet();

    assertSearchResponse(response);

    Terms terms = response.getAggregations().get("terms");
    assertThat(terms, notNullValue());
    assertThat(terms.getName(), equalTo("terms"));
    assertThat(terms.getBuckets().size(), equalTo(10));

    for (int i = 0; i < 10; i++) {
      Terms.Bucket bucket = terms.getBucketByKey("val00" + i);
      assertThat(bucket, notNullValue());
      assertThat(key(bucket), equalTo("val00" + i));
      assertThat(bucket.getDocCount(), equalTo(1l));
    }

    // include and exclude
    // we should be left with: val002, val003, val004, val005, val006, val007, val008, val009

    response =
        client()
            .prepareSearch("idx")
            .setTypes("high_card_type")
            .addAggregation(
                terms("terms")
                    .field(SINGLE_VALUED_FIELD_NAME)
                    .include("val00.+")
                    .exclude("(val000|val001)"))
            .execute()
            .actionGet();

    assertSearchResponse(response);

    terms = response.getAggregations().get("terms");
    assertThat(terms, notNullValue());
    assertThat(terms.getName(), equalTo("terms"));
    assertThat(terms.getBuckets().size(), equalTo(8));

    for (int i = 2; i < 10; i++) {
      Terms.Bucket bucket = terms.getBucketByKey("val00" + i);
      assertThat(bucket, notNullValue());
      assertThat(key(bucket), equalTo("val00" + i));
      assertThat(bucket.getDocCount(), equalTo(1l));
    }

    // exclude without include
    // we should be left with: val000, val001, val002, val003, val004, val005, val006, val007,
    // val008, val009

    response =
        client()
            .prepareSearch("idx")
            .setTypes("high_card_type")
            .addAggregation(terms("terms").field(SINGLE_VALUED_FIELD_NAME).exclude("val0[1-9]+.+"))
            .execute()
            .actionGet();

    assertSearchResponse(response);

    terms = response.getAggregations().get("terms");
    assertThat(terms, notNullValue());
    assertThat(terms.getName(), equalTo("terms"));
    assertThat(terms.getBuckets().size(), equalTo(10));

    for (int i = 0; i < 10; i++) {
      Terms.Bucket bucket = terms.getBucketByKey("val00" + i);
      assertThat(bucket, notNullValue());
      assertThat(key(bucket), equalTo("val00" + i));
      assertThat(bucket.getDocCount(), equalTo(1l));
    }
  }
  @Test
  public void singleValueField_WithRegexFiltering_WithFlags() throws Exception {

    // include without exclude
    // we should be left with: val000, val001, val002, val003, val004, val005, val006, val007,
    // val008, val009
    // with case insensitive flag on the include regex

    SearchResponse response =
        client()
            .prepareSearch("idx")
            .setTypes("high_card_type")
            .addAggregation(
                terms("terms")
                    .field(SINGLE_VALUED_FIELD_NAME)
                    .include("VAL00.+", Pattern.CASE_INSENSITIVE))
            .execute()
            .actionGet();

    assertSearchResponse(response);

    Terms terms = response.getAggregations().get("terms");
    assertThat(terms, notNullValue());
    assertThat(terms.getName(), equalTo("terms"));
    assertThat(terms.getBuckets().size(), equalTo(10));

    for (int i = 0; i < 10; i++) {
      Terms.Bucket bucket = terms.getBucketByKey("val00" + i);
      assertThat(bucket, notNullValue());
      assertThat(key(bucket), equalTo("val00" + i));
      assertThat(bucket.getDocCount(), equalTo(1l));
    }

    // include and exclude
    // we should be left with: val002, val003, val004, val005, val006, val007, val008, val009
    // with multi-flag masking on the exclude regex

    response =
        client()
            .prepareSearch("idx")
            .setTypes("high_card_type")
            .addAggregation(
                terms("terms")
                    .field(SINGLE_VALUED_FIELD_NAME)
                    .include("val00.+")
                    .exclude(
                        "( val000 | VAL001 )#this is a comment",
                        Pattern.CASE_INSENSITIVE | Pattern.COMMENTS))
            .execute()
            .actionGet();

    assertSearchResponse(response);

    terms = response.getAggregations().get("terms");
    assertThat(terms, notNullValue());
    assertThat(terms.getName(), equalTo("terms"));
    assertThat(terms.getBuckets().size(), equalTo(8));

    for (int i = 2; i < 10; i++) {
      Terms.Bucket bucket = terms.getBucketByKey("val00" + i);
      assertThat(bucket, notNullValue());
      assertThat(key(bucket), equalTo("val00" + i));
      assertThat(bucket.getDocCount(), equalTo(1l));
    }

    // exclude without include
    // we should be left with: val000, val001, val002, val003, val004, val005, val006, val007,
    // val008, val009
    // with a "no flag" flag

    response =
        client()
            .prepareSearch("idx")
            .setTypes("high_card_type")
            .addAggregation(
                terms("terms").field(SINGLE_VALUED_FIELD_NAME).exclude("val0[1-9]+.+", 0))
            .execute()
            .actionGet();

    assertSearchResponse(response);

    terms = response.getAggregations().get("terms");
    assertThat(terms, notNullValue());
    assertThat(terms.getName(), equalTo("terms"));
    assertThat(terms.getBuckets().size(), equalTo(10));

    for (int i = 0; i < 10; i++) {
      Terms.Bucket bucket = terms.getBucketByKey("val00" + i);
      assertThat(bucket, notNullValue());
      assertThat(key(bucket), equalTo("val00" + i));
      assertThat(bucket.getDocCount(), equalTo(1l));
    }
  }