From 38d77f8cf30e358597e255b5d8d865f243175aca Mon Sep 17 00:00:00 2001 From: kimchy Date: Sat, 13 Nov 2010 02:26:14 +0200 Subject: [PATCH] Query DSL: Allow to provide pattern field names when using query_string query, closes #511. --- .../org/elasticsearch/common/regex/Regex.java | 7 ++++ .../index/mapper/DocumentFieldMappers.java | 21 ++++++++-- .../index/mapper/MapperService.java | 42 +++++++++++++++++++ .../xcontent/QueryStringQueryParser.java | 24 ++++++++--- .../xcontent/SimpleIndexQueryParserTests.java | 29 +++++++++---- .../query/xcontent/query-fields-match.json | 7 ++++ 6 files changed, 112 insertions(+), 18 deletions(-) create mode 100644 modules/elasticsearch/src/test/java/org/elasticsearch/index/query/xcontent/query-fields-match.json diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/common/regex/Regex.java b/modules/elasticsearch/src/main/java/org/elasticsearch/common/regex/Regex.java index 3cd9b6463b1..acc365745ac 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/common/regex/Regex.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/common/regex/Regex.java @@ -29,6 +29,13 @@ import java.util.regex.Pattern; */ public class Regex { + /** + * Is the str a simple match pattern. + */ + public static boolean isSimpleMatchPattern(String str) { + return str.indexOf('*') != -1; + } + /** * Match a String against the given pattern, supporting the following simple * pattern styles: "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/DocumentFieldMappers.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/DocumentFieldMappers.java index e0d73c6328a..dca26edeb03 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/DocumentFieldMappers.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/DocumentFieldMappers.java @@ -20,14 +20,13 @@ package org.elasticsearch.index.mapper; import org.apache.lucene.analysis.Analyzer; -import org.elasticsearch.common.collect.ImmutableList; -import org.elasticsearch.common.collect.ImmutableMap; -import org.elasticsearch.common.collect.Iterables; -import org.elasticsearch.common.collect.UnmodifiableIterator; +import org.elasticsearch.common.collect.*; +import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.util.concurrent.Immutable; import org.elasticsearch.index.analysis.FieldNameAnalyzer; import java.util.Map; +import java.util.Set; import static org.elasticsearch.common.collect.Lists.*; import static org.elasticsearch.common.collect.Maps.*; @@ -119,6 +118,20 @@ public class DocumentFieldMappers implements Iterable { return fullNameFieldMappers.get(fullName); } + public Set simpleMatchToIndexNames(String pattern) { + Set fields = Sets.newHashSet(); + for (FieldMapper fieldMapper : fieldMappers) { + if (Regex.simpleMatch(pattern, fieldMapper.names().fullName())) { + fields.add(fieldMapper.names().indexName()); + } else if (Regex.simpleMatch(pattern, fieldMapper.names().indexName())) { + fields.add(fieldMapper.names().name()); + } else if (Regex.simpleMatch(pattern, fieldMapper.names().name())) { + fields.add(fieldMapper.names().indexName()); + } + } + return fields; + } + /** * Tries to find first based on {@link #fullName(String)}, then by {@link #indexName(String)}, and last * by {@link #name(String)}. diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/MapperService.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/MapperService.java index 0584fa16472..f03ca73204c 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/MapperService.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/MapperService.java @@ -26,9 +26,11 @@ import org.apache.lucene.index.Term; import org.apache.lucene.search.Filter; import org.apache.lucene.search.TermsFilter; import org.elasticsearch.common.collect.ImmutableMap; +import org.elasticsearch.common.collect.Sets; import org.elasticsearch.common.collect.UnmodifiableIterator; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.Streams; +import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadSafe; import org.elasticsearch.env.Environment; @@ -46,6 +48,8 @@ import java.io.InputStreamReader; import java.io.Reader; import java.net.MalformedURLException; import java.net.URL; +import java.util.Map; +import java.util.Set; import static org.elasticsearch.common.collect.MapBuilder.*; @@ -280,6 +284,44 @@ public class MapperService extends AbstractIndexComponent implements Iterable simpleMatchToIndexNames(String pattern) { + int dotIndex = pattern.indexOf('.'); + if (dotIndex != -1) { + String possibleType = pattern.substring(0, dotIndex); + DocumentMapper possibleDocMapper = mappers.get(possibleType); + if (possibleDocMapper != null) { + Set typedFields = Sets.newHashSet(); + for (String indexName : possibleDocMapper.mappers().simpleMatchToIndexNames(pattern)) { + typedFields.add(possibleType + "." + indexName); + } + return typedFields; + } + } + Set fields = Sets.newHashSet(); + for (Map.Entry entry : fullNameFieldMappers.entrySet()) { + if (Regex.simpleMatch(pattern, entry.getKey())) { + for (FieldMapper mapper : entry.getValue()) { + fields.add(mapper.names().indexName()); + } + } + } + for (Map.Entry entry : indexNameFieldMappers.entrySet()) { + if (Regex.simpleMatch(pattern, entry.getKey())) { + for (FieldMapper mapper : entry.getValue()) { + fields.add(mapper.names().indexName()); + } + } + } + for (Map.Entry entry : nameFieldMappers.entrySet()) { + if (Regex.simpleMatch(pattern, entry.getKey())) { + for (FieldMapper mapper : entry.getValue()) { + fields.add(mapper.names().indexName()); + } + } + } + return fields; + } + /** * Same as {@link #smartName(String)}, except it returns just the field mappers. */ diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/xcontent/QueryStringQueryParser.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/xcontent/QueryStringQueryParser.java index 841f738dbde..3102a87d1e6 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/xcontent/QueryStringQueryParser.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/xcontent/QueryStringQueryParser.java @@ -28,6 +28,7 @@ import org.elasticsearch.cache.query.parser.QueryParserCache; import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.Lists; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.trove.ExtTObjectFloatHashMap; import org.elasticsearch.common.xcontent.XContentParser; @@ -95,12 +96,25 @@ public class QueryStringQueryParser extends AbstractIndexComponent implements XC if (qpSettings.fields() == null) { qpSettings.fields(Lists.newArrayList()); } - qpSettings.fields().add(fField); - if (fBoost != -1) { - if (qpSettings.boosts() == null) { - qpSettings.boosts(new ExtTObjectFloatHashMap().defaultReturnValue(1.0f)); + + if (Regex.isSimpleMatchPattern(fField)) { + for (String field : parseContext.mapperService().simpleMatchToIndexNames(fField)) { + qpSettings.fields().add(field); + if (fBoost != -1) { + if (qpSettings.boosts() == null) { + qpSettings.boosts(new ExtTObjectFloatHashMap().defaultReturnValue(1.0f)); + } + qpSettings.boosts().put(field, fBoost); + } + } + } else { + qpSettings.fields().add(fField); + if (fBoost != -1) { + if (qpSettings.boosts() == null) { + qpSettings.boosts(new ExtTObjectFloatHashMap().defaultReturnValue(1.0f)); + } + qpSettings.boosts().put(fField, fBoost); } - qpSettings.boosts().put(fField, fBoost); } } } diff --git a/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/xcontent/SimpleIndexQueryParserTests.java b/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/xcontent/SimpleIndexQueryParserTests.java index 0691a3a2433..6f98da40437 100644 --- a/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/xcontent/SimpleIndexQueryParserTests.java +++ b/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/xcontent/SimpleIndexQueryParserTests.java @@ -140,6 +140,17 @@ public class SimpleIndexQueryParserTests { assertThat(((TermQuery) bQuery.clauses().get(1).getQuery()).getTerm(), equalTo(new Term("name", "test"))); } + @Test public void testQueryStringFieldsMatch() throws Exception { + IndexQueryParser queryParser = queryParser(); + String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/query-fields-match.json"); + Query parsedQuery = queryParser.parse(query).query(); + assertThat(parsedQuery, instanceOf(BooleanQuery.class)); + BooleanQuery bQuery = (BooleanQuery) parsedQuery; + assertThat(bQuery.clauses().size(), equalTo(2)); + assertThat(((TermQuery) bQuery.clauses().get(0).getQuery()).getTerm(), equalTo(new Term("name.first", "test"))); + assertThat(((TermQuery) bQuery.clauses().get(1).getQuery()).getTerm(), equalTo(new Term("name.last", "test"))); + } + @Test public void testQueryStringFields2Builder() throws Exception { IndexQueryParser queryParser = queryParser(); Query parsedQuery = queryParser.parse(queryString("test").field("content").field("name").useDisMax(true)).query(); @@ -400,7 +411,7 @@ public class SimpleIndexQueryParserTests { @Test public void testPrefixFilteredQueryBuilder() throws IOException { IndexQueryParser queryParser = queryParser(); - Query parsedQuery = queryParser.parse(filtered(termQuery("name.first", "shay"), prefixFilter("name.first", "sh"))).query(); + Query parsedQuery = queryParser.parse(filteredQuery(termQuery("name.first", "shay"), prefixFilter("name.first", "sh"))).query(); assertThat(parsedQuery, instanceOf(FilteredQuery.class)); FilteredQuery filteredQuery = (FilteredQuery) parsedQuery; PrefixFilter prefixFilter = (PrefixFilter) filteredQuery.getFilter(); @@ -507,7 +518,7 @@ public class SimpleIndexQueryParserTests { @Test public void testRangeFilteredQueryBuilder() throws IOException { IndexQueryParser queryParser = queryParser(); - Query parsedQuery = queryParser.parse(filtered(termQuery("name.first", "shay"), rangeFilter("age").from(23).to(54).includeLower(true).includeUpper(false))).query(); + Query parsedQuery = queryParser.parse(filteredQuery(termQuery("name.first", "shay"), rangeFilter("age").from(23).to(54).includeLower(true).includeUpper(false))).query(); // since age is automatically registered in data, we encode it as numeric assertThat(parsedQuery, instanceOf(FilteredQuery.class)); Filter filter = ((FilteredQuery) parsedQuery).getFilter(); @@ -554,7 +565,7 @@ public class SimpleIndexQueryParserTests { @Test public void testNumericRangeFilteredQueryBuilder() throws IOException { IndexQueryParser queryParser = queryParser(); - Query parsedQuery = queryParser.parse(filtered(termQuery("name.first", "shay"), numericRangeFilter("age").from(23).to(54).includeLower(true).includeUpper(false))).query(); + Query parsedQuery = queryParser.parse(filteredQuery(termQuery("name.first", "shay"), numericRangeFilter("age").from(23).to(54).includeLower(true).includeUpper(false))).query(); assertThat(parsedQuery, instanceOf(FilteredQuery.class)); Filter filter = ((FilteredQuery) parsedQuery).getFilter(); assertThat(filter, instanceOf(NumericRangeFieldDataFilter.class)); @@ -594,7 +605,7 @@ public class SimpleIndexQueryParserTests { @Test public void testAndFilteredQueryBuilder() throws IOException { IndexQueryParser queryParser = queryParser(); - Query parsedQuery = queryParser.parse(filtered(matchAllQuery(), andFilter(termFilter("name.first", "shay1"), termFilter("name.first", "shay4")))).query(); + Query parsedQuery = queryParser.parse(filteredQuery(matchAllQuery(), andFilter(termFilter("name.first", "shay1"), termFilter("name.first", "shay4")))).query(); assertThat(parsedQuery, instanceOf(FilteredQuery.class)); FilteredQuery filteredQuery = (FilteredQuery) parsedQuery; @@ -646,7 +657,7 @@ public class SimpleIndexQueryParserTests { @Test public void testOrFilteredQueryBuilder() throws IOException { IndexQueryParser queryParser = queryParser(); - Query parsedQuery = queryParser.parse(filtered(matchAllQuery(), orFilter(termFilter("name.first", "shay1"), termFilter("name.first", "shay4")))).query(); + Query parsedQuery = queryParser.parse(filteredQuery(matchAllQuery(), orFilter(termFilter("name.first", "shay1"), termFilter("name.first", "shay4")))).query(); assertThat(parsedQuery, instanceOf(FilteredQuery.class)); FilteredQuery filteredQuery = (FilteredQuery) parsedQuery; @@ -684,7 +695,7 @@ public class SimpleIndexQueryParserTests { @Test public void testNotFilteredQueryBuilder() throws IOException { IndexQueryParser queryParser = queryParser(); - Query parsedQuery = queryParser.parse(filtered(matchAllQuery(), notFilter(termFilter("name.first", "shay1")))).query(); + Query parsedQuery = queryParser.parse(filteredQuery(matchAllQuery(), notFilter(termFilter("name.first", "shay1")))).query(); assertThat(parsedQuery, instanceOf(FilteredQuery.class)); FilteredQuery filteredQuery = (FilteredQuery) parsedQuery; @@ -751,7 +762,7 @@ public class SimpleIndexQueryParserTests { @Test public void testFilteredQueryBuilder() throws IOException { IndexQueryParser queryParser = queryParser(); - Query parsedQuery = queryParser.parse(filtered(termQuery("name.first", "shay"), termFilter("name.last", "banon"))).query(); + Query parsedQuery = queryParser.parse(filteredQuery(termQuery("name.first", "shay"), termFilter("name.last", "banon"))).query(); assertThat(parsedQuery, instanceOf(FilteredQuery.class)); FilteredQuery filteredQuery = (FilteredQuery) parsedQuery; assertThat(((TermQuery) filteredQuery.getQuery()).getTerm(), equalTo(new Term("name.first", "shay"))); @@ -834,7 +845,7 @@ public class SimpleIndexQueryParserTests { @Test public void testTermsFilterQueryBuilder() throws Exception { IndexQueryParser queryParser = queryParser(); - Query parsedQuery = queryParser.parse(filtered(termQuery("name.first", "shay"), termsFilter("name.last", "banon", "kimchy"))).query(); + Query parsedQuery = queryParser.parse(filteredQuery(termQuery("name.first", "shay"), termsFilter("name.last", "banon", "kimchy"))).query(); assertThat(parsedQuery, instanceOf(FilteredQuery.class)); FilteredQuery filteredQuery = (FilteredQuery) parsedQuery; assertThat(filteredQuery.getFilter(), instanceOf(TermsFilter.class)); @@ -1037,7 +1048,7 @@ public class SimpleIndexQueryParserTests { @Test public void testQueryFilterBuilder() throws Exception { IndexQueryParser queryParser = queryParser(); - Query parsedQuery = queryParser.parse(filtered(termQuery("name.first", "shay"), queryFilter(termQuery("name.last", "banon")))).query(); + Query parsedQuery = queryParser.parse(filteredQuery(termQuery("name.first", "shay"), queryFilter(termQuery("name.last", "banon")))).query(); assertThat(parsedQuery, instanceOf(FilteredQuery.class)); FilteredQuery filteredQuery = (FilteredQuery) parsedQuery; QueryWrapperFilter queryWrapperFilter = (QueryWrapperFilter) filteredQuery.getFilter(); diff --git a/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/xcontent/query-fields-match.json b/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/xcontent/query-fields-match.json new file mode 100644 index 00000000000..0805cb4d10a --- /dev/null +++ b/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/xcontent/query-fields-match.json @@ -0,0 +1,7 @@ +{ + query_string : { + fields : ["name.*"], + use_dis_max : false, + query: "test" + } +} \ No newline at end of file