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;
  }
 @Override
 public Filter regexpFilter(
     Object value, int flags, int maxDeterminizedStates, @Nullable QueryParseContext context) {
   if (fieldType.indexOptions() != IndexOptions.NONE || context == null) {
     return super.regexpFilter(value, flags, maxDeterminizedStates, context);
   }
   Collection<String> queryTypes = context.queryTypes();
   if (queryTypes.size() == 1) {
     return new RegexpFilter(
         new Term(
             UidFieldMapper.NAME,
             Uid.createUidAsBytes(
                 Iterables.getFirst(queryTypes, null), BytesRefs.toBytesRef(value))),
         flags,
         maxDeterminizedStates);
   }
   XBooleanFilter filter = new XBooleanFilter();
   for (String queryType : queryTypes) {
     filter.add(
         new RegexpFilter(
             new Term(
                 UidFieldMapper.NAME,
                 Uid.createUidAsBytes(queryType, BytesRefs.toBytesRef(value))),
             flags,
             maxDeterminizedStates),
         BooleanClause.Occur.SHOULD);
   }
   return filter;
 }
 /**
  * A filter for search. If a filter is required, will return it, otherwise, will return
  * <tt>null</tt>.
  */
 public Filter searchFilter(String... types) {
   if (types == null || types.length == 0) {
     if (hasNested) {
       return NonNestedDocsFilter.INSTANCE;
     } else {
       return null;
     }
   }
   // if we filter by types, we don't need to filter by non nested docs
   // since they have different types (starting with __)
   if (types.length == 1) {
     DocumentMapper docMapper = documentMapper(types[0]);
     if (docMapper == null) {
       return new TermFilter(new Term(types[0]));
     }
     return docMapper.typeFilter();
   }
   // see if we can use terms filter
   boolean useTermsFilter = true;
   for (String type : types) {
     DocumentMapper docMapper = documentMapper(type);
     if (docMapper == null) {
       useTermsFilter = false;
       break;
     }
     if (!docMapper.typeMapper().indexed()) {
       useTermsFilter = false;
       break;
     }
   }
   if (useTermsFilter) {
     Term[] typesTerms = new Term[types.length];
     for (int i = 0; i < typesTerms.length; i++) {
       typesTerms[i] = TypeFieldMapper.TERM_FACTORY.createTerm(types[i]);
     }
     return new XTermsFilter(typesTerms);
   } else {
     XBooleanFilter bool = new XBooleanFilter();
     for (String type : types) {
       DocumentMapper docMapper = documentMapper(type);
       if (docMapper == null) {
         bool.add(
             new FilterClause(
                 new TermFilter(TypeFieldMapper.TERM_FACTORY.createTerm(type)),
                 BooleanClause.Occur.SHOULD));
       } else {
         bool.add(new FilterClause(docMapper.typeFilter(), BooleanClause.Occur.SHOULD));
       }
     }
     return bool;
   }
 }
  @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 Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
    XContentParser parser = parseContext.parser();

    String fieldName = null;
    ShapeRelation shapeRelation = ShapeRelation.INTERSECTS;
    String strategyName = null;
    ShapeBuilder shape = null;
    FilterCachingPolicy cache = parseContext.autoFilterCachePolicy();
    HashedBytesRef cacheKey = null;
    String filterName = null;

    String id = null;
    String type = null;
    String index = DEFAULTS.INDEX_NAME;
    String shapePath = DEFAULTS.SHAPE_FIELD_NAME;

    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 == XContentParser.Token.START_OBJECT) {
        fieldName = currentFieldName;

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

            token = parser.nextToken();
            if ("shape".equals(currentFieldName)) {
              shape = ShapeBuilder.parse(parser);
            } else if ("relation".equals(currentFieldName)) {
              shapeRelation = ShapeRelation.getRelationByName(parser.text());
              if (shapeRelation == null) {
                throw new QueryParsingException(
                    parseContext.index(), "Unknown shape operation [" + parser.text() + "]");
              }
            } else if ("strategy".equals(currentFieldName)) {
              strategyName = parser.text();
            } else if ("indexed_shape".equals(currentFieldName)
                || "indexedShape".equals(currentFieldName)) {
              while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
                if (token == XContentParser.Token.FIELD_NAME) {
                  currentFieldName = parser.currentName();
                } else if (token.isValue()) {
                  if ("id".equals(currentFieldName)) {
                    id = parser.text();
                  } else if ("type".equals(currentFieldName)) {
                    type = parser.text();
                  } else if ("index".equals(currentFieldName)) {
                    index = parser.text();
                  } else if ("path".equals(currentFieldName)) {
                    shapePath = parser.text();
                  }
                }
              }
              if (id == null) {
                throw new QueryParsingException(
                    parseContext.index(), "ID for indexed shape not provided");
              } else if (type == null) {
                throw new QueryParsingException(
                    parseContext.index(), "Type for indexed shape not provided");
              }
              shape = fetchService.fetch(id, type, index, shapePath);
            } else {
              throw new QueryParsingException(
                  parseContext.index(),
                  "[geo_shape] filter does not support [" + currentFieldName + "]");
            }
          }
        }
      } else if (token.isValue()) {
        if ("_name".equals(currentFieldName)) {
          filterName = parser.text();
        } else if ("_cache".equals(currentFieldName)) {
          cache = parseContext.parseFilterCachePolicy();
        } else if ("_cache_key".equals(currentFieldName)) {
          cacheKey = new HashedBytesRef(parser.text());
        } else {
          throw new QueryParsingException(
              parseContext.index(),
              "[geo_shape] filter does not support [" + currentFieldName + "]");
        }
      }
    }

    if (shape == null) {
      throw new QueryParsingException(parseContext.index(), "No Shape defined");
    } else if (shapeRelation == null) {
      throw new QueryParsingException(parseContext.index(), "No Shape Relation defined");
    }

    MapperService.SmartNameFieldMappers smartNameFieldMappers =
        parseContext.smartFieldMappers(fieldName);
    if (smartNameFieldMappers == null || !smartNameFieldMappers.hasMapper()) {
      throw new QueryParsingException(
          parseContext.index(), "Failed to find geo_shape field [" + fieldName + "]");
    }

    FieldMapper fieldMapper = smartNameFieldMappers.mapper();
    // TODO: This isn't the nicest way to check this
    if (!(fieldMapper instanceof GeoShapeFieldMapper)) {
      throw new QueryParsingException(
          parseContext.index(), "Field [" + fieldName + "] is not a geo_shape");
    }

    GeoShapeFieldMapper shapeFieldMapper = (GeoShapeFieldMapper) fieldMapper;
    PrefixTreeStrategy strategy = shapeFieldMapper.defaultStrategy();
    if (strategyName != null) {
      strategy = shapeFieldMapper.resolveStrategy(strategyName);
    }

    Filter filter;
    if (strategy instanceof RecursivePrefixTreeStrategy
        && shapeRelation == ShapeRelation.DISJOINT) {
      // this strategy doesn't support disjoint anymore: but it did before, including creating
      // lucene fieldcache (!)
      // in this case, execute disjoint as exists && !intersects
      XBooleanFilter bool = new XBooleanFilter();
      Filter exists = ExistsFilterParser.newFilter(parseContext, fieldName, null);
      Filter intersects =
          strategy.makeFilter(GeoShapeQueryParser.getArgs(shape, ShapeRelation.INTERSECTS));
      bool.add(exists, BooleanClause.Occur.MUST);
      bool.add(intersects, BooleanClause.Occur.MUST_NOT);
      filter = bool;
    } else {
      filter = strategy.makeFilter(GeoShapeQueryParser.getArgs(shape, shapeRelation));
    }

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

    if (filterName != null) {
      parseContext.addNamedFilter(filterName, filter);
    }

    return filter;
  }
  /**
   * A filter for search. If a filter is required, will return it, otherwise, will return
   * <tt>null</tt>.
   */
  @Nullable
  public Filter searchFilter(String... types) {
    boolean filterPercolateType = hasMapping(PercolatorService.TYPE_NAME);
    if (types != null && filterPercolateType) {
      for (String type : types) {
        if (PercolatorService.TYPE_NAME.equals(type)) {
          filterPercolateType = false;
          break;
        }
      }
    }
    Filter excludePercolatorType = null;
    if (filterPercolateType) {
      excludePercolatorType =
          new NotFilter(documentMapper(PercolatorService.TYPE_NAME).typeFilter());
    }

    if (types == null || types.length == 0) {
      if (hasNested && filterPercolateType) {
        return new AndFilter(ImmutableList.of(excludePercolatorType, NonNestedDocsFilter.INSTANCE));
      } else if (hasNested) {
        return NonNestedDocsFilter.INSTANCE;
      } else if (filterPercolateType) {
        return excludePercolatorType;
      } else {
        return null;
      }
    }
    // if we filter by types, we don't need to filter by non nested docs
    // since they have different types (starting with __)
    if (types.length == 1) {
      DocumentMapper docMapper = documentMapper(types[0]);
      if (docMapper == null) {
        return new TermFilter(new Term(types[0]));
      }
      return docMapper.typeFilter();
    }
    // see if we can use terms filter
    boolean useTermsFilter = true;
    for (String type : types) {
      DocumentMapper docMapper = documentMapper(type);
      if (docMapper == null) {
        useTermsFilter = false;
        break;
      }
      if (!docMapper.typeMapper().fieldType().indexed()) {
        useTermsFilter = false;
        break;
      }
    }

    if (useTermsFilter) {
      BytesRef[] typesBytes = new BytesRef[types.length];
      for (int i = 0; i < typesBytes.length; i++) {
        typesBytes[i] = new BytesRef(types[i]);
      }
      TermsFilter termsFilter = new TermsFilter(TypeFieldMapper.NAME, typesBytes);
      if (filterPercolateType) {
        return new AndFilter(ImmutableList.of(excludePercolatorType, termsFilter));
      } else {
        return termsFilter;
      }
    } else {
      // Current bool filter requires that at least one should clause matches, even with a must
      // clause.
      XBooleanFilter bool = new XBooleanFilter();
      for (String type : types) {
        DocumentMapper docMapper = documentMapper(type);
        if (docMapper == null) {
          bool.add(
              new FilterClause(
                  new TermFilter(new Term(TypeFieldMapper.NAME, type)),
                  BooleanClause.Occur.SHOULD));
        } else {
          bool.add(new FilterClause(docMapper.typeFilter(), BooleanClause.Occur.SHOULD));
        }
      }
      if (filterPercolateType) {
        bool.add(excludePercolatorType, BooleanClause.Occur.MUST);
      }

      return bool;
    }
  }
  @Override
  public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
    XContentParser parser = parseContext.parser();

    boolean queryFound = false;
    float boost = 1.0f;
    String parentType = null;
    boolean score = false;
    String queryName = 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, parentType == null ? null : new String[] {parentType});
          queryFound = true;
        } else {
          throw new QueryParsingException(
              parseContext.index(),
              "[has_parent] query 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] query has been removed, use a filter as a facet_filter in the relevant global facet");
        } else if ("score_type".equals(currentFieldName) || "scoreType".equals(currentFieldName)) {
          String scoreTypeValue = parser.text();
          if ("score".equals(scoreTypeValue)) {
            score = true;
          } else if ("none".equals(scoreTypeValue)) {
            score = false;
          }
        } else if ("score_mode".equals(currentFieldName) || "scoreMode".equals(currentFieldName)) {
          String scoreModeValue = parser.text();
          if ("score".equals(scoreModeValue)) {
            score = true;
          } else if ("none".equals(scoreModeValue)) {
            score = false;
          }
        } else if ("boost".equals(currentFieldName)) {
          boost = parser.floatValue();
        } else if ("_name".equals(currentFieldName)) {
          queryName = parser.text();
        } else {
          throw new QueryParsingException(
              parseContext.index(),
              "[has_parent] query does not support [" + currentFieldName + "]");
        }
      }
    }
    if (!queryFound) {
      throw new QueryParsingException(
          parseContext.index(), "[has_parent] query requires 'query' field");
    }
    if (parentType == null) {
      throw new QueryParsingException(
          parseContext.index(), "[has_parent] query requires 'parent_type' field");
    }

    Query innerQuery = iq.asQuery(parentType);

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

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

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

    ParentChildIndexFieldData parentChildIndexFieldData = null;
    Set<String> parentTypes = new HashSet<>(5);
    parentTypes.add(parentType);
    for (DocumentMapper documentMapper : parseContext.mapperService()) {
      ParentFieldMapper parentFieldMapper = documentMapper.parentFieldMapper();
      if (parentFieldMapper.active()) {
        parentChildIndexFieldData = parseContext.fieldData().getForField(parentFieldMapper);
        DocumentMapper parentTypeDocumentMapper =
            parseContext.mapperService().documentMapper(parentFieldMapper.type());
        if (parentTypeDocumentMapper == null) {
          // Only add this, if this parentFieldMapper (also a parent)  isn't a child of another
          // parent.
          parentTypes.add(parentFieldMapper.type());
        }
      }
    }
    if (parentChildIndexFieldData == null) {
      throw new QueryParsingException(
          parseContext.index(), "[has_parent] no _parent field configured");
    }

    Filter parentFilter;
    if (parentTypes.size() == 1) {
      DocumentMapper documentMapper =
          parseContext.mapperService().documentMapper(parentTypes.iterator().next());
      parentFilter = parseContext.cacheFilter(documentMapper.typeFilter(), null);
    } else {
      XBooleanFilter parentsFilter = new XBooleanFilter();
      for (String parentTypeStr : parentTypes) {
        DocumentMapper documentMapper = parseContext.mapperService().documentMapper(parentTypeStr);
        Filter filter = parseContext.cacheFilter(documentMapper.typeFilter(), null);
        parentsFilter.add(filter, BooleanClause.Occur.SHOULD);
      }
      parentFilter = parentsFilter;
    }
    Filter childrenFilter = parseContext.cacheFilter(new NotFilter(parentFilter), null);

    boolean deleteByQuery = "delete_by_query".equals(SearchContext.current().source());
    Query query;
    if (!deleteByQuery && score) {
      query = new ParentQuery(parentChildIndexFieldData, innerQuery, parentType, childrenFilter);
    } else {
      query =
          new ParentConstantScoreQuery(
              parentChildIndexFieldData, innerQuery, parentType, childrenFilter);
      if (deleteByQuery) {
        query = new XConstantScoreQuery(new DeleteByQueryWrappingFilter(query));
      }
    }
    query.setBoost(boost);
    if (queryName != null) {
      parseContext.addNamedFilter(queryName, new CustomQueryWrappingFilter(query));
    }
    return query;
  }