diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/JsonFilterBuilders.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/JsonFilterBuilders.java index 65f2bad0213cb..0b06d05341a8a 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/JsonFilterBuilders.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/JsonFilterBuilders.java @@ -44,6 +44,26 @@ public static TermJsonFilterBuilder termFilter(String name, double value) { return new TermJsonFilterBuilder(name, value); } + public static TermsJsonFilterBuilder termsFilter(String name, String... values) { + return new TermsJsonFilterBuilder(name, values); + } + + public static TermsJsonFilterBuilder termsFilter(String name, int... values) { + return new TermsJsonFilterBuilder(name, values); + } + + public static TermsJsonFilterBuilder termsFilter(String name, long... values) { + return new TermsJsonFilterBuilder(name, values); + } + + public static TermsJsonFilterBuilder termsFilter(String name, float... values) { + return new TermsJsonFilterBuilder(name, values); + } + + public static TermsJsonFilterBuilder termsFilter(String name, double... values) { + return new TermsJsonFilterBuilder(name, values); + } + public static PrefixJsonFilterBuilder prefixFilter(String name, String value) { return new PrefixJsonFilterBuilder(name, value); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/JsonQueryParserRegistry.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/JsonQueryParserRegistry.java index 4bb2608640b36..7f30c3a3de901 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/JsonQueryParserRegistry.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/JsonQueryParserRegistry.java @@ -74,6 +74,7 @@ public JsonQueryParserRegistry(Index index, Map filterParsersMap = newHashMap(); // add defaults add(filterParsersMap, new TermJsonFilterParser(index, indexSettings)); + add(filterParsersMap, new TermsJsonFilterParser(index, indexSettings)); add(filterParsersMap, new RangeJsonFilterParser(index, indexSettings)); add(filterParsersMap, new PrefixJsonFilterParser(index, indexSettings)); add(filterParsersMap, new QueryJsonFilterParser(index, indexSettings)); diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/TermsJsonFilterBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/TermsJsonFilterBuilder.java new file mode 100644 index 0000000000000..96e355fbfe8fb --- /dev/null +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/TermsJsonFilterBuilder.java @@ -0,0 +1,85 @@ +/* + * Licensed to Elastic Search and Shay Banon under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Elastic Search 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.json; + +import org.elasticsearch.util.json.JsonBuilder; + +import java.io.IOException; + +/** + * @author kimchy (Shay Banon) + */ +public class TermsJsonFilterBuilder extends BaseJsonFilterBuilder { + + private final String name; + + private final Object[] values; + + public TermsJsonFilterBuilder(String name, String... values) { + this(name, (Object[]) values); + } + + public TermsJsonFilterBuilder(String name, int... values) { + this.name = name; + this.values = new Integer[values.length]; + for (int i = 0; i < values.length; i++) { + this.values[i] = values[i]; + } + } + + public TermsJsonFilterBuilder(String name, long... values) { + this.name = name; + this.values = new Long[values.length]; + for (int i = 0; i < values.length; i++) { + this.values[i] = values[i]; + } + } + + public TermsJsonFilterBuilder(String name, float... values) { + this.name = name; + this.values = new Float[values.length]; + for (int i = 0; i < values.length; i++) { + this.values[i] = values[i]; + } + } + + public TermsJsonFilterBuilder(String name, double... values) { + this.name = name; + this.values = new Double[values.length]; + for (int i = 0; i < values.length; i++) { + this.values[i] = values[i]; + } + } + + private TermsJsonFilterBuilder(String name, Object... values) { + this.name = name; + this.values = values; + } + + @Override public void doJson(JsonBuilder builder) throws IOException { + builder.startObject(TermsJsonFilterParser.NAME); + builder.startArray(name); + for (Object value : values) { + builder.value(value); + } + builder.endArray(); + builder.endObject(); + } +} \ No newline at end of file diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/TermsJsonFilterParser.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/TermsJsonFilterParser.java new file mode 100644 index 0000000000000..0dc3d73c8978d --- /dev/null +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/TermsJsonFilterParser.java @@ -0,0 +1,96 @@ +/* + * Licensed to Elastic Search and Shay Banon under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Elastic Search 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.json; + +import com.google.inject.Inject; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.Filter; +import org.apache.lucene.search.TermsFilter; +import org.codehaus.jackson.JsonParser; +import org.codehaus.jackson.JsonToken; +import org.elasticsearch.index.AbstractIndexComponent; +import org.elasticsearch.index.Index; +import org.elasticsearch.index.mapper.FieldMapper; +import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.index.query.QueryParsingException; +import org.elasticsearch.index.settings.IndexSettings; +import org.elasticsearch.util.settings.Settings; + +import java.io.IOException; + +import static org.elasticsearch.index.query.support.QueryParsers.*; + +/** + * @author kimchy (Shay Banon) + */ +public class TermsJsonFilterParser extends AbstractIndexComponent implements JsonFilterParser { + + public static final String NAME = "terms"; + + @Inject public TermsJsonFilterParser(Index index, @IndexSettings Settings settings) { + super(index, settings); + } + + @Override public String name() { + return NAME; + } + + @Override public Filter parse(JsonQueryParseContext parseContext) throws IOException, QueryParsingException { + JsonParser jp = parseContext.jp(); + + JsonToken token = jp.getCurrentToken(); + if (token == JsonToken.START_OBJECT) { + token = jp.nextToken(); + } + assert token == JsonToken.FIELD_NAME; + String fieldName = jp.getCurrentName(); + + FieldMapper fieldMapper = null; + MapperService.SmartNameFieldMappers smartNameFieldMappers = parseContext.smartFieldMappers(fieldName); + if (smartNameFieldMappers != null) { + fieldMapper = smartNameFieldMappers.fieldMappers().mapper(); + if (fieldMapper != null) { + fieldName = fieldMapper.indexName(); + } + } + + token = jp.nextToken(); + if (token != JsonToken.START_ARRAY) { + throw new QueryParsingException(index, "Terms filter must define the terms to filter on as an array"); + } + + TermsFilter termsFilter = new TermsFilter(); + while ((token = jp.nextToken()) != JsonToken.END_ARRAY) { + String value = jp.getText(); + if (value == null) { + throw new QueryParsingException(index, "No value specified for term filter"); + } + if (fieldMapper != null) { + value = fieldMapper.indexedValue(value); + } + termsFilter.addTerm(new Term(fieldName, value)); + } + jp.nextToken(); + + + Filter filter = parseContext.cacheFilterIfPossible(termsFilter); + return wrapSmartNameFilter(filter, smartNameFieldMappers, parseContext.filterCache()); + } +} diff --git a/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/json/SimpleJsonIndexQueryParserTests.java b/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/json/SimpleJsonIndexQueryParserTests.java index 2b5675c1d5ead..9f61c8ec3420e 100644 --- a/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/json/SimpleJsonIndexQueryParserTests.java +++ b/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/json/SimpleJsonIndexQueryParserTests.java @@ -35,6 +35,7 @@ import java.io.IOException; import java.lang.reflect.Field; import java.util.List; +import java.util.Set; import static org.elasticsearch.index.query.json.JsonFilterBuilders.*; import static org.elasticsearch.index.query.json.JsonQueryBuilders.*; @@ -376,6 +377,36 @@ public class SimpleJsonIndexQueryParserTests { assertThat(((TermFilter) filteredQuery.getFilter()).getTerm(), equalTo(new Term("name.last", "banon"))); } + @Test public void testTermsFilterQueryBuilder() throws Exception { + IndexQueryParser queryParser = newQueryParser(); + Query parsedQuery = queryParser.parse(filteredQuery(termQuery("name.first", "shay"), termsFilter("name.last", "banon", "kimchy")).build()); + assertThat(parsedQuery, instanceOf(FilteredQuery.class)); + FilteredQuery filteredQuery = (FilteredQuery) parsedQuery; + assertThat(filteredQuery.getFilter(), instanceOf(TermsFilter.class)); + TermsFilter termsFilter = (TermsFilter) filteredQuery.getFilter(); + Field field = TermsFilter.class.getDeclaredField("terms"); + field.setAccessible(true); + Set terms = (Set) field.get(termsFilter); + assertThat(terms.size(), equalTo(2)); + assertThat(terms.iterator().next().text(), equalTo("banon")); + } + + + @Test public void testTermsFilterQuery() throws Exception { + IndexQueryParser queryParser = newQueryParser(); + String query = copyToStringFromClasspath("/org/elasticsearch/index/query/json/terms-filter.json"); + Query parsedQuery = queryParser.parse(query); + assertThat(parsedQuery, instanceOf(FilteredQuery.class)); + FilteredQuery filteredQuery = (FilteredQuery) parsedQuery; + assertThat(filteredQuery.getFilter(), instanceOf(TermsFilter.class)); + TermsFilter termsFilter = (TermsFilter) filteredQuery.getFilter(); + Field field = TermsFilter.class.getDeclaredField("terms"); + field.setAccessible(true); + Set terms = (Set) field.get(termsFilter); + assertThat(terms.size(), equalTo(2)); + assertThat(terms.iterator().next().text(), equalTo("banon")); + } + @Test public void testConstantScoreQueryBuilder() throws IOException { IndexQueryParser queryParser = newQueryParser(); Query parsedQuery = queryParser.parse(constantScoreQuery(termFilter("name.last", "banon"))); diff --git a/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/json/terms-filter.json b/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/json/terms-filter.json new file mode 100644 index 0000000000000..2ea9c5d219400 --- /dev/null +++ b/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/json/terms-filter.json @@ -0,0 +1,12 @@ +{ + filteredQuery : { + query : { + term : { "name.first" : "shay" } + }, + filter : { + terms : { + "name.last" : ["banon", "kimchy"] + } + } + } +} \ No newline at end of file