Skip to content

Commit

Permalink
Handle MatchNoDocsQuery in span query wrappers (#34106)
Browse files Browse the repository at this point in the history
* Handle MatchNoDocsQuery in span query wrappers

This change adds a new SpanMatchNoDocsQuery query that replaces
MatchNoDocsQuery in the span query wrappers.
The `wildcard` query now returns MatchNoDocsQuery if the target field is not
in the mapping (#34093) so we need the equivalent span query in order to
be able to pass it to other span wrappers.

Closes #34105
  • Loading branch information
jimczi authored and kcm committed Oct 30, 2018
1 parent 8a3828f commit 68a98ac
Show file tree
Hide file tree
Showing 12 changed files with 233 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* 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.queries;

import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.TermStates;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.ScoreMode;
import org.apache.lucene.search.spans.SpanQuery;
import org.apache.lucene.search.spans.SpanWeight;
import org.apache.lucene.search.spans.Spans;

import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.Set;

/**
* A {@link SpanQuery} that matches no documents.
*/
public class SpanMatchNoDocsQuery extends SpanQuery {
private final String field;
private final String reason;

public SpanMatchNoDocsQuery(String field, String reason) {
this.field = field;
this.reason = reason;
}

@Override
public String getField() {
return field;
}

@Override
public String toString(String field) {
return "SpanMatchNoDocsQuery(\"" + reason + "\")";
}

@Override
public boolean equals(Object o) {
return sameClassAs(o);
}

@Override
public int hashCode() {
return classHash();
}

@Override
public SpanWeight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
return new SpanWeight(this, searcher, Collections.emptyMap(), boost) {
@Override
public void extractTermStates(Map<Term, TermStates> contexts) {}

@Override
public Spans getSpans(LeafReaderContext ctx, Postings requiredPostings) {
return null;
}

@Override
public void extractTerms(Set<Term> terms) {}

@Override
public boolean isCacheable(LeafReaderContext ctx) {
return true;
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ protected void doWriteTo(StreamOutput out) throws IOException {
out.writeOptionalString(this.rewrite);
}

@Override
public String fieldName() {
return this.fieldName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,8 @@
package org.elasticsearch.index.query;

public interface MultiTermQueryBuilder extends QueryBuilder {

/**
* Get the field name for this query.
*/
String fieldName();
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ protected void doWriteTo(StreamOutput out) throws IOException {
out.writeOptionalString(rewrite);
}

@Override
public String fieldName() {
return this.fieldName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ protected void doWriteTo(StreamOutput out) throws IOException {
/**
* Get the field name for this query.
*/
@Override
public String fieldName() {
return this.fieldName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ protected void doWriteTo(StreamOutput out) throws IOException {
}

/** Returns the field name used in this query. */
@Override
public String fieldName() {
return this.fieldName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@
import org.apache.lucene.index.Term;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.TermStates;
import org.apache.lucene.queries.SpanMatchNoDocsQuery;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.BoostQuery;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.MultiTermQuery;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
Expand Down Expand Up @@ -190,9 +192,14 @@ protected Query doToQuery(QueryShardContext context) throws IOException {
break;
}
}
final SpanQuery spanQuery;
// no MultiTermQuery extends SpanQuery, so SpanBoostQuery is not supported here
assert subQuery instanceof SpanBoostQuery == false;

if (subQuery instanceof MatchNoDocsQuery) {
return new SpanMatchNoDocsQuery(multiTermQueryBuilder.fieldName(), subQuery.toString());
}

final SpanQuery spanQuery;
if (subQuery instanceof TermQuery) {
/**
* Text fields that index prefixes can rewrite prefix queries
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ protected void doWriteTo(StreamOutput out) throws IOException {
out.writeOptionalString(rewrite);
}

@Override
public String fieldName() {
return fieldName;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* 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.queries;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.MockAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryUtils;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.spans.SpanNearQuery;
import org.apache.lucene.search.spans.SpanOrQuery;
import org.apache.lucene.search.spans.SpanQuery;
import org.apache.lucene.search.spans.SpanTermQuery;
import org.apache.lucene.store.Directory;
import org.elasticsearch.test.ESTestCase;

import java.io.IOException;

public class SpanMatchNoDocsQueryTests extends ESTestCase {
public void testSimple() throws Exception {
SpanMatchNoDocsQuery query = new SpanMatchNoDocsQuery("field", "a good reason");
assertEquals(query.toString(), "SpanMatchNoDocsQuery(\"a good reason\")");
Query rewrite = query.rewrite(null);
assertTrue(rewrite instanceof SpanMatchNoDocsQuery);
assertEquals(rewrite.toString(), "SpanMatchNoDocsQuery(\"a good reason\")");
}

public void testQuery() throws Exception {
Directory dir = newDirectory();
Analyzer analyzer = new MockAnalyzer(random());
IndexWriter iw = new IndexWriter(dir,
newIndexWriterConfig(analyzer).setMaxBufferedDocs(2).setMergePolicy(newLogMergePolicy()));
addDoc("one", iw);
addDoc("two", iw);
addDoc("three", iw);
IndexReader ir = DirectoryReader.open(iw);
IndexSearcher searcher = new IndexSearcher(ir);

Query query = new SpanMatchNoDocsQuery("unkwown", "field not found");
assertEquals(searcher.count(query), 0);

ScoreDoc[] hits;
hits = searcher.search(query, 1000).scoreDocs;
assertEquals(0, hits.length);
assertEquals(query.toString(), "SpanMatchNoDocsQuery(\"field not found\")");

SpanOrQuery orQuery = new SpanOrQuery(
new SpanMatchNoDocsQuery("unknown", "field not found"),
new SpanTermQuery(new Term("unknown", "one"))
);
assertEquals(searcher.count(orQuery), 0);
hits = searcher.search(orQuery, 1000).scoreDocs;
assertEquals(0, hits.length);

orQuery = new SpanOrQuery(
new SpanMatchNoDocsQuery("key", "a good reason"),
new SpanTermQuery(new Term("key", "one"))
);
assertEquals(searcher.count(orQuery), 1);
hits = searcher.search(orQuery, 1000).scoreDocs;
assertEquals(1, hits.length);
Query rewrite = orQuery.rewrite(ir);
assertEquals(rewrite, orQuery);

SpanNearQuery nearQuery = new SpanNearQuery(
new SpanQuery[] {new SpanMatchNoDocsQuery("same", ""), new SpanMatchNoDocsQuery("same", "")},
0, true);
assertEquals(searcher.count(nearQuery), 0);
hits = searcher.search(nearQuery, 1000).scoreDocs;
assertEquals(0, hits.length);
rewrite = nearQuery.rewrite(ir);
assertEquals(rewrite, nearQuery);

iw.close();
ir.close();
dir.close();
}

public void testEquals() {
Query q1 = new SpanMatchNoDocsQuery("key1", "one");
Query q2 = new SpanMatchNoDocsQuery("key2", "two");
assertTrue(q1.equals(q2));
QueryUtils.check(q1);
}

private void addDoc(String text, IndexWriter iw) throws IOException {
Document doc = new Document();
Field f = newTextField("key", text, Field.Store.YES);
doc.add(f);
iw.addDocument(doc);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.RandomIndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.queries.SpanMatchNoDocsQuery;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.BoostQuery;
import org.apache.lucene.search.MultiTermQuery;
Expand Down Expand Up @@ -81,6 +82,9 @@ protected SpanMultiTermQueryBuilder doCreateTestQueryBuilder() {

@Override
protected void doAssertLuceneQuery(SpanMultiTermQueryBuilder queryBuilder, Query query, SearchContext context) throws IOException {
if (query instanceof SpanMatchNoDocsQuery) {
return;
}
if (queryBuilder.innerQuery().boost() != AbstractQueryBuilder.DEFAULT_BOOST) {
assertThat(query, instanceOf(SpanBoostQuery.class));
SpanBoostQuery boostQuery = (SpanBoostQuery) query;
Expand All @@ -97,7 +101,7 @@ protected void doAssertLuceneQuery(SpanMultiTermQueryBuilder queryBuilder, Query
}
assertThat(multiTermQuery, either(instanceOf(MultiTermQuery.class)).or(instanceOf(TermQuery.class)));
assertThat(spanMultiTermQueryWrapper.getWrappedQuery(),
equalTo(new SpanMultiTermQueryWrapper<>((MultiTermQuery)multiTermQuery).getWrappedQuery()));
equalTo(new SpanMultiTermQueryWrapper<>((MultiTermQuery) multiTermQuery).getWrappedQuery()));
}

public void testIllegalArgument() {
Expand Down Expand Up @@ -154,6 +158,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
public void writeTo(StreamOutput out) throws IOException {

}

@Override
public String fieldName() {
return "foo";
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

package org.elasticsearch.index.query;

import org.apache.lucene.queries.SpanMatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.spans.SpanBoostQuery;
import org.apache.lucene.search.spans.SpanNearQuery;
Expand Down Expand Up @@ -53,6 +54,7 @@ protected void doAssertLuceneQuery(SpanNearQueryBuilder queryBuilder, Query quer
assertThat(query, either(instanceOf(SpanNearQuery.class))
.or(instanceOf(SpanTermQuery.class))
.or(instanceOf(SpanBoostQuery.class))
.or(instanceOf(SpanMatchNoDocsQuery.class))
.or(instanceOf(MatchAllQueryBuilder.class)));
if (query instanceof SpanNearQuery) {
SpanNearQuery spanNearQuery = (SpanNearQuery) query;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,6 @@ protected boolean builderGeneratesCacheableQueries() {
* Test creates the {@link Query} from the {@link QueryBuilder} under test and delegates the
* assertions being made on the result to the implementing subclass.
*/
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/34105")
public void testToQuery() throws IOException {
for (int runs = 0; runs < NUMBER_OF_TESTQUERIES; runs++) {
QueryShardContext context = createShardContext();
Expand Down

0 comments on commit 68a98ac

Please sign in to comment.