public void testDefaultMinShouldMatch() throws Exception {
    // Queries have a minShouldMatch of 0
    BooleanQuery bq =
        (BooleanQuery)
            parseQuery(boolQuery().must(termQuery("foo", "bar")).buildAsBytes())
                .toQuery(createShardContext());
    assertEquals(0, bq.getMinimumNumberShouldMatch());

    bq =
        (BooleanQuery)
            parseQuery(boolQuery().should(termQuery("foo", "bar")).buildAsBytes())
                .toQuery(createShardContext());
    assertEquals(0, bq.getMinimumNumberShouldMatch());

    // Filters have a minShouldMatch of 0/1
    ConstantScoreQuery csq =
        (ConstantScoreQuery)
            parseQuery(constantScoreQuery(boolQuery().must(termQuery("foo", "bar"))).buildAsBytes())
                .toQuery(createShardContext());
    bq = (BooleanQuery) csq.getQuery();
    assertEquals(0, bq.getMinimumNumberShouldMatch());

    csq =
        (ConstantScoreQuery)
            parseQuery(
                    constantScoreQuery(boolQuery().should(termQuery("foo", "bar"))).buildAsBytes())
                .toQuery(createShardContext());
    bq = (BooleanQuery) csq.getQuery();
    assertEquals(1, bq.getMinimumNumberShouldMatch());
  }
 public void testMinShouldMatchFilterWithoutShouldClauses() throws Exception {
   BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
   boolQueryBuilder.filter(new BoolQueryBuilder().must(new MatchAllQueryBuilder()));
   Query query = boolQueryBuilder.toQuery(createShardContext());
   assertThat(query, instanceOf(BooleanQuery.class));
   BooleanQuery booleanQuery = (BooleanQuery) query;
   assertThat(booleanQuery.getMinimumNumberShouldMatch(), equalTo(0));
   assertThat(booleanQuery.clauses().size(), equalTo(1));
   BooleanClause booleanClause = booleanQuery.clauses().get(0);
   assertThat(booleanClause.getOccur(), equalTo(BooleanClause.Occur.FILTER));
   assertThat(booleanClause.getQuery(), instanceOf(BooleanQuery.class));
   BooleanQuery innerBooleanQuery = (BooleanQuery) booleanClause.getQuery();
   // we didn't set minimum should match initially, there are no should clauses so it should be 0
   assertThat(innerBooleanQuery.getMinimumNumberShouldMatch(), equalTo(0));
   assertThat(innerBooleanQuery.clauses().size(), equalTo(1));
   BooleanClause innerBooleanClause = innerBooleanQuery.clauses().get(0);
   assertThat(innerBooleanClause.getOccur(), equalTo(BooleanClause.Occur.MUST));
   assertThat(innerBooleanClause.getQuery(), instanceOf(MatchAllDocsQuery.class));
 }
 @Override
 public void write(DataOutput out) throws IOException {
   out.writeBoolean(query.isCoordDisabled());
   out.writeFloat(query.getBoost());
   out.writeInt(query.getMinimumNumberShouldMatch());
   BooleanClause[] clauses = query.getClauses();
   out.writeInt(clauses.length);
   for (int i = 0; i < clauses.length; i++) {
     BooleanClauseWritable booleanClauseWritable = new BooleanClauseWritable(clauses[i]);
     booleanClauseWritable.write(out);
   }
 }
  public void testToQueryBooleanQueryMultipleBoosts() throws Exception {
    assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
    int numBoosts = randomIntBetween(2, 10);
    float[] boosts = new float[numBoosts + 1];
    String queryStringPrefix = "";
    String queryStringSuffix = "";
    for (int i = 0; i < boosts.length - 1; i++) {
      float boost = 2.0f / randomIntBetween(3, 20);
      boosts[i] = boost;
      queryStringPrefix += "(";
      queryStringSuffix += ")^" + boost;
    }
    String queryString = queryStringPrefix + "foo bar" + queryStringSuffix;

    float mainBoost = 2.0f / randomIntBetween(3, 20);
    boosts[boosts.length - 1] = mainBoost;
    QueryStringQueryBuilder queryStringQueryBuilder =
        new QueryStringQueryBuilder(queryString)
            .field(STRING_FIELD_NAME)
            .minimumShouldMatch("2")
            .boost(mainBoost);
    Query query = queryStringQueryBuilder.toQuery(createShardContext());

    for (int i = boosts.length - 1; i >= 0; i--) {
      assertThat(query, instanceOf(BoostQuery.class));
      BoostQuery boostQuery = (BoostQuery) query;
      assertThat(boostQuery.getBoost(), equalTo(boosts[i]));
      query = boostQuery.getQuery();
    }

    assertThat(query, instanceOf(BooleanQuery.class));
    BooleanQuery booleanQuery = (BooleanQuery) query;
    assertThat(booleanQuery.getMinimumNumberShouldMatch(), equalTo(2));
    assertThat(booleanQuery.clauses().get(0).getOccur(), equalTo(BooleanClause.Occur.SHOULD));
    assertThat(
        booleanQuery.clauses().get(0).getQuery(),
        equalTo(new TermQuery(new Term(STRING_FIELD_NAME, "foo"))));
    assertThat(booleanQuery.clauses().get(1).getOccur(), equalTo(BooleanClause.Occur.SHOULD));
    assertThat(
        booleanQuery.clauses().get(1).getQuery(),
        equalTo(new TermQuery(new Term(STRING_FIELD_NAME, "bar"))));
  }
  /** @see #toString(Query,IndexSchema) */
  public static void toString(Query query, IndexSchema schema, Appendable out, int flags)
      throws IOException {
    boolean writeBoost = true;

    if (query instanceof TermQuery) {
      TermQuery q = (TermQuery) query;
      Term t = q.getTerm();
      FieldType ft = writeFieldName(t.field(), schema, out, flags);
      writeFieldVal(t.bytes(), ft, out, flags);
    } else if (query instanceof TermRangeQuery) {
      TermRangeQuery q = (TermRangeQuery) query;
      String fname = q.getField();
      FieldType ft = writeFieldName(fname, schema, out, flags);
      out.append(q.includesLower() ? '[' : '{');
      BytesRef lt = q.getLowerTerm();
      BytesRef ut = q.getUpperTerm();
      if (lt == null) {
        out.append('*');
      } else {
        writeFieldVal(lt, ft, out, flags);
      }

      out.append(" TO ");

      if (ut == null) {
        out.append('*');
      } else {
        writeFieldVal(ut, ft, out, flags);
      }

      out.append(q.includesUpper() ? ']' : '}');
    } else if (query instanceof NumericRangeQuery) {
      NumericRangeQuery q = (NumericRangeQuery) query;
      String fname = q.getField();
      FieldType ft = writeFieldName(fname, schema, out, flags);
      out.append(q.includesMin() ? '[' : '{');
      Number lt = q.getMin();
      Number ut = q.getMax();
      if (lt == null) {
        out.append('*');
      } else {
        out.append(lt.toString());
      }

      out.append(" TO ");

      if (ut == null) {
        out.append('*');
      } else {
        out.append(ut.toString());
      }

      out.append(q.includesMax() ? ']' : '}');
    } else if (query instanceof BooleanQuery) {
      BooleanQuery q = (BooleanQuery) query;
      boolean needParens = false;

      if (q.getBoost() != 1.0 || q.getMinimumNumberShouldMatch() != 0 || q.isCoordDisabled()) {
        needParens = true;
      }
      if (needParens) {
        out.append('(');
      }
      boolean first = true;
      for (BooleanClause c : q.clauses()) {
        if (!first) {
          out.append(' ');
        } else {
          first = false;
        }

        if (c.isProhibited()) {
          out.append('-');
        } else if (c.isRequired()) {
          out.append('+');
        }
        Query subQuery = c.getQuery();
        boolean wrapQuery = false;

        // TODO: may need to put parens around other types
        // of queries too, depending on future syntax.
        if (subQuery instanceof BooleanQuery) {
          wrapQuery = true;
        }

        if (wrapQuery) {
          out.append('(');
        }

        toString(subQuery, schema, out, flags);

        if (wrapQuery) {
          out.append(')');
        }
      }

      if (needParens) {
        out.append(')');
      }
      if (q.getMinimumNumberShouldMatch() > 0) {
        out.append('~');
        out.append(Integer.toString(q.getMinimumNumberShouldMatch()));
      }
      if (q.isCoordDisabled()) {
        out.append("/no_coord");
      }

    } else if (query instanceof PrefixQuery) {
      PrefixQuery q = (PrefixQuery) query;
      Term prefix = q.getPrefix();
      FieldType ft = writeFieldName(prefix.field(), schema, out, flags);
      out.append(prefix.text());
      out.append('*');
    } else if (query instanceof WildcardQuery) {
      out.append(query.toString());
      writeBoost = false;
    } else if (query instanceof FuzzyQuery) {
      out.append(query.toString());
      writeBoost = false;
    } else if (query instanceof ConstantScoreQuery) {
      out.append(query.toString());
      writeBoost = false;
    } else if (query instanceof WrappedQuery) {
      WrappedQuery q = (WrappedQuery) query;
      out.append(q.getOptions());
      toString(q.getWrappedQuery(), schema, out, flags);
      writeBoost = false; // we don't use the boost on wrapped queries
    } else {
      out.append(query.getClass().getSimpleName() + '(' + query.toString() + ')');
      writeBoost = false;
    }

    if (writeBoost && query.getBoost() != 1.0f) {
      out.append("^");
      out.append(Float.toString(query.getBoost()));
    }
  }
  /**
   * Construct the query (using spans). This method will be called recursively.
   *
   * @param q Query
   * @param scope Author, affiliation, or Reference
   * @param level Used only for formatting (indentation) the level of recursion
   * @param andSpans ArrayList of Spans that should be 'and'
   * @param orSpans ArrayList of Spans that should be 'or'
   * @param notSpans ArrayList of Spans that should be 'not'
   * @return SpanQuery
   */
  private SpanQuery buildQuery_recursive(
      Query q,
      String scope,
      int level,
      ArrayList<SpanQuery> andSpans,
      ArrayList<SpanQuery> orSpans,
      ArrayList<SpanQuery> notSpans) {

    BooleanQuery castQuery = (BooleanQuery) q;
    String subscope = null;

    for (BooleanClause clause : castQuery.getClauses()) {

      Class queryclazz = clause.getQuery().getClass();

      System.out.println(
          repeat(' ', level)
              + "["
              + queryclazz
              + "]["
              + clause.getOccur()
              + "] "
              + clause.toString());

      if (queryclazz == BooleanQuery.class) {

        System.out.println("Number of Clauses is " + castQuery.clauses().size());
        System.out.println("Minimum Number to Match is " + castQuery.getMinimumNumberShouldMatch());

        if (subscope == null) {

          if (clause.getOccur() == BooleanClause.Occur.MUST) {
            andSpans.add(
                buildQuery_recursive(
                    clause.getQuery(),
                    scope,
                    level + 1,
                    new ArrayList<SpanQuery>(),
                    new ArrayList<SpanQuery>(),
                    new ArrayList<SpanQuery>()));
          } else if (clause.getOccur() == BooleanClause.Occur.SHOULD) {
            orSpans.add(
                buildQuery_recursive(
                    clause.getQuery(),
                    scope,
                    level + 1,
                    new ArrayList<SpanQuery>(),
                    new ArrayList<SpanQuery>(),
                    new ArrayList<SpanQuery>()));
          } else if (clause.getOccur() == BooleanClause.Occur.MUST_NOT) {
            // FIX
            notSpans.add(
                buildQuery_recursive(
                    clause.getQuery(),
                    scope,
                    level + 1,
                    new ArrayList<SpanQuery>(),
                    new ArrayList<SpanQuery>(),
                    new ArrayList<SpanQuery>()));
          }

        } else {

          ArrayList<SpanQuery> subscopeQuery = new ArrayList<SpanQuery>();
          subscopeQuery.add(
              new SpanTermQuery(new Term(scope, getTag(scope, subscope, TAG_TYPE.BEG_TAG))));
          subscopeQuery.add(
              new SpanTermQuery(new Term(scope, getTag(scope, subscope, TAG_TYPE.END_TAG))));
          subscopeQuery.add(
              buildQuery_recursive(
                  clause.getQuery(),
                  scope,
                  level + 1,
                  new ArrayList<SpanQuery>(),
                  new ArrayList<SpanQuery>(),
                  new ArrayList<SpanQuery>()));
          if (clause.getOccur() == BooleanClause.Occur.MUST) {
            andSpans.add(
                new SpanBetweenQuery(subscopeQuery.toArray(new SpanQuery[subscopeQuery.size()])));
          } else if (clause.getOccur() == BooleanClause.Occur.SHOULD) {
            orSpans.add(
                new SpanBetweenQuery(subscopeQuery.toArray(new SpanQuery[subscopeQuery.size()])));
          } else if (clause.getOccur() == BooleanClause.Occur.MUST_NOT) {
            // FIX
            notSpans.add(
                new SpanBetweenQuery(subscopeQuery.toArray(new SpanQuery[subscopeQuery.size()])));
          }
        }

      } else if (queryclazz == TermQuery.class) {

        TermQuery tq = (TermQuery) clause.getQuery();

        if (tq.getTerm().field().compareTo(SUBSCOPE_FIELD) == 0) {

          // Set the subscope
          subscope = tq.getTerm().text();

          // Need to add a term here (otherwise we have problems)
          WildcardQuery wildcard = new WildcardQuery(new Term(scope, "*"));
          if (clause.getOccur() == BooleanClause.Occur.MUST) {
            andSpans.add(new SpanMultiTermQueryWrapper<WildcardQuery>(wildcard));
          } else if (clause.getOccur() == BooleanClause.Occur.SHOULD) {
            orSpans.add(new SpanMultiTermQueryWrapper<WildcardQuery>(wildcard));
          } else if (clause.getOccur() == BooleanClause.Occur.MUST_NOT) {
            notSpans.add(new SpanMultiTermQueryWrapper<WildcardQuery>(wildcard));
          }

        } else if (subscope == null) {

          if (clause.getOccur() == BooleanClause.Occur.MUST) {
            andSpans.add(buildTermQuery(scope, tq));
          } else if (clause.getOccur() == BooleanClause.Occur.SHOULD) {
            orSpans.add(buildTermQuery(scope, tq));
          } else if (clause.getOccur() == BooleanClause.Occur.MUST_NOT) {
            notSpans.add(buildTermQuery(scope, tq));
          }

        } else {

          ArrayList<SpanQuery> subscopeQuery = new ArrayList<SpanQuery>();
          subscopeQuery.add(
              new SpanTermQuery(new Term(scope, getTag(scope, subscope, TAG_TYPE.BEG_TAG))));
          subscopeQuery.add(
              new SpanTermQuery(new Term(scope, getTag(scope, subscope, TAG_TYPE.END_TAG))));
          subscopeQuery.add(buildTermQuery(scope, tq));
          if (clause.getOccur() == BooleanClause.Occur.MUST) {
            andSpans.add(
                new SpanBetweenQuery(subscopeQuery.toArray(new SpanQuery[subscopeQuery.size()])));
          } else if (clause.getOccur() == BooleanClause.Occur.SHOULD) {
            orSpans.add(
                new SpanBetweenQuery(subscopeQuery.toArray(new SpanQuery[subscopeQuery.size()])));
          } else if (clause.getOccur() == BooleanClause.Occur.MUST_NOT) {
            // FIX
            notSpans.add(
                new SpanBetweenQuery(subscopeQuery.toArray(new SpanQuery[subscopeQuery.size()])));
          }
        }

      } else if (queryclazz == WildcardQuery.class) {

        if (subscope == null) {

          if (clause.getOccur() == BooleanClause.Occur.MUST) {
            andSpans.add(buildWildcardQuery(scope, clause.getQuery()));
          } else if (clause.getOccur() == BooleanClause.Occur.SHOULD) {
            orSpans.add(buildWildcardQuery(scope, clause.getQuery()));
          } else if (clause.getOccur() == BooleanClause.Occur.MUST_NOT) {
            // FIX
            notSpans.add(buildWildcardQuery(scope, clause.getQuery()));
          }

        } else {

          ArrayList<SpanQuery> subscopeQuery = new ArrayList<SpanQuery>();
          subscopeQuery.add(
              new SpanTermQuery(new Term(scope, getTag(scope, subscope, TAG_TYPE.BEG_TAG))));
          subscopeQuery.add(
              new SpanTermQuery(new Term(scope, getTag(scope, subscope, TAG_TYPE.END_TAG))));
          subscopeQuery.add(buildWildcardQuery(scope, clause.getQuery()));
          if (clause.getOccur() == BooleanClause.Occur.MUST) {
            andSpans.add(
                new SpanBetweenQuery(subscopeQuery.toArray(new SpanQuery[subscopeQuery.size()])));
          } else if (clause.getOccur() == BooleanClause.Occur.SHOULD) {
            orSpans.add(
                new SpanBetweenQuery(subscopeQuery.toArray(new SpanQuery[subscopeQuery.size()])));
          } else if (clause.getOccur() == BooleanClause.Occur.MUST_NOT) {
            // FIX
            notSpans.add(
                new SpanBetweenQuery(subscopeQuery.toArray(new SpanQuery[subscopeQuery.size()])));
          }
        }

      } else if (queryclazz == PrefixQuery.class) {

        if (subscope == null) {

          if (clause.getOccur() == BooleanClause.Occur.MUST) {
            andSpans.add(buildPrefixQuery(scope, clause.getQuery()));
          } else if (clause.getOccur() == BooleanClause.Occur.SHOULD) {
            orSpans.add(buildPrefixQuery(scope, clause.getQuery()));
          } else if (clause.getOccur() == BooleanClause.Occur.MUST_NOT) {
            // FIX
            notSpans.add(buildPrefixQuery(scope, clause.getQuery()));
          }

        } else {

          ArrayList<SpanQuery> subscopeQuery = new ArrayList<SpanQuery>();
          subscopeQuery.add(
              new SpanTermQuery(new Term(scope, getTag(scope, subscope, TAG_TYPE.BEG_TAG))));
          subscopeQuery.add(
              new SpanTermQuery(new Term(scope, getTag(scope, subscope, TAG_TYPE.END_TAG))));
          subscopeQuery.add(buildPrefixQuery(scope, clause.getQuery()));
          if (clause.getOccur() == BooleanClause.Occur.MUST) {
            andSpans.add(
                new SpanBetweenQuery(subscopeQuery.toArray(new SpanQuery[subscopeQuery.size()])));
          } else if (clause.getOccur() == BooleanClause.Occur.SHOULD) {
            orSpans.add(
                new SpanBetweenQuery(subscopeQuery.toArray(new SpanQuery[subscopeQuery.size()])));
          } else if (clause.getOccur() == BooleanClause.Occur.MUST_NOT) {
            // FIX
            notSpans.add(
                new SpanBetweenQuery(subscopeQuery.toArray(new SpanQuery[subscopeQuery.size()])));
          }
        }

      } else if (queryclazz == PhraseQuery.class) {

        if (subscope == null) {

          if (clause.getOccur() == BooleanClause.Occur.MUST) {
            andSpans.add(buildPhraseQuery(scope, clause.getQuery()));
          } else if (clause.getOccur() == BooleanClause.Occur.SHOULD) {
            orSpans.add(buildPhraseQuery(scope, clause.getQuery()));
          } else if (clause.getOccur() == BooleanClause.Occur.MUST_NOT) {
            // FIX
            notSpans.add(buildPhraseQuery(scope, clause.getQuery()));
          }

        } else {

          ArrayList<SpanQuery> subscopeQuery = new ArrayList<SpanQuery>();
          subscopeQuery.add(
              new SpanTermQuery(new Term(scope, getTag(scope, subscope, TAG_TYPE.BEG_TAG))));
          subscopeQuery.add(
              new SpanTermQuery(new Term(scope, getTag(scope, subscope, TAG_TYPE.END_TAG))));
          subscopeQuery.add(buildPhraseQuery(scope, clause.getQuery()));
          if (clause.getOccur() == BooleanClause.Occur.MUST) {
            andSpans.add(
                new SpanBetweenQuery(subscopeQuery.toArray(new SpanQuery[subscopeQuery.size()])));
          } else if (clause.getOccur() == BooleanClause.Occur.SHOULD) {
            orSpans.add(
                new SpanBetweenQuery(subscopeQuery.toArray(new SpanQuery[subscopeQuery.size()])));
          } else if (clause.getOccur() == BooleanClause.Occur.MUST_NOT) {
            // FIX
            notSpans.add(
                new SpanBetweenQuery(subscopeQuery.toArray(new SpanQuery[subscopeQuery.size()])));
          }
        }

      } else {

        System.out.println("[" + q.getClass() + "]");
      }
    }

    ArrayList<SpanQuery> includeSpans = new ArrayList<SpanQuery>();
    ;

    // Add the 'and' queries to the includeSpans (if there were any)
    if (!andSpans.isEmpty()) {
      if (andSpans.size() > 1) {
        includeSpans.add(new SpanAndQuery(andSpans.toArray(new SpanQuery[andSpans.size()])));
      } else {
        includeSpans.add(andSpans.get(0));
      }
    }

    // Add the 'or' queries to the includeSpans (if there were any)
    if (!orSpans.isEmpty()) {
      includeSpans.add(new SpanOrQuery(orSpans.toArray(new SpanQuery[orSpans.size()])));
    }

    // Exclude the 'not' queries from the includeSpans (if there were any)
    if (!notSpans.isEmpty()) {
      if (includeSpans.size() > 1) {
        if (notSpans.size() > 1) {
          return new SpanNotQuery(
              new SpanAndQuery(includeSpans.toArray(new SpanQuery[includeSpans.size()])),
              new SpanAndQuery(notSpans.toArray(new SpanQuery[notSpans.size()])));
        } else {
          return new SpanNotQuery(
              new SpanAndQuery(includeSpans.toArray(new SpanQuery[includeSpans.size()])),
              notSpans.get(0));
        }
      } else {
        if (notSpans.size() > 1) {
          return new SpanNotQuery(
              includeSpans.get(0),
              new SpanAndQuery(notSpans.toArray(new SpanQuery[notSpans.size()])));
        } else {
          return new SpanNotQuery(includeSpans.get(0), notSpans.get(0));
        }
      }
    } else {
      if (includeSpans.size() > 1) {
        return new SpanAndQuery(includeSpans.toArray(new SpanQuery[includeSpans.size()]));
      } else {
        return includeSpans.get(0);
      }
    }
  }