diff --git a/src/main/java/org/elasticsearch/action/mlt/TransportMoreLikeThisAction.java b/src/main/java/org/elasticsearch/action/mlt/TransportMoreLikeThisAction.java index c0a36f5702f..808c6077364 100644 --- a/src/main/java/org/elasticsearch/action/mlt/TransportMoreLikeThisAction.java +++ b/src/main/java/org/elasticsearch/action/mlt/TransportMoreLikeThisAction.java @@ -160,7 +160,7 @@ public class TransportMoreLikeThisAction extends TransportActionAlthough most analyzers generate character terms (CharTermAttribute), + * some token only contain binary terms (BinaryTermAttribute, + * CharTermAttribute being a special type of BinaryTermAttribute), such as + * {@link NumericTokenStream} and unsuitable for highlighting and + * more-like-this queries which expect character terms.

+ */ + public static boolean isCharacterTokenStream(TokenStream tokenStream) { + try { + tokenStream.addAttribute(CharTermAttribute.class); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } + + /** + * Check whether {@link TokenStream}s generated with analyzer + * provide with character terms. + * @see #isCharacterTokenStream(TokenStream) + */ + public static boolean generatesCharacterTokenStream(Analyzer analyzer, String fieldName) throws IOException { + return isCharacterTokenStream(analyzer.tokenStream(fieldName, new StringReader(""))); + } + } diff --git a/src/main/java/org/elasticsearch/index/mapper/core/ByteFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/core/ByteFieldMapper.java index a1a45140baf..5cce2cc8c88 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/ByteFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/ByteFieldMapper.java @@ -356,7 +356,7 @@ public class ByteFieldMapper extends NumberFieldMapper { private final NumberFieldMapper mapper; public CustomByteNumericField(NumberFieldMapper mapper, byte number, FieldType fieldType) { - super(mapper, mapper.fieldType.stored() ? number : null, fieldType); + super(mapper, number, fieldType); this.mapper = mapper; this.number = number; } diff --git a/src/main/java/org/elasticsearch/index/mapper/core/DoubleFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/core/DoubleFieldMapper.java index 8d88cc73bd8..988ab328a16 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/DoubleFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/DoubleFieldMapper.java @@ -354,7 +354,7 @@ public class DoubleFieldMapper extends NumberFieldMapper { private final NumberFieldMapper mapper; public CustomDoubleNumericField(NumberFieldMapper mapper, double number, FieldType fieldType) { - super(mapper, mapper.fieldType().stored() ? number : null, fieldType); + super(mapper, number, fieldType); this.mapper = mapper; this.number = number; } diff --git a/src/main/java/org/elasticsearch/index/mapper/core/FloatFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/core/FloatFieldMapper.java index 04eaf10740d..a8372b567c2 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/FloatFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/FloatFieldMapper.java @@ -350,7 +350,7 @@ public class FloatFieldMapper extends NumberFieldMapper { private final NumberFieldMapper mapper; public CustomFloatNumericField(NumberFieldMapper mapper, float number, FieldType fieldType) { - super(mapper, mapper.fieldType().stored() ? number : null, fieldType); + super(mapper, number, fieldType); this.mapper = mapper; this.number = number; } diff --git a/src/main/java/org/elasticsearch/index/mapper/core/IntegerFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/core/IntegerFieldMapper.java index a673e6e04d7..6c0b7327bb9 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/IntegerFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/IntegerFieldMapper.java @@ -353,7 +353,7 @@ public class IntegerFieldMapper extends NumberFieldMapper { private final NumberFieldMapper mapper; public CustomIntegerNumericField(NumberFieldMapper mapper, int number, FieldType fieldType) { - super(mapper, mapper.fieldType().stored() ? number : null, fieldType); + super(mapper, number, fieldType); this.mapper = mapper; this.number = number; } diff --git a/src/main/java/org/elasticsearch/index/mapper/core/LongFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/core/LongFieldMapper.java index 9a7e1841a43..f90955335d4 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/LongFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/LongFieldMapper.java @@ -353,7 +353,7 @@ public class LongFieldMapper extends NumberFieldMapper { private final NumberFieldMapper mapper; public CustomLongNumericField(NumberFieldMapper mapper, long number, FieldType fieldType) { - super(mapper, mapper.fieldType.stored() ? number : null, fieldType); + super(mapper, number, fieldType); this.mapper = mapper; this.number = number; } diff --git a/src/main/java/org/elasticsearch/index/mapper/core/ShortFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/core/ShortFieldMapper.java index c1fc88be058..b4d9f7a613a 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/ShortFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/ShortFieldMapper.java @@ -358,7 +358,7 @@ public class ShortFieldMapper extends NumberFieldMapper { private final NumberFieldMapper mapper; public CustomShortNumericField(NumberFieldMapper mapper, short number, FieldType fieldType) { - super(mapper, mapper.fieldType().stored() ? number : null, fieldType); + super(mapper, number, fieldType); this.mapper = mapper; this.number = number; } diff --git a/src/main/java/org/elasticsearch/index/query/FuzzyLikeThisFieldQueryBuilder.java b/src/main/java/org/elasticsearch/index/query/FuzzyLikeThisFieldQueryBuilder.java index 12a3c27662e..66f3badbea5 100644 --- a/src/main/java/org/elasticsearch/index/query/FuzzyLikeThisFieldQueryBuilder.java +++ b/src/main/java/org/elasticsearch/index/query/FuzzyLikeThisFieldQueryBuilder.java @@ -38,6 +38,7 @@ public class FuzzyLikeThisFieldQueryBuilder extends BaseQueryBuilder implements private Integer maxQueryTerms; private Boolean ignoreTF; private String analyzer; + private boolean failOnUnsupportedField; /** * A fuzzy more like this query on the provided field. @@ -89,6 +90,14 @@ public class FuzzyLikeThisFieldQueryBuilder extends BaseQueryBuilder implements return this; } + /** + * Whether to fail or return no result when this query is run against a field which is not supported such as binary/numeric fields. + */ + public FuzzyLikeThisFieldQueryBuilder failOnUnsupportedField(boolean fail) { + failOnUnsupportedField = fail; + return this; + } + @Override protected void doXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(FuzzyLikeThisFieldQueryParser.NAME); @@ -115,6 +124,9 @@ public class FuzzyLikeThisFieldQueryBuilder extends BaseQueryBuilder implements if (analyzer != null) { builder.field("analyzer", analyzer); } + if (!failOnUnsupportedField) { + builder.field("fail_on_unsupported_field", failOnUnsupportedField); + } builder.endObject(); builder.endObject(); } diff --git a/src/main/java/org/elasticsearch/index/query/FuzzyLikeThisFieldQueryParser.java b/src/main/java/org/elasticsearch/index/query/FuzzyLikeThisFieldQueryParser.java index 34b977303a4..77ce20de294 100644 --- a/src/main/java/org/elasticsearch/index/query/FuzzyLikeThisFieldQueryParser.java +++ b/src/main/java/org/elasticsearch/index/query/FuzzyLikeThisFieldQueryParser.java @@ -22,9 +22,11 @@ package org.elasticsearch.index.query; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.sandbox.queries.FuzzyLikeThisQuery; import org.apache.lucene.search.Query; +import org.elasticsearch.ElasticSearchIllegalArgumentException; import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.index.analysis.Analysis; import org.elasticsearch.index.mapper.MapperService; import java.io.IOException; @@ -67,6 +69,7 @@ public class FuzzyLikeThisFieldQueryParser implements QueryParser { int prefixLength = 0; boolean ignoreTF = false; Analyzer analyzer = null; + boolean failOnUnsupportedField = true; XContentParser.Token token = parser.nextToken(); if (token != XContentParser.Token.FIELD_NAME) { @@ -100,6 +103,8 @@ public class FuzzyLikeThisFieldQueryParser implements QueryParser { prefixLength = parser.intValue(); } else if ("analyzer".equals(currentFieldName)) { analyzer = parseContext.analysisService().analyzer(parser.text()); + } else if ("fail_on_unsupported_field".equals(currentFieldName) || "failOnUnsupportedField".equals(currentFieldName)) { + failOnUnsupportedField = parser.booleanValue(); } else { throw new QueryParsingException(parseContext.index(), "[flt_field] query does not support [" + currentFieldName + "]"); } @@ -122,6 +127,13 @@ public class FuzzyLikeThisFieldQueryParser implements QueryParser { if (analyzer == null) { analyzer = parseContext.mapperService().searchAnalyzer(); } + if (!Analysis.generatesCharacterTokenStream(analyzer, fieldName)) { + if (failOnUnsupportedField) { + throw new ElasticSearchIllegalArgumentException("fuzzy_like_this_field doesn't support binary/numeric fields: [" + fieldName + "]"); + } else { + return null; + } + } FuzzyLikeThisQuery query = new FuzzyLikeThisQuery(maxNumTerms, analyzer); query.addTerms(likeText, fieldName, minSimilarity, prefixLength); diff --git a/src/main/java/org/elasticsearch/index/query/FuzzyLikeThisQueryBuilder.java b/src/main/java/org/elasticsearch/index/query/FuzzyLikeThisQueryBuilder.java index df95b235172..44856254194 100644 --- a/src/main/java/org/elasticsearch/index/query/FuzzyLikeThisQueryBuilder.java +++ b/src/main/java/org/elasticsearch/index/query/FuzzyLikeThisQueryBuilder.java @@ -38,6 +38,7 @@ public class FuzzyLikeThisQueryBuilder extends BaseQueryBuilder implements Boost private Integer maxQueryTerms; private Boolean ignoreTF; private String analyzer; + private boolean failOnUnsupportedField = true;; /** * Constructs a new fuzzy like this query which uses the "_all" field. @@ -96,6 +97,14 @@ public class FuzzyLikeThisQueryBuilder extends BaseQueryBuilder implements Boost return this; } + /** + * Whether to fail or return no result when this query is run against a field which is not supported such as binary/numeric fields. + */ + public FuzzyLikeThisQueryBuilder failOnUnsupportedField(boolean fail) { + failOnUnsupportedField = fail; + return this; + } + @Override protected void doXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(FuzzyLikeThisQueryParser.NAME); @@ -128,6 +137,9 @@ public class FuzzyLikeThisQueryBuilder extends BaseQueryBuilder implements Boost if (analyzer != null) { builder.field("analyzer", analyzer); } + if (!failOnUnsupportedField) { + builder.field("fail_on_unsupported_field", failOnUnsupportedField); + } builder.endObject(); } } \ No newline at end of file diff --git a/src/main/java/org/elasticsearch/index/query/FuzzyLikeThisQueryParser.java b/src/main/java/org/elasticsearch/index/query/FuzzyLikeThisQueryParser.java index 0a1f9c02421..79f15bafdf8 100644 --- a/src/main/java/org/elasticsearch/index/query/FuzzyLikeThisQueryParser.java +++ b/src/main/java/org/elasticsearch/index/query/FuzzyLikeThisQueryParser.java @@ -19,14 +19,18 @@ package org.elasticsearch.index.query; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.sandbox.queries.FuzzyLikeThisQuery; import org.apache.lucene.search.Query; +import org.elasticsearch.ElasticSearchIllegalArgumentException; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.index.analysis.Analysis; import java.io.IOException; +import java.util.Iterator; import java.util.List; /** @@ -66,6 +70,7 @@ public class FuzzyLikeThisQueryParser implements QueryParser { int prefixLength = 0; boolean ignoreTF = false; Analyzer analyzer = null; + boolean failOnUnsupportedField = true; XContentParser.Token token; String currentFieldName = null; @@ -87,12 +92,14 @@ public class FuzzyLikeThisQueryParser implements QueryParser { prefixLength = parser.intValue(); } else if ("analyzer".equals(currentFieldName)) { analyzer = parseContext.analysisService().analyzer(parser.text()); + } else if ("fail_on_unsupported_field".equals(currentFieldName) || "failOnUnsupportedField".equals(currentFieldName)) { + failOnUnsupportedField = parser.booleanValue(); } else { throw new QueryParsingException(parseContext.index(), "[flt] query does not support [" + currentFieldName + "]"); } } else if (token == XContentParser.Token.START_ARRAY) { if ("fields".equals(currentFieldName)) { - fields = Lists.newArrayList(); + fields = Lists.newLinkedList(); while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { fields.add(parseContext.indexName(parser.text())); } @@ -112,13 +119,26 @@ public class FuzzyLikeThisQueryParser implements QueryParser { FuzzyLikeThisQuery query = new FuzzyLikeThisQuery(maxNumTerms, analyzer); if (fields == null) { - // add the default _all field - query.addTerms(likeText, parseContext.defaultField(), minSimilarity, prefixLength); - } else { - for (String field : fields) { - query.addTerms(likeText, field, minSimilarity, prefixLength); + fields = Lists.newArrayList(parseContext.defaultField()); + } else if (fields.isEmpty()) { + throw new QueryParsingException(parseContext.index(), "fuzzy_like_this requires 'fields' to be non-empty"); + } + for (Iterator it = fields.iterator(); it.hasNext(); ) { + final String fieldName = it.next(); + if (!Analysis.generatesCharacterTokenStream(analyzer, fieldName)) { + if (failOnUnsupportedField) { + throw new ElasticSearchIllegalArgumentException("more_like_this doesn't support binary/numeric fields: [" + fieldName + "]"); + } else { + it.remove(); + } } } + if (fields.isEmpty()) { + return null; + } + for (String field : fields) { + query.addTerms(likeText, field, minSimilarity, prefixLength); + } query.setBoost(boost); query.setIgnoreTF(ignoreTF); diff --git a/src/main/java/org/elasticsearch/index/query/MoreLikeThisFieldQueryBuilder.java b/src/main/java/org/elasticsearch/index/query/MoreLikeThisFieldQueryBuilder.java index 9130507d47f..bf5a3c18161 100644 --- a/src/main/java/org/elasticsearch/index/query/MoreLikeThisFieldQueryBuilder.java +++ b/src/main/java/org/elasticsearch/index/query/MoreLikeThisFieldQueryBuilder.java @@ -44,6 +44,7 @@ public class MoreLikeThisFieldQueryBuilder extends BaseQueryBuilder implements B private float boostTerms = -1; private float boost = -1; private String analyzer; + private boolean failOnUnsupportedField; /** * A more like this query that runs against a specific field. @@ -157,6 +158,14 @@ public class MoreLikeThisFieldQueryBuilder extends BaseQueryBuilder implements B return this; } + /** + * Whether to fail or return no result when this query is run against a field which is not supported such as binary/numeric fields. + */ + public MoreLikeThisFieldQueryBuilder failOnUnsupportedField(boolean fail) { + failOnUnsupportedField = fail; + return this; + } + @Override protected void doXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(MoreLikeThisFieldQueryParser.NAME); @@ -202,6 +211,9 @@ public class MoreLikeThisFieldQueryBuilder extends BaseQueryBuilder implements B if (analyzer != null) { builder.field("analyzer", analyzer); } + if (!failOnUnsupportedField) { + builder.field("fail_on_unsupported_field", failOnUnsupportedField); + } builder.endObject(); builder.endObject(); } diff --git a/src/main/java/org/elasticsearch/index/query/MoreLikeThisFieldQueryParser.java b/src/main/java/org/elasticsearch/index/query/MoreLikeThisFieldQueryParser.java index aa5af80a20a..6ad95c7f3a9 100644 --- a/src/main/java/org/elasticsearch/index/query/MoreLikeThisFieldQueryParser.java +++ b/src/main/java/org/elasticsearch/index/query/MoreLikeThisFieldQueryParser.java @@ -22,10 +22,12 @@ package org.elasticsearch.index.query; import com.google.common.collect.Sets; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.search.Query; +import org.elasticsearch.ElasticSearchIllegalArgumentException; import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.lucene.search.MoreLikeThisQuery; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.index.analysis.Analysis; import org.elasticsearch.index.mapper.MapperService; import java.io.IOException; @@ -65,6 +67,7 @@ public class MoreLikeThisFieldQueryParser implements QueryParser { MoreLikeThisQuery mltQuery = new MoreLikeThisQuery(); mltQuery.setSimilarity(parseContext.searchSimilarity()); Analyzer analyzer = null; + boolean failOnUnsupportedField = true; String currentFieldName = null; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { @@ -94,6 +97,8 @@ public class MoreLikeThisFieldQueryParser implements QueryParser { analyzer = parseContext.analysisService().analyzer(parser.text()); } else if ("boost".equals(currentFieldName)) { mltQuery.setBoost(parser.floatValue()); + } else if ("fail_on_unsupported_field".equals(currentFieldName) || "failOnUnsupportedField".equals(currentFieldName)) { + failOnUnsupportedField = parser.booleanValue(); } else { throw new QueryParsingException(parseContext.index(), "[mlt_field] query does not support [" + currentFieldName + "]"); } @@ -130,6 +135,13 @@ public class MoreLikeThisFieldQueryParser implements QueryParser { if (analyzer == null) { analyzer = parseContext.mapperService().searchAnalyzer(); } + if (!Analysis.generatesCharacterTokenStream(analyzer, fieldName)) { + if (failOnUnsupportedField) { + throw new ElasticSearchIllegalArgumentException("more_like_this_field doesn't support binary/numeric fields: [" + fieldName + "]"); + } else { + return null; + } + } mltQuery.setAnalyzer(analyzer); mltQuery.setMoreLikeFields(new String[]{fieldName}); return wrapSmartNameQuery(mltQuery, smartNameFieldMappers, parseContext); diff --git a/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilder.java b/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilder.java index 2c07bc56bcb..adb68924401 100644 --- a/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilder.java +++ b/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilder.java @@ -45,6 +45,7 @@ public class MoreLikeThisQueryBuilder extends BaseQueryBuilder implements Boosta private float boostTerms = -1; private float boost = -1; private String analyzer; + private boolean failOnUnsupportedField = true; /** * Constructs a new more like this query which uses the "_all" field. @@ -165,6 +166,14 @@ public class MoreLikeThisQueryBuilder extends BaseQueryBuilder implements Boosta return this; } + /** + * Whether to fail or return no result when this query is run against a field which is not supported such as binary/numeric fields. + */ + public MoreLikeThisQueryBuilder failOnUnsupportedField(boolean fail) { + failOnUnsupportedField = fail; + return this; + } + @Override protected void doXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(MoreLikeThisQueryParser.NAME); @@ -216,6 +225,9 @@ public class MoreLikeThisQueryBuilder extends BaseQueryBuilder implements Boosta if (analyzer != null) { builder.field("analyzer", analyzer); } + if (!failOnUnsupportedField) { + builder.field("fail_on_unsupported_field", failOnUnsupportedField); + } builder.endObject(); } } \ No newline at end of file diff --git a/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryParser.java b/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryParser.java index 0a77a0ad2f4..48c476c619a 100644 --- a/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryParser.java +++ b/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryParser.java @@ -23,11 +23,15 @@ import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.search.Query; +import org.elasticsearch.ElasticSearchIllegalArgumentException; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.lucene.search.MoreLikeThisQuery; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.index.analysis.Analysis; import java.io.IOException; +import java.util.Iterator; import java.util.List; import java.util.Set; @@ -52,9 +56,10 @@ public class MoreLikeThisQueryParser implements QueryParser { XContentParser parser = parseContext.parser(); MoreLikeThisQuery mltQuery = new MoreLikeThisQuery(); - mltQuery.setMoreLikeFields(new String[]{parseContext.defaultField()}); mltQuery.setSimilarity(parseContext.searchSimilarity()); Analyzer analyzer = null; + List moreLikeFields = null; + boolean failOnUnsupportedField = true; XContentParser.Token token; String currentFieldName = null; @@ -85,6 +90,8 @@ public class MoreLikeThisQueryParser implements QueryParser { analyzer = parseContext.analysisService().analyzer(parser.text()); } else if ("boost".equals(currentFieldName)) { mltQuery.setBoost(parser.floatValue()); + } else if ("fail_on_unsupported_field".equals(currentFieldName) || "failOnUnsupportedField".equals(currentFieldName)) { + failOnUnsupportedField = parser.booleanValue(); } else { throw new QueryParsingException(parseContext.index(), "[mlt] query does not support [" + currentFieldName + "]"); } @@ -96,11 +103,10 @@ public class MoreLikeThisQueryParser implements QueryParser { } mltQuery.setStopWords(stopWords); } else if ("fields".equals(currentFieldName)) { - List fields = Lists.newArrayList(); + moreLikeFields = Lists.newLinkedList(); while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { - fields.add(parseContext.indexName(parser.text())); + moreLikeFields.add(parseContext.indexName(parser.text())); } - mltQuery.setMoreLikeFields(fields.toArray(new String[fields.size()])); } else { throw new QueryParsingException(parseContext.index(), "[mlt] query does not support [" + currentFieldName + "]"); } @@ -110,15 +116,33 @@ public class MoreLikeThisQueryParser implements QueryParser { if (mltQuery.getLikeText() == null) { throw new QueryParsingException(parseContext.index(), "more_like_this requires 'like_text' to be specified"); } - if (mltQuery.getMoreLikeFields() == null || mltQuery.getMoreLikeFields().length == 0) { - throw new QueryParsingException(parseContext.index(), "more_like_this requires 'fields' to be specified"); - } if (analyzer == null) { analyzer = parseContext.mapperService().searchAnalyzer(); } - mltQuery.setAnalyzer(analyzer); + + if (moreLikeFields == null) { + moreLikeFields = Lists.newArrayList(parseContext.defaultField()); + } else if (moreLikeFields.isEmpty()) { + throw new QueryParsingException(parseContext.index(), "more_like_this requires 'fields' to be non-empty"); + } + + for (Iterator it = moreLikeFields.iterator(); it.hasNext(); ) { + final String fieldName = it.next(); + if (!Analysis.generatesCharacterTokenStream(analyzer, fieldName)) { + if (failOnUnsupportedField) { + throw new ElasticSearchIllegalArgumentException("more_like_this doesn't support binary/numeric fields: [" + fieldName + "]"); + } else { + it.remove(); + } + } + } + if (moreLikeFields.isEmpty()) { + return null; + } + mltQuery.setMoreLikeFields(moreLikeFields.toArray(Strings.EMPTY_ARRAY)); + return mltQuery; } } \ No newline at end of file diff --git a/src/test/java/org/elasticsearch/test/integration/flt/FuzzyLikeThisActionTests.java b/src/test/java/org/elasticsearch/test/integration/flt/FuzzyLikeThisActionTests.java new file mode 100644 index 00000000000..e9876bbeab3 --- /dev/null +++ b/src/test/java/org/elasticsearch/test/integration/flt/FuzzyLikeThisActionTests.java @@ -0,0 +1,89 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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.test.integration.flt; + +import org.elasticsearch.action.search.SearchPhaseExecutionException; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.test.integration.AbstractSharedClusterTest; +import org.testng.annotations.Test; + +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.elasticsearch.index.query.QueryBuilders.fuzzyLikeThisFieldQuery; +import static org.elasticsearch.index.query.QueryBuilders.fuzzyLikeThisQuery; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.testng.Assert.fail; + +/** + * + */ +public class FuzzyLikeThisActionTests extends AbstractSharedClusterTest { + + @Test + // See issue https://github.com/elasticsearch/elasticsearch/issues/3252 + public void testNumericField() throws Exception { + prepareCreate("test").execute().actionGet(); + ensureGreen(); + client().prepareIndex("test", "type", "1") + .setSource(jsonBuilder().startObject().field("string_value", "lucene index").field("int_value", 1).endObject()) + .execute().actionGet(); + client().prepareIndex("test", "type", "2") + .setSource(jsonBuilder().startObject().field("string_value", "elasticsearch index").field("int_value", 42).endObject()) + .execute().actionGet(); + + refresh(); + + // flt query with no field -> OK + SearchResponse searchResponse = client().prepareSearch().setQuery(fuzzyLikeThisQuery().likeText("index")).execute().actionGet(); + assertThat(searchResponse.getFailedShards(), equalTo(0)); + assertThat(searchResponse.getHits().getTotalHits(), equalTo(2L)); + + // flt query with string fields + searchResponse = client().prepareSearch().setQuery(fuzzyLikeThisQuery("string_value").likeText("index")).execute().actionGet(); + assertThat(searchResponse.getFailedShards(), equalTo(0)); + assertThat(searchResponse.getHits().getTotalHits(), equalTo(2L)); + + // flt query with at least a numeric field -> fail + try { + searchResponse = client().prepareSearch().setQuery(fuzzyLikeThisQuery("string_value", "int_value").likeText("index")).execute().actionGet(); + fail(); + } catch (SearchPhaseExecutionException e) { + // OK + } + + // flt query with at least a numeric field but fail_on_unsupported_field set to false + searchResponse = client().prepareSearch().setQuery(fuzzyLikeThisQuery("string_value", "int_value").likeText("index").failOnUnsupportedField(false)).execute().actionGet(); + assertThat(searchResponse.getFailedShards(), equalTo(0)); + assertThat(searchResponse.getHits().getTotalHits(), equalTo(2L)); + + // flt field query on a numeric field -> failure + try { + searchResponse = client().prepareSearch().setQuery(fuzzyLikeThisFieldQuery("int_value").likeText("42")).execute().actionGet(); + } catch (SearchPhaseExecutionException e) { + // OK + } + + // flt field query on a numeric field but fail_on_unsupported_field set to false + searchResponse = client().prepareSearch().setQuery(fuzzyLikeThisFieldQuery("int_value").likeText("42").failOnUnsupportedField(false)).execute().actionGet(); + assertThat(searchResponse.getFailedShards(), equalTo(0)); + assertThat(searchResponse.getHits().getTotalHits(), equalTo(0L)); + } + +} diff --git a/src/test/java/org/elasticsearch/test/integration/mlt/MoreLikeThisActionTests.java b/src/test/java/org/elasticsearch/test/integration/mlt/MoreLikeThisActionTests.java index a38fbc467d3..38dbcc92de0 100644 --- a/src/test/java/org/elasticsearch/test/integration/mlt/MoreLikeThisActionTests.java +++ b/src/test/java/org/elasticsearch/test/integration/mlt/MoreLikeThisActionTests.java @@ -19,17 +19,8 @@ package org.elasticsearch.test.integration.mlt; -import static org.elasticsearch.client.Requests.indexAliasesRequest; -import static org.elasticsearch.client.Requests.indexRequest; -import static org.elasticsearch.client.Requests.moreLikeThisRequest; -import static org.elasticsearch.client.Requests.refreshRequest; -import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; -import static org.elasticsearch.index.query.FilterBuilders.termFilter; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.notNullValue; - import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus; +import org.elasticsearch.action.search.SearchPhaseExecutionException; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.Client; import org.elasticsearch.common.settings.ImmutableSettings; @@ -37,6 +28,16 @@ import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.test.integration.AbstractSharedClusterTest; import org.testng.annotations.Test; +import static org.elasticsearch.client.Requests.*; +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.elasticsearch.index.query.FilterBuilders.termFilter; +import static org.elasticsearch.index.query.QueryBuilders.moreLikeThisFieldQuery; +import static org.elasticsearch.index.query.QueryBuilders.moreLikeThisQuery; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; +import static org.testng.Assert.fail; + /** * */ @@ -151,4 +152,67 @@ public class MoreLikeThisActionTests extends AbstractSharedClusterTest { assertThat(searchResponse, notNullValue()); } + @Test + // See issue https://github.com/elasticsearch/elasticsearch/issues/3252 + public void testNumericField() throws Exception { + prepareCreate("test").execute().actionGet(); + ensureGreen(); + client().prepareIndex("test", "type", "1") + .setSource(jsonBuilder().startObject().field("string_value", "lucene index").field("int_value", 1).endObject()) + .execute().actionGet(); + client().prepareIndex("test", "type", "2") + .setSource(jsonBuilder().startObject().field("string_value", "elasticsearch index").field("int_value", 42).endObject()) + .execute().actionGet(); + + refresh(); + + // Implicit list of fields -> ignore numeric fields + SearchResponse searchResponse = client().prepareMoreLikeThis("test", "type", "1").setMinDocFreq(1).setMinTermFreq(1).execute().actionGet(); + assertThat(searchResponse.getFailedShards(), equalTo(0)); + assertThat(searchResponse.getHits().totalHits(), equalTo(1L)); + + // Explicit list of fields including numeric fields -> fail + try { + searchResponse = client().prepareMoreLikeThis("test", "type", "1").setField("string_value", "int_value").execute().actionGet(); + fail(); + } catch (SearchPhaseExecutionException e) { + // OK + } + + // mlt query with no field -> OK + searchResponse = client().prepareSearch().setQuery(moreLikeThisQuery().likeText("index").minTermFreq(1).minDocFreq(1)).execute().actionGet(); + assertThat(searchResponse.getFailedShards(), equalTo(0)); + assertThat(searchResponse.getHits().getTotalHits(), equalTo(2L)); + + // mlt query with string fields + searchResponse = client().prepareSearch().setQuery(moreLikeThisQuery("string_value").likeText("index").minTermFreq(1).minDocFreq(1)).execute().actionGet(); + assertThat(searchResponse.getFailedShards(), equalTo(0)); + assertThat(searchResponse.getHits().getTotalHits(), equalTo(2L)); + + // mlt query with at least a numeric field -> fail + try { + searchResponse = client().prepareSearch().setQuery(moreLikeThisQuery("string_value", "int_value").likeText("index")).execute().actionGet(); + fail(); + } catch (SearchPhaseExecutionException e) { + // OK + } + + // mlt query with at least a numeric field but fail_on_unsupported_field set to false + searchResponse = client().prepareSearch().setQuery(moreLikeThisQuery("string_value", "int_value").likeText("index").minTermFreq(1).minDocFreq(1).failOnUnsupportedField(false)).execute().actionGet(); + assertThat(searchResponse.getFailedShards(), equalTo(0)); + assertThat(searchResponse.getHits().getTotalHits(), equalTo(2L)); + + // mlt field query on a numeric field -> failure + try { + searchResponse = client().prepareSearch().setQuery(moreLikeThisFieldQuery("int_value").likeText("42").minTermFreq(1).minDocFreq(1)).execute().actionGet(); + } catch (SearchPhaseExecutionException e) { + // OK + } + + // mlt field query on a numeric field but fail_on_unsupported_field set to false + searchResponse = client().prepareSearch().setQuery(moreLikeThisFieldQuery("int_value").likeText("42").minTermFreq(1).minDocFreq(1).failOnUnsupportedField(false)).execute().actionGet(); + assertThat(searchResponse.getFailedShards(), equalTo(0)); + assertThat(searchResponse.getHits().getTotalHits(), equalTo(0L)); + } + } diff --git a/src/test/java/org/elasticsearch/test/unit/index/mapper/geo/LatLonMappingGeoPointTests.java b/src/test/java/org/elasticsearch/test/unit/index/mapper/geo/LatLonMappingGeoPointTests.java index 891a805685f..8fdea387130 100644 --- a/src/test/java/org/elasticsearch/test/unit/index/mapper/geo/LatLonMappingGeoPointTests.java +++ b/src/test/java/org/elasticsearch/test/unit/index/mapper/geo/LatLonMappingGeoPointTests.java @@ -185,9 +185,9 @@ public class LatLonMappingGeoPointTests { .bytes()); assertThat(doc.rootDoc().getField("point.lat"), notNullValue()); - assertThat(doc.rootDoc().getField("point.lat").numericValue(), nullValue()); + assertThat(doc.rootDoc().getField("point.lat").fieldType().stored(), is(false)); assertThat(doc.rootDoc().getField("point.lon"), notNullValue()); - assertThat(doc.rootDoc().getField("point.lon").numericValue(), nullValue()); + assertThat(doc.rootDoc().getField("point.lon").fieldType().stored(), is(false)); assertThat(doc.rootDoc().getField("point.geohash"), nullValue()); assertThat(doc.rootDoc().get("point"), equalTo("1.2,1.3")); }