@Test
  public void testScoring() throws Exception {
    int numDocs = indexRandomNumbers("keyword");

    String[] scoreModes = new String[] {"max", "min", "avg", "total", "multiply", ""};
    float primaryWeight = 1.1f;
    float secondaryWeight = 1.6f;

    for (String scoreMode : scoreModes) {
      for (int i = 0; i < numDocs - 4; i++) {
        String[] intToEnglish =
            new String[] {
              English.intToEnglish(i),
              English.intToEnglish(i + 1),
              English.intToEnglish(i + 2),
              English.intToEnglish(i + 3)
            };

        QueryRescorer rescoreQuery =
            RescoreBuilder.queryRescorer(
                    QueryBuilders.boolQuery()
                        .disableCoord(true)
                        .should(
                            QueryBuilders.functionScoreQuery(
                                    QueryBuilders.termQuery("field1", intToEnglish[0]))
                                .boostMode(CombineFunction.REPLACE)
                                .add(ScoreFunctionBuilders.scriptFunction("5.0f")))
                        .should(
                            QueryBuilders.functionScoreQuery(
                                    QueryBuilders.termQuery("field1", intToEnglish[1]))
                                .boostMode(CombineFunction.REPLACE)
                                .add(ScoreFunctionBuilders.scriptFunction("7.0f")))
                        .should(
                            QueryBuilders.functionScoreQuery(
                                    QueryBuilders.termQuery("field1", intToEnglish[3]))
                                .boostMode(CombineFunction.REPLACE)
                                .add(ScoreFunctionBuilders.scriptFunction("0.0f"))))
                .setQueryWeight(primaryWeight)
                .setRescoreQueryWeight(secondaryWeight);

        if (!"".equals(scoreMode)) {
          rescoreQuery.setScoreMode(scoreMode);
        }

        SearchResponse rescored =
            client()
                .prepareSearch()
                .setPreference("test") // ensure we hit the same shards for tie-breaking
                .setQuery(
                    QueryBuilders.boolQuery()
                        .disableCoord(true)
                        .should(
                            QueryBuilders.functionScoreQuery(
                                    QueryBuilders.termQuery("field1", intToEnglish[0]))
                                .boostMode(CombineFunction.REPLACE)
                                .add(ScoreFunctionBuilders.scriptFunction("2.0f")))
                        .should(
                            QueryBuilders.functionScoreQuery(
                                    QueryBuilders.termQuery("field1", intToEnglish[1]))
                                .boostMode(CombineFunction.REPLACE)
                                .add(ScoreFunctionBuilders.scriptFunction("3.0f")))
                        .should(
                            QueryBuilders.functionScoreQuery(
                                    QueryBuilders.termQuery("field1", intToEnglish[2]))
                                .boostMode(CombineFunction.REPLACE)
                                .add(ScoreFunctionBuilders.scriptFunction("5.0f")))
                        .should(
                            QueryBuilders.functionScoreQuery(
                                    QueryBuilders.termQuery("field1", intToEnglish[3]))
                                .boostMode(CombineFunction.REPLACE)
                                .add(ScoreFunctionBuilders.scriptFunction("0.2f"))))
                .setFrom(0)
                .setSize(10)
                .setRescorer(rescoreQuery)
                .setRescoreWindow(50)
                .execute()
                .actionGet();

        assertHitCount(rescored, 4);

        if ("total".equals(scoreMode) || "".equals(scoreMode)) {
          assertFirstHit(rescored, hasId(String.valueOf(i + 1)));
          assertSecondHit(rescored, hasId(String.valueOf(i)));
          assertThirdHit(rescored, hasId(String.valueOf(i + 2)));
          assertThat(
              rescored.getHits().getHits()[0].getScore(),
              equalTo(3.0f * primaryWeight + 7.0f * secondaryWeight));
          assertThat(
              rescored.getHits().getHits()[1].getScore(),
              equalTo(2.0f * primaryWeight + 5.0f * secondaryWeight));
          assertThat(rescored.getHits().getHits()[2].getScore(), equalTo(5.0f * primaryWeight));
          assertThat(
              rescored.getHits().getHits()[3].getScore(),
              equalTo(0.2f * primaryWeight + 0.0f * secondaryWeight));
        } else if ("max".equals(scoreMode)) {
          assertFirstHit(rescored, hasId(String.valueOf(i + 1)));
          assertSecondHit(rescored, hasId(String.valueOf(i)));
          assertThirdHit(rescored, hasId(String.valueOf(i + 2)));
          assertThat(rescored.getHits().getHits()[0].getScore(), equalTo(7.0f * secondaryWeight));
          assertThat(rescored.getHits().getHits()[1].getScore(), equalTo(5.0f * secondaryWeight));
          assertThat(rescored.getHits().getHits()[2].getScore(), equalTo(5.0f * primaryWeight));
          assertThat(rescored.getHits().getHits()[3].getScore(), equalTo(0.2f * primaryWeight));
        } else if ("min".equals(scoreMode)) {
          assertFirstHit(rescored, hasId(String.valueOf(i + 2)));
          assertSecondHit(rescored, hasId(String.valueOf(i + 1)));
          assertThirdHit(rescored, hasId(String.valueOf(i)));
          assertThat(rescored.getHits().getHits()[0].getScore(), equalTo(5.0f * primaryWeight));
          assertThat(rescored.getHits().getHits()[1].getScore(), equalTo(3.0f * primaryWeight));
          assertThat(rescored.getHits().getHits()[2].getScore(), equalTo(2.0f * primaryWeight));
          assertThat(rescored.getHits().getHits()[3].getScore(), equalTo(0.0f * secondaryWeight));
        } else if ("avg".equals(scoreMode)) {
          assertFirstHit(rescored, hasId(String.valueOf(i + 1)));
          assertSecondHit(rescored, hasId(String.valueOf(i + 2)));
          assertThirdHit(rescored, hasId(String.valueOf(i)));
          assertThat(
              rescored.getHits().getHits()[0].getScore(),
              equalTo((3.0f * primaryWeight + 7.0f * secondaryWeight) / 2.0f));
          assertThat(rescored.getHits().getHits()[1].getScore(), equalTo(5.0f * primaryWeight));
          assertThat(
              rescored.getHits().getHits()[2].getScore(),
              equalTo((2.0f * primaryWeight + 5.0f * secondaryWeight) / 2.0f));
          assertThat(
              rescored.getHits().getHits()[3].getScore(), equalTo((0.2f * primaryWeight) / 2.0f));
        } else if ("multiply".equals(scoreMode)) {
          assertFirstHit(rescored, hasId(String.valueOf(i + 1)));
          assertSecondHit(rescored, hasId(String.valueOf(i)));
          assertThirdHit(rescored, hasId(String.valueOf(i + 2)));
          assertThat(
              rescored.getHits().getHits()[0].getScore(),
              equalTo(3.0f * primaryWeight * 7.0f * secondaryWeight));
          assertThat(
              rescored.getHits().getHits()[1].getScore(),
              equalTo(2.0f * primaryWeight * 5.0f * secondaryWeight));
          assertThat(rescored.getHits().getHits()[2].getScore(), equalTo(5.0f * primaryWeight));
          assertThat(
              rescored.getHits().getHits()[3].getScore(),
              equalTo(0.2f * primaryWeight * 0.0f * secondaryWeight));
        }
      }
    }
  }
  @Test
  public void testExplain() throws Exception {
    assertAcked(
        prepareCreate("test")
            .addMapping(
                "type1",
                jsonBuilder()
                    .startObject()
                    .startObject("type1")
                    .startObject("properties")
                    .startObject("field1")
                    .field("analyzer", "whitespace")
                    .field("type", "string")
                    .endObject()
                    .endObject()
                    .endObject()
                    .endObject()));
    ensureGreen();
    client()
        .prepareIndex("test", "type1", "1")
        .setSource("field1", "the quick brown fox")
        .execute()
        .actionGet();
    client()
        .prepareIndex("test", "type1", "2")
        .setSource("field1", "the quick lazy huge brown fox jumps over the tree")
        .execute()
        .actionGet();
    client()
        .prepareIndex("test", "type1", "3")
        .setSource(
            "field1",
            "quick huge brown",
            "field2",
            "the quick lazy huge brown fox jumps over the tree")
        .execute()
        .actionGet();
    refresh();

    {
      SearchResponse searchResponse =
          client()
              .prepareSearch()
              .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
              .setQuery(
                  QueryBuilders.matchQuery("field1", "the quick brown")
                      .operator(MatchQueryBuilder.Operator.OR))
              .setRescorer(
                  RescoreBuilder.queryRescorer(
                          QueryBuilders.matchPhraseQuery("field1", "the quick brown")
                              .slop(2)
                              .boost(4.0f))
                      .setQueryWeight(0.5f)
                      .setRescoreQueryWeight(0.4f))
              .setRescoreWindow(5)
              .setExplain(true)
              .execute()
              .actionGet();
      assertHitCount(searchResponse, 3);
      assertFirstHit(searchResponse, hasId("1"));
      assertSecondHit(searchResponse, hasId("2"));
      assertThirdHit(searchResponse, hasId("3"));

      for (int i = 0; i < 3; i++) {
        assertThat(searchResponse.getHits().getAt(i).explanation(), notNullValue());
        assertThat(searchResponse.getHits().getAt(i).explanation().isMatch(), equalTo(true));
        assertThat(searchResponse.getHits().getAt(i).explanation().getDetails().length, equalTo(2));
        assertThat(
            searchResponse.getHits().getAt(i).explanation().getDetails()[0].isMatch(),
            equalTo(true));
        if (i == 2) {
          assertThat(
              searchResponse.getHits().getAt(i).explanation().getDetails()[1].getValue(),
              equalTo(0.5f));
        } else {
          assertThat(
              searchResponse.getHits().getAt(i).explanation().getDescription(), equalTo("sum of:"));
          assertThat(
              searchResponse
                  .getHits()
                  .getAt(i)
                  .explanation()
                  .getDetails()[0]
                  .getDetails()[1]
                  .getValue(),
              equalTo(0.5f));
          assertThat(
              searchResponse
                  .getHits()
                  .getAt(i)
                  .explanation()
                  .getDetails()[1]
                  .getDetails()[1]
                  .getValue(),
              equalTo(0.4f));
        }
      }
    }

    String[] scoreModes = new String[] {"max", "min", "avg", "total", "multiply", ""};
    String[] descriptionModes =
        new String[] {"max of:", "min of:", "avg of:", "sum of:", "product of:", "sum of:"};
    for (int innerMode = 0; innerMode < scoreModes.length; innerMode++) {
      QueryRescorer innerRescoreQuery =
          RescoreBuilder.queryRescorer(
                  QueryBuilders.matchQuery("field1", "the quick brown").boost(4.0f))
              .setQueryWeight(0.5f)
              .setRescoreQueryWeight(0.4f);

      if (!"".equals(scoreModes[innerMode])) {
        innerRescoreQuery.setScoreMode(scoreModes[innerMode]);
      }

      SearchResponse searchResponse =
          client()
              .prepareSearch()
              .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
              .setQuery(
                  QueryBuilders.matchQuery("field1", "the quick brown")
                      .operator(MatchQueryBuilder.Operator.OR))
              .setRescorer(innerRescoreQuery)
              .setRescoreWindow(5)
              .setExplain(true)
              .execute()
              .actionGet();
      assertHitCount(searchResponse, 3);
      assertFirstHit(searchResponse, hasId("1"));
      assertSecondHit(searchResponse, hasId("2"));
      assertThirdHit(searchResponse, hasId("3"));

      for (int j = 0; j < 3; j++) {
        assertThat(
            searchResponse.getHits().getAt(j).explanation().getDescription(),
            equalTo(descriptionModes[innerMode]));
      }

      for (int outerMode = 0; outerMode < scoreModes.length; outerMode++) {
        QueryRescorer outerRescoreQuery =
            RescoreBuilder.queryRescorer(
                    QueryBuilders.matchQuery("field1", "the quick brown").boost(4.0f))
                .setQueryWeight(0.5f)
                .setRescoreQueryWeight(0.4f);

        if (!"".equals(scoreModes[outerMode])) {
          outerRescoreQuery.setScoreMode(scoreModes[outerMode]);
        }

        searchResponse =
            client()
                .prepareSearch()
                .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                .setQuery(
                    QueryBuilders.matchQuery("field1", "the quick brown")
                        .operator(MatchQueryBuilder.Operator.OR))
                .addRescorer(innerRescoreQuery)
                .setRescoreWindow(5)
                .addRescorer(outerRescoreQuery)
                .setRescoreWindow(10)
                .setExplain(true)
                .get();
        assertHitCount(searchResponse, 3);
        assertFirstHit(searchResponse, hasId("1"));
        assertSecondHit(searchResponse, hasId("2"));
        assertThirdHit(searchResponse, hasId("3"));

        for (int j = 0; j < 3; j++) {
          Explanation explanation = searchResponse.getHits().getAt(j).explanation();
          assertThat(explanation.getDescription(), equalTo(descriptionModes[outerMode]));
          assertThat(
              explanation.getDetails()[0].getDetails()[0].getDescription(),
              equalTo(descriptionModes[innerMode]));
        }
      }
    }
  }