gt:             duel.duel(new ConcurrentDuel.DuelJudge<Facets>() {
context: /*
   * Licensed to ElasticSearch and Shay Banon under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership. ElasticSearch 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.elasticsearch.search.facet;
  
  import org.apache.lucene.util.LuceneTestCase.Slow;
  import org.elasticsearch.ElasticSearchException;
  import org.elasticsearch.action.search.SearchRequestBuilder;
  import org.elasticsearch.action.search.SearchResponse;
  import org.elasticsearch.action.search.SearchType;
  import org.elasticsearch.action.search.ShardSearchFailure;
  import org.elasticsearch.client.Client;
  import org.elasticsearch.common.Priority;
  import org.elasticsearch.common.bytes.BytesArray;
  import org.elasticsearch.common.settings.Settings;
  import org.elasticsearch.common.unit.TimeValue;
  import org.elasticsearch.common.xcontent.XContentFactory;
  import org.elasticsearch.search.facet.datehistogram.DateHistogramFacet;
  import org.elasticsearch.search.facet.filter.FilterFacet;
  import org.elasticsearch.search.facet.histogram.HistogramFacet;
  import org.elasticsearch.search.facet.query.QueryFacet;
  import org.elasticsearch.search.facet.range.RangeFacet;
  import org.elasticsearch.search.facet.statistical.StatisticalFacet;
  import org.elasticsearch.search.facet.terms.TermsFacet;
  import org.elasticsearch.search.facet.terms.TermsFacet.Entry;
  import org.elasticsearch.search.facet.terms.doubles.InternalDoubleTermsFacet;
  import org.elasticsearch.search.facet.terms.longs.InternalLongTermsFacet;
  import org.elasticsearch.search.facet.termsstats.TermsStatsFacet;
  import org.elasticsearch.test.AbstractIntegrationTest;
  import org.joda.time.DateTimeZone;
  import org.joda.time.format.ISODateTimeFormat;
  import org.junit.Test;
  
  import java.io.IOException;
  import java.util.List;
  import java.util.concurrent.ExecutionException;
  import java.util.concurrent.TimeUnit;
  import java.util.concurrent.atomic.AtomicInteger;
  
  import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
  import static org.elasticsearch.index.query.FilterBuilders.termFilter;
  import static org.elasticsearch.index.query.QueryBuilders.*;
  import static org.elasticsearch.search.facet.FacetBuilders.*;
  import static org.elasticsearch.test.hamcrest.ElasticSearchAssertions.assertHitCount;
  import static org.hamcrest.Matchers.*;
  
  /**
   *
   */
  public class SimpleFacetsTests extends AbstractIntegrationTest {
  
  
      @Override
      public Settings getSettings() {
          return randomSettingsBuilder()
                  .put("index.number_of_shards", numberOfShards())
                  .put("index.number_of_replicas", 0)
                  .build();
      }
  
      protected int numberOfShards() {
          return 1;
      }
  
      protected int numberOfRuns() {
          return 5;
      }
  
      @Test
      public void testSimpleFacetEmptyFacetFilter() throws Exception {
          createIndex("test");
          ensureGreen();
          client().prepareIndex("test", "type1").setSource(jsonBuilder().startObject()
                  .field("tag", "green")
                  .endObject()).execute().actionGet();
          refresh();
  
          SearchResponse searchResponse = client().prepareSearch()
                  .setSearchType(SearchType.COUNT)
                  .setFacets(new BytesArray(
                          "{\"facet1\":{\"filter\":{ }}}").array())
                  .get();
  
          assertHitCount(searchResponse, 1l);
          assertThat(searchResponse.getHits().hits().length, equalTo(0));
          FilterFacet facet = searchResponse.getFacets().facet("facet1");
          assertThat(facet.getName(), equalTo("facet1"));
          assertThat(facet.getCount(), equalTo(0l));
      }
  
      @Test
      public void testSimpleFacetEmptyFilterFacet() throws Exception {
          createIndex("test");
          ensureGreen();
          client().prepareIndex("test", "type1").setSource(jsonBuilder().startObject()
                  .field("tag", "green")
                  .endObject()).execute().actionGet();
          refresh();
  
          SearchResponse searchResponse = client().prepareSearch()
                  .setSearchType(SearchType.COUNT)
                  .setFacets(new BytesArray(
                          "{\"facet1\":{\"terms\":{\"field\":\"tag\"},\"facet_filter\":{ }}}").array())
                  .get();
  
          assertHitCount(searchResponse, 1l);
          assertThat(searchResponse.getHits().hits().length, equalTo(0));
          TermsFacet facet = searchResponse.getFacets().facet("facet1");
          assertThat(facet.getName(), equalTo("facet1"));
          assertThat(facet.getEntries().size(), equalTo(1));
          assertThat(facet.getEntries().get(0).getTerm().string(), equalTo("green"));
          assertThat(facet.getEntries().get(0).getCount(), equalTo(1));
      }
  
      @Test
      public void testBinaryFacet() throws Exception {
          createIndex("test");
          client().admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet();
  
          client().admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet();
  
          client().prepareIndex("test", "type1").setSource(jsonBuilder().startObject()
                  .field("tag", "green")
                  .endObject()).execute().actionGet();
  
          flushAndRefresh();
  
          client().prepareIndex("test", "type1").setSource(jsonBuilder().startObject()
                  .field("tag", "blue")
                  .endObject()).execute().actionGet();
  
          client().admin().indices().prepareRefresh().execute().actionGet();
  
          for (int i = 0; i < numberOfRuns(); i++) {
              SearchResponse searchResponse = client().prepareSearch()
                      .setSearchType(SearchType.COUNT)
                      .setFacets(XContentFactory.jsonBuilder().startObject()
                              .startObject("facet1")
                              .startObject("terms")
                              .field("field", "tag")
                              .endObject()
                              .endObject()
                              .endObject().bytes())
                      .execute().actionGet();
  
              assertThat(searchResponse.getHits().totalHits(), equalTo(2l));
              assertThat(searchResponse.getHits().hits().length, equalTo(0));
              TermsFacet facet = searchResponse.getFacets().facet("facet1");
              assertThat(facet.getName(), equalTo("facet1"));
              assertThat(facet.getEntries().size(), equalTo(2));
              assertThat(facet.getEntries().get(0).getTerm().string(), anyOf(equalTo("green"), equalTo("blue")));
              assertThat(facet.getEntries().get(0).getCount(), equalTo(1));
              assertThat(facet.getEntries().get(1).getTerm().string(), anyOf(equalTo("green"), equalTo("blue")));
              assertThat(facet.getEntries().get(1).getCount(), equalTo(1));
          }
      }
  
      @Test
      public void testFacetNumeric() throws ElasticSearchException, IOException {
          prepareCreate("test").addMapping("type", jsonBuilder().startObject().startObject("type").startObject("properties")
                  .startObject("byte").field("type", "byte").endObject()
                  .startObject("short").field("type", "short").endObject()
                  .startObject("integer").field("type", "integer").endObject()
                  .startObject("long").field("type", "long").endObject()
                  .startObject("float").field("type", "float").endObject()
                  .startObject("double").field("type", "double").endObject()
                  .endObject().endObject().endObject())
                  .execute().actionGet();
          client().admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet();
  
          for (int i = 0; i < 100; i++) {
              client().prepareIndex("test", "type", "" + i).setSource(jsonBuilder().startObject()
                      .field("name", "" + i)
                      .field("multiValued", "" + i, "" + (90 + i % 10))
                      .field("byte", i)
                      .field("short", i + Byte.MAX_VALUE)
                      .field("integer", i + Short.MAX_VALUE)
                      .field("long", i + Integer.MAX_VALUE)
                      .field("float", (float) i)
                      .field("double", (double) i)
                      .endObject()).execute().actionGet();
          }
  
          for (int i = 0; i < 10; i++) {
              client().prepareIndex("test", "type", "" + (i + 100)).setSource(jsonBuilder().startObject()
                      .field("foo", "" + i)
                      .endObject()).execute().actionGet();
          }
  
          String[] execHint = new String[]{"map", null};
          for (String hint : execHint) {
  
              flushAndRefresh();
              SearchResponse searchResponse = client().prepareSearch()
                      .setQuery(matchAllQuery())
                      .addFacet(termsFacet("double").executionHint(hint).field("double").size(10))
                      .addFacet(termsFacet("float").executionHint(hint).field("float").size(10))
                      .addFacet(termsFacet("integer").executionHint(hint).field("integer").size(10))
                      .addFacet(termsFacet("long").executionHint(hint).field("long").size(10))
                      .addFacet(termsFacet("short").executionHint(hint).field("short").size(10))
                      .addFacet(termsFacet("byte").executionHint(hint).field("byte").size(10))
                      .addFacet(termsFacet("termFacet").executionHint(hint).field("name").size(10))
                      .addFacet(termsFacet("termFacetRegex").executionHint(hint).field("multiValued").regex("9\\d").size(20))
                      .addFacet(termsFacet("termFacetScript").executionHint(hint).field("multiValued").script("Integer.toHexString(Integer.parseInt(term))").size(10))
                      .addFacet(termsFacet("termFacetScriptRegex").executionHint(hint).field("multiValued").script("Integer.toHexString(Integer.parseInt(term))").regex("9\\d").size(20))
  
                      .execute().actionGet();
  
              assertThat(searchResponse.getHits().getTotalHits(), equalTo(110l));
              TermsFacet facet = searchResponse.getFacets().facet("termFacet");
              assertThat(facet.getName(), equalTo("termFacet"));
              assertThat(facet.getEntries().size(), equalTo(10));
              assertThat(facet.getTotalCount(), equalTo(100l));
              assertThat(facet.getOtherCount(), equalTo(90l));
              assertThat(facet.getMissingCount(), equalTo(10l));
  
              facet = searchResponse.getFacets().facet("termFacetRegex");
              assertThat(facet.getName(), equalTo("termFacetRegex"));
              assertThat(facet.getEntries().size(), equalTo(10));
              assertThat(facet.getTotalCount(), equalTo(190l));
              assertThat(facet.getOtherCount(), equalTo(90l));
              assertThat(facet.getMissingCount(), equalTo(10l));
  
              int count = 99;
              for (Entry entry : facet) {
                  assertThat(Integer.parseInt(entry.getTerm().string()), equalTo(count--));
                  assertThat(entry.getCount(), equalTo(10));
              }
  
              facet = searchResponse.getFacets().facet("termFacetScriptRegex");
              assertThat(facet.getName(), equalTo("termFacetScriptRegex"));
              assertThat(facet.getEntries().size(), equalTo(10));
              assertThat(facet.getTotalCount(), equalTo(190l));
              assertThat(facet.getOtherCount(), equalTo(90l));
              assertThat(facet.getMissingCount(), equalTo(10l));
  
              count = 99;
              for (Entry entry : facet) {
                  assertThat(entry.getTerm().string(), equalTo(Integer.toHexString(count--)));
                  assertThat(entry.getCount(), equalTo(10));
              }
  
              facet = searchResponse.getFacets().facet("termFacetScript");
              assertThat(facet.getName(), equalTo("termFacetScript"));
              assertThat(facet.getEntries().size(), equalTo(10));
              assertThat(facet.getTotalCount(), equalTo(190l));
              assertThat(facet.getOtherCount(), equalTo(90l));
              assertThat(facet.getMissingCount(), equalTo(10l));
  
              count = 99;
              for (Entry entry : facet) {
                  assertThat(entry.getTerm().string(), equalTo(Integer.toHexString(count--)));
                  assertThat(entry.getCount(), equalTo(10));
              }
  
              facet = searchResponse.getFacets().facet("double");
              assertThat(facet.getName(), equalTo("double"));
              assertThat(facet.getEntries().size(), equalTo(10));
              assertThat(facet.getTotalCount(), equalTo(100l));
              assertThat(facet.getOtherCount(), equalTo(90l));
              assertThat(facet.getMissingCount(), equalTo(10l));
  
              facet = searchResponse.getFacets().facet("float");
              assertThat(facet.getName(), equalTo("float"));
              assertThat(facet.getEntries().size(), equalTo(10));
              assertThat(facet.getTotalCount(), equalTo(100l));
              assertThat(facet.getOtherCount(), equalTo(90l));
              assertThat(facet.getMissingCount(), equalTo(10l));
  
              facet = searchResponse.getFacets().facet("long");
              assertThat(facet.getName(), equalTo("long"));
              assertThat(facet.getEntries().size(), equalTo(10));
              assertThat(facet.getTotalCount(), equalTo(100l));
              assertThat(facet.getOtherCount(), equalTo(90l));
              assertThat(facet.getMissingCount(), equalTo(10l));
  
              facet = searchResponse.getFacets().facet("integer");
              assertThat(facet.getName(), equalTo("integer"));
              assertThat(facet.getEntries().size(), equalTo(10));
              assertThat(facet.getTotalCount(), equalTo(100l));
              assertThat(facet.getOtherCount(), equalTo(90l));
              assertThat(facet.getMissingCount(), equalTo(10l));
  
              facet = searchResponse.getFacets().facet("short");
              assertThat(facet.getName(), equalTo("short"));
              assertThat(facet.getEntries().size(), equalTo(10));
              assertThat(facet.getTotalCount(), equalTo(100l));
              assertThat(facet.getOtherCount(), equalTo(90l));
              assertThat(facet.getMissingCount(), equalTo(10l));
          }
  
      }
  
  
      @Test
      @Slow
      public void testConcurrentFacets() throws ElasticSearchException, IOException, InterruptedException, ExecutionException {
          prepareCreate("test")
                  .addMapping("type", jsonBuilder().startObject().startObject("type").startObject("properties")
                          .startObject("byte").field("type", "byte").endObject()
                          .startObject("short").field("type", "short").endObject()
                          .startObject("integer").field("type", "integer").endObject()
                          .startObject("long").field("type", "long").endObject()
                          .startObject("float").field("type", "float").endObject()
                          .startObject("double").field("type", "double").endObject()
                          .endObject().endObject().endObject())
                  .execute().actionGet();
          client().admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet();
  
          for (int i = 0; i < 100; i++) {
              client().prepareIndex("test", "type", "" + i).setSource(jsonBuilder().startObject()
                      .field("name", "" + i)
                      .field("byte", i)
                      .field("short", i + Byte.MAX_VALUE)
                      .field("integer", i + Short.MAX_VALUE)
                      .field("long", i + Integer.MAX_VALUE)
                      .field("float", (float) i)
                      .field("double", (double) i)
                      .endObject()).execute().actionGet();
          }
  
          for (int i = 0; i < 10; i++) {
              client().prepareIndex("test", "type", "" + (i + 100)).setSource(jsonBuilder().startObject()
                      .field("foo", "" + i)
                      .endObject()).execute().actionGet();
          }
  
          flushAndRefresh();
          ConcurrentDuel<Facets> duel = new ConcurrentDuel<Facets>(5);
          {
              final Client cl = client();
  
              duel.duel(new ConcurrentDuel.DuelJudge<Facets>() {
  
                            @Override
                            public void judge(Facets firstRun, Facets result) {
                                for (Facet f : result) {
                                    TermsFacet facet = (TermsFacet) f;
                                    assertThat(facet.getName(), isIn(new String[]{"short", "double", "byte", "float", "integer", "long", "termFacet"}));
                                    TermsFacet firstRunFacet = (TermsFacet) firstRun.getFacets().get(facet.getName());
                                    assertThat(facet.getEntries().size(), equalTo(firstRunFacet.getEntries().size()));
  
                                    assertThat(facet.getEntries().size(), equalTo(10));
                                    assertThat(facet.getTotalCount(), equalTo(100l));
                                    assertThat(facet.getOtherCount(), equalTo(90l));
                                    assertThat(facet.getMissingCount(), equalTo(10l));
  
                                    List<? extends Entry> right = facet.getEntries();
                                    List<? extends Entry> left = firstRunFacet.getEntries();
  
                                    for (int i = 0; i < facet.getEntries().size(); i++) {
                                        assertThat(left.get(i).getTerm(), equalTo(right.get(i).getTerm()));
                                        assertThat(left.get(i).getCount(), equalTo(right.get(i).getCount()));
                                    }
                                }
                            }
                        }, new ConcurrentDuel.DuelExecutor<Facets>() {
                            AtomicInteger count = new AtomicInteger();
  
                            @Override
                            public Facets run() {
                                final SearchRequestBuilder facetRequest;
                                if (count.incrementAndGet() % 2 == 0) { // every second request is mapped
                                    facetRequest = cl.prepareSearch().setQuery(matchAllQuery())
                                            .addFacet(termsFacet("double").field("double").size(10))
                                            .addFacet(termsFacet("float").field("float").size(10))
                                            .addFacet(termsFacet("integer").field("integer").size(10))
                                            .addFacet(termsFacet("long").field("long").size(10))
                                            .addFacet(termsFacet("short").field("short").size(10))
                                            .addFacet(termsFacet("byte").field("byte").size(10))
                                            .addFacet(termsFacet("termFacet").field("name").size(10));
                                } else {
                                    facetRequest = cl.prepareSearch()
                                            .setQuery(matchAllQuery())
                                            .addFacet(termsFacet("double").executionHint("map").field("double").size(10))
                                            .addFacet(termsFacet("float").executionHint("map").field("float").size(10))
                                            .addFacet(termsFacet("integer").executionHint("map").field("integer").size(10))
                                            .addFacet(termsFacet("long").executionHint("map").field("long").size(10))
                                            .addFacet(termsFacet("short").executionHint("map").field("short").size(10))
                                            .addFacet(termsFacet("byte").executionHint("map").field("byte").size(10))
                                            .addFacet(termsFacet("termFacet").executionHint("map").field("name").size(10));
                                }
  
                                SearchResponse actionGet = facetRequest.execute().actionGet();
                                return actionGet.getFacets();
                            }
                        }, 5000
              );
          }
          {
              duel.duel(new ConcurrentDuel.DuelJudge<Facets>() {
  
                            @Override
                            public void judge(Facets firstRun, Facets result) {
                                for (Facet f : result) {
                                    TermsFacet facet = (TermsFacet) f;
                                    assertThat(facet.getName(), equalTo("termFacet"));
                                    TermsFacet firstRunFacet = (TermsFacet) firstRun.getFacets().get(facet.getName());
                                    assertThat(facet.getEntries().size(), equalTo(firstRunFacet.getEntries().size()));
  
                                    assertThat(facet.getEntries().size(), equalTo(10));
                                    assertThat(facet.getTotalCount(), equalTo(100l));
                                    assertThat(facet.getOtherCount(), equalTo(90l));
                                    assertThat(facet.getMissingCount(), equalTo(10l));
  
                                    List<? extends Entry> right = facet.getEntries();
                                    List<? extends Entry> left = firstRunFacet.getEntries();
  
                                    for (int i = 0; i < facet.getEntries().size(); i++) {
                                        assertThat(left.get(i).getTerm(), equalTo(right.get(i).getTerm()));
                                        assertThat(left.get(i).getCount(), equalTo(right.get(i).getCount()));
                                    }
                                }
                            }
                        }, new ConcurrentDuel.DuelExecutor<Facets>() {
                            AtomicInteger count = new AtomicInteger();
  
                            @Override
                            public Facets run() {
                                final SearchRequestBuilder facetRequest;
                                switch (count.incrementAndGet() % 6) {
                                    case 4:
                                        facetRequest = client().prepareSearch()
                                                .setQuery(matchAllQuery())
                                                .addFacet(termsFacet("termFacet").executionHint("map").field("name").script("\"\" + (Integer.parseInt(term) % 100)").size(10));
                                        break;
                                    case 3:
                                        facetRequest = client().prepareSearch()
                                                .setQuery(matchAllQuery())
                                                .addFacet(termsFacet("termFacet").field("name").regex("\\d+").size(10));
                                        break;
                                    case 2:
                                        facetRequest = client().prepareSearch()
                                                .setQuery(matchAllQuery())
                                                .addFacet(termsFacet("termFacet").executionHint("map").field("name").regex("\\d+").script("term").size(10));
                                        break;
                                    case 1:
                                        facetRequest = client().prepareSearch()
                                                .setQuery(matchAllQuery())
                                                .addFacet(termsFacet("termFacet").field("name").regex("\\d+").script("term").size(10));
                                        break;
                                    case 0:
                                        facetRequest = client().prepareSearch()
                                                .setQuery(matchAllQuery())
                                                .addFacet(termsFacet("termFacet").field("name").size(10));
                                        break;
                                    default:
                                        facetRequest = client().prepareSearch()
                                                .setQuery(matchAllQuery())
                                                .addFacet(termsFacet("termFacet").executionHint("map").field("name").size(10));
                                        break;
                                }
                                SearchResponse actionGet = facetRequest.execute().actionGet();
                                return actionGet.getFacets();
                            }
                        }, 5000
              );
          }
  
          duel.close();
      }
  
      @Test
      @Slow
      public void testDuelByteFieldDataImpl() throws ElasticSearchException, IOException, InterruptedException, ExecutionException {
          prepareCreate("test")
                  .addMapping("type", jsonBuilder().startObject().startObject("type").startObject("properties")
                          .startObject("name_paged")
                          .field("type", "string")
                          .startObject("fielddata").field("format", "paged_bytes").endObject()
                          .endObject()
                          .startObject("name_fst")
                          .field("type", "string")
                          .startObject("fielddata").field("format", "fst").endObject()
                          .endObject()
                          .startObject("name_paged_mv")
                          .field("type", "string")
                          .startObject("fielddata").field("format", "paged_bytes").endObject()
                          .endObject()
                          .startObject("name_fst_mv")
                          .field("type", "string")
                          .startObject("fielddata").field("format", "fst").endObject()
                          .endObject()
                          .startObject("filtered")
                          .field("type", "string")
                          .startObject("fielddata").field("format", "fst").startObject("filter")
                          .startObject("regex").field("pattern", "\\d{1,2}").endObject().endObject()
                          .endObject()
                                  // only 1 or 2 digits
                          .endObject()
                          .startObject("filtered_mv")
                          .field("type", "string")
                          .startObject("fielddata").field("format", "fst").startObject("filter")
                          .startObject("regex").field("pattern", "\\d{1,2}").endObject().endObject()
                          .endObject()
                          .endObject().endObject().endObject())
                  .execute().actionGet();
          client().admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet();
  
          for (int i = 0; i < 100; i++) {
              client().prepareIndex("test", "type", "" + i).setSource(jsonBuilder().startObject()
                      .field("name_paged", "" + i)
                      .field("name_fst", "" + i)
                      .field("filtered", "" + i)
                      .field("name_paged_mv", "" + i, "" + Math.min(99, i + 1))
                      .field("name_fst_mv", "" + i, "" + Math.min(99, i + 1))
                      .field("filtered_mv", "" + i, "" + Math.min(99, i + 1), "" + (100 + i))
                      .endObject()).execute().actionGet();
          }
  
          for (int i = 0; i < 10; i++) {
              client().prepareIndex("test", "type", "" + (i + 100)).setSource(jsonBuilder().startObject()
                      .field("foo", "" + i)
                      .endObject()).execute().actionGet();
          }
  
          flushAndRefresh();
          ConcurrentDuel<Facets> duel = new ConcurrentDuel<Facets>(5);
          String[] fieldPostFix = new String[]{"", "_mv"};
          for (final String postfix : fieldPostFix) {
  