@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);
   }
 }
  @Override
  protected void doAssertLuceneQuery(
      BoolQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException {
    if (!queryBuilder.hasClauses()) {
      assertThat(query, instanceOf(MatchAllDocsQuery.class));
    } else {
      List<BooleanClause> clauses = new ArrayList<>();
      clauses.addAll(getBooleanClauses(queryBuilder.must(), BooleanClause.Occur.MUST, context));
      clauses.addAll(
          getBooleanClauses(queryBuilder.mustNot(), BooleanClause.Occur.MUST_NOT, context));
      clauses.addAll(getBooleanClauses(queryBuilder.should(), BooleanClause.Occur.SHOULD, context));
      clauses.addAll(getBooleanClauses(queryBuilder.filter(), BooleanClause.Occur.FILTER, context));

      if (clauses.isEmpty()) {
        assertThat(query, instanceOf(MatchAllDocsQuery.class));
      } else {
        assertThat(query, instanceOf(BooleanQuery.class));
        BooleanQuery booleanQuery = (BooleanQuery) query;
        assertThat(booleanQuery.isCoordDisabled(), equalTo(queryBuilder.disableCoord()));
        if (queryBuilder.adjustPureNegative()) {
          boolean isNegative = true;
          for (BooleanClause clause : clauses) {
            if (clause.isProhibited() == false) {
              isNegative = false;
              break;
            }
          }
          if (isNegative) {
            clauses.add(new BooleanClause(new MatchAllDocsQuery(), BooleanClause.Occur.MUST));
          }
        }
        assertThat(booleanQuery.clauses().size(), equalTo(clauses.size()));
        Iterator<BooleanClause> clauseIterator = clauses.iterator();
        for (BooleanClause booleanClause : booleanQuery.getClauses()) {
          assertThat(booleanClause, instanceOf(clauseIterator.next().getClass()));
        }
      }
    }
  }
  /** @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()));
    }
  }