diff --git a/src/main/java/org/elasticsearch/index/mapper/internal/IndexFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/internal/IndexFieldMapper.java index 3c809580f5fe3..133a08bf1622a 100644 --- a/src/main/java/org/elasticsearch/index/mapper/internal/IndexFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/internal/IndexFieldMapper.java @@ -19,13 +19,23 @@ package org.elasticsearch.index.mapper.internal; +import com.google.common.base.Charsets; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.FieldType; import org.apache.lucene.index.FieldInfo.IndexOptions; +import org.apache.lucene.index.Term; +import org.apache.lucene.queries.TermFilter; +import org.apache.lucene.search.Filter; +import org.apache.lucene.search.PrefixFilter; +import org.apache.lucene.search.Query; +import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.lucene.BytesRefs; import org.elasticsearch.common.lucene.Lucene; +import org.elasticsearch.common.lucene.search.Queries; +import org.elasticsearch.common.lucene.search.XConstantScoreQuery; import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -34,8 +44,11 @@ import org.elasticsearch.index.fielddata.FieldDataType; import org.elasticsearch.index.mapper.*; import org.elasticsearch.index.mapper.core.AbstractFieldMapper; +import org.elasticsearch.index.query.QueryParseContext; import java.io.IOException; +import java.nio.charset.Charset; +import java.util.Arrays; import java.util.List; import java.util.Map; @@ -58,7 +71,7 @@ public static class Defaults extends AbstractFieldMapper.Defaults { public static final FieldType FIELD_TYPE = new FieldType(AbstractFieldMapper.Defaults.FIELD_TYPE); static { - FIELD_TYPE.setIndexed(true); + FIELD_TYPE.setIndexed(false); FIELD_TYPE.setTokenized(false); FIELD_TYPE.setStored(false); FIELD_TYPE.setOmitNorms(true); @@ -156,6 +169,62 @@ public String value(Object value) { return value.toString(); } + @Override + public Filter termsFilter(List values, @Nullable QueryParseContext context) { + if (fieldType.indexed() || context == null){ + super.termsFilter(values, context); + } + for (Object value : values) { + if (isSameIndex(value, context)) { + return Queries.MATCH_ALL_FILTER; + } + } + return Queries.MATCH_NO_FILTER; + } + + + /** + * This termQuery impl looks at the context to determine the index that is being queried and + * then creates returns a MATCH_ALL_FILTER or MATCH_NO_FILTER 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 (fieldType.indexed() || context == null) { + return super.termQuery(value, context); + } + if (isSameIndex(value, context)) { + return new XConstantScoreQuery(Queries.MATCH_ALL_FILTER); + } else { + return new XConstantScoreQuery(Queries.MATCH_NO_FILTER); + } + + } + + private boolean isSameIndex(Object value, QueryParseContext context) { + if (value instanceof BytesRef) { + BytesRef indexNameRef = new BytesRef(context.index().getName().getBytes(Charsets.UTF_8)); + return (indexNameRef.bytesEquals((BytesRef) value)); + } else if (value instanceof String) { + return context.index().getName().equals(value); + } else { + return context.index().getName().equals(value.toString()); + } + } + + @Override + public Filter termFilter(Object value, @Nullable QueryParseContext context) { + if (fieldType.indexed() || context == null) { + return new TermFilter(names().createIndexNameTerm(BytesRefs.toBytesRef(value))); + } + if (isSameIndex(value, context)) { + return Queries.MATCH_ALL_FILTER; + } else { + return Queries.MATCH_NO_FILTER; + } + } + + @Override public void preParse(ParseContext context) throws IOException { // we pre parse it and not in parse, since its not part of the root object diff --git a/src/test/java/org/elasticsearch/index/query/TermFilterTests.java b/src/test/java/org/elasticsearch/index/query/TermFilterTests.java new file mode 100644 index 0000000000000..f5440eba6817a --- /dev/null +++ b/src/test/java/org/elasticsearch/index/query/TermFilterTests.java @@ -0,0 +1,97 @@ +/* + * 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.junit.Test; + + +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.bytes.BytesReference; +import org.junit.Test; + +import java.util.concurrent.ExecutionException; + +import static org.hamcrest.Matchers.*; + +/** + */ +public class TermFilterTests extends TermQueryTests { + + @Test + public void testIndexTermFilter() throws InterruptedException, ExecutionException { + createIndex( "test1", "test2"); + ensureGreen( "test1", "test2"); + + long docsInTest1 = scaledRandomIntBetween(10,100); + long docsInTest2 = scaledRandomIntBetween(10,100); + + indexDocsToIndex("test1", docsInTest1); + indexDocsToIndex("test2", docsInTest2); + + { + SearchRequest searchRequest = new SearchRequest(); + searchRequest.indices("_all"); + String query = "{\"query\":\n" + + "{\n" + + " \"filtered\": {\n" + + " \"query\": {\n" + + " \"match_all\": {}\n" + + " },\n" + + " \"filter\": {\n" + + " \"term\": { \"_index\": { \"value\": \"test1\" }}\n" + + " }\n" + + " }\n" + + "} }"; + + BytesReference bytesRef = new BytesArray(query); + searchRequest.source(bytesRef, false); + SearchResponse response = client().search(searchRequest).get(); + + assertThat(response.getHits().totalHits(), is(docsInTest1)); + } + + { + SearchRequest searchRequest = new SearchRequest(); + searchRequest.indices("_all"); + + String query = "{\"query\":\n" + + "{\n" + + " \"filtered\": {\n" + + " \"query\": {\n" + + " \"match_all\": {}\n" + + " },\n" + + " \"filter\": {\n" + + " \"term\": { \"_index\": { \"value\": \"test2\" }}\n" + + " }\n" + + " }\n" + + "} }"; + + BytesReference bytesRef = new BytesArray(query); + searchRequest.source(bytesRef, false); + SearchResponse response = client().search(searchRequest).get(); + + assertThat(response.getHits().totalHits(), is(docsInTest2)); + } + + } + +} diff --git a/src/test/java/org/elasticsearch/index/query/TermQueryTests.java b/src/test/java/org/elasticsearch/index/query/TermQueryTests.java new file mode 100644 index 0000000000000..710bb50c44625 --- /dev/null +++ b/src/test/java/org/elasticsearch/index/query/TermQueryTests.java @@ -0,0 +1,91 @@ +/* + * 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.action.index.IndexRequestBuilder; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.test.ElasticsearchIntegrationTest; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; + +import static org.hamcrest.Matchers.*; + +public class TermQueryTests extends ElasticsearchIntegrationTest{ + + @Test + public void testIndexTermQuery() throws InterruptedException, ExecutionException { + createIndex( "test1" , "test2"); + ensureGreen( "test1", "test2"); + + long docsInTest1 = scaledRandomIntBetween(10,100); + long docsInTest2 = scaledRandomIntBetween(10,100); + + indexDocsToIndex("test1", docsInTest1); + indexDocsToIndex("test2", docsInTest2); + + { + SearchRequest searchRequest = new SearchRequest(); + searchRequest.indices("_all"); + + String query = "{ \"query\" : " + + "{ \"term\": " + + "{ \"_index\" : " + + "{\"value\" : \"test1\"} " + + "} } } "; + BytesReference bytesRef = new BytesArray(query); + searchRequest.source(bytesRef, false); + SearchResponse response = client().search(searchRequest).get(); + + assertThat(response.getHits().totalHits(), is(docsInTest1)); + } + + { + SearchRequest searchRequest = new SearchRequest(); + searchRequest.indices("_all"); + + String query = "{ \"query\" : " + + "{ \"term\": " + + "{ \"_index\" : " + + "{\"value\" : \"test2\"} " + + "} } } "; + BytesReference bytesRef = new BytesArray(query); + searchRequest.source(bytesRef, false); + SearchResponse response = client().search(searchRequest).get(); + + assertThat(response.getHits().totalHits(), is(docsInTest2)); + } + + } + + protected void indexDocsToIndex(String index, long count) throws InterruptedException, ExecutionException { + List builders = new ArrayList<>(); + + for (int i = 0; i < count; ++i) { + builders.add(client().prepareIndex(index, "type", "" + (i + 1) ).setSource("{\"theField\":\"foo\"}")); + } + + indexRandom(true,builders); + } +} diff --git a/src/test/java/org/elasticsearch/index/query/TermsFilterTest.java b/src/test/java/org/elasticsearch/index/query/TermsFilterTest.java new file mode 100644 index 0000000000000..f82e6fddcd86a --- /dev/null +++ b/src/test/java/org/elasticsearch/index/query/TermsFilterTest.java @@ -0,0 +1,143 @@ +/* + * 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.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.bytes.BytesReference; + +import java.util.concurrent.ExecutionException; + +import static org.hamcrest.Matchers.*; +/** + */ +public class TermsFilterTest extends TermQueryTests { + + public void testIndexTermsFilter() throws InterruptedException, ExecutionException { + + createIndex( "test1", "test2", "test3"); + ensureGreen( "test1", "test2", "test3"); + + long docsInTest1 = scaledRandomIntBetween(10,100); + long docsInTest2 = scaledRandomIntBetween(10,100); + long docsInTest3 = scaledRandomIntBetween(10,100); + + + indexDocsToIndex("test1", docsInTest1); + indexDocsToIndex("test2", docsInTest2); + indexDocsToIndex("test3", docsInTest3); + + + { + SearchRequest searchRequest = new SearchRequest(); + searchRequest.indices("_all"); + String query = "{\"query\":\n" + + "{\n" + + " \"filtered\": {\n" + + " \"query\": {\n" + + " \"match_all\": {}\n" + + " },\n" + + " \"filter\": {\n" + + " \"terms\": { \"_index\": [ \"test1\" ]}\n" + + " }\n" + + " }\n" + + "} }"; + + BytesReference bytesRef = new BytesArray(query); + searchRequest.source(bytesRef, false); + SearchResponse response = client().search(searchRequest).get(); + + assertThat(response.getHits().totalHits(), is(docsInTest1)); + } + + { + SearchRequest searchRequest = new SearchRequest(); + searchRequest.indices("_all"); + + String query = "{\"query\":\n" + + "{\n" + + " \"filtered\": {\n" + + " \"query\": {\n" + + " \"match_all\": {}\n" + + " },\n" + + " \"filter\": {\n" + + " \"terms\": { \"_index\": [ \"test2\" ]}\n" + + " }\n" + + " }\n" + + "} }"; + + BytesReference bytesRef = new BytesArray(query); + searchRequest.source(bytesRef, false); + SearchResponse response = client().search(searchRequest).get(); + + assertThat(response.getHits().totalHits(), is(docsInTest2)); + } + + { + SearchRequest searchRequest = new SearchRequest(); + searchRequest.indices("_all"); + + String query = "{\"query\":\n" + + "{\n" + + " \"filtered\": {\n" + + " \"query\": {\n" + + " \"match_all\": {}\n" + + " },\n" + + " \"filter\": {\n" + + " \"terms\": { \"_index\": [ \"test2\", \"doesNotExist\" ]}\n" + + " }\n" + + " }\n" + + "} }"; + + BytesReference bytesRef = new BytesArray(query); + searchRequest.source(bytesRef, false); + SearchResponse response = client().search(searchRequest).get(); + + assertThat(response.getHits().totalHits(), is(docsInTest2)); + } + + + { + SearchRequest searchRequest = new SearchRequest(); + searchRequest.indices("_all"); + + String query = "{\"query\":\n" + + "{\n" + + " \"filtered\": {\n" + + " \"query\": {\n" + + " \"match_all\": {}\n" + + " },\n" + + " \"filter\": {\n" + + " \"terms\": { \"_index\": [ \"test1\", \"test2\", \"test3\" ]}\n" + + " }\n" + + " }\n" + + "} }"; + + BytesReference bytesRef = new BytesArray(query); + searchRequest.source(bytesRef, false); + SearchResponse response = client().search(searchRequest).get(); + + assertThat(response.getHits().totalHits(), is(docsInTest1 + docsInTest2 + docsInTest3)); + } + + + } + +} diff --git a/src/test/java/org/elasticsearch/index/query/TermsQueryTest.java b/src/test/java/org/elasticsearch/index/query/TermsQueryTest.java new file mode 100644 index 0000000000000..800b274b2935f --- /dev/null +++ b/src/test/java/org/elasticsearch/index/query/TermsQueryTest.java @@ -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.elasticsearch.index.query; + +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.bytes.BytesReference; +import org.junit.Test; + +import java.util.concurrent.ExecutionException; + +import static org.hamcrest.Matchers.*; + +/** + */ +public class TermsQueryTest extends TermQueryTests { + + @Test + public void testIndexTermsQuery() throws InterruptedException, ExecutionException { + createIndex( "test1", "test2", "test3"); + ensureGreen( "test1", "test2", "test3"); + + long docsInTest1 = scaledRandomIntBetween(10,100); + long docsInTest2 = scaledRandomIntBetween(10,100); + long docsInTest3 = scaledRandomIntBetween(10,100); + + + indexDocsToIndex("test1", docsInTest1); + indexDocsToIndex("test2", docsInTest2); + indexDocsToIndex("test3", docsInTest3); + + { + SearchRequest searchRequest = new SearchRequest(); + searchRequest.indices("_all"); + + String query = "{ \"query\" : " + + "{ \"terms\": " + + "{ \"_index\" : [ \"test1\" ] " + + "} } } "; + BytesReference bytesRef = new BytesArray(query); + searchRequest.source(bytesRef, false); + SearchResponse response = client().search(searchRequest).get(); + + assertThat(response.getHits().totalHits(), is(docsInTest1)); + } + + { + SearchRequest searchRequest = new SearchRequest(); + searchRequest.indices("_all"); + + String query = "{ \"query\" : " + + "{ \"terms\": " + + "{ \"_index\" : [ \"test2\" ] " + + "} } } "; + + + BytesReference bytesRef = new BytesArray(query); + searchRequest.source(bytesRef, false); + SearchResponse response = client().search(searchRequest).get(); + + assertThat(response.getHits().totalHits(), is(docsInTest2)); + } + + { + SearchRequest searchRequest = new SearchRequest(); + searchRequest.indices("_all"); + + String query = "{ \"query\" : " + + "{ \"terms\": " + + "{ \"_index\" : [ \"test2\", \"doesNotExist\" ] " + + "} } } "; + + + BytesReference bytesRef = new BytesArray(query); + searchRequest.source(bytesRef, false); + SearchResponse response = client().search(searchRequest).get(); + + assertThat(response.getHits().totalHits(), is(docsInTest2)); + } + + + { + SearchRequest searchRequest = new SearchRequest(); + searchRequest.indices("_all"); + + String query = "{ \"query\" : " + + "{ \"terms\": " + + "{ \"_index\" : [ \"test2\", \"test1\", \"test3\" ] " + + "} } } "; + + BytesReference bytesRef = new BytesArray(query); + searchRequest.source(bytesRef, false); + SearchResponse response = client().search(searchRequest).get(); + + assertThat(response.getHits().totalHits(), is(docsInTest1 + docsInTest2 + docsInTest3)); + } + + } + +}