mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-03-25 09:28:27 +00:00
Merge pull request #15364 from jimferenczi/missing_query_removal
Remove the MissingQueryBuilder which was deprecated in 2.2.0.
This commit is contained in:
commit
d78e24689b
@ -54,7 +54,6 @@ public class MapperQueryParser extends QueryParser {
|
||||
static {
|
||||
Map<String, FieldQueryExtension> fieldQueryExtensions = new HashMap<>();
|
||||
fieldQueryExtensions.put(ExistsFieldQueryExtension.NAME, new ExistsFieldQueryExtension());
|
||||
fieldQueryExtensions.put(MissingFieldQueryExtension.NAME, new MissingFieldQueryExtension());
|
||||
FIELD_QUERY_EXTENSIONS = unmodifiableMap(fieldQueryExtensions);
|
||||
}
|
||||
|
||||
|
@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.queryparser.classic;
|
||||
|
||||
import org.apache.lucene.search.ConstantScoreQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.index.query.MissingQueryBuilder;
|
||||
import org.elasticsearch.index.query.QueryShardContext;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class MissingFieldQueryExtension implements FieldQueryExtension {
|
||||
|
||||
public static final String NAME = "_missing_";
|
||||
|
||||
@Override
|
||||
public Query query(QueryShardContext context, String queryText) {
|
||||
Query query = MissingQueryBuilder.newFilter(context, queryText, MissingQueryBuilder.DEFAULT_EXISTENCE_VALUE, MissingQueryBuilder.DEFAULT_NULL_VALUE);
|
||||
if (query != null) {
|
||||
return new ConstantScoreQuery(query);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,234 +0,0 @@
|
||||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.index.query;
|
||||
|
||||
import org.apache.lucene.search.BooleanClause;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.ConstantScoreQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.TermRangeQuery;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.lucene.search.Queries;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.index.mapper.MappedFieldType;
|
||||
import org.elasticsearch.index.mapper.internal.FieldNamesFieldMapper;
|
||||
import org.elasticsearch.index.mapper.object.ObjectMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Constructs a filter that have only null values or no value in the original field.
|
||||
*/
|
||||
public class MissingQueryBuilder extends AbstractQueryBuilder<MissingQueryBuilder> {
|
||||
|
||||
public static final String NAME = "missing";
|
||||
|
||||
public static final boolean DEFAULT_NULL_VALUE = false;
|
||||
|
||||
public static final boolean DEFAULT_EXISTENCE_VALUE = true;
|
||||
|
||||
private final String fieldPattern;
|
||||
|
||||
private final boolean nullValue;
|
||||
|
||||
private final boolean existence;
|
||||
|
||||
static final MissingQueryBuilder PROTOTYPE = new MissingQueryBuilder("field", DEFAULT_NULL_VALUE, DEFAULT_EXISTENCE_VALUE);
|
||||
|
||||
/**
|
||||
* Constructs a filter that returns documents with only null values or no value in the original field.
|
||||
* @param fieldPattern the field to query
|
||||
* @param nullValue should the missing filter automatically include fields with null value configured in the
|
||||
* mappings. Defaults to <tt>false</tt>.
|
||||
* @param existence should the missing filter include documents where the field doesn't exist in the docs.
|
||||
* Defaults to <tt>true</tt>.
|
||||
* @throws IllegalArgumentException when both <tt>existence</tt> and <tt>nullValue</tt> are set to false
|
||||
*/
|
||||
public MissingQueryBuilder(String fieldPattern, boolean nullValue, boolean existence) {
|
||||
if (Strings.isEmpty(fieldPattern)) {
|
||||
throw new IllegalArgumentException("missing query must be provided with a [field]");
|
||||
}
|
||||
if (nullValue == false && existence == false) {
|
||||
throw new IllegalArgumentException("missing query must have either 'existence', or 'null_value', or both set to true");
|
||||
}
|
||||
this.fieldPattern = fieldPattern;
|
||||
this.nullValue = nullValue;
|
||||
this.existence = existence;
|
||||
}
|
||||
|
||||
public MissingQueryBuilder(String fieldPattern) {
|
||||
this(fieldPattern, DEFAULT_NULL_VALUE, DEFAULT_EXISTENCE_VALUE);
|
||||
}
|
||||
|
||||
public String fieldPattern() {
|
||||
return this.fieldPattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the missing filter will include documents where the field contains a null value, otherwise
|
||||
* these documents will not be included.
|
||||
*/
|
||||
public boolean nullValue() {
|
||||
return this.nullValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the missing filter will include documents where the field has no values, otherwise
|
||||
* these documents will not be included.
|
||||
*/
|
||||
public boolean existence() {
|
||||
return this.existence;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(NAME);
|
||||
builder.field(MissingQueryParser.FIELD_FIELD.getPreferredName(), fieldPattern);
|
||||
builder.field(MissingQueryParser.NULL_VALUE_FIELD.getPreferredName(), nullValue);
|
||||
builder.field(MissingQueryParser.EXISTENCE_FIELD.getPreferredName(), existence);
|
||||
printBoostAndQueryName(builder);
|
||||
builder.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWriteableName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query doToQuery(QueryShardContext context) throws IOException {
|
||||
return newFilter(context, fieldPattern, existence, nullValue);
|
||||
}
|
||||
|
||||
public static Query newFilter(QueryShardContext context, String fieldPattern, boolean existence, boolean nullValue) {
|
||||
if (!existence && !nullValue) {
|
||||
throw new QueryShardException(context, "missing must have either existence, or null_value, or both set to true");
|
||||
}
|
||||
|
||||
final FieldNamesFieldMapper.FieldNamesFieldType fieldNamesFieldType = (FieldNamesFieldMapper.FieldNamesFieldType) context.getMapperService().fullName(FieldNamesFieldMapper.NAME);
|
||||
if (fieldNamesFieldType == null) {
|
||||
// can only happen when no types exist, so no docs exist either
|
||||
return Queries.newMatchNoDocsQuery();
|
||||
}
|
||||
|
||||
ObjectMapper objectMapper = context.getObjectMapper(fieldPattern);
|
||||
if (objectMapper != null) {
|
||||
// automatic make the object mapper pattern
|
||||
fieldPattern = fieldPattern + ".*";
|
||||
}
|
||||
|
||||
Collection<String> fields = context.simpleMatchToIndexNames(fieldPattern);
|
||||
if (fields.isEmpty()) {
|
||||
if (existence) {
|
||||
// if we ask for existence of fields, and we found none, then we should match on all
|
||||
return Queries.newMatchAllQuery();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Query existenceFilter = null;
|
||||
Query nullFilter = null;
|
||||
|
||||
if (existence) {
|
||||
BooleanQuery.Builder boolFilter = new BooleanQuery.Builder();
|
||||
for (String field : fields) {
|
||||
MappedFieldType fieldType = context.fieldMapper(field);
|
||||
Query filter = null;
|
||||
if (fieldNamesFieldType.isEnabled()) {
|
||||
final String f;
|
||||
if (fieldType != null) {
|
||||
f = fieldType.names().indexName();
|
||||
} else {
|
||||
f = field;
|
||||
}
|
||||
filter = fieldNamesFieldType.termQuery(f, context);
|
||||
}
|
||||
// if _field_names are not indexed, we need to go the slow way
|
||||
if (filter == null && fieldType != null) {
|
||||
filter = fieldType.rangeQuery(null, null, true, true);
|
||||
}
|
||||
if (filter == null) {
|
||||
filter = new TermRangeQuery(field, null, null, true, true);
|
||||
}
|
||||
boolFilter.add(filter, BooleanClause.Occur.SHOULD);
|
||||
}
|
||||
|
||||
existenceFilter = boolFilter.build();
|
||||
existenceFilter = Queries.not(existenceFilter);;
|
||||
}
|
||||
|
||||
if (nullValue) {
|
||||
for (String field : fields) {
|
||||
MappedFieldType fieldType = context.fieldMapper(field);
|
||||
if (fieldType != null) {
|
||||
nullFilter = fieldType.nullValueQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Query filter;
|
||||
if (nullFilter != null) {
|
||||
if (existenceFilter != null) {
|
||||
filter = new BooleanQuery.Builder()
|
||||
.add(existenceFilter, BooleanClause.Occur.SHOULD)
|
||||
.add(nullFilter, BooleanClause.Occur.SHOULD)
|
||||
.build();
|
||||
} else {
|
||||
filter = nullFilter;
|
||||
}
|
||||
} else {
|
||||
filter = existenceFilter;
|
||||
}
|
||||
|
||||
if (filter == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ConstantScoreQuery(filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MissingQueryBuilder doReadFrom(StreamInput in) throws IOException {
|
||||
return new MissingQueryBuilder(in.readString(), in.readBoolean(), in.readBoolean());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doWriteTo(StreamOutput out) throws IOException {
|
||||
out.writeString(fieldPattern);
|
||||
out.writeBoolean(nullValue);
|
||||
out.writeBoolean(existence);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int doHashCode() {
|
||||
return Objects.hash(fieldPattern, nullValue, existence);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doEquals(MissingQueryBuilder other) {
|
||||
return Objects.equals(fieldPattern, other.fieldPattern) &&
|
||||
Objects.equals(nullValue, other.nullValue) &&
|
||||
Objects.equals(existence, other.existence);
|
||||
}
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.index.query;
|
||||
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.ParsingException;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Parser for missing query
|
||||
*/
|
||||
public class MissingQueryParser implements QueryParser<MissingQueryBuilder> {
|
||||
|
||||
public static final ParseField FIELD_FIELD = new ParseField("field");
|
||||
public static final ParseField NULL_VALUE_FIELD = new ParseField("null_value");
|
||||
public static final ParseField EXISTENCE_FIELD = new ParseField("existence");
|
||||
|
||||
@Override
|
||||
public String[] names() {
|
||||
return new String[]{MissingQueryBuilder.NAME};
|
||||
}
|
||||
|
||||
@Override
|
||||
public MissingQueryBuilder fromXContent(QueryParseContext parseContext) throws IOException {
|
||||
XContentParser parser = parseContext.parser();
|
||||
|
||||
String fieldPattern = null;
|
||||
String queryName = null;
|
||||
float boost = AbstractQueryBuilder.DEFAULT_BOOST;
|
||||
boolean nullValue = MissingQueryBuilder.DEFAULT_NULL_VALUE;
|
||||
boolean existence = MissingQueryBuilder.DEFAULT_EXISTENCE_VALUE;
|
||||
|
||||
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 (parseContext.parseFieldMatcher().match(currentFieldName, FIELD_FIELD)) {
|
||||
fieldPattern = parser.text();
|
||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, NULL_VALUE_FIELD)) {
|
||||
nullValue = parser.booleanValue();
|
||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, EXISTENCE_FIELD)) {
|
||||
existence = parser.booleanValue();
|
||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
|
||||
queryName = parser.text();
|
||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
|
||||
boost = parser.floatValue();
|
||||
} else {
|
||||
throw new ParsingException(parser.getTokenLocation(), "[" + MissingQueryBuilder.NAME + "] query does not support [" + currentFieldName + "]");
|
||||
}
|
||||
} else {
|
||||
throw new ParsingException(parser.getTokenLocation(), "[" + MissingQueryBuilder.NAME + "] unknown token [" + token + "] after [" + currentFieldName + "]");
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldPattern == null) {
|
||||
throw new ParsingException(parser.getTokenLocation(), "missing must be provided with a [field]");
|
||||
}
|
||||
return new MissingQueryBuilder(fieldPattern, nullValue, existence)
|
||||
.boost(boost)
|
||||
.queryName(queryName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MissingQueryBuilder getBuilderPrototype() {
|
||||
return MissingQueryBuilder.PROTOTYPE;
|
||||
}
|
||||
}
|
@ -810,27 +810,6 @@ public abstract class QueryBuilders {
|
||||
return new ExistsQueryBuilder(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* A filter to filter only documents where a field does not exists in them.
|
||||
* @param name the field to query
|
||||
*/
|
||||
public static MissingQueryBuilder missingQuery(String name) {
|
||||
return missingQuery(name, MissingQueryBuilder.DEFAULT_NULL_VALUE, MissingQueryBuilder.DEFAULT_EXISTENCE_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* A filter to filter only documents where a field does not exists in them.
|
||||
* @param name the field to query
|
||||
* @param nullValue should the missing filter automatically include fields with null value configured in the
|
||||
* mappings. Defaults to <tt>false</tt>.
|
||||
* @param existence should the missing filter include documents where the field doesn't exist in the docs.
|
||||
* Defaults to <tt>true</tt>.
|
||||
* @throws IllegalArgumentException when both <tt>existence</tt> and <tt>nullValue</tt> are set to false
|
||||
*/
|
||||
public static MissingQueryBuilder missingQuery(String name, boolean nullValue, boolean existence) {
|
||||
return new MissingQueryBuilder(name, nullValue, existence);
|
||||
}
|
||||
|
||||
private QueryBuilders() {
|
||||
|
||||
}
|
||||
|
@ -120,7 +120,6 @@ public class IndicesModule extends AbstractModule {
|
||||
registerQueryParser(GeohashCellQuery.Parser.class);
|
||||
registerQueryParser(GeoPolygonQueryParser.class);
|
||||
registerQueryParser(ExistsQueryParser.class);
|
||||
registerQueryParser(MissingQueryParser.class);
|
||||
registerQueryParser(MatchNoneQueryParser.class);
|
||||
|
||||
if (ShapesAvailability.JTS_AVAILABLE) {
|
||||
|
@ -865,7 +865,7 @@ public class IndexAliasesIT extends ESIntegTestCase {
|
||||
assertAcked(prepareCreate("test")
|
||||
.addMapping("type", "field", "type=string")
|
||||
.addAlias(new Alias("alias1"))
|
||||
.addAlias(new Alias("alias2").filter(QueryBuilders.missingQuery("field")))
|
||||
.addAlias(new Alias("alias2").filter(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery("field"))))
|
||||
.addAlias(new Alias("alias3").indexRouting("index").searchRouting("search")));
|
||||
|
||||
checkAliases();
|
||||
|
@ -71,7 +71,6 @@ import java.util.concurrent.ExecutionException;
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.constantScoreQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.existsQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.missingQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
|
||||
@ -440,25 +439,9 @@ public class BasicBackwardsCompatibilityIT extends ESBackcompatTestCase {
|
||||
countResponse = client().prepareSearch().setSize(0).setQuery(existsQuery("obj1")).get();
|
||||
assertHitCount(countResponse, 2l);
|
||||
|
||||
countResponse = client().prepareSearch().setSize(0).setQuery(missingQuery("field1")).get();
|
||||
assertHitCount(countResponse, 2l);
|
||||
|
||||
countResponse = client().prepareSearch().setSize(0).setQuery(missingQuery("field1")).get();
|
||||
assertHitCount(countResponse, 2l);
|
||||
|
||||
countResponse = client().prepareSearch().setSize(0).setQuery(constantScoreQuery(missingQuery("field1"))).get();
|
||||
assertHitCount(countResponse, 2l);
|
||||
|
||||
countResponse = client().prepareSearch().setSize(0).setQuery(queryStringQuery("_missing_:field1")).get();
|
||||
assertHitCount(countResponse, 2l);
|
||||
|
||||
// wildcard check
|
||||
countResponse = client().prepareSearch().setSize(0).setQuery(missingQuery("x*")).get();
|
||||
assertHitCount(countResponse, 2l);
|
||||
|
||||
// object check
|
||||
countResponse = client().prepareSearch().setSize(0).setQuery(missingQuery("obj1")).get();
|
||||
assertHitCount(countResponse, 2l);
|
||||
if (!backwardsCluster().upgradeOneNode()) {
|
||||
break;
|
||||
}
|
||||
|
@ -341,13 +341,6 @@ public class OldIndexBackwardsCompatibilityIT extends ESIntegTestCase {
|
||||
searchRsp = searchReq.get();
|
||||
ElasticsearchAssertions.assertNoFailures(searchRsp);
|
||||
assertEquals(numDocs, searchRsp.getHits().getTotalHits());
|
||||
|
||||
logger.info("--> testing missing filter");
|
||||
// the field for the missing filter here needs to be different than the exists filter above, to avoid being found in the cache
|
||||
searchReq = client().prepareSearch(indexName).setQuery(QueryBuilders.missingQuery("long_sort"));
|
||||
searchRsp = searchReq.get();
|
||||
ElasticsearchAssertions.assertNoFailures(searchRsp);
|
||||
assertEquals(0, searchRsp.getHits().getTotalHits());
|
||||
}
|
||||
|
||||
void assertBasicAggregationWorks(String indexName) {
|
||||
|
@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.index.query;
|
||||
|
||||
import org.apache.lucene.search.Query;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
|
||||
public class MissingQueryBuilderTests extends AbstractQueryTestCase<MissingQueryBuilder> {
|
||||
|
||||
@Override
|
||||
protected MissingQueryBuilder doCreateTestQueryBuilder() {
|
||||
String fieldName = randomBoolean() ? randomFrom(MAPPED_FIELD_NAMES) : randomAsciiOfLengthBetween(1, 10);
|
||||
Boolean existence = randomBoolean();
|
||||
Boolean nullValue = randomBoolean();
|
||||
if (existence == false && nullValue == false) {
|
||||
if (randomBoolean()) {
|
||||
existence = true;
|
||||
} else {
|
||||
nullValue = true;
|
||||
}
|
||||
}
|
||||
return new MissingQueryBuilder(fieldName, nullValue, existence);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doAssertLuceneQuery(MissingQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException {
|
||||
// too many mapping dependent cases to test, we don't want to end up
|
||||
// duplication the toQuery method
|
||||
}
|
||||
|
||||
public void testIllegalArguments() {
|
||||
try {
|
||||
if (randomBoolean()) {
|
||||
new MissingQueryBuilder("", true, true);
|
||||
} else {
|
||||
new MissingQueryBuilder(null, true, true);
|
||||
}
|
||||
fail("must not be null or empty");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
new MissingQueryBuilder("fieldname", false, false);
|
||||
fail("existence and nullValue cannot both be false");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
new MissingQueryBuilder("fieldname", MissingQueryBuilder.DEFAULT_NULL_VALUE, false);
|
||||
fail("existence and nullValue cannot both be false");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
public void testBothNullValueAndExistenceFalse() throws IOException {
|
||||
QueryShardContext context = createShardContext();
|
||||
context.setAllowUnmappedFields(true);
|
||||
try {
|
||||
MissingQueryBuilder.newFilter(context, "field", false, false);
|
||||
fail("Expected QueryShardException");
|
||||
} catch (QueryShardException e) {
|
||||
assertThat(e.getMessage(), containsString("missing must have either existence, or null_value"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testFromJson() throws IOException {
|
||||
String json =
|
||||
"{\n" +
|
||||
" \"missing\" : {\n" +
|
||||
" \"field\" : \"user\",\n" +
|
||||
" \"null_value\" : false,\n" +
|
||||
" \"existence\" : true,\n" +
|
||||
" \"boost\" : 1.0\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
|
||||
MissingQueryBuilder parsed = (MissingQueryBuilder) parseQuery(json);
|
||||
checkGeneratedJson(json, parsed);
|
||||
|
||||
assertEquals(json, false, parsed.nullValue());
|
||||
assertEquals(json, true, parsed.existence());
|
||||
assertEquals(json, "user", parsed.fieldPattern());
|
||||
}
|
||||
}
|
@ -58,7 +58,6 @@ import static org.elasticsearch.index.query.QueryBuilders.idsQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.indicesQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.missingQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.moreLikeThisQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.nestedQuery;
|
||||
@ -240,10 +239,6 @@ public class QueryDSLDocumentationTests extends ESTestCase {
|
||||
matchQuery("name", "kimchy elasticsearch");
|
||||
}
|
||||
|
||||
public void testMissing() {
|
||||
missingQuery("user", true, true);
|
||||
}
|
||||
|
||||
public void testMLT() {
|
||||
String[] fields = {"name.first", "name.last"};
|
||||
String[] texts = {"text like this one"};
|
||||
|
@ -560,7 +560,7 @@ public class SimpleIndexTemplateIT extends ESIntegTestCase {
|
||||
.setOrder(0)
|
||||
.addAlias(new Alias("alias1"))
|
||||
.addAlias(new Alias("{index}-alias"))
|
||||
.addAlias(new Alias("alias3").filter(QueryBuilders.missingQuery("test")))
|
||||
.addAlias(new Alias("alias3").filter(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery("test"))))
|
||||
.addAlias(new Alias("alias4")).get();
|
||||
|
||||
client().admin().indices().preparePutTemplate("template2")
|
||||
|
@ -58,7 +58,6 @@ import static org.elasticsearch.index.query.QueryBuilders.fuzzyQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.matchPhrasePrefixQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.matchPhraseQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.missingQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.prefixQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery;
|
||||
@ -67,6 +66,7 @@ import static org.elasticsearch.index.query.QueryBuilders.regexpQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.typeQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.wildcardQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.existsQuery;
|
||||
import static org.elasticsearch.search.builder.SearchSourceBuilder.highlight;
|
||||
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||
@ -2471,7 +2471,7 @@ public class HighlighterSearchIT extends ESIntegTestCase {
|
||||
|
||||
logger.info("--> highlighting and searching on field1");
|
||||
SearchSourceBuilder source = searchSource().query(boolQuery()
|
||||
.should(constantScoreQuery(QueryBuilders.missingQuery("field1")))
|
||||
.should(boolQuery().mustNot(QueryBuilders.existsQuery("field1")))
|
||||
.should(matchQuery("field1", "test"))
|
||||
.should(constantScoreQuery(queryStringQuery("field1:photo*"))))
|
||||
.highlighter(highlight().field("field1"));
|
||||
@ -2501,7 +2501,9 @@ public class HighlighterSearchIT extends ESIntegTestCase {
|
||||
refresh();
|
||||
|
||||
logger.info("--> highlighting and searching on field1");
|
||||
SearchSourceBuilder source = searchSource().query(boolQuery().must(queryStringQuery("field1:photo*")).filter(missingQuery("field_null")))
|
||||
SearchSourceBuilder source = searchSource().query(boolQuery()
|
||||
.must(queryStringQuery("field1:photo*"))
|
||||
.mustNot(existsQuery("field_null")))
|
||||
.highlighter(highlight().field("field1"));
|
||||
SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get();
|
||||
assertHighlight(searchResponse, 0, "field1", 0, 1, equalTo("The <em>photography</em> word will get highlighted"));
|
||||
|
@ -43,7 +43,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitC
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse;
|
||||
|
||||
public class ExistsMissingIT extends ESIntegTestCase {
|
||||
public class ExistsIT extends ESIntegTestCase {
|
||||
|
||||
// TODO: move this to a unit test somewhere...
|
||||
public void testEmptyIndex() throws Exception {
|
||||
@ -51,11 +51,11 @@ public class ExistsMissingIT extends ESIntegTestCase {
|
||||
ensureYellow("test");
|
||||
SearchResponse resp = client().prepareSearch("test").setQuery(QueryBuilders.existsQuery("foo")).execute().actionGet();
|
||||
assertSearchResponse(resp);
|
||||
resp = client().prepareSearch("test").setQuery(QueryBuilders.missingQuery("foo")).execute().actionGet();
|
||||
resp = client().prepareSearch("test").setQuery(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery("foo"))).execute().actionGet();
|
||||
assertSearchResponse(resp);
|
||||
}
|
||||
|
||||
public void testExistsMissing() throws Exception {
|
||||
public void testExists() throws Exception {
|
||||
XContentBuilder mapping = XContentBuilder.builder(JsonXContent.jsonXContent)
|
||||
.startObject()
|
||||
.startObject("type")
|
||||
@ -145,62 +145,6 @@ public class ExistsMissingIT extends ESIntegTestCase {
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
// missing
|
||||
resp = client().prepareSearch("idx").setQuery(QueryBuilders.missingQuery(fieldName)).execute().actionGet();
|
||||
assertSearchResponse(resp);
|
||||
assertEquals(String.format(Locale.ROOT, "missing(%s, %d) mapping: %s response: %s", fieldName, count, mapping.string(), resp), numDocs - count, resp.getHits().totalHits());
|
||||
}
|
||||
}
|
||||
|
||||
public void testNullValueUnset() throws Exception {
|
||||
assertAcked(client().admin().indices().prepareCreate("idx").addMapping("type", "f", "type=string,index=not_analyzed"));
|
||||
indexRandom(true,
|
||||
client().prepareIndex("idx", "type", "1").setSource("f", "foo"),
|
||||
client().prepareIndex("idx", "type", "2").setSource("f", null),
|
||||
client().prepareIndex("idx", "type", "3").setSource("g", "bar"),
|
||||
client().prepareIndex("idx", "type", "4").setSource("f", "bar"));
|
||||
|
||||
SearchResponse resp = client().prepareSearch("idx").setQuery(QueryBuilders.missingQuery("f", true, true)).get();
|
||||
assertSearchHits(resp, "2", "3");
|
||||
|
||||
resp = client().prepareSearch("idx").setQuery(QueryBuilders.missingQuery("f", false, true)).get();
|
||||
assertSearchHits(resp, "2", "3");
|
||||
|
||||
resp = client().prepareSearch("idx").setQuery(QueryBuilders.missingQuery("f", true, false)).get();
|
||||
assertSearchHits(resp);
|
||||
|
||||
try {
|
||||
client().prepareSearch("idx").setQuery(QueryBuilders.missingQuery("f", false, false)).get();
|
||||
fail("both existence and null_value can't be false");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
public void testNullValueSet() throws Exception {
|
||||
assertAcked(client().admin().indices().prepareCreate("idx").addMapping("type", "f", "type=string,index=not_analyzed,null_value=bar"));
|
||||
indexRandom(true,
|
||||
client().prepareIndex("idx", "type", "1").setSource("f", "foo"),
|
||||
client().prepareIndex("idx", "type", "2").setSource("f", null),
|
||||
client().prepareIndex("idx", "type", "3").setSource("g", "bar"),
|
||||
client().prepareIndex("idx", "type", "4").setSource("f", "bar"));
|
||||
|
||||
SearchResponse resp = client().prepareSearch("idx").setQuery(QueryBuilders.missingQuery("f", true, true)).get();
|
||||
assertSearchHits(resp, "2", "3", "4");
|
||||
|
||||
resp = client().prepareSearch("idx").setQuery(QueryBuilders.missingQuery("f", false, true)).get();
|
||||
assertSearchHits(resp, "3");
|
||||
|
||||
resp = client().prepareSearch("idx").setQuery(QueryBuilders.missingQuery("f", true, false)).get();
|
||||
assertSearchHits(resp, "2", "4");
|
||||
|
||||
try {
|
||||
client().prepareSearch("idx").setQuery(QueryBuilders.missingQuery("f", false, false)).get();
|
||||
fail("both existence and null_value can't be false");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -70,7 +70,6 @@ import static org.elasticsearch.index.query.QueryBuilders.idsQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.indicesQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.missingQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.prefixQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery;
|
||||
@ -805,32 +804,6 @@ public class SearchQueryIT extends ESIntegTestCase {
|
||||
searchResponse = client().prepareSearch().setQuery(existsQuery("obj1")).get();
|
||||
assertHitCount(searchResponse, 2l);
|
||||
assertSearchHits(searchResponse, "1", "2");
|
||||
|
||||
searchResponse = client().prepareSearch().setQuery(missingQuery("field1")).get();
|
||||
assertHitCount(searchResponse, 2l);
|
||||
assertSearchHits(searchResponse, "3", "4");
|
||||
|
||||
searchResponse = client().prepareSearch().setQuery(missingQuery("field1")).get();
|
||||
assertHitCount(searchResponse, 2l);
|
||||
assertSearchHits(searchResponse, "3", "4");
|
||||
|
||||
searchResponse = client().prepareSearch().setQuery(constantScoreQuery(missingQuery("field1"))).get();
|
||||
assertHitCount(searchResponse, 2l);
|
||||
assertSearchHits(searchResponse, "3", "4");
|
||||
|
||||
searchResponse = client().prepareSearch().setQuery(queryStringQuery("_missing_:field1")).get();
|
||||
assertHitCount(searchResponse, 2l);
|
||||
assertSearchHits(searchResponse, "3", "4");
|
||||
|
||||
// wildcard check
|
||||
searchResponse = client().prepareSearch().setQuery(missingQuery("x*")).get();
|
||||
assertHitCount(searchResponse, 2l);
|
||||
assertSearchHits(searchResponse, "3", "4");
|
||||
|
||||
// object check
|
||||
searchResponse = client().prepareSearch().setQuery(missingQuery("obj1")).get();
|
||||
assertHitCount(searchResponse, 2l);
|
||||
assertSearchHits(searchResponse, "3", "4");
|
||||
}
|
||||
|
||||
public void testPassQueryOrFilterAsJSONString() throws Exception {
|
||||
|
@ -1,14 +0,0 @@
|
||||
[[java-query-dsl-missing-query]]
|
||||
==== Missing Query
|
||||
|
||||
See {ref}/query-dsl-missing-query.html[Missing Query]
|
||||
|
||||
[source,java]
|
||||
--------------------------------------------------
|
||||
QueryBuilder qb = missingQuery("user", <1>
|
||||
true, <2>
|
||||
true); <3>
|
||||
--------------------------------------------------
|
||||
<1> field
|
||||
<2> find missing field with an explicit `null` value
|
||||
<3> find missing field that doesn’t exist
|
@ -427,9 +427,9 @@ Use the `field(String, float)` method instead.
|
||||
|
||||
==== MissingQueryBuilder
|
||||
|
||||
The two individual setters for existence() and nullValue() were removed in favour of
|
||||
optional constructor settings in order to better capture and validate their interdependent
|
||||
settings at construction time.
|
||||
The MissingQueryBuilder which was deprecated in 2.2.0 is removed. As a replacement use ExistsQueryBuilder
|
||||
inside a mustNot() clause. So instead of using `new ExistsQueryBuilder(name)` now use
|
||||
`new BoolQueryBuilder().mustNot(new ExistsQueryBuilder(name))`.
|
||||
|
||||
==== NotQueryBuilder
|
||||
|
||||
@ -507,3 +507,4 @@ from `OsStats.Cpu#getPercent`.
|
||||
=== Fields option
|
||||
Only stored fields are retrievable with this option.
|
||||
The fields option won't be able to load non stored fields from _source anymore.
|
||||
|
||||
|
@ -38,7 +38,7 @@ These documents would *not* match the above query:
|
||||
<3> The `user` field is missing completely.
|
||||
|
||||
[float]
|
||||
===== `null_value` mapping
|
||||
==== `null_value` mapping
|
||||
|
||||
If the field mapping includes the <<null-value,`null_value`>> setting
|
||||
then explicit `null` values are replaced with the specified `null_value`. For
|
||||
@ -70,3 +70,21 @@ no values in the `user` field and thus would not match the `exists` filter:
|
||||
{ "foo": "bar" }
|
||||
--------------------------------------------------
|
||||
|
||||
==== `missing` query
|
||||
|
||||
'missing' query has been removed because it can be advantageously replaced by an `exists` query inside a must_not
|
||||
clause as follows:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
"bool": {
|
||||
"must_not": {
|
||||
"exists": {
|
||||
"field": "user"
|
||||
}
|
||||
}
|
||||
}
|
||||
--------------------------------------------------
|
||||
|
||||
This query returns documents that have no value in the user field.
|
||||
|
||||
|
@ -1,132 +0,0 @@
|
||||
[[query-dsl-missing-query]]
|
||||
=== Missing Query
|
||||
|
||||
Returns documents that have only `null` values or no value in the original field:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
{
|
||||
"constant_score" : {
|
||||
"filter" : {
|
||||
"missing" : { "field" : "user" }
|
||||
}
|
||||
}
|
||||
}
|
||||
--------------------------------------------------
|
||||
|
||||
For instance, the following docs would match the above filter:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
{ "user": null }
|
||||
{ "user": [] } <1>
|
||||
{ "user": [null] } <2>
|
||||
{ "foo": "bar" } <3>
|
||||
--------------------------------------------------
|
||||
<1> This field has no values.
|
||||
<2> This field has no non-`null` values.
|
||||
<3> The `user` field is missing completely.
|
||||
|
||||
These documents would *not* match the above filter:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
{ "user": "jane" }
|
||||
{ "user": "" } <1>
|
||||
{ "user": "-" } <2>
|
||||
{ "user": ["jane"] }
|
||||
{ "user": ["jane", null ] } <3>
|
||||
--------------------------------------------------
|
||||
<1> An empty string is a non-`null` value.
|
||||
<2> Even though the `standard` analyzer would emit zero tokens, the original field is non-`null`.
|
||||
<3> This field has one non-`null` value.
|
||||
|
||||
[float]
|
||||
==== `null_value` mapping
|
||||
|
||||
If the field mapping includes a <<null-value,`null_value`>> then explicit `null` values
|
||||
are replaced with the specified `null_value`. For instance, if the `user` field were mapped
|
||||
as follows:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
"user": {
|
||||
"type": "string",
|
||||
"null_value": "_null_"
|
||||
}
|
||||
--------------------------------------------------
|
||||
|
||||
then explicit `null` values would be indexed as the string `_null_`, and the
|
||||
the following docs would *not* match the `missing` filter:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
{ "user": null }
|
||||
{ "user": [null] }
|
||||
--------------------------------------------------
|
||||
|
||||
However, these docs--without explicit `null` values--would still have
|
||||
no values in the `user` field and thus would match the `missing` filter:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
{ "user": [] }
|
||||
{ "foo": "bar" }
|
||||
--------------------------------------------------
|
||||
|
||||
[float]
|
||||
===== `existence` and `null_value` parameters
|
||||
|
||||
When the field being queried has a `null_value` mapping, then the behaviour of
|
||||
the `missing` filter can be altered with the `existence` and `null_value`
|
||||
parameters:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
{
|
||||
"constant_score" : {
|
||||
"filter" : {
|
||||
"missing" : {
|
||||
"field" : "user",
|
||||
"existence" : true,
|
||||
"null_value" : false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
--------------------------------------------------
|
||||
|
||||
|
||||
`existence`::
|
||||
+
|
||||
--
|
||||
When the `existence` parameter is set to `true` (the default), the missing
|
||||
filter will include documents where the field has *no* values, ie:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
{ "user": [] }
|
||||
{ "foo": "bar" }
|
||||
--------------------------------------------------
|
||||
|
||||
When set to `false`, these documents will not be included.
|
||||
--
|
||||
|
||||
`null_value`::
|
||||
+
|
||||
--
|
||||
When the `null_value` parameter is set to `true`, the missing
|
||||
filter will include documents where the field contains a `null` value, ie:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
{ "user": null }
|
||||
{ "user": [null] }
|
||||
{ "user": ["jane",null] } <1>
|
||||
--------------------------------------------------
|
||||
<1> Matches because the field contains a `null` value, even though it also contains a non-`null` value.
|
||||
|
||||
When set to `false` (the default), these documents will not be included.
|
||||
--
|
||||
|
||||
NOTE: Either `existence` or `null_value` or both must be set to `true`.
|
Loading…
x
Reference in New Issue
Block a user