org.elasticsearch.index.query.ExistsQueryBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of elasticsearch Show documentation
Show all versions of elasticsearch Show documentation
Elasticsearch subproject :server
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.index.query;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.elasticsearch.Version;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.index.mapper.FieldNamesFieldMapper;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.xcontent.ParseField;
import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.XContentParser;
import java.io.IOException;
import java.util.Collection;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Constructs a query that only match on documents that the field has a value in them.
*/
public class ExistsQueryBuilder extends AbstractQueryBuilder {
public static final String NAME = "exists";
public static final ParseField FIELD_FIELD = new ParseField("field");
private final String fieldName;
public ExistsQueryBuilder(String fieldName) {
if (Strings.isEmpty(fieldName)) {
throw new IllegalArgumentException("field name is null or empty");
}
this.fieldName = fieldName;
}
/**
* Read from a stream.
*/
public ExistsQueryBuilder(StreamInput in) throws IOException {
super(in);
fieldName = in.readString();
}
@Override
protected void doWriteTo(StreamOutput out) throws IOException {
out.writeString(fieldName);
}
/**
* @return the field name that has to exist for this query to match
*/
public String fieldName() {
return this.fieldName;
}
@Override
protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
SearchExecutionContext context = queryRewriteContext.convertToSearchExecutionContext();
if (context != null) {
if (getMappedFields(context, fieldName).isEmpty()) {
return new MatchNoneQueryBuilder();
}
}
return super.doRewrite(queryRewriteContext);
}
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(NAME);
builder.field(FIELD_FIELD.getPreferredName(), fieldName);
printBoostAndQueryName(builder);
builder.endObject();
}
public static ExistsQueryBuilder fromXContent(XContentParser parser) throws IOException {
String fieldPattern = null;
String queryName = null;
float boost = AbstractQueryBuilder.DEFAULT_BOOST;
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_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
fieldPattern = parser.text();
} else if (AbstractQueryBuilder.NAME_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
queryName = parser.text();
} else if (AbstractQueryBuilder.BOOST_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
boost = parser.floatValue();
} else {
throw new ParsingException(
parser.getTokenLocation(),
"[" + ExistsQueryBuilder.NAME + "] query does not support [" + currentFieldName + "]"
);
}
} else {
throw new ParsingException(
parser.getTokenLocation(),
"[" + ExistsQueryBuilder.NAME + "] unknown token [" + token + "] after [" + currentFieldName + "]"
);
}
}
if (fieldPattern == null) {
throw new ParsingException(parser.getTokenLocation(), "[" + ExistsQueryBuilder.NAME + "] must be provided with a [field]");
}
ExistsQueryBuilder builder = new ExistsQueryBuilder(fieldPattern);
builder.queryName(queryName);
builder.boost(boost);
return builder;
}
@Override
protected Query doToQuery(SearchExecutionContext context) throws IOException {
return newFilter(context, fieldName, true);
}
public static Query newFilter(SearchExecutionContext context, String fieldPattern, boolean checkRewrite) {
Collection fields = getMappedFields(context, fieldPattern).stream()
.map(context::getFieldType)
.collect(Collectors.toList());
if (fields.isEmpty()) {
if (checkRewrite) {
throw new IllegalStateException("Rewrite first");
} else {
return new MatchNoDocsQuery("unmapped field:" + fieldPattern);
}
}
if (context.indexVersionCreated().before(Version.V_6_1_0)) {
return newLegacyExistsQuery(fields);
}
if (fields.size() == 1) {
MappedFieldType field = fields.iterator().next();
return new ConstantScoreQuery(field.existsQuery(context));
}
BooleanQuery.Builder boolFilterBuilder = new BooleanQuery.Builder();
for (MappedFieldType field : fields) {
boolFilterBuilder.add(field.existsQuery(context), BooleanClause.Occur.SHOULD);
}
return new ConstantScoreQuery(boolFilterBuilder.build());
}
private static Query newLegacyExistsQuery(Collection fields) {
// We create TermsQuery directly here rather than using FieldNamesFieldType.termsQuery()
// so we don't end up with deprecation warnings
if (fields.size() == 1) {
return new ConstantScoreQuery(new TermQuery(new Term(FieldNamesFieldMapper.NAME, fields.iterator().next().name())));
}
BooleanQuery.Builder boolFilterBuilder = new BooleanQuery.Builder();
for (MappedFieldType field : fields) {
Query filter = new TermQuery(new Term(FieldNamesFieldMapper.NAME, field.name()));
boolFilterBuilder.add(filter, BooleanClause.Occur.SHOULD);
}
return new ConstantScoreQuery(boolFilterBuilder.build());
}
private static Collection getMappedFields(SearchExecutionContext context, String fieldPattern) {
Set matchingFieldNames = context.getMatchingFieldNames(fieldPattern);
if (matchingFieldNames.isEmpty()) {
// might be an object field, so try matching it as an object prefix pattern
matchingFieldNames = context.getMatchingFieldNames(fieldPattern + ".*");
}
return matchingFieldNames;
}
@Override
protected int doHashCode() {
return Objects.hash(fieldName);
}
@Override
protected boolean doEquals(ExistsQueryBuilder other) {
return Objects.equals(fieldName, other.fieldName);
}
@Override
public String getWriteableName() {
return NAME;
}
}