Search enhancement: adds a special case for the “_index” field which allows queries to test the name of the index. Adding this capability means we can deprecate the specialist indices query.

IndexFieldMapper is changed to make the term query factories produce match_all or match_none queries based on tests on the index name.

Closes #3316
This commit is contained in:
markharwood 2015-07-03 17:29:45 +01:00
parent d085088793
commit f95981b977
2 changed files with 113 additions and 1 deletions

View File

@ -22,10 +22,13 @@ package org.elasticsearch.index.mapper.internal;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexOptions;
import org.apache.lucene.search.Query;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.Version;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.fielddata.FieldDataType;
@ -34,9 +37,10 @@ import org.elasticsearch.index.mapper.Mapper;
import org.elasticsearch.index.mapper.MapperParsingException;
import org.elasticsearch.index.mapper.MergeMappingException;
import org.elasticsearch.index.mapper.MergeResult;
import org.elasticsearch.index.mapper.ParseContext;
import org.elasticsearch.index.mapper.MetadataFieldMapper;
import org.elasticsearch.index.mapper.ParseContext;
import org.elasticsearch.index.mapper.core.AbstractFieldMapper;
import org.elasticsearch.index.query.QueryParseContext;
import java.io.IOException;
import java.util.Iterator;
@ -135,6 +139,62 @@ public class IndexFieldMapper extends MetadataFieldMapper {
return CONTENT_TYPE;
}
@Override
public boolean useTermQueryWithQueryString() {
// As we spoof the presence of an indexed field we have to override
// the default of returning false which otherwise leads MatchQuery
// et al to run an analyzer over the query string and then try to
// hit the search index. We need them to use our termQuery(..)
// method which checks index names
return true;
}
/**
* This termQuery impl looks at the context to determine the index that
* is being queried and then returns a MATCH_ALL_QUERY or MATCH_NO_QUERY
* if the value matches this index. This can be useful if aliases or
* wildcards are used but the aim is to restrict the query to specific
* indices
*/
@Override
public Query termQuery(Object value, @Nullable QueryParseContext context) {
if (context == null) {
return super.termQuery(value, context);
}
if (isSameIndex(value, context.index().getName())) {
return Queries.newMatchAllQuery();
} else {
return Queries.newMatchNoDocsQuery();
}
}
@Override
public Query termsQuery(List values, QueryParseContext context) {
if (context == null) {
return super.termsQuery(values, context);
}
for (Object value : values) {
if (isSameIndex(value, context.index().getName())) {
// No need to OR these clauses - we can only logically be
// running in the context of just one of these index names.
return Queries.newMatchAllQuery();
}
}
// None of the listed index names are this one
return Queries.newMatchNoDocsQuery();
}
private boolean isSameIndex(Object value, String indexName) {
if (value instanceof BytesRef) {
BytesRef indexNameRef = new BytesRef(indexName);
return (indexNameRef.bytesEquals((BytesRef) value));
} else {
return indexName.equals(value.toString());
}
}
@Override
public String value(Object value) {
if (value == null) {

View File

@ -699,6 +699,58 @@ public class SearchQueryTests extends ElasticsearchIntegrationTest {
assertSearchHits(searchResponse, "1", "3");
}
@Test
public void term_indexQueryTestsIndexed() throws Exception {
term_indexQueryTests("not_analyzed");
}
@Test
public void term_indexQueryTestsNotIndexed() throws Exception {
term_indexQueryTests("no");
}
private void term_indexQueryTests(String index) throws Exception {
Settings indexSettings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.V_1_4_2.id).build();
String[] indexNames = { "test1", "test2" };
for (String indexName : indexNames) {
assertAcked(client()
.admin()
.indices()
.prepareCreate(indexName)
.setSettings(indexSettings)
.addMapping(
"type1",
jsonBuilder().startObject().startObject("type1").startObject("_index").field("index", index).endObject()
.endObject().endObject()));
indexRandom(true, client().prepareIndex(indexName, "type1", indexName + "1").setSource("field1", "value1"));
}
for (String indexName : indexNames) {
SearchResponse request = client().prepareSearch().setQuery(constantScoreQuery(termQuery("_index", indexName))).get();
SearchResponse searchResponse = assertSearchResponse(request);
assertHitCount(searchResponse, 1l);
assertSearchHits(searchResponse, indexName + "1");
}
for (String indexName : indexNames) {
SearchResponse request = client().prepareSearch().setQuery(constantScoreQuery(termsQuery("_index", indexName))).get();
SearchResponse searchResponse = assertSearchResponse(request);
assertHitCount(searchResponse, 1l);
assertSearchHits(searchResponse, indexName + "1");
}
for (String indexName : indexNames) {
SearchResponse request = client().prepareSearch().setQuery(constantScoreQuery(matchQuery("_index", indexName))).get();
SearchResponse searchResponse = assertSearchResponse(request);
assertHitCount(searchResponse, 1l);
assertSearchHits(searchResponse, indexName + "1");
}
{
SearchResponse request = client().prepareSearch().setQuery(constantScoreQuery(termsQuery("_index", indexNames))).get();
SearchResponse searchResponse = assertSearchResponse(request);
assertHitCount(searchResponse, indexNames.length);
}
}
@Test
public void testLimitFilter() throws Exception {
assertAcked(client().admin().indices().prepareCreate("test").setSettings(SETTING_NUMBER_OF_SHARDS, 1));