diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index d2a26c09f88..c1c7293f969 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -156,6 +156,10 @@ Bug Fixes
* SOLR-10223: Allow running examples as root on Linux with -force option (janhoy)
+* SOLR-10830: Solr now correctly enforces that the '_root_' field has the same fieldType as the
+ uniqueKey field. With out this enforcement, child document updating was unreliable. (hossman)
+
+
Optimizations
----------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/DoublePointField.java b/solr/core/src/java/org/apache/solr/schema/DoublePointField.java
index 05a1ce7d124..d2cf6ed36db 100644
--- a/solr/core/src/java/org/apache/solr/schema/DoublePointField.java
+++ b/solr/core/src/java/org/apache/solr/schema/DoublePointField.java
@@ -61,7 +61,7 @@ public class DoublePointField extends PointField implements DoubleValueFieldType
if (min == null) {
actualMin = Double.NEGATIVE_INFINITY;
} else {
- actualMin = Double.parseDouble(min);
+ actualMin = parseDoubleFromUser(field.getName(), min);
if (!minInclusive) {
actualMin = DoublePoint.nextUp(actualMin);
}
@@ -69,7 +69,7 @@ public class DoublePointField extends PointField implements DoubleValueFieldType
if (max == null) {
actualMax = Double.POSITIVE_INFINITY;
} else {
- actualMax = Double.parseDouble(max);
+ actualMax = parseDoubleFromUser(field.getName(), max);
if (!maxInclusive) {
actualMax = DoublePoint.nextDown(actualMax);
}
@@ -100,7 +100,7 @@ public class DoublePointField extends PointField implements DoubleValueFieldType
@Override
protected Query getExactQuery(SchemaField field, String externalVal) {
- return DoublePoint.newExactQuery(field.getName(), Double.parseDouble(externalVal));
+ return DoublePoint.newExactQuery(field.getName(), parseDoubleFromUser(field.getName(), externalVal));
}
@Override
@@ -112,7 +112,7 @@ public class DoublePointField extends PointField implements DoubleValueFieldType
double[] values = new double[externalVal.size()];
int i = 0;
for (String val:externalVal) {
- values[i] = Double.parseDouble(val);
+ values[i] = parseDoubleFromUser(field.getName(), val);
i++;
}
return DoublePoint.newSetQuery(field.getName(), values);
@@ -127,7 +127,7 @@ public class DoublePointField extends PointField implements DoubleValueFieldType
public void readableToIndexed(CharSequence val, BytesRefBuilder result) {
result.grow(Double.BYTES);
result.setLength(Double.BYTES);
- DoublePoint.encodeDimension(Double.parseDouble(val.toString()), result.bytes(), 0);
+ DoublePoint.encodeDimension(parseDoubleFromUser(null, val.toString()), result.bytes(), 0);
}
@Override
diff --git a/solr/core/src/java/org/apache/solr/schema/FloatPointField.java b/solr/core/src/java/org/apache/solr/schema/FloatPointField.java
index fe9c7533802..e1a97410323 100644
--- a/solr/core/src/java/org/apache/solr/schema/FloatPointField.java
+++ b/solr/core/src/java/org/apache/solr/schema/FloatPointField.java
@@ -61,7 +61,7 @@ public class FloatPointField extends PointField implements FloatValueFieldType {
if (min == null) {
actualMin = Float.NEGATIVE_INFINITY;
} else {
- actualMin = Float.parseFloat(min);
+ actualMin = parseFloatFromUser(field.getName(), min);
if (!minInclusive) {
actualMin = FloatPoint.nextUp(actualMin);
}
@@ -69,7 +69,7 @@ public class FloatPointField extends PointField implements FloatValueFieldType {
if (max == null) {
actualMax = Float.POSITIVE_INFINITY;
} else {
- actualMax = Float.parseFloat(max);
+ actualMax = parseFloatFromUser(field.getName(), max);
if (!maxInclusive) {
actualMax = FloatPoint.nextDown(actualMax);
}
@@ -100,7 +100,7 @@ public class FloatPointField extends PointField implements FloatValueFieldType {
@Override
protected Query getExactQuery(SchemaField field, String externalVal) {
- return FloatPoint.newExactQuery(field.getName(), Float.parseFloat(externalVal));
+ return FloatPoint.newExactQuery(field.getName(), parseFloatFromUser(field.getName(), externalVal));
}
@Override
@@ -112,7 +112,7 @@ public class FloatPointField extends PointField implements FloatValueFieldType {
float[] values = new float[externalVal.size()];
int i = 0;
for (String val:externalVal) {
- values[i] = Float.parseFloat(val);
+ values[i] = parseFloatFromUser(field.getName(), val);
i++;
}
return FloatPoint.newSetQuery(field.getName(), values);
@@ -127,7 +127,7 @@ public class FloatPointField extends PointField implements FloatValueFieldType {
public void readableToIndexed(CharSequence val, BytesRefBuilder result) {
result.grow(Float.BYTES);
result.setLength(Float.BYTES);
- FloatPoint.encodeDimension(Float.parseFloat(val.toString()), result.bytes(), 0);
+ FloatPoint.encodeDimension(parseFloatFromUser(null, val.toString()), result.bytes(), 0);
}
@Override
diff --git a/solr/core/src/java/org/apache/solr/schema/IndexSchema.java b/solr/core/src/java/org/apache/solr/schema/IndexSchema.java
index 463df3ee681..36efbcf8a25 100644
--- a/solr/core/src/java/org/apache/solr/schema/IndexSchema.java
+++ b/solr/core/src/java/org/apache/solr/schema/IndexSchema.java
@@ -113,6 +113,7 @@ public class IndexSchema {
public static final String SOURCE = "source";
public static final String TYPE = "type";
public static final String TYPES = "types";
+ public static final String ROOT_FIELD_NAME = "_root_";
public static final String UNIQUE_KEY = "uniqueKey";
public static final String VERSION = "version";
@@ -517,6 +518,20 @@ public class IndexSchema {
log.warn("no " + UNIQUE_KEY + " specified in schema.");
} else {
uniqueKeyField=getIndexedField(node.getNodeValue().trim());
+ uniqueKeyFieldName=uniqueKeyField.getName();
+ uniqueKeyFieldType=uniqueKeyField.getType();
+
+ // we fail on init if the ROOT field is *explicitly* defined as incompatible with uniqueKey
+ // we don't want ot fail if there happens to be a dynamicField matching ROOT, (ie: "*")
+ // because the user may not care about child docs at all. The run time code
+ // related to child docs can catch that if it happens
+ if (fields.containsKey(ROOT_FIELD_NAME) && ! isUsableForChildDocs()) {
+ String msg = ROOT_FIELD_NAME + " field must be defined using the exact same fieldType as the " +
+ UNIQUE_KEY + " field ("+uniqueKeyFieldName+") uses: " + uniqueKeyFieldType.getTypeName();
+ log.error(msg);
+ throw new SolrException(ErrorCode.SERVER_ERROR, msg);
+ }
+
if (null != uniqueKeyField.getDefaultValue()) {
String msg = UNIQUE_KEY + " field ("+uniqueKeyFieldName+
") can not be configured with a default value ("+
@@ -542,9 +557,6 @@ public class IndexSchema {
throw new SolrException(ErrorCode.SERVER_ERROR, msg);
}
- uniqueKeyFieldName=uniqueKeyField.getName();
- uniqueKeyFieldType=uniqueKeyField.getType();
-
// Unless the uniqueKeyField is marked 'required=false' then make sure it exists
if( Boolean.FALSE != explicitRequiredProp.get( uniqueKeyFieldName ) ) {
uniqueKeyField.required = true;
@@ -1914,4 +1926,17 @@ public class IndexSchema {
+ XPATH_OR + stepsToPath(SCHEMA, TYPES, FIELD_TYPE);
return expression;
}
+
+ /**
+ * Helper method that returns true
if the {@link #ROOT_FIELD_NAME} uses the exact
+ * same 'type' as the {@link #getUniqueKeyField()}
+ *
+ * @lucene.internal
+ */
+ public boolean isUsableForChildDocs() {
+ FieldType rootType = getFieldType(ROOT_FIELD_NAME);
+ return (null != uniqueKeyFieldType &&
+ null != rootType &&
+ rootType.getTypeName().equals(uniqueKeyFieldType.getTypeName()));
+ }
}
diff --git a/solr/core/src/java/org/apache/solr/schema/IntPointField.java b/solr/core/src/java/org/apache/solr/schema/IntPointField.java
index f47f45079e9..7d366127c0c 100644
--- a/solr/core/src/java/org/apache/solr/schema/IntPointField.java
+++ b/solr/core/src/java/org/apache/solr/schema/IntPointField.java
@@ -64,7 +64,7 @@ public class IntPointField extends PointField implements IntValueFieldType {
if (min == null) {
actualMin = Integer.MIN_VALUE;
} else {
- actualMin = Integer.parseInt(min);
+ actualMin = parseIntFromUser(field.getName(), min);
if (!minInclusive) {
actualMin++;
}
@@ -72,7 +72,7 @@ public class IntPointField extends PointField implements IntValueFieldType {
if (max == null) {
actualMax = Integer.MAX_VALUE;
} else {
- actualMax = Integer.parseInt(max);
+ actualMax = parseIntFromUser(field.getName(), max);
if (!maxInclusive) {
actualMax--;
}
@@ -97,7 +97,7 @@ public class IntPointField extends PointField implements IntValueFieldType {
@Override
protected Query getExactQuery(SchemaField field, String externalVal) {
- return IntPoint.newExactQuery(field.getName(), Integer.parseInt(externalVal));
+ return IntPoint.newExactQuery(field.getName(), parseIntFromUser(field.getName(), externalVal));
}
@Override
@@ -109,7 +109,7 @@ public class IntPointField extends PointField implements IntValueFieldType {
int[] values = new int[externalVal.size()];
int i = 0;
for (String val:externalVal) {
- values[i] = Integer.parseInt(val);
+ values[i] = parseIntFromUser(field.getName(), val);
i++;
}
return IntPoint.newSetQuery(field.getName(), values);
@@ -124,7 +124,7 @@ public class IntPointField extends PointField implements IntValueFieldType {
public void readableToIndexed(CharSequence val, BytesRefBuilder result) {
result.grow(Integer.BYTES);
result.setLength(Integer.BYTES);
- IntPoint.encodeDimension(Integer.parseInt(val.toString()), result.bytes(), 0);
+ IntPoint.encodeDimension(parseIntFromUser(null, val.toString()), result.bytes(), 0);
}
@Override
diff --git a/solr/core/src/java/org/apache/solr/schema/LongPointField.java b/solr/core/src/java/org/apache/solr/schema/LongPointField.java
index bef6c472346..b2d8a3a03b4 100644
--- a/solr/core/src/java/org/apache/solr/schema/LongPointField.java
+++ b/solr/core/src/java/org/apache/solr/schema/LongPointField.java
@@ -63,7 +63,7 @@ public class LongPointField extends PointField implements LongValueFieldType {
if (min == null) {
actualMin = Long.MIN_VALUE;
} else {
- actualMin = Long.parseLong(min);
+ actualMin = parseLongFromUser(field.getName(), min);
if (!minInclusive) {
actualMin++;
}
@@ -71,7 +71,7 @@ public class LongPointField extends PointField implements LongValueFieldType {
if (max == null) {
actualMax = Long.MAX_VALUE;
} else {
- actualMax = Long.parseLong(max);
+ actualMax = parseLongFromUser(field.getName(), max);
if (!maxInclusive) {
actualMax--;
}
@@ -96,7 +96,7 @@ public class LongPointField extends PointField implements LongValueFieldType {
@Override
protected Query getExactQuery(SchemaField field, String externalVal) {
- return LongPoint.newExactQuery(field.getName(), Long.parseLong(externalVal));
+ return LongPoint.newExactQuery(field.getName(), parseLongFromUser(field.getName(), externalVal));
}
@Override
@@ -108,7 +108,7 @@ public class LongPointField extends PointField implements LongValueFieldType {
long[] values = new long[externalVal.size()];
int i = 0;
for (String val:externalVal) {
- values[i] = Long.parseLong(val);
+ values[i] = parseLongFromUser(field.getName(), val);
i++;
}
return LongPoint.newSetQuery(field.getName(), values);
@@ -123,7 +123,7 @@ public class LongPointField extends PointField implements LongValueFieldType {
public void readableToIndexed(CharSequence val, BytesRefBuilder result) {
result.grow(Long.BYTES);
result.setLength(Long.BYTES);
- LongPoint.encodeDimension(Long.parseLong(val.toString()), result.bytes(), 0);
+ LongPoint.encodeDimension(parseLongFromUser(null, val.toString()), result.bytes(), 0);
}
@Override
diff --git a/solr/core/src/java/org/apache/solr/schema/NumericFieldType.java b/solr/core/src/java/org/apache/solr/schema/NumericFieldType.java
index 6cda9ca6438..cf17aaf7480 100644
--- a/solr/core/src/java/org/apache/solr/schema/NumericFieldType.java
+++ b/solr/core/src/java/org/apache/solr/schema/NumericFieldType.java
@@ -56,8 +56,8 @@ public abstract class NumericFieldType extends PrimitiveFieldType {
switch (getNumberType()) {
case INTEGER:
return numericDocValuesRangeQuery(field.getName(),
- min == null ? null : (long) Integer.parseInt(min),
- max == null ? null : (long) Integer.parseInt(max),
+ min == null ? null : (long) parseIntFromUser(field.getName(), min),
+ max == null ? null : (long) parseIntFromUser(field.getName(), max),
minInclusive, maxInclusive, field.multiValued());
case FLOAT:
if (field.multiValued()) {
@@ -67,8 +67,8 @@ public abstract class NumericFieldType extends PrimitiveFieldType {
}
case LONG:
return numericDocValuesRangeQuery(field.getName(),
- min == null ? null : Long.parseLong(min),
- max == null ? null : Long.parseLong(max),
+ min == null ? null : parseLongFromUser(field.getName(), min),
+ max == null ? null : parseLongFromUser(field.getName(),max),
minInclusive, maxInclusive, field.multiValued());
case DOUBLE:
if (field.multiValued()) {
@@ -90,8 +90,8 @@ public abstract class NumericFieldType extends PrimitiveFieldType {
Query query;
String fieldName = sf.getName();
- Number minVal = min == null ? null : getNumberType() == NumberType.FLOAT ? Float.parseFloat(min): Double.parseDouble(min);
- Number maxVal = max == null ? null : getNumberType() == NumberType.FLOAT ? Float.parseFloat(max): Double.parseDouble(max);
+ Number minVal = min == null ? null : getNumberType() == NumberType.FLOAT ? parseFloatFromUser(sf.getName(), min): parseDoubleFromUser(sf.getName(), min);
+ Number maxVal = max == null ? null : getNumberType() == NumberType.FLOAT ? parseFloatFromUser(sf.getName(), max): parseDoubleFromUser(sf.getName(), max);
Long minBits =
min == null ? null : getNumberType() == NumberType.FLOAT ? (long) Float.floatToIntBits(minVal.floatValue()): Double.doubleToLongBits(minVal.doubleValue());
@@ -124,14 +124,14 @@ public abstract class NumericFieldType extends PrimitiveFieldType {
}
protected Query getRangeQueryForMultiValuedDoubleDocValues(SchemaField sf, String min, String max, boolean minInclusive, boolean maxInclusive) {
- Long minBits = min == null ? NumericUtils.doubleToSortableLong(Double.NEGATIVE_INFINITY): NumericUtils.doubleToSortableLong(Double.parseDouble(min));
- Long maxBits = max == null ? NumericUtils.doubleToSortableLong(Double.POSITIVE_INFINITY): NumericUtils.doubleToSortableLong(Double.parseDouble(max));
+ Long minBits = min == null ? NumericUtils.doubleToSortableLong(Double.NEGATIVE_INFINITY): NumericUtils.doubleToSortableLong(parseDoubleFromUser(sf.getName(), min));
+ Long maxBits = max == null ? NumericUtils.doubleToSortableLong(Double.POSITIVE_INFINITY): NumericUtils.doubleToSortableLong(parseDoubleFromUser(sf.getName(), max));
return numericDocValuesRangeQuery(sf.getName(), minBits, maxBits, minInclusive, maxInclusive, true);
}
protected Query getRangeQueryForMultiValuedFloatDocValues(SchemaField sf, String min, String max, boolean minInclusive, boolean maxInclusive) {
- Long minBits = (long)(min == null ? NumericUtils.floatToSortableInt(Float.NEGATIVE_INFINITY): NumericUtils.floatToSortableInt(Float.parseFloat(min)));
- Long maxBits = (long)(max == null ? NumericUtils.floatToSortableInt(Float.POSITIVE_INFINITY): NumericUtils.floatToSortableInt(Float.parseFloat(max)));
+ Long minBits = (long)(min == null ? NumericUtils.floatToSortableInt(Float.NEGATIVE_INFINITY): NumericUtils.floatToSortableInt(parseFloatFromUser(sf.getName(), min)));
+ Long maxBits = (long)(max == null ? NumericUtils.floatToSortableInt(Float.POSITIVE_INFINITY): NumericUtils.floatToSortableInt(parseFloatFromUser(sf.getName(), max)));
return numericDocValuesRangeQuery(sf.getName(), minBits, maxBits, minInclusive, maxInclusive, true);
}
@@ -169,4 +169,72 @@ public abstract class NumericFieldType extends PrimitiveFieldType {
return NumericDocValuesField.newRangeQuery(field, actualLowerValue, actualUpperValue);
}
}
+
+ /**
+ * Wrapper for {@link Long#parseLong(String)} that throws a BAD_REQUEST error if the input is not valid
+ * @param fieldName used in any exception, may be null
+ * @param val string to parse, NPE if null
+ */
+ static long parseLongFromUser(String fieldName, String val) {
+ if (val == null) {
+ throw new NullPointerException("Invalid input" + (null == fieldName ? "" : " for field " + fieldName));
+ }
+ try {
+ return Long.parseLong(val);
+ } catch (NumberFormatException e) {
+ String msg = "Invalid Number: " + val + (null == fieldName ? "" : " for field " + fieldName);
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, msg);
+ }
+ }
+
+ /**
+ * Wrapper for {@link Integer#parseInt(String)} that throws a BAD_REQUEST error if the input is not valid
+ * @param fieldName used in any exception, may be null
+ * @param val string to parse, NPE if null
+ */
+ static int parseIntFromUser(String fieldName, String val) {
+ if (val == null) {
+ throw new NullPointerException("Invalid input" + (null == fieldName ? "" : " for field " + fieldName));
+ }
+ try {
+ return Integer.parseInt(val);
+ } catch (NumberFormatException e) {
+ String msg = "Invalid Number: " + val + (null == fieldName ? "" : " for field " + fieldName);
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, msg);
+ }
+ }
+
+ /**
+ * Wrapper for {@link Double#parseDouble(String)} that throws a BAD_REQUEST error if the input is not valid
+ * @param fieldName used in any exception, may be null
+ * @param val string to parse, NPE if null
+ */
+ static double parseDoubleFromUser(String fieldName, String val) {
+ if (val == null) {
+ throw new NullPointerException("Invalid input" + (null == fieldName ? "" : " for field " + fieldName));
+ }
+ try {
+ return Double.parseDouble(val);
+ } catch (NumberFormatException e) {
+ String msg = "Invalid Number: " + val + (null == fieldName ? "" : " for field " + fieldName);
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, msg);
+ }
+ }
+
+ /**
+ * Wrapper for {@link Float#parseFloat(String)} that throws a BAD_REQUEST error if the input is not valid
+ * @param fieldName used in any exception, may be null
+ * @param val string to parse, NPE if null
+ */
+ static float parseFloatFromUser(String fieldName, String val) {
+ if (val == null) {
+ throw new NullPointerException("Invalid input" + (null == fieldName ? "" : " for field " + fieldName));
+ }
+ try {
+ return Float.parseFloat(val);
+ } catch (NumberFormatException e) {
+ String msg = "Invalid Number: " + val + (null == fieldName ? "" : " for field " + fieldName);
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, msg);
+ }
+ }
}
diff --git a/solr/core/src/java/org/apache/solr/schema/PointField.java b/solr/core/src/java/org/apache/solr/schema/PointField.java
index cad3c7e9706..98105af2ec8 100644
--- a/solr/core/src/java/org/apache/solr/schema/PointField.java
+++ b/solr/core/src/java/org/apache/solr/schema/PointField.java
@@ -127,7 +127,7 @@ public abstract class PointField extends NumericFieldType {
return new IndexOrDocValuesQuery(pointsQuery, dvQuery);
} else {
return getExactQuery(field, externalVal);
- }
+ }
}
protected abstract Query getExactQuery(SchemaField field, String externalVal);
@@ -191,6 +191,11 @@ public abstract class PointField extends NumericFieldType {
protected abstract String indexedToReadable(BytesRef indexedForm);
+ @Override
+ public Query getPrefixQuery(QParser parser, SchemaField sf, String termStr) {
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Can't run prefix queries on numeric fields");
+ }
+
protected boolean isFieldUsed(SchemaField field) {
boolean indexed = field.indexed();
boolean stored = field.stored();
diff --git a/solr/core/src/java/org/apache/solr/schema/TrieField.java b/solr/core/src/java/org/apache/solr/schema/TrieField.java
index f90877cbfdd..998583b449e 100644
--- a/solr/core/src/java/org/apache/solr/schema/TrieField.java
+++ b/solr/core/src/java/org/apache/solr/schema/TrieField.java
@@ -344,7 +344,7 @@ public class TrieField extends NumericFieldType {
}
int ps = precisionStep;
Query query;
-
+
if (field.hasDocValues() && !field.indexed()) {
return getDocValuesRangeQuery(parser, field, min, max, minInclusive, maxInclusive);
}
@@ -352,26 +352,26 @@ public class TrieField extends NumericFieldType {
switch (type) {
case INTEGER:
query = LegacyNumericRangeQuery.newIntRange(field.getName(), ps,
- min == null ? null : Integer.parseInt(min),
- max == null ? null : Integer.parseInt(max),
+ min == null ? null : parseIntFromUser(field.getName(), min),
+ max == null ? null : parseIntFromUser(field.getName(), max),
minInclusive, maxInclusive);
break;
case FLOAT:
query = LegacyNumericRangeQuery.newFloatRange(field.getName(), ps,
- min == null ? null : Float.parseFloat(min),
- max == null ? null : Float.parseFloat(max),
+ min == null ? null : parseFloatFromUser(field.getName(), min),
+ max == null ? null : parseFloatFromUser(field.getName(), max),
minInclusive, maxInclusive);
break;
case LONG:
query = LegacyNumericRangeQuery.newLongRange(field.getName(), ps,
- min == null ? null : Long.parseLong(min),
- max == null ? null : Long.parseLong(max),
+ min == null ? null : parseLongFromUser(field.getName(), min),
+ max == null ? null : parseLongFromUser(field.getName(), max),
minInclusive, maxInclusive);
break;
case DOUBLE:
query = LegacyNumericRangeQuery.newDoubleRange(field.getName(), ps,
- min == null ? null : Double.parseDouble(min),
- max == null ? null : Double.parseDouble(max),
+ min == null ? null : parseDoubleFromUser(field.getName(), min),
+ max == null ? null : parseDoubleFromUser(field.getName(), max),
minInclusive, maxInclusive);
break;
case DATE:
@@ -383,7 +383,6 @@ public class TrieField extends NumericFieldType {
default:
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field");
}
-
return query;
}
@@ -413,29 +412,24 @@ public class TrieField extends NumericFieldType {
@Override
public void readableToIndexed(CharSequence val, BytesRefBuilder result) {
String s = val.toString();
- try {
- switch (type) {
- case INTEGER:
- LegacyNumericUtils.intToPrefixCoded(Integer.parseInt(s), 0, result);
- break;
- case FLOAT:
- LegacyNumericUtils.intToPrefixCoded(NumericUtils.floatToSortableInt(Float.parseFloat(s)), 0, result);
- break;
- case LONG:
- LegacyNumericUtils.longToPrefixCoded(Long.parseLong(s), 0, result);
- break;
- case DOUBLE:
- LegacyNumericUtils.longToPrefixCoded(NumericUtils.doubleToSortableLong(Double.parseDouble(s)), 0, result);
- break;
- case DATE:
- LegacyNumericUtils.longToPrefixCoded(DateMathParser.parseMath(null, s).getTime(), 0, result);
- break;
- default:
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + type);
- }
- } catch (NumberFormatException nfe) {
- throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
- "Invalid Number: " + val);
+ switch (type) {
+ case INTEGER:
+ LegacyNumericUtils.intToPrefixCoded(parseIntFromUser(null, s), 0, result);
+ break;
+ case FLOAT:
+ LegacyNumericUtils.intToPrefixCoded(NumericUtils.floatToSortableInt(parseFloatFromUser(null, s)), 0, result);
+ break;
+ case LONG:
+ LegacyNumericUtils.longToPrefixCoded(parseLongFromUser(null, s), 0, result);
+ break;
+ case DOUBLE:
+ LegacyNumericUtils.longToPrefixCoded(NumericUtils.doubleToSortableLong(parseDoubleFromUser(null, s)), 0, result);
+ break;
+ case DATE:
+ LegacyNumericUtils.longToPrefixCoded(DateMathParser.parseMath(null, s).getTime(), 0, result);
+ break;
+ default:
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + type);
}
}
diff --git a/solr/core/src/java/org/apache/solr/update/AddUpdateCommand.java b/solr/core/src/java/org/apache/solr/update/AddUpdateCommand.java
index 596ddd3d418..cb1af9a29f2 100644
--- a/solr/core/src/java/org/apache/solr/update/AddUpdateCommand.java
+++ b/solr/core/src/java/org/apache/solr/update/AddUpdateCommand.java
@@ -191,7 +191,7 @@ public class AddUpdateCommand extends UpdateCommand implements Iterable flatten(SolrInputDocument root) {
List unwrappedDocs = new ArrayList<>();
recUnwrapp(unwrappedDocs, root);
+ if (1 < unwrappedDocs.size() && ! req.getSchema().isUsableForChildDocs()) {
+ throw new SolrException
+ (SolrException.ErrorCode.BAD_REQUEST, "Unable to index docs with children: the schema must " +
+ "include definitions for both a uniqueKey field and the '" + IndexSchema.ROOT_FIELD_NAME +
+ "' field, using the exact same fieldType");
+ }
return unwrappedDocs;
}
diff --git a/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java b/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java
index e4811091c73..3efb748fd02 100644
--- a/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java
+++ b/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java
@@ -55,6 +55,7 @@ import org.apache.solr.request.LocalSolrQueryRequest;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestInfo;
import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.search.FunctionRangeQuery;
import org.apache.solr.search.QParser;
@@ -394,7 +395,7 @@ public class DirectUpdateHandler2 extends UpdateHandler implements SolrCoreState
}
private Term getIdTerm(AddUpdateCommand cmd) {
- return new Term(cmd.isBlock() ? "_root_" : idField.getName(), cmd.getIndexedId());
+ return new Term(cmd.isBlock() ? IndexSchema.ROOT_FIELD_NAME : idField.getName(), cmd.getIndexedId());
}
private void updateDeleteTrackers(DeleteUpdateCommand cmd) {
diff --git a/solr/core/src/java/org/apache/solr/update/DocumentBuilder.java b/solr/core/src/java/org/apache/solr/update/DocumentBuilder.java
index b97af3bcb05..58638ae63c4 100644
--- a/solr/core/src/java/org/apache/solr/update/DocumentBuilder.java
+++ b/solr/core/src/java/org/apache/solr/update/DocumentBuilder.java
@@ -176,8 +176,8 @@ public class DocumentBuilder {
// check if the copy field is a multivalued or not
if (!destinationField.multiValued() && destHasValues) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
- "ERROR: "+getID(doc, schema)+"multiple values encountered for non multiValued copy field " +
- destinationField.getName() + ": " + v);
+ "Multiple values encountered for non multiValued copy field " +
+ destinationField.getName() + ": " + v);
}
used = true;
@@ -198,7 +198,9 @@ public class DocumentBuilder {
}
}
catch( SolrException ex ) {
- throw ex;
+ throw new SolrException(SolrException.ErrorCode.getErrorCode(ex.code()),
+ "ERROR: "+getID(doc, schema)+"Error adding field '" +
+ field.getName() + "'='" +field.getValue()+"' msg=" + ex.getMessage(), ex );
}
catch( Exception ex ) {
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,
diff --git a/solr/core/src/test-files/solr/collection1/conf/bad-schema-uniquekey-diff-type-dynamic-root.xml b/solr/core/src/test-files/solr/collection1/conf/bad-schema-uniquekey-diff-type-dynamic-root.xml
new file mode 100644
index 00000000000..4f06bd1270c
--- /dev/null
+++ b/solr/core/src/test-files/solr/collection1/conf/bad-schema-uniquekey-diff-type-dynamic-root.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+ id
+
+
+
+
+
+
+
+
diff --git a/solr/core/src/test-files/solr/collection1/conf/bad-schema-uniquekey-diff-type-root.xml b/solr/core/src/test-files/solr/collection1/conf/bad-schema-uniquekey-diff-type-root.xml
new file mode 100644
index 00000000000..377762d0443
--- /dev/null
+++ b/solr/core/src/test-files/solr/collection1/conf/bad-schema-uniquekey-diff-type-root.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+ id
+
+
+
+
+
+
+
diff --git a/solr/core/src/test-files/solr/collection1/conf/schema.xml b/solr/core/src/test-files/solr/collection1/conf/schema.xml
index 05145f9464f..f0ddae44c46 100644
--- a/solr/core/src/test-files/solr/collection1/conf/schema.xml
+++ b/solr/core/src/test-files/solr/collection1/conf/schema.xml
@@ -734,11 +734,25 @@
useDocValuesAsStored="true"/>
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/solr/core/src/test-files/solr/collection1/conf/schema11.xml b/solr/core/src/test-files/solr/collection1/conf/schema11.xml
index caa24cc2f25..819b6d1b9eb 100644
--- a/solr/core/src/test-files/solr/collection1/conf/schema11.xml
+++ b/solr/core/src/test-files/solr/collection1/conf/schema11.xml
@@ -413,6 +413,10 @@ valued. -->
+
+
+
+
diff --git a/solr/core/src/test-files/solr/collection1/conf/schema12.xml b/solr/core/src/test-files/solr/collection1/conf/schema12.xml
index 214fc26c65b..22363952b1a 100644
--- a/solr/core/src/test-files/solr/collection1/conf/schema12.xml
+++ b/solr/core/src/test-files/solr/collection1/conf/schema12.xml
@@ -604,16 +604,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
diff --git a/solr/core/src/test-files/solr/configsets/cloud-hdfs/conf/schema.xml b/solr/core/src/test-files/solr/configsets/cloud-hdfs/conf/schema.xml
index aab5e811110..7b8b690d395 100644
--- a/solr/core/src/test-files/solr/configsets/cloud-hdfs/conf/schema.xml
+++ b/solr/core/src/test-files/solr/configsets/cloud-hdfs/conf/schema.xml
@@ -22,7 +22,7 @@
-
+
id
diff --git a/solr/core/src/test-files/solr/configsets/cloud-managed-preanalyzed/conf/managed-schema b/solr/core/src/test-files/solr/configsets/cloud-managed-preanalyzed/conf/managed-schema
index e70e02b36f8..39e142e2417 100644
--- a/solr/core/src/test-files/solr/configsets/cloud-managed-preanalyzed/conf/managed-schema
+++ b/solr/core/src/test-files/solr/configsets/cloud-managed-preanalyzed/conf/managed-schema
@@ -35,7 +35,7 @@
-
+
id
diff --git a/solr/core/src/test-files/solr/configsets/cloud-managed-upgrade/conf/schema.xml b/solr/core/src/test-files/solr/configsets/cloud-managed-upgrade/conf/schema.xml
index b9f09f9a50b..1d97a2ac5eb 100644
--- a/solr/core/src/test-files/solr/configsets/cloud-managed-upgrade/conf/schema.xml
+++ b/solr/core/src/test-files/solr/configsets/cloud-managed-upgrade/conf/schema.xml
@@ -21,7 +21,7 @@
-
+
id
diff --git a/solr/core/src/test-files/solr/configsets/cloud-managed/conf/managed-schema b/solr/core/src/test-files/solr/configsets/cloud-managed/conf/managed-schema
index b9f09f9a50b..1d97a2ac5eb 100644
--- a/solr/core/src/test-files/solr/configsets/cloud-managed/conf/managed-schema
+++ b/solr/core/src/test-files/solr/configsets/cloud-managed/conf/managed-schema
@@ -21,7 +21,7 @@
-
+
id
diff --git a/solr/core/src/test-files/solr/configsets/cloud-minimal-jmx/conf/schema.xml b/solr/core/src/test-files/solr/configsets/cloud-minimal-jmx/conf/schema.xml
index aab5e811110..7b8b690d395 100644
--- a/solr/core/src/test-files/solr/configsets/cloud-minimal-jmx/conf/schema.xml
+++ b/solr/core/src/test-files/solr/configsets/cloud-minimal-jmx/conf/schema.xml
@@ -22,7 +22,7 @@
-
+
id
diff --git a/solr/core/src/test-files/solr/configsets/cloud-minimal/conf/schema.xml b/solr/core/src/test-files/solr/configsets/cloud-minimal/conf/schema.xml
index aab5e811110..7b8b690d395 100644
--- a/solr/core/src/test-files/solr/configsets/cloud-minimal/conf/schema.xml
+++ b/solr/core/src/test-files/solr/configsets/cloud-minimal/conf/schema.xml
@@ -22,7 +22,7 @@
-
+
id
diff --git a/solr/core/src/test-files/solr/configsets/cloud-subdirs/conf/schema.xml b/solr/core/src/test-files/solr/configsets/cloud-subdirs/conf/schema.xml
index aab5e811110..7b8b690d395 100644
--- a/solr/core/src/test-files/solr/configsets/cloud-subdirs/conf/schema.xml
+++ b/solr/core/src/test-files/solr/configsets/cloud-subdirs/conf/schema.xml
@@ -22,7 +22,7 @@
-
+
id
diff --git a/solr/core/src/test-files/solr/configsets/exitable-directory/conf/schema.xml b/solr/core/src/test-files/solr/configsets/exitable-directory/conf/schema.xml
index aab5e811110..7b8b690d395 100644
--- a/solr/core/src/test-files/solr/configsets/exitable-directory/conf/schema.xml
+++ b/solr/core/src/test-files/solr/configsets/exitable-directory/conf/schema.xml
@@ -22,7 +22,7 @@
-
+
id
diff --git a/solr/core/src/test/org/apache/solr/BasicFunctionalityTest.java b/solr/core/src/test/org/apache/solr/BasicFunctionalityTest.java
index 02ae888bd7d..b1747fd9afb 100644
--- a/solr/core/src/test/org/apache/solr/BasicFunctionalityTest.java
+++ b/solr/core/src/test/org/apache/solr/BasicFunctionalityTest.java
@@ -341,6 +341,59 @@ public class BasicFunctionalityTest extends SolrTestCaseJ4 {
);
}
+ @Test
+ public void testClientErrorOnMalformedDate() throws Exception {
+ final String BAD_VALUE = "NOT_A_DATE";
+ ignoreException(BAD_VALUE);
+
+ final List FIELDS = new LinkedList<>();
+ for (String type : new String[] {
+ "tdt", "tdt1", "tdtdv", "tdtdv1",
+ "dt_dv", "dt_dvo", "dt", "dt1", "dt_os"
+ }) {
+ FIELDS.add("malformed_" + type);
+ }
+
+ // test that malformed numerics cause client error not server error
+ for (String field : FIELDS) {
+ try {
+ h.update(add( doc("id","100", field, BAD_VALUE)));
+ fail("Didn't encounter an error trying to add a bad date: " + field);
+ } catch (SolrException e) {
+ String msg = e.toString();
+ assertTrue("not an (update) client error on field: " + field +" : "+ msg,
+ 400 <= e.code() && e.code() < 500);
+ assertTrue("(update) client error does not mention bad value: " + msg,
+ msg.contains(BAD_VALUE));
+ assertTrue("client error does not mention document id: " + msg,
+ msg.contains("[doc=100]"));
+ }
+ SchemaField sf = h.getCore().getLatestSchema().getField(field);
+ if (!sf.hasDocValues() && !sf.indexed()) {
+ continue;
+ }
+ try {
+ h.query(req("q",field + ":" + BAD_VALUE));
+ fail("Didn't encounter an error trying to query a bad date: " + field);
+ } catch (SolrException e) {
+ String msg = e.toString();
+ assertTrue("not a (search) client error on field: " + field +" : "+ msg,
+ 400 <= e.code() && e.code() < 500);
+ assertTrue("(search) client error does not mention bad value: " + msg,
+ msg.contains(BAD_VALUE));
+ }
+ try {
+ h.query(req("q",field + ":[NOW TO " + BAD_VALUE + "]"));
+ fail("Didn't encounter an error trying to query a bad date: " + field);
+ } catch (SolrException e) {
+ String msg = e.toString();
+ assertTrue("not a (search) client error on field: " + field +" : "+ msg,
+ 400 <= e.code() && e.code() < 500);
+ assertTrue("(search) client error does not mention bad value: " + msg,
+ msg.contains(BAD_VALUE));
+ }
+ }
+ }
@Test
public void testClientErrorOnMalformedNumbers() throws Exception {
@@ -349,7 +402,13 @@ public class BasicFunctionalityTest extends SolrTestCaseJ4 {
ignoreException(BAD_VALUE);
final List FIELDS = new LinkedList<>();
- for (String type : new String[] { "ti", "tf", "td", "tl" }) {
+ for (String type : new String[] {
+ "ti", "tf", "td", "tl",
+ "i", "f", "d", "l",
+ "i_dv", "f_dv", "d_dv", "l_dv",
+ "i_dvo", "f_dvo", "d_dvo", "l_dvo",
+ "i_os", "f_os", "d_os", "l_os"
+ }) {
FIELDS.add("malformed_" + type);
}
@@ -364,6 +423,12 @@ public class BasicFunctionalityTest extends SolrTestCaseJ4 {
400 <= e.code() && e.code() < 500);
assertTrue("(update) client error does not mention bad value: " + msg,
msg.contains(BAD_VALUE));
+ assertTrue("client error does not mention document id",
+ msg.contains("[doc=100]"));
+ }
+ SchemaField sf = h.getCore().getLatestSchema().getField(field);
+ if (!sf.hasDocValues() && !sf.indexed()) {
+ continue;
}
try {
h.query(req("q",field + ":" + BAD_VALUE));
@@ -375,6 +440,16 @@ public class BasicFunctionalityTest extends SolrTestCaseJ4 {
assertTrue("(search) client error does not mention bad value: " + msg,
msg.contains(BAD_VALUE));
}
+ try {
+ h.query(req("q",field + ":[10 TO " + BAD_VALUE + "]"));
+ fail("Didn't encounter an error trying to query a non-number: " + field);
+ } catch (SolrException e) {
+ String msg = e.toString();
+ assertTrue("not a (search) client error on field: " + field +" : "+ msg,
+ 400 <= e.code() && e.code() < 500);
+ assertTrue("(search) client error does not mention bad value: " + msg,
+ msg.contains(BAD_VALUE));
+ }
}
}
diff --git a/solr/core/src/test/org/apache/solr/schema/BadIndexSchemaTest.java b/solr/core/src/test/org/apache/solr/schema/BadIndexSchemaTest.java
index 5545d959026..f635436fa83 100644
--- a/solr/core/src/test/org/apache/solr/schema/BadIndexSchemaTest.java
+++ b/solr/core/src/test/org/apache/solr/schema/BadIndexSchemaTest.java
@@ -101,6 +101,21 @@ public class BadIndexSchemaTest extends AbstractBadConfigTestBase {
public void testDocValuesUnsupported() throws Exception {
doTest("bad-schema-unsupported-docValues.xml", "does not support doc values");
}
+
+ public void testRootTypeMissmatchWithUniqueKey() throws Exception {
+ doTest("bad-schema-uniquekey-diff-type-root.xml",
+ "using the exact same fieldType as the uniqueKey field (id) uses: string1");
+ }
+
+ public void testRootTypeDynamicMissmatchWithUniqueKey() throws Exception {
+ // in this case, the core should load fine -- but we should get an error adding docs
+ try {
+ initCore("solrconfig.xml","bad-schema-uniquekey-diff-type-dynamic-root.xml");
+ assertFailedU("Unable to index docs with children", adoc(sdocWithChildren("1","-1")));
+ } finally {
+ deleteCore();
+ }
+ }
public void testSweetSpotSimBadConfig() throws Exception {
doTest("bad-schema-sweetspot-both-tf.xml", "Can not mix");
diff --git a/solr/core/src/test/org/apache/solr/search/TestQueryTypes.java b/solr/core/src/test/org/apache/solr/search/TestQueryTypes.java
index 3f57ee5da0c..f282c3f2171 100644
--- a/solr/core/src/test/org/apache/solr/search/TestQueryTypes.java
+++ b/solr/core/src/test/org/apache/solr/search/TestQueryTypes.java
@@ -20,6 +20,7 @@ import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.util.AbstractSolrTestCase;
import org.junit.BeforeClass;
+import org.junit.Test;
public class TestQueryTypes extends AbstractSolrTestCase {
@@ -209,7 +210,6 @@ public class TestQueryTypes extends AbstractSolrTestCase {
,"//result[@numFound='2']"
);
-
//
// test escapes in quoted strings
//
@@ -436,5 +436,49 @@ public class TestQueryTypes extends AbstractSolrTestCase {
assertQ("Test text field with no analysis doesn't NPE with wildcards (SOLR-4318)",
req("q", "text_no_analyzer:should*"), "//result[@numFound='1']");
+
+ }
+
+ @Test
+ public void testNumericBadRequests() {
+ String[] suffixes = new String[50];
+ int fieldNum = 0;
+ for (String type:new String[]{"i", "l", "f", "d", "dt"}) {
+ for (String s:new String[]{"", "s"}) {
+ //Trie
+ suffixes[fieldNum++] = "t" + type + s;
+ suffixes[fieldNum++] = "t" + type + s + "_dv";
+ suffixes[fieldNum++] = "t" + type + s + "_ni_dv";
+
+ //Points
+ suffixes[fieldNum++] = type + s + "_p";
+ suffixes[fieldNum++] = type + s + "_ni_p";
+ }
+ }
+ assertEquals(fieldNum,suffixes.length);
+
+ String badNumber = "NOT_A_NUMBER";
+ for (String suffix:suffixes) {
+ // Numeric bad requests
+ assertQEx("Expecting exception for suffix: " + suffix, badNumber, req("q","{!term f=foo_" + suffix + "}" + badNumber), SolrException.ErrorCode.BAD_REQUEST);
+ assertQEx("Expecting exception for suffix: " + suffix, badNumber, req("q","{!terms f=foo_" + suffix + "}1 2 3 4 5 " + badNumber), SolrException.ErrorCode.BAD_REQUEST);
+ assertQEx("Expecting exception for suffix: " + suffix, badNumber, req("q","{!lucene}foo_" + suffix + ":" + badNumber), SolrException.ErrorCode.BAD_REQUEST);
+ assertQEx("Expecting exception for suffix: " + suffix, badNumber, req("q","{!field f=foo_" + suffix + "}" + badNumber), SolrException.ErrorCode.BAD_REQUEST);
+ assertQEx("Expecting exception for suffix: " + suffix, badNumber, req("q","{!maxscore}foo_" + suffix + ":" + badNumber), SolrException.ErrorCode.BAD_REQUEST);
+ assertQEx("Expecting exception for suffix: " + suffix, badNumber,
+ req("q","{!xmlparser}"), SolrException.ErrorCode.BAD_REQUEST);
+ if (suffix.contains("_p")) {
+ // prefix queries work in Trie fields
+ assertQEx("Expecting exception for suffix: " + suffix, "Can't run prefix queries on numeric fields",
+ req("q","{!prefix f=foo_" + suffix + "}NOT_A_NUMBER"), SolrException.ErrorCode.BAD_REQUEST);
+ assertQEx("Expecting exception for suffix: " + suffix, "Can't run prefix queries on numeric fields",
+ req("q","{!lucene}foo_" + suffix + ":123*"), SolrException.ErrorCode.BAD_REQUEST);
+ }
+
+ // Skipping: func, boost, raw, nested, frange, spatial*, join, surround, switch, parent, child, collapsing,
+ // complexphrase, rerank, export, mlt, hash, graph, graphTerms, igain, tlogit, sigificantTerms, payload*
+ // Maybe add: raw, join, parent, child, collapsing, graphTerms, igain, sigificantTerms, simple
+ }
+
}
}
diff --git a/solr/core/src/test/org/apache/solr/search/TestSolrQueryParser.java b/solr/core/src/test/org/apache/solr/search/TestSolrQueryParser.java
index 1a2e5720822..4b82e6243d3 100644
--- a/solr/core/src/test/org/apache/solr/search/TestSolrQueryParser.java
+++ b/solr/core/src/test/org/apache/solr/search/TestSolrQueryParser.java
@@ -33,6 +33,7 @@ import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermInSetQuery;
import org.apache.lucene.search.TermQuery;
import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.MapSolrParams;
import org.apache.solr.metrics.MetricsMap;
import org.apache.solr.common.params.ModifiableSolrParams;
@@ -1038,4 +1039,39 @@ public class TestSolrQueryParser extends SolrTestCaseJ4 {
, "/response/numFound==1"
);
}
+
+ @Test
+ public void testBadRequestInSetQuery() throws SyntaxError {
+ SolrQueryRequest req = req();
+ QParser qParser;
+ String[] fieldSuffix = new String[] {
+ "ti", "tf", "td", "tl",
+ "i", "f", "d", "l",
+ "is", "fs", "ds", "ls",
+ "i_dv", "f_dv", "d_dv", "l_dv",
+ "is_dv", "fs_dv", "ds_dv", "ls_dv",
+ "i_dvo", "f_dvo", "d_dvo", "l_dvo",
+ };
+
+ for (String suffix:fieldSuffix) {
+ //Good queries
+ qParser = QParser.getParser("foo_" + suffix + ":(1 2 3 4 5 6 7 8 9 10 20 19 18 17 16 15 14 13 12 25)", req);
+ qParser.setIsFilter(true);
+ qParser.getQuery();
+ }
+
+ for (String suffix:fieldSuffix) {
+ qParser = QParser.getParser("foo_" + suffix + ":(1 2 3 4 5 6 7 8 9 10 20 19 18 17 16 15 14 13 12 NOT_A_NUMBER)", req);
+ qParser.setIsFilter(true); // this may change in the future
+ try {
+ qParser.getQuery();
+ fail("Expecting exception");
+ } catch (SolrException e) {
+ assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, e.code());
+ assertTrue("Unexpected exception: " + e.getMessage(), e.getMessage().contains("Invalid Number: NOT_A_NUMBER"));
+ }
+ }
+
+
+ }
}
\ No newline at end of file