@Override
  public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
    XContentParser parser = parseContext.parser();

    Filter filter = null;
    boolean cache = true;

    String currentFieldName = null;
    XContentParser.Token token;
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.START_OBJECT) {
        if ("filter".equals(currentFieldName)) {
          filter = parseContext.parseInnerFilter();
        }
      } else if (token.isValue()) {
        if ("cache".equals(currentFieldName)) {
          cache = parser.booleanValue();
        }
      }
    }

    if (filter == null) {
      throw new QueryParsingException(index, "filter is required when using `not` filter");
    }

    if (cache) {
      filter = parseContext.cacheFilterIfPossible(filter);
    }
    // no need to cache this one
    return new NotFilter(filter);
  }
  @Override
  public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
    XContentParser parser = parseContext.parser();

    String fieldPattern = null;
    String filterName = null;

    XContentParser.Token token;
    String currentFieldName = null;
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token.isValue()) {
        if ("field".equals(currentFieldName)) {
          fieldPattern = parser.text();
        } else if ("_name".equals(currentFieldName)) {
          filterName = parser.text();
        } else {
          throw new QueryParsingException(
              parseContext.index(), "[exists] filter does not support [" + currentFieldName + "]");
        }
      }
    }

    if (fieldPattern == null) {
      throw new QueryParsingException(
          parseContext.index(), "exists must be provided with a [field]");
    }

    return newFilter(parseContext, fieldPattern, filterName);
  }
  @Override
  public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
    XContentParser parser = parseContext.parser();

    Query query = null;

    String filterName = null;
    String currentFieldName = null;
    XContentParser.Token token;
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.START_OBJECT) {
        if ("query".equals(currentFieldName)) {
          query = parseContext.parseInnerQuery();
        }
      } else if (token.isValue()) {
        if ("_name".equals(currentFieldName)) {
          filterName = parser.text();
        }
      }
    }
    Filter filter = new QueryWrapperFilter(query);
    if (filterName != null) {
      parseContext.addNamedFilter(filterName, filter);
    }
    return filter;
  }
  @Override
  public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
    XContentParser parser = parseContext.parser();

    Query query = null;
    float boost = 1.0f;
    String script = null;
    String scriptLang = null;
    Map<String, Object> vars = null;

    String currentFieldName = null;
    XContentParser.Token token;
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.START_OBJECT) {
        if ("query".equals(currentFieldName)) {
          query = parseContext.parseInnerQuery();
        } else if ("params".equals(currentFieldName)) {
          vars = parser.map();
        }
      } else if (token.isValue()) {
        if ("script".equals(currentFieldName)) {
          script = parser.text();
        } else if ("lang".equals(currentFieldName)) {
          scriptLang = parser.text();
        } else if ("boost".equals(currentFieldName)) {
          boost = parser.floatValue();
        }
      }
    }
    if (query == null) {
      throw new QueryParsingException(
          parseContext.index(), "[custom_score] requires 'query' field");
    }
    if (script == null) {
      throw new QueryParsingException(
          parseContext.index(), "[custom_score] requires 'script' field");
    }

    SearchContext context = SearchContext.current();
    if (context == null) {
      throw new ElasticSearchIllegalStateException("No search context on going...");
    }
    SearchScript searchScript =
        context.scriptService().search(context.lookup(), scriptLang, script, vars);
    FunctionScoreQuery functionScoreQuery =
        new FunctionScoreQuery(query, new ScriptScoreFunction(script, vars, searchScript));
    functionScoreQuery.setBoost(boost);
    return functionScoreQuery;
  }
  @Override
  protected QueryBuilder doRewrite(QueryRewriteContext context) throws IOException {
    try (XContentParser qSourceParser =
        XContentFactory.xContent(source).createParser(context.getXContentRegistry(), source)) {
      QueryParseContext parseContext = context.newParseContext(qSourceParser);

      final QueryBuilder queryBuilder = parseContext.parseInnerQueryBuilder();
      if (boost() != DEFAULT_BOOST || queryName() != null) {
        final BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.must(queryBuilder);
        return boolQueryBuilder;
      }
      return queryBuilder;
    }
  }
  public static WrapperQueryBuilder fromXContent(QueryParseContext parseContext)
      throws IOException {
    XContentParser parser = parseContext.parser();

    XContentParser.Token token = parser.nextToken();
    if (token != XContentParser.Token.FIELD_NAME) {
      throw new ParsingException(parser.getTokenLocation(), "[wrapper] query malformed");
    }
    String fieldName = parser.currentName();
    if (!QUERY_FIELD.match(fieldName)) {
      throw new ParsingException(
          parser.getTokenLocation(),
          "[wrapper] query malformed, expected `query` but was " + fieldName);
    }
    parser.nextToken();

    byte[] source = parser.binaryValue();

    parser.nextToken();

    if (source == null) {
      throw new ParsingException(
          parser.getTokenLocation(), "wrapper query has no [query] specified");
    }
    return new WrapperQueryBuilder(source);
  }
  @Override
  public NotQueryBuilder fromXContent(QueryParseContext parseContext) throws IOException {
    XContentParser parser = parseContext.parser();

    QueryBuilder query = null;
    boolean queryFound = false;

    String queryName = null;
    String currentFieldName = null;
    float boost = AbstractQueryBuilder.DEFAULT_BOOST;
    XContentParser.Token token;
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (parseContext.isDeprecatedSetting(currentFieldName)) {
        // skip
      } else if (token == XContentParser.Token.START_OBJECT) {
        if (parseContext.parseFieldMatcher().match(currentFieldName, QUERY_FIELD)) {
          query = parseContext.parseInnerQueryBuilder();
          queryFound = true;
        } else {
          queryFound = true;
          // its the filter, and the name is the field
          query = parseContext.parseInnerQueryBuilderByName(currentFieldName);
        }
      } else if (token.isValue()) {
        if ("_name".equals(currentFieldName)) {
          queryName = parser.text();
        } else if ("boost".equals(currentFieldName)) {
          boost = parser.floatValue();
        } else {
          throw new ParsingException(
              parser.getTokenLocation(), "[not] query does not support [" + currentFieldName + "]");
        }
      }
    }

    if (!queryFound) {
      throw new ParsingException(
          parser.getTokenLocation(), "query is required when using `not` query");
    }

    NotQueryBuilder notQueryBuilder = new NotQueryBuilder(query);
    notQueryBuilder.queryName(queryName);
    notQueryBuilder.boost(boost);
    return notQueryBuilder;
  }
 @Override
 public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
   Query query = parseContext.parseInnerQuery();
   if (query == null) {
     return null;
   }
   return Queries.wrap(query, parseContext);
 }
  public static Optional<SpanWithinQueryBuilder> fromXContent(QueryParseContext parseContext)
      throws IOException {
    XContentParser parser = parseContext.parser();

    float boost = AbstractQueryBuilder.DEFAULT_BOOST;
    String queryName = null;
    SpanQueryBuilder big = null;
    SpanQueryBuilder little = null;

    String currentFieldName = null;
    XContentParser.Token token;
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.START_OBJECT) {
        if (parseContext.getParseFieldMatcher().match(currentFieldName, BIG_FIELD)) {
          Optional<QueryBuilder> query = parseContext.parseInnerQueryBuilder();
          if (query.isPresent() == false || query.get() instanceof SpanQueryBuilder == false) {
            throw new ParsingException(
                parser.getTokenLocation(), "span_within [big] must be of type span query");
          }
          big = (SpanQueryBuilder) query.get();
        } else if (parseContext.getParseFieldMatcher().match(currentFieldName, LITTLE_FIELD)) {
          Optional<QueryBuilder> query = parseContext.parseInnerQueryBuilder();
          if (query.isPresent() == false || query.get() instanceof SpanQueryBuilder == false) {
            throw new ParsingException(
                parser.getTokenLocation(), "span_within [little] must be of type span query");
          }
          little = (SpanQueryBuilder) query.get();
        } else {
          throw new ParsingException(
              parser.getTokenLocation(),
              "[span_within] query does not support [" + currentFieldName + "]");
        }
      } else if (parseContext
          .getParseFieldMatcher()
          .match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
        boost = parser.floatValue();
      } else if (parseContext
          .getParseFieldMatcher()
          .match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
        queryName = parser.text();
      } else {
        throw new ParsingException(
            parser.getTokenLocation(),
            "[span_within] query does not support [" + currentFieldName + "]");
      }
    }

    if (big == null) {
      throw new ParsingException(parser.getTokenLocation(), "span_within must include [big]");
    }
    if (little == null) {
      throw new ParsingException(parser.getTokenLocation(), "span_within must include [little]");
    }

    SpanWithinQueryBuilder query = new SpanWithinQueryBuilder(big, little);
    query.boost(boost).queryName(queryName);
    return Optional.of(query);
  }
  public static Filter newFilter(
      QueryParseContext parseContext, String fieldPattern, String filterName) {
    final FieldMappers fieldNamesMappers =
        parseContext.mapperService().indexName(FieldNamesFieldMapper.NAME);
    final FieldNamesFieldMapper fieldNamesMapper =
        fieldNamesMappers == null ? null : (FieldNamesFieldMapper) fieldNamesMappers.mapper();

    MapperService.SmartNameObjectMapper smartNameObjectMapper =
        parseContext.smartObjectMapper(fieldPattern);
    if (smartNameObjectMapper != null && smartNameObjectMapper.hasMapper()) {
      // automatic make the object mapper pattern
      fieldPattern = fieldPattern + ".*";
    }

    List<String> fields = parseContext.simpleMatchToIndexNames(fieldPattern);
    if (fields.isEmpty()) {
      // no fields exists, so we should not match anything
      return Queries.MATCH_NO_FILTER;
    }
    MapperService.SmartNameFieldMappers nonNullFieldMappers = null;

    XBooleanFilter boolFilter = new XBooleanFilter();
    for (String field : fields) {
      MapperService.SmartNameFieldMappers smartNameFieldMappers =
          parseContext.smartFieldMappers(field);
      if (smartNameFieldMappers != null) {
        nonNullFieldMappers = smartNameFieldMappers;
      }
      Filter filter = null;
      if (fieldNamesMapper != null && fieldNamesMapper.enabled()) {
        final String f;
        if (smartNameFieldMappers != null && smartNameFieldMappers.hasMapper()) {
          f = smartNameFieldMappers.mapper().names().indexName();
        } else {
          f = field;
        }
        filter = fieldNamesMapper.termFilter(f, parseContext);
      }
      // if _field_names are not indexed, we need to go the slow way
      if (filter == null && smartNameFieldMappers != null && smartNameFieldMappers.hasMapper()) {
        filter = smartNameFieldMappers.mapper().rangeFilter(null, null, true, true, parseContext);
      }
      if (filter == null) {
        filter = new TermRangeFilter(field, null, null, true, true);
      }
      boolFilter.add(filter, BooleanClause.Occur.SHOULD);
    }

    // we always cache this one, really does not change... (exists)
    // its ok to cache under the fieldName cacheKey, since its per segment and the mapping applies
    // to this data on this segment...
    Filter filter =
        parseContext.cacheFilter(boolFilter, new CacheKeyFilter.Key("$exists$" + fieldPattern));

    filter = wrapSmartNameFilter(filter, nonNullFieldMappers, parseContext);
    if (filterName != null) {
      parseContext.addNamedFilter(filterName, filter);
    }
    return filter;
  }
  public static FieldMaskingSpanQueryBuilder fromXContent(QueryParseContext parseContext)
      throws IOException {
    XContentParser parser = parseContext.parser();

    float boost = AbstractQueryBuilder.DEFAULT_BOOST;

    SpanQueryBuilder inner = null;
    String field = null;
    String queryName = null;

    String currentFieldName = null;
    XContentParser.Token token;
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.START_OBJECT) {
        if (parseContext.getParseFieldMatcher().match(currentFieldName, QUERY_FIELD)) {
          QueryBuilder query = parseContext.parseInnerQueryBuilder();
          if (!(query instanceof SpanQueryBuilder)) {
            throw new ParsingException(
                parser.getTokenLocation(), "[field_masking_span] query must be of type span query");
          }
          inner = (SpanQueryBuilder) query;
        } else {
          throw new ParsingException(
              parser.getTokenLocation(),
              "[field_masking_span] query does not support [" + currentFieldName + "]");
        }
      } else {
        if (parseContext
            .getParseFieldMatcher()
            .match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
          boost = parser.floatValue();
        } else if (parseContext.getParseFieldMatcher().match(currentFieldName, FIELD_FIELD)) {
          field = parser.text();
        } else if (parseContext
            .getParseFieldMatcher()
            .match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
          queryName = parser.text();
        } else {
          throw new ParsingException(
              parser.getTokenLocation(),
              "[field_masking_span] query does not support [" + currentFieldName + "]");
        }
      }
    }
    if (inner == null) {
      throw new ParsingException(
          parser.getTokenLocation(), "field_masking_span must have [query] span query clause");
    }
    if (field == null) {
      throw new ParsingException(
          parser.getTokenLocation(), "field_masking_span must have [field] set for it");
    }

    FieldMaskingSpanQueryBuilder queryBuilder = new FieldMaskingSpanQueryBuilder(inner, field);
    queryBuilder.boost(boost);
    queryBuilder.queryName(queryName);
    return queryBuilder;
  }
  @Override
  @Nullable
  public Query parse(QueryParseContext parseContext) throws IOException {
    XContentParser parser = parseContext.parser();
    TemplateContext templateContext = parse(parser, QUERY, PARAMS);
    ExecutableScript executable =
        this.scriptService.executable(
            "mustache", templateContext.template(), templateContext.params());
    BytesReference querySource = (BytesReference) executable.run();

    XContentParser qSourceParser = XContentFactory.xContent(querySource).createParser(querySource);
    try {
      final QueryParseContext context =
          new QueryParseContext(parseContext.index(), parseContext.indexQueryParser);
      context.reset(qSourceParser);
      Query result = context.parseInnerQuery();
      parser.nextToken();
      return result;
    } finally {
      qSourceParser.close();
    }
  }
  /** we resolve empty inner clauses by representing this whole query as empty optional upstream */
  public void testFromJsonEmptyQueryBody() throws IOException {
    String query =
        "{\n"
            + "  \"has_parent\" : {\n"
            + "    \"query\" : { },\n"
            + "    \"parent_type\" : \"blog\""
            + "   }"
            + "}";
    XContentParser parser = XContentFactory.xContent(query).createParser(query);
    QueryParseContext context = createParseContext(parser, ParseFieldMatcher.EMPTY);
    Optional<QueryBuilder> innerQueryBuilder = context.parseInnerQueryBuilder();
    assertTrue(innerQueryBuilder.isPresent() == false);

    checkWarningHeaders("query malformed, empty clause found at [3:17]");

    parser = XContentFactory.xContent(query).createParser(query);
    QueryParseContext otherContext = createParseContext(parser, ParseFieldMatcher.STRICT);
    IllegalArgumentException ex =
        expectThrows(IllegalArgumentException.class, () -> otherContext.parseInnerQueryBuilder());
    assertThat(ex.getMessage(), equalTo("query malformed, empty clause found at [3:17]"));
    checkWarningHeaders("query malformed, empty clause found at [3:17]");
  }
 public static ParentIdQueryBuilder fromXContent(QueryParseContext parseContext)
     throws IOException {
   XContentParser parser = parseContext.parser();
   float boost = AbstractQueryBuilder.DEFAULT_BOOST;
   String type = null;
   String id = null;
   String queryName = null;
   String currentFieldName = null;
   boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;
   XContentParser.Token token;
   while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
     if (token == XContentParser.Token.FIELD_NAME) {
       currentFieldName = parser.currentName();
     } else if (token.isValue()) {
       if (parseContext.getParseFieldMatcher().match(currentFieldName, TYPE_FIELD)) {
         type = parser.text();
       } else if (parseContext.getParseFieldMatcher().match(currentFieldName, ID_FIELD)) {
         id = parser.text();
       } else if (parseContext
           .getParseFieldMatcher()
           .match(currentFieldName, IGNORE_UNMAPPED_FIELD)) {
         ignoreUnmapped = parser.booleanValue();
       } else if (parseContext
           .getParseFieldMatcher()
           .match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
         boost = parser.floatValue();
       } else if (parseContext
           .getParseFieldMatcher()
           .match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
         queryName = parser.text();
       } else {
         throw new ParsingException(
             parser.getTokenLocation(),
             "[parent_id] query does not support [" + currentFieldName + "]");
       }
     } else {
       throw new ParsingException(
           parser.getTokenLocation(),
           "[parent_id] query does not support [" + currentFieldName + "]");
     }
   }
   ParentIdQueryBuilder queryBuilder = new ParentIdQueryBuilder(type, id);
   queryBuilder.queryName(queryName);
   queryBuilder.boost(boost);
   queryBuilder.ignoreUnmapped(ignoreUnmapped);
   return queryBuilder;
 }
  @Override
  public TypeQueryBuilder fromXContent(QueryParseContext parseContext) throws IOException {
    XContentParser parser = parseContext.parser();
    BytesRef type = null;

    String queryName = null;
    float boost = AbstractQueryBuilder.DEFAULT_BOOST;

    String currentFieldName = null;
    XContentParser.Token token;
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token.isValue()) {
        if ("_name".equals(currentFieldName)) {
          queryName = parser.text();
        } else if ("boost".equals(currentFieldName)) {
          boost = parser.floatValue();
        } else if ("value".equals(currentFieldName)) {
          type = parser.utf8Bytes();
        } else {
          throw new ParsingException(
              parser.getTokenLocation(),
              "[" + TypeQueryBuilder.NAME + "] filter doesn't support [" + currentFieldName + "]");
        }
      } else {
        throw new ParsingException(
            parser.getTokenLocation(),
            "[" + TypeQueryBuilder.NAME + "] filter doesn't support [" + currentFieldName + "]");
      }
    }

    if (type == null) {
      throw new ParsingException(
          parser.getTokenLocation(),
          "[" + TypeQueryBuilder.NAME + "] filter needs to be provided with a value for the type");
    }
    return new TypeQueryBuilder(type).boost(boost).queryName(queryName);
  }
  @Override
  public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
    ensureNotDeleteByQuery(NAME, parseContext);
    XContentParser parser = parseContext.parser();

    boolean queryFound = false;
    float boost = 1.0f;
    String childType = null;
    ScoreType scoreType = ScoreType.NONE;
    int minChildren = 0;
    int maxChildren = 0;
    int shortCircuitParentDocSet = 8192;
    String queryName = null;
    Tuple<String, SubSearchContext> innerHits = null;

    String currentFieldName = null;
    XContentParser.Token token;
    XContentStructure.InnerQuery iq = null;
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.START_OBJECT) {
        // Usually, the query would be parsed here, but the child
        // type may not have been extracted yet, so use the
        // XContentStructure.<type> facade to parse if available,
        // or delay parsing if not.
        if ("query".equals(currentFieldName)) {
          iq =
              new XContentStructure.InnerQuery(
                  parseContext, childType == null ? null : new String[] {childType});
          queryFound = true;
        } else if ("inner_hits".equals(currentFieldName)) {
          innerHits = innerHitsQueryParserHelper.parse(parseContext);
        } else {
          throw new QueryParsingException(
              parseContext.index(),
              "[has_child] query does not support [" + currentFieldName + "]");
        }
      } else if (token.isValue()) {
        if ("type".equals(currentFieldName)
            || "child_type".equals(currentFieldName)
            || "childType".equals(currentFieldName)) {
          childType = parser.text();
        } else if ("score_type".equals(currentFieldName) || "scoreType".equals(currentFieldName)) {
          scoreType = ScoreType.fromString(parser.text());
        } else if ("score_mode".equals(currentFieldName) || "scoreMode".equals(currentFieldName)) {
          scoreType = ScoreType.fromString(parser.text());
        } else if ("boost".equals(currentFieldName)) {
          boost = parser.floatValue();
        } else if ("min_children".equals(currentFieldName)
            || "minChildren".equals(currentFieldName)) {
          minChildren = parser.intValue(true);
        } else if ("max_children".equals(currentFieldName)
            || "maxChildren".equals(currentFieldName)) {
          maxChildren = parser.intValue(true);
        } else if ("short_circuit_cutoff".equals(currentFieldName)) {
          shortCircuitParentDocSet = parser.intValue();
        } else if ("_name".equals(currentFieldName)) {
          queryName = parser.text();
        } else {
          throw new QueryParsingException(
              parseContext.index(),
              "[has_child] query does not support [" + currentFieldName + "]");
        }
      }
    }
    if (!queryFound) {
      throw new QueryParsingException(parseContext.index(), "[has_child] requires 'query' field");
    }
    if (childType == null) {
      throw new QueryParsingException(parseContext.index(), "[has_child] requires 'type' field");
    }

    Query innerQuery = iq.asQuery(childType);

    if (innerQuery == null) {
      return null;
    }
    innerQuery.setBoost(boost);

    DocumentMapper childDocMapper = parseContext.mapperService().documentMapper(childType);
    if (childDocMapper == null) {
      throw new QueryParsingException(
          parseContext.index(), "[has_child] No mapping for for type [" + childType + "]");
    }
    if (!childDocMapper.parentFieldMapper().active()) {
      throw new QueryParsingException(
          parseContext.index(),
          "[has_child]  Type [" + childType + "] does not have parent mapping");
    }

    if (innerHits != null) {
      InnerHitsContext.ParentChildInnerHits parentChildInnerHits =
          new InnerHitsContext.ParentChildInnerHits(
              innerHits.v2(), innerQuery, null, childDocMapper);
      String name = innerHits.v1() != null ? innerHits.v1() : childType;
      parseContext.addInnerHits(name, parentChildInnerHits);
    }

    ParentFieldMapper parentFieldMapper = childDocMapper.parentFieldMapper();
    if (!parentFieldMapper.active()) {
      throw new QueryParsingException(
          parseContext.index(), "[has_child] _parent field not configured");
    }

    String parentType = parentFieldMapper.type();
    DocumentMapper parentDocMapper = parseContext.mapperService().documentMapper(parentType);
    if (parentDocMapper == null) {
      throw new QueryParsingException(
          parseContext.index(),
          "[has_child]  Type ["
              + childType
              + "] points to a non existent parent type ["
              + parentType
              + "]");
    }

    if (maxChildren > 0 && maxChildren < minChildren) {
      throw new QueryParsingException(
          parseContext.index(), "[has_child] 'max_children' is less than 'min_children'");
    }

    BitDocIdSetFilter nonNestedDocsFilter = null;
    if (parentDocMapper.hasNestedObjects()) {
      nonNestedDocsFilter = parseContext.bitsetFilter(NonNestedDocsFilter.INSTANCE);
    }

    // wrap the query with type query
    innerQuery =
        new FilteredQuery(
            innerQuery,
            parseContext.cacheFilter(
                childDocMapper.typeFilter(), null, parseContext.autoFilterCachePolicy()));

    Query query;
    Filter parentFilter =
        parseContext.cacheFilter(
            parentDocMapper.typeFilter(), null, parseContext.autoFilterCachePolicy());
    ParentChildIndexFieldData parentChildIndexFieldData =
        parseContext.getForField(parentFieldMapper);
    if (minChildren > 1 || maxChildren > 0 || scoreType != ScoreType.NONE) {
      query =
          new ChildrenQuery(
              parentChildIndexFieldData,
              parentType,
              childType,
              parentFilter,
              innerQuery,
              scoreType,
              minChildren,
              maxChildren,
              shortCircuitParentDocSet,
              nonNestedDocsFilter);
    } else {
      query =
          new ChildrenConstantScoreQuery(
              parentChildIndexFieldData,
              innerQuery,
              parentType,
              childType,
              parentFilter,
              shortCircuitParentDocSet,
              nonNestedDocsFilter);
    }
    if (queryName != null) {
      parseContext.addNamedFilter(queryName, new CustomQueryWrappingFilter(query));
    }
    query.setBoost(boost);
    return query;
  }
  @Override
  public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
    XContentParser parser = parseContext.parser();

    String fieldName = null;
    String filterName = null;
    boolean nullValue = false;
    boolean existence = true;

    XContentParser.Token token;
    String currentFieldName = null;
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token.isValue()) {
        if ("field".equals(currentFieldName)) {
          fieldName = parser.text();
        } else if ("null_value".equals(currentFieldName)) {
          nullValue = parser.booleanValue();
        } else if ("existence".equals(currentFieldName)) {
          existence = parser.booleanValue();
        } else if ("_name".equals(currentFieldName)) {
          filterName = parser.text();
        } else {
          throw new QueryParsingException(
              parseContext.index(), "[missing] filter does not support [" + currentFieldName + "]");
        }
      }
    }

    if (fieldName == null) {
      throw new QueryParsingException(
          parseContext.index(), "missing must be provided with a [field]");
    }

    if (!existence && !nullValue) {
      throw new QueryParsingException(
          parseContext.index(),
          "missing must have either existence, or null_value, or both set to true");
    }

    Filter existenceFilter = null;
    Filter nullFilter = null;

    MapperService.SmartNameFieldMappers smartNameFieldMappers =
        parseContext.smartFieldMappers(fieldName);

    if (existence) {
      if (smartNameFieldMappers != null && smartNameFieldMappers.hasMapper()) {
        existenceFilter =
            smartNameFieldMappers.mapper().rangeFilter(null, null, true, true, parseContext);
      }
      if (existenceFilter == null) {
        existenceFilter = new TermRangeFilter(fieldName, null, null, true, true);
      }

      // we always cache this one, really does not change... (exists)
      existenceFilter = parseContext.cacheFilter(existenceFilter, null);
      existenceFilter = new NotFilter(existenceFilter);
      // cache the not filter as well, so it will be faster
      existenceFilter = parseContext.cacheFilter(existenceFilter, null);
    }

    if (nullValue) {
      if (smartNameFieldMappers != null && smartNameFieldMappers.hasMapper()) {
        nullFilter = smartNameFieldMappers.mapper().nullValueFilter();
        if (nullFilter != null) {
          // cache the not filter as well, so it will be faster
          nullFilter = parseContext.cacheFilter(nullFilter, null);
        }
      }
    }

    Filter filter;
    if (nullFilter != null) {
      if (existenceFilter != null) {
        XBooleanFilter combined = new XBooleanFilter();
        combined.addShould(existenceFilter);
        combined.addShould(nullFilter);
        // cache the not filter as well, so it will be faster
        filter = parseContext.cacheFilter(combined, null);
      } else {
        filter = nullFilter;
      }
    } else {
      filter = existenceFilter;
    }

    if (filter == null) {
      return null;
    }

    filter = wrapSmartNameFilter(filter, smartNameFieldMappers, parseContext);
    if (filterName != null) {
      parseContext.addNamedFilter(filterName, existenceFilter);
    }
    return filter;
  }
  @Override
  public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
    XContentParser parser = parseContext.parser();

    XContentParser.Token token = parser.nextToken();
    if (token != XContentParser.Token.FIELD_NAME) {
      throw new QueryParsingException(
          parseContext.index(), "[range] query malformed, no field to indicate field name");
    }
    String fieldName = parser.currentName();
    token = parser.nextToken();
    if (token != XContentParser.Token.START_OBJECT) {
      throw new QueryParsingException(
          parseContext.index(), "[range] query malformed, after field missing start object");
    }

    BytesRef from = null;
    BytesRef to = null;
    boolean includeLower = true;
    boolean includeUpper = true;
    float boost = 1.0f;

    String currentFieldName = null;
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else {
        if ("from".equals(currentFieldName)) {
          from = parser.bytesOrNull();
        } else if ("to".equals(currentFieldName)) {
          to = parser.bytesOrNull();
        } else if ("include_lower".equals(currentFieldName)
            || "includeLower".equals(currentFieldName)) {
          includeLower = parser.booleanValue();
        } else if ("include_upper".equals(currentFieldName)
            || "includeUpper".equals(currentFieldName)) {
          includeUpper = parser.booleanValue();
        } else if ("boost".equals(currentFieldName)) {
          boost = parser.floatValue();
        } else if ("gt".equals(currentFieldName)) {
          from = parser.bytesOrNull();
          includeLower = false;
        } else if ("gte".equals(currentFieldName) || "ge".equals(currentFieldName)) {
          from = parser.bytesOrNull();
          includeLower = true;
        } else if ("lt".equals(currentFieldName)) {
          to = parser.bytesOrNull();
          includeUpper = false;
        } else if ("lte".equals(currentFieldName) || "le".equals(currentFieldName)) {
          to = parser.bytesOrNull();
          includeUpper = true;
        } else {
          throw new QueryParsingException(
              parseContext.index(), "[range] query does not support [" + currentFieldName + "]");
        }
      }
    }

    // move to the next end object, to close the field name
    token = parser.nextToken();
    if (token != XContentParser.Token.END_OBJECT) {
      throw new QueryParsingException(
          parseContext.index(), "[range] query malformed, does not end with an object");
    }

    Query query = null;
    MapperService.SmartNameFieldMappers smartNameFieldMappers =
        parseContext.smartFieldMappers(fieldName);
    if (smartNameFieldMappers != null) {
      if (smartNameFieldMappers.hasMapper()) {
        // LUCENE 4 UPGRADE Mapper#rangeQuery should use bytesref as well?
        query =
            smartNameFieldMappers
                .mapper()
                .rangeQuery(
                    from != null ? from.utf8ToString() : null,
                    to != null ? to.utf8ToString() : null,
                    includeLower,
                    includeUpper,
                    parseContext);
      }
    }
    if (query == null) {
      query = new TermRangeQuery(fieldName, from, to, includeLower, includeUpper);
    }
    query.setBoost(boost);
    return wrapSmartNameQuery(query, smartNameFieldMappers, parseContext);
  }
  @Override
  public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
    XContentParser parser = parseContext.parser();

    Query query = Queries.newMatchAllQuery();
    Filter filter = null;
    boolean filterFound = false;
    float boost = 1.0f;
    FilterCachingPolicy cache = parseContext.autoFilterCachePolicy();
    HashedBytesRef cacheKey = null;
    String queryName = null;

    String currentFieldName = null;
    XContentParser.Token token;
    FilteredQuery.FilterStrategy filterStrategy = CUSTOM_FILTER_STRATEGY;

    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.START_OBJECT) {
        if ("query".equals(currentFieldName)) {
          query = parseContext.parseInnerQuery();
        } else if ("filter".equals(currentFieldName)) {
          filterFound = true;
          filter = parseContext.parseInnerFilter();
        } else {
          throw new QueryParsingException(
              parseContext.index(), "[filtered] query does not support [" + currentFieldName + "]");
        }
      } else if (token.isValue()) {
        if ("strategy".equals(currentFieldName)) {
          String value = parser.text();
          if ("query_first".equals(value) || "queryFirst".equals(value)) {
            filterStrategy = FilteredQuery.QUERY_FIRST_FILTER_STRATEGY;
          } else if ("random_access_always".equals(value) || "randomAccessAlways".equals(value)) {
            filterStrategy = ALWAYS_RANDOM_ACCESS_FILTER_STRATEGY;
          } else if ("leap_frog".equals(value) || "leapFrog".equals(value)) {
            filterStrategy = FilteredQuery.LEAP_FROG_QUERY_FIRST_STRATEGY;
          } else if (value.startsWith("random_access_")) {
            int threshold = Integer.parseInt(value.substring("random_access_".length()));
            filterStrategy = new CustomRandomAccessFilterStrategy(threshold);
          } else if (value.startsWith("randomAccess")) {
            int threshold = Integer.parseInt(value.substring("randomAccess".length()));
            filterStrategy = new CustomRandomAccessFilterStrategy(threshold);
          } else if ("leap_frog_query_first".equals(value) || "leapFrogQueryFirst".equals(value)) {
            filterStrategy = FilteredQuery.LEAP_FROG_QUERY_FIRST_STRATEGY;
          } else if ("leap_frog_filter_first".equals(value)
              || "leapFrogFilterFirst".equals(value)) {
            filterStrategy = FilteredQuery.LEAP_FROG_FILTER_FIRST_STRATEGY;
          } else {
            throw new QueryParsingException(
                parseContext.index(), "[filtered] strategy value not supported [" + value + "]");
          }
        } else if ("_name".equals(currentFieldName)) {
          queryName = parser.text();
        } else if ("boost".equals(currentFieldName)) {
          boost = parser.floatValue();
        } else if ("_cache".equals(currentFieldName)) {
          cache = parseContext.parseFilterCachePolicy();
        } else if ("_cache_key".equals(currentFieldName) || "_cacheKey".equals(currentFieldName)) {
          cacheKey = new HashedBytesRef(parser.text());
        } else {
          throw new QueryParsingException(
              parseContext.index(), "[filtered] query does not support [" + currentFieldName + "]");
        }
      }
    }

    // parsed internally, but returned null during parsing...
    if (query == null) {
      return null;
    }

    if (filter == null) {
      if (!filterFound) {
        // we allow for null filter, so it makes compositions on the client side to be simpler
        return query;
      } else {
        // even if the filter is not found, and its null, we should simply ignore it, and go
        // by the query
        return query;
      }
    }
    if (filter == Queries.MATCH_ALL_FILTER) {
      // this is an instance of match all filter, just execute the query
      return query;
    }

    // cache if required
    if (cache != null) {
      filter = parseContext.cacheFilter(filter, cacheKey, cache);
    }

    // if its a match_all query, use constant_score
    if (Queries.isConstantMatchAllQuery(query)) {
      Query q = new ConstantScoreQuery(filter);
      q.setBoost(boost);
      return q;
    }

    FilteredQuery filteredQuery = new FilteredQuery(query, filter, filterStrategy);
    filteredQuery.setBoost(boost);
    if (queryName != null) {
      parseContext.addNamedQuery(queryName, filteredQuery);
    }
    return filteredQuery;
  }
  public static SpanNearQueryBuilder fromXContent(QueryParseContext parseContext)
      throws IOException {
    XContentParser parser = parseContext.parser();

    float boost = AbstractQueryBuilder.DEFAULT_BOOST;
    Integer slop = null;
    boolean inOrder = SpanNearQueryBuilder.DEFAULT_IN_ORDER;
    String queryName = null;

    List<SpanQueryBuilder> clauses = new ArrayList<>();

    String currentFieldName = null;
    XContentParser.Token token;
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.START_ARRAY) {
        if (parseContext.getParseFieldMatcher().match(currentFieldName, CLAUSES_FIELD)) {
          while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
            QueryBuilder query = parseContext.parseInnerQueryBuilder();
            if (!(query instanceof SpanQueryBuilder)) {
              throw new ParsingException(
                  parser.getTokenLocation(), "spanNear [clauses] must be of type span query");
            }
            clauses.add((SpanQueryBuilder) query);
          }
        } else {
          throw new ParsingException(
              parser.getTokenLocation(),
              "[span_near] query does not support [" + currentFieldName + "]");
        }
      } else if (token.isValue()) {
        if (parseContext.getParseFieldMatcher().match(currentFieldName, IN_ORDER_FIELD)) {
          inOrder = parser.booleanValue();
        } else if (parseContext
            .getParseFieldMatcher()
            .match(currentFieldName, COLLECT_PAYLOADS_FIELD)) {
          // Deprecated in 3.0.0
        } else if (parseContext.getParseFieldMatcher().match(currentFieldName, SLOP_FIELD)) {
          slop = parser.intValue();
        } else if (parseContext
            .getParseFieldMatcher()
            .match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
          boost = parser.floatValue();
        } else if (parseContext
            .getParseFieldMatcher()
            .match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
          queryName = parser.text();
        } else {
          throw new ParsingException(
              parser.getTokenLocation(),
              "[span_near] query does not support [" + currentFieldName + "]");
        }
      } else {
        throw new ParsingException(
            parser.getTokenLocation(),
            "[span_near] query does not support [" + currentFieldName + "]");
      }
    }

    if (clauses.isEmpty()) {
      throw new ParsingException(parser.getTokenLocation(), "span_near must include [clauses]");
    }

    if (slop == null) {
      throw new ParsingException(parser.getTokenLocation(), "span_near must include [slop]");
    }

    SpanNearQueryBuilder queryBuilder = new SpanNearQueryBuilder(clauses.get(0), slop);
    for (int i = 1; i < clauses.size(); i++) {
      queryBuilder.clause(clauses.get(i));
    }
    queryBuilder.inOrder(inOrder);
    queryBuilder.boost(boost);
    queryBuilder.queryName(queryName);
    return queryBuilder;
  }
  @Override
  public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
    XContentParser parser = parseContext.parser();

    Query query = null;
    Query noMatchQuery = Queries.newMatchAllQuery();
    boolean queryFound = false;
    boolean indicesFound = false;
    boolean currentIndexMatchesIndices = false;
    String queryName = null;

    String currentFieldName = null;
    XContentParser.Token token;
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.START_OBJECT) {
        if ("query".equals(currentFieldName)) {
          // TODO We are able to decide whether to parse the query or not only if indices in the
          // query appears first
          queryFound = true;
          if (indicesFound && !currentIndexMatchesIndices) {
            parseContext.parser().skipChildren(); // skip the query object without parsing it
          } else {
            query = parseContext.parseInnerQuery();
          }
        } else if ("no_match_query".equals(currentFieldName)) {
          if (indicesFound && currentIndexMatchesIndices) {
            parseContext.parser().skipChildren(); // skip the query object without parsing it
          } else {
            noMatchQuery = parseContext.parseInnerQuery();
          }
        } else {
          throw new QueryParsingException(
              parseContext.index(), "[indices] query does not support [" + currentFieldName + "]");
        }
      } else if (token == XContentParser.Token.START_ARRAY) {
        if ("indices".equals(currentFieldName)) {
          if (indicesFound) {
            throw new QueryParsingException(
                parseContext.index(), "[indices] indices or index already specified");
          }
          indicesFound = true;
          Collection<String> indices = new ArrayList<>();
          while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
            String value = parser.textOrNull();
            if (value == null) {
              throw new QueryParsingException(
                  parseContext.index(), "[indices] no value specified for 'indices' entry");
            }
            indices.add(value);
          }
          currentIndexMatchesIndices =
              matchesIndices(
                  parseContext.index().name(), indices.toArray(new String[indices.size()]));
        } else {
          throw new QueryParsingException(
              parseContext.index(), "[indices] query does not support [" + currentFieldName + "]");
        }
      } else if (token.isValue()) {
        if ("index".equals(currentFieldName)) {
          if (indicesFound) {
            throw new QueryParsingException(
                parseContext.index(), "[indices] indices or index already specified");
          }
          indicesFound = true;
          currentIndexMatchesIndices = matchesIndices(parseContext.index().name(), parser.text());
        } else if ("no_match_query".equals(currentFieldName)) {
          String type = parser.text();
          if ("all".equals(type)) {
            noMatchQuery = Queries.newMatchAllQuery();
          } else if ("none".equals(type)) {
            noMatchQuery = Queries.newMatchNoDocsQuery();
          }
        } else if ("_name".equals(currentFieldName)) {
          queryName = parser.text();
        } else {
          throw new QueryParsingException(
              parseContext.index(), "[indices] query does not support [" + currentFieldName + "]");
        }
      }
    }
    if (!queryFound) {
      throw new QueryParsingException(parseContext.index(), "[indices] requires 'query' element");
    }
    if (!indicesFound) {
      throw new QueryParsingException(
          parseContext.index(), "[indices] requires 'indices' or 'index' element");
    }

    Query chosenQuery;
    if (currentIndexMatchesIndices) {
      chosenQuery = query;
    } else {
      chosenQuery = noMatchQuery;
    }
    if (queryName != null) {
      parseContext.addNamedQuery(queryName, chosenQuery);
    }
    return chosenQuery;
  }
  @Override
  public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
    XContentParser parser = parseContext.parser();

    Query query = null;
    boolean queryFound = false;
    String parentType = null;

    String filterName = null;
    String currentFieldName = null;
    XContentParser.Token token;
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.START_OBJECT) {
        if ("query".equals(currentFieldName)) {
          // TODO handle `query` element before `type` element...
          String[] origTypes =
              QueryParseContext.setTypesWithPrevious(
                  parentType == null ? null : new String[] {parentType});
          try {
            query = parseContext.parseInnerQuery();
            queryFound = true;
          } finally {
            QueryParseContext.setTypes(origTypes);
          }
        } else if ("filter".equals(currentFieldName)) {
          // TODO handle `filter` element before `type` element...
          String[] origTypes =
              QueryParseContext.setTypesWithPrevious(
                  parentType == null ? null : new String[] {parentType});
          try {
            Filter innerFilter = parseContext.parseInnerFilter();
            query = new XConstantScoreQuery(innerFilter);
            queryFound = true;
          } finally {
            QueryParseContext.setTypes(origTypes);
          }
        } else {
          throw new QueryParsingException(
              parseContext.index(),
              "[has_parent] filter does not support [" + currentFieldName + "]");
        }
      } else if (token.isValue()) {
        if ("type".equals(currentFieldName)
            || "parent_type".equals(currentFieldName)
            || "parentType".equals(currentFieldName)) {
          parentType = parser.text();
        } else if ("_scope".equals(currentFieldName)) {
          throw new QueryParsingException(
              parseContext.index(),
              "the [_scope] support in [has_parent] filter has been removed, use a filter as a facet_filter in the relevant global facet");
        } else if ("_name".equals(currentFieldName)) {
          filterName = parser.text();
        } else {
          throw new QueryParsingException(
              parseContext.index(),
              "[has_parent] filter does not support [" + currentFieldName + "]");
        }
      }
    }
    if (!queryFound) {
      throw new QueryParsingException(
          parseContext.index(), "[parent] filter requires 'query' field");
    }
    if (query == null) {
      return null;
    }

    if (parentType == null) {
      throw new QueryParsingException(
          parseContext.index(), "[parent] filter requires 'parent_type' field");
    }

    DocumentMapper parentDocMapper = parseContext.mapperService().documentMapper(parentType);
    if (parentDocMapper == null) {
      throw new QueryParsingException(
          parseContext.index(),
          "[parent] filter configured 'parent_type' [" + parentType + "] is not a valid type");
    }

    // wrap the query with type query
    query = new XFilteredQuery(query, parseContext.cacheFilter(parentDocMapper.typeFilter(), null));

    SearchContext searchContext = SearchContext.current();

    HasParentFilter parentFilter = HasParentFilter.create(query, parentType, searchContext);
    searchContext.addRewrite(parentFilter);

    if (filterName != null) {
      parseContext.addNamedFilter(filterName, parentFilter);
    }
    return parentFilter;
  }
  @Override
  public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
    XContentParser parser = parseContext.parser();

    Query query = null;
    boolean queryFound = false;
    Filter filter = null;
    boolean filterFound = false;
    float boost = 1.0f;
    boolean cache = false;
    CacheKeyFilter.Key cacheKey = null;

    String currentFieldName = null;
    XContentParser.Token token;
    FilteredQuery.FilterStrategy filterStrategy = XFilteredQuery.CUSTOM_FILTER_STRATEGY;

    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.START_OBJECT) {
        if ("query".equals(currentFieldName)) {
          queryFound = true;
          query = parseContext.parseInnerQuery();
        } else if ("filter".equals(currentFieldName)) {
          filterFound = true;
          filter = parseContext.parseInnerFilter();
        } else {
          throw new QueryParsingException(
              parseContext.index(), "[filtered] query does not support [" + currentFieldName + "]");
        }
      } else if (token.isValue()) {
        if ("strategy".equals(currentFieldName)) {
          String value = parser.text();
          if ("query_first".equals(value) || "queryFirst".equals(value)) {
            filterStrategy = FilteredQuery.QUERY_FIRST_FILTER_STRATEGY;
          } else if ("random_access_random".equals(value) || "randomAccessAlways".equals(value)) {
            filterStrategy = XFilteredQuery.ALWAYS_RANDOM_ACCESS_FILTER_STRATEGY;
          } else if ("leap_frog".equals(value) || "leapFrog".equals(value)) {
            filterStrategy = FilteredQuery.LEAP_FROG_QUERY_FIRST_STRATEGY;
          } else if (value.startsWith("random_access_")) {
            int threshold = Integer.parseInt(value.substring("random_access_".length()));
            filterStrategy = new XFilteredQuery.CustomRandomAccessFilterStrategy(threshold);
          } else if (value.startsWith("randomAccess")) {
            int threshold = Integer.parseInt(value.substring("randomAccess".length()));
            filterStrategy = new XFilteredQuery.CustomRandomAccessFilterStrategy(threshold);
          } else if ("leap_frog_query_first".equals(value) || "leapFrogQueryFirst".equals(value)) {
            filterStrategy = FilteredQuery.LEAP_FROG_QUERY_FIRST_STRATEGY;
          } else if ("leap_frog_filter_first".equals(value)
              || "leapFrogFilterFirst".equals(value)) {
            filterStrategy = FilteredQuery.LEAP_FROG_FILTER_FIRST_STRATEGY;
          } else {
            throw new QueryParsingException(
                parseContext.index(), "[filtered] strategy value not supported [" + value + "]");
          }
        } else if ("boost".equals(currentFieldName)) {
          boost = parser.floatValue();
        } else if ("_cache".equals(currentFieldName)) {
          cache = parser.booleanValue();
        } else if ("_cache_key".equals(currentFieldName) || "_cacheKey".equals(currentFieldName)) {
          cacheKey = new CacheKeyFilter.Key(parser.text());
        } else {
          throw new QueryParsingException(
              parseContext.index(), "[filtered] query does not support [" + currentFieldName + "]");
        }
      }
    }
    if (!queryFound) {
      throw new QueryParsingException(parseContext.index(), "[filtered] requires 'query' element");
    }
    if (query == null) {
      return null;
    }
    if (filter == null) {
      if (!filterFound) {
        // we allow for null filter, so it makes compositions on the client side to be simpler
        return query;
      } else {
        // the filter was provided, but returned null, meaning we should discard it, this means no
        // matches for this query...
        return Queries.NO_MATCH_QUERY;
      }
    }
    if (filter == Queries.MATCH_ALL_FILTER) {
      // this is an instance of match all filter, just execute the query
      return query;
    }

    // cache if required
    if (cache) {
      filter = parseContext.cacheFilter(filter, cacheKey);
    }

    // if its a match_all query, use constant_score
    if (Queries.isConstantMatchAllQuery(query)) {
      Query q = new XConstantScoreQuery(filter);
      q.setBoost(boost);
      return q;
    }

    XFilteredQuery filteredQuery = new XFilteredQuery(query, filter, filterStrategy);
    filteredQuery.setBoost(boost);
    return filteredQuery;
  }
    @Override
    public Builder fromXContent(QueryParseContext parseContext) throws IOException {
      XContentParser parser = parseContext.parser();

      String fieldName = null;
      String geohash = null;
      Integer levels = null;
      Boolean neighbors = null;
      String queryName = null;
      Float boost = null;

      XContentParser.Token token;
      if ((token = parser.currentToken()) != Token.START_OBJECT) {
        throw new ElasticsearchParseException(
            "failed to parse [{}] query. expected an object but found [{}] instead", NAME, token);
      }

      while ((token = parser.nextToken()) != Token.END_OBJECT) {
        if (token == Token.FIELD_NAME) {
          String field = parser.currentName();

          if (parseContext.isDeprecatedSetting(field)) {
            // skip
          } else if (parseContext.parseFieldMatcher().match(field, PRECISION_FIELD)) {
            token = parser.nextToken();
            if (token == Token.VALUE_NUMBER) {
              levels = parser.intValue();
            } else if (token == Token.VALUE_STRING) {
              double meters =
                  DistanceUnit.parse(parser.text(), DistanceUnit.DEFAULT, DistanceUnit.METERS);
              levels = GeoUtils.geoHashLevelsForPrecision(meters);
            }
          } else if (parseContext.parseFieldMatcher().match(field, NEIGHBORS_FIELD)) {
            parser.nextToken();
            neighbors = parser.booleanValue();
          } else if (parseContext
              .parseFieldMatcher()
              .match(field, AbstractQueryBuilder.NAME_FIELD)) {
            parser.nextToken();
            queryName = parser.text();
          } else if (parseContext
              .parseFieldMatcher()
              .match(field, AbstractQueryBuilder.BOOST_FIELD)) {
            parser.nextToken();
            boost = parser.floatValue();
          } else {
            if (fieldName == null) {
              fieldName = field;
              token = parser.nextToken();
              if (token == Token.VALUE_STRING) {
                // A string indicates either a geohash or a
                // lat/lon
                // string
                String location = parser.text();
                if (location.indexOf(",") > 0) {
                  geohash = GeoUtils.parseGeoPoint(parser).geohash();
                } else {
                  geohash = location;
                }
              } else {
                geohash = GeoUtils.parseGeoPoint(parser).geohash();
              }
            } else {
              throw new ParsingException(
                  parser.getTokenLocation(),
                  "["
                      + NAME
                      + "] field name already set to ["
                      + fieldName
                      + "] but found ["
                      + field
                      + "]");
            }
          }
        } else {
          throw new ElasticsearchParseException(
              "failed to parse [{}] query. unexpected token [{}]", NAME, token);
        }
      }
      Builder builder = new Builder(fieldName, geohash);
      if (levels != null) {
        builder.precision(levels);
      }
      if (neighbors != null) {
        builder.neighbors(neighbors);
      }
      if (queryName != null) {
        builder.queryName(queryName);
      }
      if (boost != null) {
        builder.boost(boost);
      }
      return builder;
    }
  @Override
  public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
    XContentParser parser = parseContext.parser();

    XContentParser.Token token;

    boolean cache = false;
    CacheKeyFilter.Key cacheKey = null;
    String filterName = null;
    String currentFieldName = null;
    double lat = 0;
    double lon = 0;
    String fieldName = null;
    Object vFrom = null;
    Object vTo = null;
    boolean includeLower = true;
    boolean includeUpper = true;
    DistanceUnit unit = DistanceUnit.KILOMETERS; // default unit
    GeoDistance geoDistance = GeoDistance.ARC;
    String optimizeBbox = "memory";
    boolean normalizeLon = true;
    boolean normalizeLat = true;
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.START_ARRAY) {
        token = parser.nextToken();
        lon = parser.doubleValue();
        token = parser.nextToken();
        lat = parser.doubleValue();
        while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {}

        fieldName = currentFieldName;
      } else if (token == XContentParser.Token.START_OBJECT) {
        // the json in the format of -> field : { lat : 30, lon : 12 }
        String currentName = parser.currentName();
        fieldName = currentFieldName;
        while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
          if (token == XContentParser.Token.FIELD_NAME) {
            currentName = parser.currentName();
          } else if (token.isValue()) {
            if (currentName.equals(GeoPointFieldMapper.Names.LAT)) {
              lat = parser.doubleValue();
            } else if (currentName.equals(GeoPointFieldMapper.Names.LON)) {
              lon = parser.doubleValue();
            } else if (currentName.equals(GeoPointFieldMapper.Names.GEOHASH)) {
              double[] values = GeoHashUtils.decode(parser.text());
              lat = values[0];
              lon = values[1];
            }
          }
        }
      } else if (token.isValue()) {
        if (currentFieldName.equals("from")) {
          if (token == XContentParser.Token.VALUE_NULL) {
          } else if (token == XContentParser.Token.VALUE_STRING) {
            vFrom = parser.text(); // a String
          } else {
            vFrom = parser.numberValue(); // a Number
          }
        } else if (currentFieldName.equals("to")) {
          if (token == XContentParser.Token.VALUE_NULL) {
          } else if (token == XContentParser.Token.VALUE_STRING) {
            vTo = parser.text(); // a String
          } else {
            vTo = parser.numberValue(); // a Number
          }
        } else if ("include_lower".equals(currentFieldName)
            || "includeLower".equals(currentFieldName)) {
          includeLower = parser.booleanValue();
        } else if ("include_upper".equals(currentFieldName)
            || "includeUpper".equals(currentFieldName)) {
          includeUpper = parser.booleanValue();
        } else if ("gt".equals(currentFieldName)) {
          if (token == XContentParser.Token.VALUE_NULL) {
          } else if (token == XContentParser.Token.VALUE_STRING) {
            vFrom = parser.text(); // a String
          } else {
            vFrom = parser.numberValue(); // a Number
          }
          includeLower = false;
        } else if ("gte".equals(currentFieldName) || "ge".equals(currentFieldName)) {
          if (token == XContentParser.Token.VALUE_NULL) {
          } else if (token == XContentParser.Token.VALUE_STRING) {
            vFrom = parser.text(); // a String
          } else {
            vFrom = parser.numberValue(); // a Number
          }
          includeLower = true;
        } else if ("lt".equals(currentFieldName)) {
          if (token == XContentParser.Token.VALUE_NULL) {
          } else if (token == XContentParser.Token.VALUE_STRING) {
            vTo = parser.text(); // a String
          } else {
            vTo = parser.numberValue(); // a Number
          }
          includeUpper = false;
        } else if ("lte".equals(currentFieldName) || "le".equals(currentFieldName)) {
          if (token == XContentParser.Token.VALUE_NULL) {
          } else if (token == XContentParser.Token.VALUE_STRING) {
            vTo = parser.text(); // a String
          } else {
            vTo = parser.numberValue(); // a Number
          }
          includeUpper = true;
        } else if (currentFieldName.equals("unit")) {
          unit = DistanceUnit.fromString(parser.text());
        } else if (currentFieldName.equals("distance_type")
            || currentFieldName.equals("distanceType")) {
          geoDistance = GeoDistance.fromString(parser.text());
        } else if (currentFieldName.endsWith(GeoPointFieldMapper.Names.LAT_SUFFIX)) {
          lat = parser.doubleValue();
          fieldName =
              currentFieldName.substring(
                  0, currentFieldName.length() - GeoPointFieldMapper.Names.LAT_SUFFIX.length());
        } else if (currentFieldName.endsWith(GeoPointFieldMapper.Names.LON_SUFFIX)) {
          lon = parser.doubleValue();
          fieldName =
              currentFieldName.substring(
                  0, currentFieldName.length() - GeoPointFieldMapper.Names.LON_SUFFIX.length());
        } else if (currentFieldName.endsWith(GeoPointFieldMapper.Names.GEOHASH_SUFFIX)) {
          double[] values = GeoHashUtils.decode(parser.text());
          lat = values[0];
          lon = values[1];
          fieldName =
              currentFieldName.substring(
                  0, currentFieldName.length() - GeoPointFieldMapper.Names.GEOHASH_SUFFIX.length());
        } else if ("_name".equals(currentFieldName)) {
          filterName = parser.text();
        } else if ("_cache".equals(currentFieldName)) {
          cache = parser.booleanValue();
        } else if ("_cache_key".equals(currentFieldName) || "_cacheKey".equals(currentFieldName)) {
          cacheKey = new CacheKeyFilter.Key(parser.text());
        } else if ("optimize_bbox".equals(currentFieldName)
            || "optimizeBbox".equals(currentFieldName)) {
          optimizeBbox = parser.textOrNull();
        } else if ("normalize".equals(currentFieldName)) {
          normalizeLat = parser.booleanValue();
          normalizeLon = parser.booleanValue();
        } else {
          // assume the value is the actual value
          String value = parser.text();
          int comma = value.indexOf(',');
          if (comma != -1) {
            lat = Double.parseDouble(value.substring(0, comma).trim());
            lon = Double.parseDouble(value.substring(comma + 1).trim());
          } else {
            double[] values = GeoHashUtils.decode(value);
            lat = values[0];
            lon = values[1];
          }
          fieldName = currentFieldName;
        }
      }
    }

    Double from = null;
    Double to = null;
    if (vFrom != null) {
      if (vFrom instanceof Number) {
        from = unit.toMiles(((Number) vFrom).doubleValue());
      } else {
        from = DistanceUnit.parse((String) vFrom, unit, DistanceUnit.MILES);
      }
      from = geoDistance.normalize(from, DistanceUnit.MILES);
    }
    if (vTo != null) {
      if (vTo instanceof Number) {
        to = unit.toMiles(((Number) vTo).doubleValue());
      } else {
        to = DistanceUnit.parse((String) vTo, unit, DistanceUnit.MILES);
      }
      to = geoDistance.normalize(to, DistanceUnit.MILES);
    }

    if (normalizeLat) {
      lat = GeoUtils.normalizeLat(lat);
    }
    if (normalizeLon) {
      lon = GeoUtils.normalizeLon(lon);
    }

    MapperService.SmartNameFieldMappers smartMappers = parseContext.smartFieldMappers(fieldName);
    if (smartMappers == null || !smartMappers.hasMapper()) {
      throw new QueryParsingException(
          parseContext.index(), "failed to find geo_point field [" + fieldName + "]");
    }
    FieldMapper mapper = smartMappers.mapper();
    if (mapper.fieldDataType() != GeoPointFieldDataType.TYPE) {
      throw new QueryParsingException(
          parseContext.index(), "field [" + fieldName + "] is not a geo_point field");
    }
    GeoPointFieldMapper geoMapper = ((GeoPointFieldMapper.GeoStringFieldMapper) mapper).geoMapper();
    fieldName = mapper.names().indexName();

    Filter filter =
        new GeoDistanceRangeFilter(
            lat,
            lon,
            from,
            to,
            includeLower,
            includeUpper,
            geoDistance,
            fieldName,
            geoMapper,
            parseContext.indexCache().fieldData(),
            optimizeBbox);
    if (cache) {
      filter = parseContext.cacheFilter(filter, cacheKey);
    }
    filter = wrapSmartNameFilter(filter, smartMappers, parseContext);
    if (filterName != null) {
      parseContext.addNamedFilter(filterName, filter);
    }
    return filter;
  }
  @Override
  public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
    XContentParser parser = parseContext.parser();

    Query query = null;
    Filter filter = null;
    float boost = 1.0f;
    String scope = null;
    String path = null;
    boolean cache = false;
    String filterName = null;

    // we need a late binding filter so we can inject a parent nested filter inner nested queries
    NestedQueryParser.LateBindingParentFilter currentParentFilterContext =
        NestedQueryParser.parentFilterContext.get();

    NestedQueryParser.LateBindingParentFilter usAsParentFilter =
        new NestedQueryParser.LateBindingParentFilter();
    NestedQueryParser.parentFilterContext.set(usAsParentFilter);

    String currentFieldName = null;
    XContentParser.Token token;
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.START_OBJECT) {
        if ("query".equals(currentFieldName)) {
          query = parseContext.parseInnerQuery();
        } else if ("filter".equals(currentFieldName)) {
          filter = parseContext.parseInnerFilter();
        }
      } else if (token.isValue()) {
        if ("path".equals(currentFieldName)) {
          path = parser.text();
        } else if ("boost".equals(currentFieldName)) {
          boost = parser.floatValue();
        } else if ("_scope".equals(currentFieldName)) {
          scope = parser.text();
        } else if ("_name".equals(currentFieldName)) {
          filterName = parser.text();
        } else if ("_cache".equals(currentFieldName)) {
          cache = parser.booleanValue();
        }
      }
    }
    if (query == null && filter == null) {
      throw new QueryParsingException(
          parseContext.index(), "[nested] requires either 'query' or 'filter' field");
    }
    if (path == null) {
      throw new QueryParsingException(parseContext.index(), "[nested] requires 'path' field");
    }

    if (filter != null) {
      query = new DeletionAwareConstantScoreQuery(filter);
    }

    query.setBoost(boost);

    MapperService.SmartNameObjectMapper mapper =
        parseContext.mapperService().smartNameObjectMapper(path);
    if (mapper == null) {
      throw new QueryParsingException(
          parseContext.index(), "[nested] failed to find nested object under path [" + path + "]");
    }
    ObjectMapper objectMapper = mapper.mapper();
    if (objectMapper == null) {
      throw new QueryParsingException(
          parseContext.index(), "[nested] failed to find nested object under path [" + path + "]");
    }
    if (!objectMapper.nested().isNested()) {
      throw new QueryParsingException(
          parseContext.index(),
          "[nested] nested object under path [" + path + "] is not of nested type");
    }

    Filter childFilter = parseContext.cacheFilter(objectMapper.nestedTypeFilter());
    usAsParentFilter.filter = childFilter;
    // wrap the child query to only work on the nested path type
    query = new FilteredQuery(query, childFilter);

    Filter parentFilter = currentParentFilterContext;
    if (parentFilter == null) {
      parentFilter = NonNestedDocsFilter.INSTANCE;
      if (mapper.hasDocMapper()) {
        // filter based on the type...
        parentFilter = mapper.docMapper().typeFilter();
      }
      parentFilter = parseContext.cacheFilter(parentFilter);
    }

    // restore the thread local one...
    NestedQueryParser.parentFilterContext.set(currentParentFilterContext);

    BlockJoinQuery joinQuery =
        new BlockJoinQuery(query, parentFilter, BlockJoinQuery.ScoreMode.None);

    if (scope != null) {
      SearchContext.current().addNestedQuery(scope, joinQuery);
    }

    Filter joinFilter = new QueryWrapperFilter(joinQuery);
    if (cache) {
      joinFilter = parseContext.cacheFilter(joinFilter);
    }
    if (filterName != null) {
      parseContext.addNamedFilter(filterName, joinFilter);
    }
    return joinFilter;
  }
  @Override
  public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
    XContentParser parser = parseContext.parser();

    boolean disableCoord = false;
    float boost = 1.0f;
    int minimumNumberShouldMatch = -1;

    List<BooleanClause> clauses = newArrayList();

    String currentFieldName = null;
    XContentParser.Token token;
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.START_OBJECT) {
        if ("must".equals(currentFieldName)) {
          clauses.add(new BooleanClause(parseContext.parseInnerQuery(), BooleanClause.Occur.MUST));
        } else if ("must_not".equals(currentFieldName) || "mustNot".equals(currentFieldName)) {
          clauses.add(
              new BooleanClause(parseContext.parseInnerQuery(), BooleanClause.Occur.MUST_NOT));
        } else if ("should".equals(currentFieldName)) {
          clauses.add(
              new BooleanClause(parseContext.parseInnerQuery(), BooleanClause.Occur.SHOULD));
        }
      } else if (token == XContentParser.Token.START_ARRAY) {
        if ("must".equals(currentFieldName)) {
          while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
            clauses.add(
                new BooleanClause(parseContext.parseInnerQuery(), BooleanClause.Occur.MUST));
          }
        } else if ("must_not".equals(currentFieldName) || "mustNot".equals(currentFieldName)) {
          while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
            clauses.add(
                new BooleanClause(parseContext.parseInnerQuery(), BooleanClause.Occur.MUST_NOT));
          }
        } else if ("should".equals(currentFieldName)) {
          while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
            clauses.add(
                new BooleanClause(parseContext.parseInnerQuery(), BooleanClause.Occur.SHOULD));
          }
        }
      } else if (token.isValue()) {
        if ("disable_coord".equals(currentFieldName) || "disableCoord".equals(currentFieldName)) {
          disableCoord = parser.booleanValue();
        } else if ("minimum_number_should_match".equals(currentFieldName)
            || "minimumNumberShouldMatch".equals(currentFieldName)) {
          minimumNumberShouldMatch = parser.intValue();
        } else if ("boost".equals(currentFieldName)) {
          boost = parser.floatValue();
        }
      }
    }

    BooleanQuery query = new BooleanQuery(disableCoord);
    for (BooleanClause clause : clauses) {
      query.add(clause);
    }
    query.setBoost(boost);
    if (minimumNumberShouldMatch != -1) {
      query.setMinimumNumberShouldMatch(minimumNumberShouldMatch);
    }
    return optimizeQuery(fixNegativeQueryIfNeeded(query));
  }
  @Override
  public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
    XContentParser parser = parseContext.parser();

    boolean cache = true;
    CacheKeyFilter.Key cacheKey = null;
    String fieldName = null;
    Object from = null;
    Object to = null;
    boolean includeLower = true;
    boolean includeUpper = true;

    String filterName = null;
    String currentFieldName = null;
    XContentParser.Token token;
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.START_OBJECT) {
        fieldName = currentFieldName;
        while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
          if (token == XContentParser.Token.FIELD_NAME) {
            currentFieldName = parser.currentName();
          } else {
            if ("from".equals(currentFieldName)) {
              from = parser.objectBytes();
            } else if ("to".equals(currentFieldName)) {
              to = parser.objectBytes();
            } else if ("include_lower".equals(currentFieldName)
                || "includeLower".equals(currentFieldName)) {
              includeLower = parser.booleanValue();
            } else if ("include_upper".equals(currentFieldName)
                || "includeUpper".equals(currentFieldName)) {
              includeUpper = parser.booleanValue();
            } else if ("gt".equals(currentFieldName)) {
              from = parser.objectBytes();
              includeLower = false;
            } else if ("gte".equals(currentFieldName) || "ge".equals(currentFieldName)) {
              from = parser.objectBytes();
              includeLower = true;
            } else if ("lt".equals(currentFieldName)) {
              to = parser.objectBytes();
              includeUpper = false;
            } else if ("lte".equals(currentFieldName) || "le".equals(currentFieldName)) {
              to = parser.objectBytes();
              includeUpper = true;
            } else {
              throw new QueryParsingException(
                  parseContext.index(),
                  "[range] filter does not support [" + currentFieldName + "]");
            }
          }
        }
      } else if (token.isValue()) {
        if ("_name".equals(currentFieldName)) {
          filterName = parser.text();
        } else if ("_cache".equals(currentFieldName)) {
          cache = parser.booleanValue();
        } else if ("_cache_key".equals(currentFieldName) || "_cacheKey".equals(currentFieldName)) {
          cacheKey = new CacheKeyFilter.Key(parser.text());
        } else {
          throw new QueryParsingException(
              parseContext.index(), "[range] filter does not support [" + currentFieldName + "]");
        }
      }
    }

    if (fieldName == null) {
      throw new QueryParsingException(parseContext.index(), "No field specified for range filter");
    }

    Filter filter = null;
    MapperService.SmartNameFieldMappers smartNameFieldMappers =
        parseContext.smartFieldMappers(fieldName);
    if (smartNameFieldMappers != null) {
      if (smartNameFieldMappers.hasMapper()) {
        filter =
            smartNameFieldMappers
                .mapper()
                .rangeFilter(from, to, includeLower, includeUpper, parseContext);
      }
    }

    if (filter == null) {
      filter =
          new TermRangeFilter(
              fieldName,
              BytesRefs.toBytesRef(from),
              BytesRefs.toBytesRef(to),
              includeLower,
              includeUpper);
    }

    if (cache) {
      filter = parseContext.cacheFilter(filter, cacheKey);
    }

    filter = wrapSmartNameFilter(filter, smartNameFieldMappers, parseContext);
    if (filterName != null) {
      parseContext.addNamedFilter(filterName, filter);
    }
    return filter;
  }
  /* (non-Javadoc)
   * @see cn.com.rebirth.search.core.index.query.FilterParser#parse(cn.com.rebirth.search.core.index.query.QueryParseContext)
   */
  @Override
  public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
    XContentParser parser = parseContext.parser();

    XContentParser.Token token;

    boolean cache = false;
    CacheKeyFilter.Key cacheKey = null;
    String filterName = null;
    String currentFieldName = null;
    double lat = 0;
    double lon = 0;
    String fieldName = null;
    double distance = 0;
    Object vDistance = null;
    DistanceUnit unit = DistanceUnit.KILOMETERS;
    GeoDistance geoDistance = GeoDistance.ARC;
    String optimizeBbox = "memory";
    boolean normalizeLon = true;
    boolean normalizeLat = true;
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.START_ARRAY) {
        token = parser.nextToken();
        lon = parser.doubleValue();
        token = parser.nextToken();
        lat = parser.doubleValue();
        while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {}

        fieldName = currentFieldName;
      } else if (token == XContentParser.Token.START_OBJECT) {

        String currentName = parser.currentName();
        fieldName = currentFieldName;
        while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
          if (token == XContentParser.Token.FIELD_NAME) {
            currentName = parser.currentName();
          } else if (token.isValue()) {
            if (currentName.equals(GeoPointFieldMapper.Names.LAT)) {
              lat = parser.doubleValue();
            } else if (currentName.equals(GeoPointFieldMapper.Names.LON)) {
              lon = parser.doubleValue();
            } else if (currentName.equals(GeoPointFieldMapper.Names.GEOHASH)) {
              double[] values = GeoHashUtils.decode(parser.text());
              lat = values[0];
              lon = values[1];
            } else {
              throw new QueryParsingException(
                  parseContext.index(),
                  "[geo_distance] filter does not support [" + currentFieldName + "]");
            }
          }
        }
      } else if (token.isValue()) {
        if (currentFieldName.equals("distance")) {
          if (token == XContentParser.Token.VALUE_STRING) {
            vDistance = parser.text();
          } else {
            vDistance = parser.numberValue();
          }
        } else if (currentFieldName.equals("unit")) {
          unit = DistanceUnit.fromString(parser.text());
        } else if (currentFieldName.equals("distance_type")
            || currentFieldName.equals("distanceType")) {
          geoDistance = GeoDistance.fromString(parser.text());
        } else if (currentFieldName.endsWith(GeoPointFieldMapper.Names.LAT_SUFFIX)) {
          lat = parser.doubleValue();
          fieldName =
              currentFieldName.substring(
                  0, currentFieldName.length() - GeoPointFieldMapper.Names.LAT_SUFFIX.length());
        } else if (currentFieldName.endsWith(GeoPointFieldMapper.Names.LON_SUFFIX)) {
          lon = parser.doubleValue();
          fieldName =
              currentFieldName.substring(
                  0, currentFieldName.length() - GeoPointFieldMapper.Names.LON_SUFFIX.length());
        } else if (currentFieldName.endsWith(GeoPointFieldMapper.Names.GEOHASH_SUFFIX)) {
          double[] values = GeoHashUtils.decode(parser.text());
          lat = values[0];
          lon = values[1];
          fieldName =
              currentFieldName.substring(
                  0, currentFieldName.length() - GeoPointFieldMapper.Names.GEOHASH_SUFFIX.length());
        } else if ("_name".equals(currentFieldName)) {
          filterName = parser.text();
        } else if ("_cache".equals(currentFieldName)) {
          cache = parser.booleanValue();
        } else if ("_cache_key".equals(currentFieldName) || "_cacheKey".equals(currentFieldName)) {
          cacheKey = new CacheKeyFilter.Key(parser.text());
        } else if ("optimize_bbox".equals(currentFieldName)
            || "optimizeBbox".equals(currentFieldName)) {
          optimizeBbox = parser.textOrNull();
        } else if ("normalize".equals(currentFieldName)) {
          normalizeLat = parser.booleanValue();
          normalizeLon = parser.booleanValue();
        } else {

          String value = parser.text();
          int comma = value.indexOf(',');
          if (comma != -1) {
            lat = Double.parseDouble(value.substring(0, comma).trim());
            lon = Double.parseDouble(value.substring(comma + 1).trim());
          } else {
            double[] values = GeoHashUtils.decode(value);
            lat = values[0];
            lon = values[1];
          }
          fieldName = currentFieldName;
        }
      }
    }

    if (vDistance instanceof Number) {
      distance = unit.toMiles(((Number) vDistance).doubleValue());
    } else {
      distance = DistanceUnit.parse((String) vDistance, unit, DistanceUnit.MILES);
    }
    distance = geoDistance.normalize(distance, DistanceUnit.MILES);

    if (normalizeLat) {
      lat = GeoUtils.normalizeLat(lat);
    }
    if (normalizeLon) {
      lon = GeoUtils.normalizeLon(lon);
    }

    MapperService.SmartNameFieldMappers smartMappers = parseContext.smartFieldMappers(fieldName);
    if (smartMappers == null || !smartMappers.hasMapper()) {
      throw new QueryParsingException(
          parseContext.index(), "failed to find geo_point field [" + fieldName + "]");
    }
    FieldMapper mapper = smartMappers.mapper();
    if (mapper.fieldDataType() != GeoPointFieldDataType.TYPE) {
      throw new QueryParsingException(
          parseContext.index(), "field [" + fieldName + "] is not a geo_point field");
    }
    GeoPointFieldMapper geoMapper = ((GeoPointFieldMapper.GeoStringFieldMapper) mapper).geoMapper();
    fieldName = mapper.names().indexName();

    Filter filter =
        new GeoDistanceFilter(
            lat,
            lon,
            distance,
            geoDistance,
            fieldName,
            geoMapper,
            parseContext.indexCache().fieldData(),
            optimizeBbox);
    if (cache) {
      filter = parseContext.cacheFilter(filter, cacheKey);
    }
    filter = QueryParsers.wrapSmartNameFilter(filter, smartMappers, parseContext);
    if (filterName != null) {
      parseContext.addNamedFilter(filterName, filter);
    }
    return filter;
  }
  @Override
  public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
    XContentParser parser = parseContext.parser();

    MoreLikeThisQuery mltQuery = new MoreLikeThisQuery();
    mltQuery.setSimilarity(parseContext.searchSimilarity());
    Analyzer analyzer = null;
    List<String> moreLikeFields = null;
    boolean failOnUnsupportedField = true;
    String queryName = null;
    boolean include = false;

    XContentParser.Token token;
    String currentFieldName = null;

    List<String> likeTexts = new ArrayList<>();
    MultiTermVectorsRequest likeItems = new MultiTermVectorsRequest();

    List<String> unlikeTexts = new ArrayList<>();
    MultiTermVectorsRequest unlikeItems = new MultiTermVectorsRequest();

    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token.isValue()) {
        if (parseContext.parseFieldMatcher().match(currentFieldName, Fields.LIKE_TEXT)) {
          likeTexts.add(parser.text());
        } else if (parseContext.parseFieldMatcher().match(currentFieldName, Fields.LIKE)) {
          parseLikeField(parser, likeTexts, likeItems);
        } else if (parseContext.parseFieldMatcher().match(currentFieldName, Fields.UNLIKE)) {
          parseLikeField(parser, unlikeTexts, unlikeItems);
        } else if (parseContext.parseFieldMatcher().match(currentFieldName, Fields.MIN_TERM_FREQ)) {
          mltQuery.setMinTermFrequency(parser.intValue());
        } else if (parseContext
            .parseFieldMatcher()
            .match(currentFieldName, Fields.MAX_QUERY_TERMS)) {
          mltQuery.setMaxQueryTerms(parser.intValue());
        } else if (parseContext.parseFieldMatcher().match(currentFieldName, Fields.MIN_DOC_FREQ)) {
          mltQuery.setMinDocFreq(parser.intValue());
        } else if (parseContext.parseFieldMatcher().match(currentFieldName, Fields.MAX_DOC_FREQ)) {
          mltQuery.setMaxDocFreq(parser.intValue());
        } else if (parseContext
            .parseFieldMatcher()
            .match(currentFieldName, Fields.MIN_WORD_LENGTH)) {
          mltQuery.setMinWordLen(parser.intValue());
        } else if (parseContext
            .parseFieldMatcher()
            .match(currentFieldName, Fields.MAX_WORD_LENGTH)) {
          mltQuery.setMaxWordLen(parser.intValue());
        } else if (parseContext.parseFieldMatcher().match(currentFieldName, Fields.BOOST_TERMS)) {
          float boostFactor = parser.floatValue();
          if (boostFactor != 0) {
            mltQuery.setBoostTerms(true);
            mltQuery.setBoostTermsFactor(boostFactor);
          }
        } else if (parseContext
            .parseFieldMatcher()
            .match(currentFieldName, Fields.MINIMUM_SHOULD_MATCH)) {
          mltQuery.setMinimumShouldMatch(parser.text());
        } else if ("analyzer".equals(currentFieldName)) {
          analyzer = parseContext.analysisService().analyzer(parser.text());
        } else if ("boost".equals(currentFieldName)) {
          mltQuery.setBoost(parser.floatValue());
        } else if (parseContext
            .parseFieldMatcher()
            .match(currentFieldName, Fields.FAIL_ON_UNSUPPORTED_FIELD)) {
          failOnUnsupportedField = parser.booleanValue();
        } else if ("_name".equals(currentFieldName)) {
          queryName = parser.text();
        } else if (parseContext.parseFieldMatcher().match(currentFieldName, Fields.INCLUDE)) {
          include = parser.booleanValue();
        } else {
          throw new QueryParsingException(
              parseContext, "[mlt] query does not support [" + currentFieldName + "]");
        }
      } else if (token == XContentParser.Token.START_ARRAY) {
        if (parseContext.parseFieldMatcher().match(currentFieldName, Fields.STOP_WORDS)) {
          Set<String> stopWords = Sets.newHashSet();
          while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
            stopWords.add(parser.text());
          }
          mltQuery.setStopWords(stopWords);
        } else if ("fields".equals(currentFieldName)) {
          moreLikeFields = new LinkedList<>();
          while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
            String field = parser.text();
            MappedFieldType fieldType = parseContext.fieldMapper(field);
            moreLikeFields.add(fieldType == null ? field : fieldType.names().indexName());
          }
        } else if (parseContext.parseFieldMatcher().match(currentFieldName, Fields.DOCUMENT_IDS)) {
          while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
            if (!token.isValue()) {
              throw new IllegalArgumentException("ids array element should only contain ids");
            }
            likeItems.add(newTermVectorsRequest().id(parser.text()));
          }
        } else if (parseContext.parseFieldMatcher().match(currentFieldName, Fields.DOCUMENTS)) {
          while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
            if (token != XContentParser.Token.START_OBJECT) {
              throw new IllegalArgumentException("docs array element should include an object");
            }
            likeItems.add(parseDocument(parser));
          }
        } else if (parseContext.parseFieldMatcher().match(currentFieldName, Fields.LIKE)) {
          while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
            parseLikeField(parser, likeTexts, likeItems);
          }
        } else if (parseContext.parseFieldMatcher().match(currentFieldName, Fields.UNLIKE)) {
          while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
            parseLikeField(parser, unlikeTexts, unlikeItems);
          }
        } else {
          throw new QueryParsingException(
              parseContext, "[mlt] query does not support [" + currentFieldName + "]");
        }
      } else if (token == XContentParser.Token.START_OBJECT) {
        if (parseContext.parseFieldMatcher().match(currentFieldName, Fields.LIKE)) {
          parseLikeField(parser, likeTexts, likeItems);
        } else if (parseContext.parseFieldMatcher().match(currentFieldName, Fields.UNLIKE)) {
          parseLikeField(parser, unlikeTexts, unlikeItems);
        } else {
          throw new QueryParsingException(
              parseContext, "[mlt] query does not support [" + currentFieldName + "]");
        }
      }
    }

    if (likeTexts.isEmpty() && likeItems.isEmpty()) {
      throw new QueryParsingException(
          parseContext, "more_like_this requires 'like' to be specified");
    }
    if (moreLikeFields != null && moreLikeFields.isEmpty()) {
      throw new QueryParsingException(
          parseContext, "more_like_this requires 'fields' to be non-empty");
    }

    // set analyzer
    if (analyzer == null) {
      analyzer = parseContext.mapperService().searchAnalyzer();
    }
    mltQuery.setAnalyzer(analyzer);

    // set like text fields
    boolean useDefaultField = (moreLikeFields == null);
    if (useDefaultField) {
      moreLikeFields = Collections.singletonList(parseContext.defaultField());
    }
    // possibly remove unsupported fields
    removeUnsupportedFields(moreLikeFields, analyzer, failOnUnsupportedField);
    if (moreLikeFields.isEmpty()) {
      return null;
    }
    mltQuery.setMoreLikeFields(moreLikeFields.toArray(Strings.EMPTY_ARRAY));

    // support for named query
    if (queryName != null) {
      parseContext.addNamedQuery(queryName, mltQuery);
    }

    // handle like texts
    if (!likeTexts.isEmpty()) {
      mltQuery.setLikeText(likeTexts);
    }
    if (!unlikeTexts.isEmpty()) {
      mltQuery.setIgnoreText(unlikeTexts);
    }

    // handle items
    if (!likeItems.isEmpty()) {
      // set default index, type and fields if not specified
      MultiTermVectorsRequest items = likeItems;
      for (TermVectorsRequest item : unlikeItems) {
        items.add(item);
      }

      for (TermVectorsRequest item : items) {
        if (item.index() == null) {
          item.index(parseContext.index().name());
        }
        if (item.type() == null) {
          if (parseContext.queryTypes().size() > 1) {
            throw new QueryParsingException(
                parseContext,
                "ambiguous type for item with id: " + item.id() + " and index: " + item.index());
          } else {
            item.type(parseContext.queryTypes().iterator().next());
          }
        }
        // default fields if not present but don't override for artificial docs
        if (item.selectedFields() == null && item.doc() == null) {
          if (useDefaultField) {
            item.selectedFields("*");
          } else {
            item.selectedFields(moreLikeFields.toArray(new String[moreLikeFields.size()]));
          }
        }
      }
      // fetching the items with multi-termvectors API
      items.copyContextAndHeadersFrom(SearchContext.current());
      MultiTermVectorsResponse responses = fetchService.fetchResponse(items);

      // getting the Fields for liked items
      mltQuery.setLikeText(MoreLikeThisFetchService.getFields(responses, likeItems));

      // getting the Fields for ignored items
      if (!unlikeItems.isEmpty()) {
        org.apache.lucene.index.Fields[] ignoreFields =
            MoreLikeThisFetchService.getFields(responses, unlikeItems);
        if (ignoreFields.length > 0) {
          mltQuery.setUnlikeText(ignoreFields);
        }
      }

      BooleanQuery.Builder boolQuery = new BooleanQuery.Builder();
      boolQuery.add(mltQuery, BooleanClause.Occur.SHOULD);

      // exclude the items from the search
      if (!include) {
        handleExclude(boolQuery, likeItems);
      }
      return boolQuery.build();
    }

    return mltQuery;
  }