diff --git a/dev-tools/maven/pom.xml.template b/dev-tools/maven/pom.xml.template
index 57ac6da0e77..fb32c87ca05 100644
--- a/dev-tools/maven/pom.xml.template
+++ b/dev-tools/maven/pom.xml.template
@@ -182,6 +182,11 @@
commons-io${commons-io.version}
+
+ joda-time
+ joda-time
+ 2.2
+ org.apache.httpcomponentshttpclient
diff --git a/dev-tools/maven/solr/core/src/java/pom.xml.template b/dev-tools/maven/solr/core/src/java/pom.xml.template
index 7255b439358..d22f524c034 100644
--- a/dev-tools/maven/solr/core/src/java/pom.xml.template
+++ b/dev-tools/maven/solr/core/src/java/pom.xml.template
@@ -136,6 +136,10 @@
commons-fileuploadcommons-fileupload
+
+ joda-time
+ joda-time
+ org.restlet.jeeorg.restlet
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index ae64994a227..190b25b5622 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -120,6 +120,18 @@ New Features
* SOLR-4916: Add support to write and read Solr index files and transaction log
files to and from HDFS. (phunt, Mark Miller, Greg Chanan)
+
+* SOLR-4892: Add FieldMutatingUpdateProcessorFactory subclasses
+ Parse{Date,Integer,Long,Float,Double,Boolean}UpdateProcessorFactory. These
+ factories have a default selector that matches all fields that either don’t
+ match any schema field, or are in the schema with the corresponding
+ typeClass. If they see a value that is not a CharSequence, or can't parse
+ the value, they leave it as is. For multi-valued fields, these processors
+ will not convert any values unless all are first successfully parsed, or
+ already are instances of the target class. Ordering the processors, e.g.
+ [Boolean, Long, Double, Date] will allow e.g. values ["2", "5", "8.6"] to
+ be left alone by the Boolean and Long processors, but then converted by the
+ Double processor. (Steve Rowe, hossman)
Bug Fixes
----------------------
diff --git a/solr/core/ivy.xml b/solr/core/ivy.xml
index d028f35801d..b1d974b2960 100644
--- a/solr/core/ivy.xml
+++ b/solr/core/ivy.xml
@@ -35,6 +35,7 @@
+
diff --git a/solr/core/src/java/org/apache/solr/schema/DateField.java b/solr/core/src/java/org/apache/solr/schema/DateField.java
index 1001b350e45..faf9c4ff93f 100644
--- a/solr/core/src/java/org/apache/solr/schema/DateField.java
+++ b/solr/core/src/java/org/apache/solr/schema/DateField.java
@@ -111,7 +111,7 @@ import java.util.*;
* @see XML schema part 2
* @deprecated {@link TrieDateField} is recomended for all new schemas
*/
-public class DateField extends PrimitiveFieldType {
+public class DateField extends PrimitiveFieldType implements DateValueFieldType {
public static TimeZone UTC = TimeZone.getTimeZone("UTC");
diff --git a/solr/core/src/java/org/apache/solr/schema/DateValueFieldType.java b/solr/core/src/java/org/apache/solr/schema/DateValueFieldType.java
new file mode 100644
index 00000000000..c4e7984ec9c
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/schema/DateValueFieldType.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.solr.schema;
+
+/**
+ * Marker interface for Date-valued field types.
+ */
+public interface DateValueFieldType {
+}
diff --git a/solr/core/src/java/org/apache/solr/schema/DoubleField.java b/solr/core/src/java/org/apache/solr/schema/DoubleField.java
index c76b99c2592..550adae0a91 100644
--- a/solr/core/src/java/org/apache/solr/schema/DoubleField.java
+++ b/solr/core/src/java/org/apache/solr/schema/DoubleField.java
@@ -45,7 +45,7 @@ import org.apache.solr.search.QParser;
*
* @see TrieDoubleField
*/
-public class DoubleField extends PrimitiveFieldType {
+public class DoubleField extends PrimitiveFieldType implements DoubleValueFieldType {
private static final FieldCache.DoubleParser PARSER = new FieldCache.DoubleParser() {
diff --git a/solr/core/src/java/org/apache/solr/schema/DoubleValueFieldType.java b/solr/core/src/java/org/apache/solr/schema/DoubleValueFieldType.java
new file mode 100644
index 00000000000..ff9712e22db
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/schema/DoubleValueFieldType.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.solr.schema;
+
+/**
+ * Marker interface for double-valued field types.
+ */
+public interface DoubleValueFieldType extends NumericValueFieldType {
+}
diff --git a/solr/core/src/java/org/apache/solr/schema/FloatField.java b/solr/core/src/java/org/apache/solr/schema/FloatField.java
index 2c1ed4dbb44..7e23443852d 100644
--- a/solr/core/src/java/org/apache/solr/schema/FloatField.java
+++ b/solr/core/src/java/org/apache/solr/schema/FloatField.java
@@ -46,7 +46,7 @@ import java.io.IOException;
*
* @see TrieFloatField
*/
-public class FloatField extends PrimitiveFieldType {
+public class FloatField extends PrimitiveFieldType implements FloatValueFieldType {
private static final FieldCache.FloatParser PARSER = new FieldCache.FloatParser() {
diff --git a/solr/core/src/java/org/apache/solr/schema/FloatValueFieldType.java b/solr/core/src/java/org/apache/solr/schema/FloatValueFieldType.java
new file mode 100644
index 00000000000..5606caf9053
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/schema/FloatValueFieldType.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.solr.schema;
+
+/**
+ * Marker interface for float-valued field types.
+ */
+public interface FloatValueFieldType extends NumericValueFieldType {
+}
diff --git a/solr/core/src/java/org/apache/solr/schema/IntField.java b/solr/core/src/java/org/apache/solr/schema/IntField.java
index 85e3c3b3b7d..2b14867b8aa 100644
--- a/solr/core/src/java/org/apache/solr/schema/IntField.java
+++ b/solr/core/src/java/org/apache/solr/schema/IntField.java
@@ -46,7 +46,7 @@ import java.io.IOException;
*
* @see TrieIntField
*/
-public class IntField extends PrimitiveFieldType {
+public class IntField extends PrimitiveFieldType implements IntValueFieldType {
private static final FieldCache.IntParser PARSER = new FieldCache.IntParser() {
diff --git a/solr/core/src/java/org/apache/solr/schema/IntValueFieldType.java b/solr/core/src/java/org/apache/solr/schema/IntValueFieldType.java
new file mode 100644
index 00000000000..9cf81e17be5
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/schema/IntValueFieldType.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.solr.schema;
+
+/**
+ * Marker interface for int-valued field types.
+ */
+public interface IntValueFieldType extends NumericValueFieldType {
+}
+
diff --git a/solr/core/src/java/org/apache/solr/schema/LongField.java b/solr/core/src/java/org/apache/solr/schema/LongField.java
index 93f3389c2b1..5b18db8119e 100644
--- a/solr/core/src/java/org/apache/solr/schema/LongField.java
+++ b/solr/core/src/java/org/apache/solr/schema/LongField.java
@@ -46,7 +46,7 @@ import java.util.Map;
*
* @see TrieLongField
*/
-public class LongField extends PrimitiveFieldType {
+public class LongField extends PrimitiveFieldType implements LongValueFieldType {
private static final FieldCache.LongParser PARSER = new FieldCache.LongParser() {
diff --git a/solr/core/src/java/org/apache/solr/schema/LongValueFieldType.java b/solr/core/src/java/org/apache/solr/schema/LongValueFieldType.java
new file mode 100644
index 00000000000..55b8b5113da
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/schema/LongValueFieldType.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.solr.schema;
+
+/**
+ * Marker interface for long-valued field types.
+ */
+public interface LongValueFieldType extends NumericValueFieldType {
+}
diff --git a/solr/core/src/java/org/apache/solr/schema/NumericValueFieldType.java b/solr/core/src/java/org/apache/solr/schema/NumericValueFieldType.java
new file mode 100644
index 00000000000..e2238299f02
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/schema/NumericValueFieldType.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.solr.schema;
+
+/**
+ * Marker interface for numeric-valued field types.
+ */
+public interface NumericValueFieldType {
+}
diff --git a/solr/core/src/java/org/apache/solr/schema/SortableDoubleField.java b/solr/core/src/java/org/apache/solr/schema/SortableDoubleField.java
index 65436f4bd6f..2f4b0a65dda 100644
--- a/solr/core/src/java/org/apache/solr/schema/SortableDoubleField.java
+++ b/solr/core/src/java/org/apache/solr/schema/SortableDoubleField.java
@@ -54,7 +54,7 @@ import java.io.IOException;
* @deprecated use {@link DoubleField} or {@link TrieDoubleField} - will be removed in 5.x
*/
@Deprecated
-public class SortableDoubleField extends PrimitiveFieldType {
+public class SortableDoubleField extends PrimitiveFieldType implements DoubleValueFieldType {
@Override
public SortField getSortField(SchemaField field,boolean reverse) {
return getStringSort(field,reverse);
diff --git a/solr/core/src/java/org/apache/solr/schema/SortableFloatField.java b/solr/core/src/java/org/apache/solr/schema/SortableFloatField.java
index 69db7616b2f..e66e25563e7 100644
--- a/solr/core/src/java/org/apache/solr/schema/SortableFloatField.java
+++ b/solr/core/src/java/org/apache/solr/schema/SortableFloatField.java
@@ -55,7 +55,7 @@ import java.io.IOException;
* @deprecated use {@link FloatField} or {@link TrieFloatField} - will be removed in 5.x
*/
@Deprecated
-public class SortableFloatField extends PrimitiveFieldType {
+public class SortableFloatField extends PrimitiveFieldType implements FloatValueFieldType {
@Override
public SortField getSortField(SchemaField field,boolean reverse) {
return getStringSort(field,reverse);
diff --git a/solr/core/src/java/org/apache/solr/schema/SortableIntField.java b/solr/core/src/java/org/apache/solr/schema/SortableIntField.java
index cbcb913ea84..955857370f9 100644
--- a/solr/core/src/java/org/apache/solr/schema/SortableIntField.java
+++ b/solr/core/src/java/org/apache/solr/schema/SortableIntField.java
@@ -55,7 +55,7 @@ import java.io.IOException;
* @deprecated use {@link IntField} or {@link TrieIntField} - will be removed in 5.x
*/
@Deprecated
-public class SortableIntField extends PrimitiveFieldType {
+public class SortableIntField extends PrimitiveFieldType implements IntValueFieldType {
@Override
public SortField getSortField(SchemaField field,boolean reverse) {
return getStringSort(field,reverse);
diff --git a/solr/core/src/java/org/apache/solr/schema/TrieDateField.java b/solr/core/src/java/org/apache/solr/schema/TrieDateField.java
index 9012e54944b..0a652efb44c 100755
--- a/solr/core/src/java/org/apache/solr/schema/TrieDateField.java
+++ b/solr/core/src/java/org/apache/solr/schema/TrieDateField.java
@@ -55,7 +55,7 @@ import java.io.IOException;
* @see DateField
* @see TrieField
*/
-public class TrieDateField extends DateField {
+public class TrieDateField extends DateField implements DateValueFieldType {
final TrieField wrappedField = new TrieField() {{
type = TrieTypes.DATE;
diff --git a/solr/core/src/java/org/apache/solr/schema/TrieDoubleField.java b/solr/core/src/java/org/apache/solr/schema/TrieDoubleField.java
index a8884b4d22d..1f0da8aaaa8 100755
--- a/solr/core/src/java/org/apache/solr/schema/TrieDoubleField.java
+++ b/solr/core/src/java/org/apache/solr/schema/TrieDoubleField.java
@@ -33,7 +33,7 @@ package org.apache.solr.schema;
* @see Double
* @see Java Language Specification, s4.2.3
*/
-public class TrieDoubleField extends TrieField {
+public class TrieDoubleField extends TrieField implements DoubleValueFieldType {
{
type=TrieTypes.DOUBLE;
}
diff --git a/solr/core/src/java/org/apache/solr/schema/TrieFloatField.java b/solr/core/src/java/org/apache/solr/schema/TrieFloatField.java
index 2ea1d142940..1163d7285f1 100755
--- a/solr/core/src/java/org/apache/solr/schema/TrieFloatField.java
+++ b/solr/core/src/java/org/apache/solr/schema/TrieFloatField.java
@@ -33,7 +33,7 @@ package org.apache.solr.schema;
* @see Float
* @see Java Language Specification, s4.2.3
*/
-public class TrieFloatField extends TrieField {
+public class TrieFloatField extends TrieField implements FloatValueFieldType {
{
type=TrieTypes.FLOAT;
}
diff --git a/solr/core/src/java/org/apache/solr/schema/TrieIntField.java b/solr/core/src/java/org/apache/solr/schema/TrieIntField.java
index 4cc29990a57..e49f59a7fe6 100755
--- a/solr/core/src/java/org/apache/solr/schema/TrieIntField.java
+++ b/solr/core/src/java/org/apache/solr/schema/TrieIntField.java
@@ -27,7 +27,7 @@ package org.apache.solr.schema;
*
* @see Integer
*/
-public class TrieIntField extends TrieField {
+public class TrieIntField extends TrieField implements IntValueFieldType {
{
type=TrieTypes.INTEGER;
}
diff --git a/solr/core/src/java/org/apache/solr/schema/TrieLongField.java b/solr/core/src/java/org/apache/solr/schema/TrieLongField.java
index c20f252dcac..052e4a72c86 100755
--- a/solr/core/src/java/org/apache/solr/schema/TrieLongField.java
+++ b/solr/core/src/java/org/apache/solr/schema/TrieLongField.java
@@ -27,7 +27,7 @@ package org.apache.solr.schema;
*
* @see Long
*/
-public class TrieLongField extends TrieField {
+public class TrieLongField extends TrieField implements LongValueFieldType {
{
type=TrieTypes.LONG;
}
diff --git a/solr/core/src/java/org/apache/solr/update/processor/AllValuesOrNoneFieldMutatingUpdateProcessor.java b/solr/core/src/java/org/apache/solr/update/processor/AllValuesOrNoneFieldMutatingUpdateProcessor.java
new file mode 100644
index 00000000000..58969b079ea
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/update/processor/AllValuesOrNoneFieldMutatingUpdateProcessor.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.solr.update.processor;
+
+import org.apache.solr.common.SolrInputField;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Abstract subclass of FieldMutatingUpdateProcessor for implementing
+ * UpdateProcessors that will mutate all individual values of a selected
+ * field independently. If not all individual values are acceptable
+ * - i.e., mutateValue(srcVal) returns {@link #SKIP_FIELD_VALUE_LIST_SINGLETON}
+ * for at least one value - then none of the values are mutated:
+ * mutate(srcField) will return srcField.
+ *
+ * @see FieldMutatingUpdateProcessorFactory
+ * @see FieldValueMutatingUpdateProcessor
+ */
+public abstract class AllValuesOrNoneFieldMutatingUpdateProcessor extends FieldMutatingUpdateProcessor {
+
+ private static final Logger log = LoggerFactory.getLogger(AllValuesOrNoneFieldMutatingUpdateProcessor.class);
+
+ public static final Object DELETE_VALUE_SINGLETON = new Object() {
+ @Override
+ public String toString() {
+ return "!!Singleton Object Triggering Value Deletion!!";
+ }
+ };
+
+ public static final Object SKIP_FIELD_VALUE_LIST_SINGLETON= new Object() {
+ @Override
+ public String toString() {
+ return "!!Singleton Object Triggering Skipping Field Mutation!!";
+ }
+ };
+
+
+ public AllValuesOrNoneFieldMutatingUpdateProcessor(FieldNameSelector selector, UpdateRequestProcessor next) {
+ super(selector, next);
+ }
+
+ /**
+ * Mutates individual values of a field as needed, or returns the original
+ * value.
+ *
+ * @param srcVal a value from a matched field which should be mutated
+ * @return the value to use as a replacement for src, or
+ * DELETE_VALUE_SINGLETON to indicate that the value
+ * should be removed completely, or
+ * SKIP_FIELD_VALUE_LIST_SINGLETON to indicate that
+ * a field value is not consistent with
+ * @see #DELETE_VALUE_SINGLETON
+ * @see #SKIP_FIELD_VALUE_LIST_SINGLETON
+ */
+ protected abstract Object mutateValue(final Object srcVal);
+
+ protected final SolrInputField mutate(final SolrInputField srcField) {
+ List messages = null;
+ SolrInputField result = new SolrInputField(srcField.getName());
+ for (final Object srcVal : srcField.getValues()) {
+ final Object destVal = mutateValue(srcVal);
+ if (SKIP_FIELD_VALUE_LIST_SINGLETON == destVal) {
+ log.debug("field '{}' {} value '{}' is not mutatable, so no values will be mutated",
+ new Object[] { srcField.getName(), srcVal.getClass().getSimpleName(), srcVal });
+ return srcField;
+ }
+ if (DELETE_VALUE_SINGLETON == destVal) {
+ if (log.isDebugEnabled()) {
+ if (null == messages) {
+ messages = new ArrayList();
+ }
+ messages.add(String.format(Locale.ROOT, "removing value from field '%s': %s '%s'",
+ srcField.getName(), srcVal.getClass().getSimpleName(), srcVal));
+ }
+ } else {
+ if (log.isDebugEnabled()) {
+ if (null == messages) {
+ messages = new ArrayList();
+ }
+ messages.add(String.format(Locale.ROOT, "replace value from field '%s': %s '%s' with %s '%s'",
+ srcField.getName(), srcVal.getClass().getSimpleName(), srcVal,
+ destVal.getClass().getSimpleName(), destVal));
+ }
+ result.addValue(destVal, 1.0F);
+ }
+ }
+ result.setBoost(srcField.getBoost());
+
+ if (null != messages && log.isDebugEnabled()) {
+ for (String message : messages) {
+ log.debug(message);
+ }
+ }
+ return 0 == result.getValueCount() ? null : result;
+ }
+}
diff --git a/solr/core/src/java/org/apache/solr/update/processor/FieldMutatingUpdateProcessor.java b/solr/core/src/java/org/apache/solr/update/processor/FieldMutatingUpdateProcessor.java
index caf326aefbe..92fc82b216f 100644
--- a/solr/core/src/java/org/apache/solr/update/processor/FieldMutatingUpdateProcessor.java
+++ b/solr/core/src/java/org/apache/solr/update/processor/FieldMutatingUpdateProcessor.java
@@ -108,7 +108,7 @@ public abstract class FieldMutatingUpdateProcessor
// for now, don't allow it.
if (! fname.equals(dest.getName()) ) {
throw new SolrException(SERVER_ERROR,
- "mutute returned field with different name: "
+ "mutate returned field with different name: "
+ fname + " => " + dest.getName());
}
doc.put(dest.getName(), dest);
@@ -118,7 +118,7 @@ public abstract class FieldMutatingUpdateProcessor
}
/**
- * Interface for idenfifying which fileds should be mutated
+ * Interface for identifying which fields should be mutated
*/
public static interface FieldNameSelector {
public boolean shouldMutate(final String fieldName);
diff --git a/solr/core/src/java/org/apache/solr/update/processor/FieldMutatingUpdateProcessorFactory.java b/solr/core/src/java/org/apache/solr/update/processor/FieldMutatingUpdateProcessorFactory.java
index ae11932c1b7..72cb52d2187 100644
--- a/solr/core/src/java/org/apache/solr/update/processor/FieldMutatingUpdateProcessorFactory.java
+++ b/solr/core/src/java/org/apache/solr/update/processor/FieldMutatingUpdateProcessorFactory.java
@@ -65,7 +65,6 @@ import org.apache.solr.util.plugin.SolrCoreAware;
*
*
*
fieldNameMatchesSchemaField - selecting specific fields based on whether or not they match a schema field
-
*
*
* One or more excludes <lst> params may also be specified,
diff --git a/solr/core/src/java/org/apache/solr/update/processor/ParseBooleanFieldUpdateProcessorFactory.java b/solr/core/src/java/org/apache/solr/update/processor/ParseBooleanFieldUpdateProcessorFactory.java
new file mode 100644
index 00000000000..9dc0e382ca9
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/update/processor/ParseBooleanFieldUpdateProcessorFactory.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.solr.update.processor;
+
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.schema.BoolField;
+import org.apache.solr.schema.FieldType;
+import org.apache.solr.schema.IndexSchema;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ *
+ * Attempts to mutate selected fields that have only CharSequence-typed values
+ * into Boolean values.
+ *
+ *
+ * The default selection behavior is to mutate both those fields that don't match
+ * a schema field, as well as those fields that do match a schema field and have
+ * a field type that uses class solr.BooleanField.
+ *
+ *
+ * If all values are parseable as boolean (or are already Boolean), then the field
+ * will be mutated, replacing each value with its parsed Boolean equivalent;
+ * otherwise, no mutation will occur.
+ *
+ *
+ * The default true and false values are "true" and "false", respectively, and match
+ * case-insensitively. The following configuration changes the acceptable values, and
+ * requires a case-sensitive match - note that either individual <str> elements
+ * or <arr>-s of <str> elements may be used to specify the trueValue-s
+ * and falseValue-s:
+ *
+ */
+public class ParseBooleanFieldUpdateProcessorFactory extends FieldMutatingUpdateProcessorFactory {
+ private static final String TRUE_VALUES_PARAM = "trueValue";
+ private static final String FALSE_VALUES_PARAM = "falseValue";
+ private static final String CASE_SENSITIVE_PARAM = "caseSensitive";
+
+ private Set trueValues = new HashSet(Arrays.asList(new String[] { "true" }));
+ private Set falseValues = new HashSet(Arrays.asList(new String[] { "false" }));
+ private boolean caseSensitive = false;
+
+ @Override
+ public UpdateRequestProcessor getInstance(SolrQueryRequest req,
+ SolrQueryResponse rsp,
+ UpdateRequestProcessor next) {
+ return new AllValuesOrNoneFieldMutatingUpdateProcessor(getSelector(), next) {
+ @Override
+ protected Object mutateValue(Object srcVal) {
+ if (srcVal instanceof CharSequence) {
+ String stringVal = caseSensitive ? srcVal.toString() : srcVal.toString().toLowerCase(Locale.ROOT);
+ if (trueValues.contains(stringVal)) {
+ return true;
+ } else if (falseValues.contains(stringVal)) {
+ return false;
+ } else {
+ return SKIP_FIELD_VALUE_LIST_SINGLETON;
+ }
+ }
+ if (srcVal instanceof Boolean) {
+ return srcVal;
+ }
+ return SKIP_FIELD_VALUE_LIST_SINGLETON;
+ }
+ };
+ }
+
+ @Override
+ public void init(NamedList args) {
+ Object caseSensitiveParam = args.remove(CASE_SENSITIVE_PARAM);
+ if (null != caseSensitiveParam) {
+ if (caseSensitiveParam instanceof Boolean) {
+ caseSensitive = (Boolean)caseSensitiveParam;
+ } else {
+ caseSensitive = Boolean.valueOf(caseSensitiveParam.toString());
+ }
+ }
+
+ Collection trueValuesParam = oneOrMany(args, TRUE_VALUES_PARAM);
+ if ( ! trueValuesParam.isEmpty()) {
+ trueValues.clear();
+ for (String trueVal : trueValuesParam) {
+ trueValues.add(caseSensitive ? trueVal : trueVal.toLowerCase(Locale.ROOT));
+ }
+ }
+
+ Collection falseValuesParam = oneOrMany(args, FALSE_VALUES_PARAM);
+ if ( ! falseValuesParam.isEmpty()) {
+ falseValues.clear();
+ for (String val : falseValuesParam) {
+ final String falseVal = caseSensitive ? val : val.toLowerCase(Locale.ROOT);
+ if (trueValues.contains(falseVal)) {
+ throw new SolrException(ErrorCode.SERVER_ERROR,
+ "Param '" + FALSE_VALUES_PARAM + "' contains a value also in param '" + TRUE_VALUES_PARAM
+ + "': '" + val + "'");
+ }
+ falseValues.add(falseVal);
+ }
+ }
+ super.init(args);
+ }
+
+
+ /**
+ * Returns true if the field doesn't match any schema field or dynamic field,
+ * or if the matched field's type is BoolField
+ */
+ @Override
+ public FieldMutatingUpdateProcessor.FieldNameSelector
+ getDefaultSelector(final SolrCore core) {
+
+ return new FieldMutatingUpdateProcessor.FieldNameSelector() {
+ @Override
+ public boolean shouldMutate(final String fieldName) {
+ final IndexSchema schema = core.getLatestSchema();
+ FieldType type = schema.getFieldTypeNoEx(fieldName);
+ return (null == type) || (type instanceof BoolField);
+ }
+ };
+ }
+}
diff --git a/solr/core/src/java/org/apache/solr/update/processor/ParseDateFieldUpdateProcessorFactory.java b/solr/core/src/java/org/apache/solr/update/processor/ParseDateFieldUpdateProcessorFactory.java
new file mode 100644
index 00000000000..05aecbf7935
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/update/processor/ParseDateFieldUpdateProcessorFactory.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.solr.update.processor;
+
+import org.apache.commons.lang.LocaleUtils;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.schema.DateValueFieldType;
+import org.apache.solr.schema.FieldType;
+import org.apache.solr.schema.IndexSchema;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ *
+ * Attempts to mutate selected fields that have only CharSequence-typed values
+ * into Date values. Solr will continue to index date/times in the UTC time
+ * zone, but the input date/times may be expressed using other time zones,
+ * and will be converted to UTC when they are mutated.
+ *
+ *
+ * The default selection behavior is to mutate both those fields that don't match
+ * a schema field, as well as those fields that match a schema field with a field
+ * type that uses class solr.DateField or a sub-class, including solr.TrieDateField.
+ *
+ *
+ * If all values are parseable as dates (or are already Date), then the field will
+ * be mutated, replacing each value with its parsed Date equivalent; otherwise, no
+ * mutation will occur.
+ *
+ *
+ * One or more date "format" specifiers must be specified. See
+ * Joda-time's DateTimeFormat javadocs for a description of format strings.
+ *
+ *
+ * A default time zone name or offset may optionally be specified for those dates
+ * that don't include an explicit zone/offset. NOTE: three-letter zone
+ * designations like "EST" are not parseable (with the single exception of "UTC"),
+ * because they are ambiguous. If no default time zone is specified, UTC will be
+ * used. See Wikipedia's list of TZ database time zone names.
+ *
+ *
+ * The locale to use when parsing field values using the specified formats may
+ * optionally be specified. If no locale is configured, then {@link Locale#ROOT}
+ * will be used. The following configuration specifies the French/France locale and
+ * two date formats that will parse the strings "le mardi 8 janvier 2013" and
+ * "le 28 déc. 2010 à 15 h 30", respectively. Note that either individual <str>
+ * elements or <arr>-s of <str> elements may be used to specify the
+ * date format(s):
+ *
+ * See {@link Locale} for a description of acceptable language, country (optional)
+ * and variant (optional) values, joined with underscore(s).
+ *
+ */
+public class ParseDateFieldUpdateProcessorFactory extends FieldMutatingUpdateProcessorFactory {
+ public static final Logger log = LoggerFactory.getLogger(ParseDateFieldUpdateProcessorFactory.class);
+
+ private static final String FORMATS_PARAM = "format";
+ private static final String DEFAULT_TIME_ZONE_PARAM = "defaultTimeZone";
+ private static final String LOCALE_PARAM = "locale";
+
+ private Map formats = new LinkedHashMap();
+
+ @Override
+ public UpdateRequestProcessor getInstance(SolrQueryRequest req,
+ SolrQueryResponse rsp,
+ UpdateRequestProcessor next) {
+ return new AllValuesOrNoneFieldMutatingUpdateProcessor(getSelector(), next) {
+ @Override
+ protected Object mutateValue(Object srcVal) {
+ if (srcVal instanceof CharSequence) {
+ String srcStringVal = srcVal.toString();
+ for (Map.Entry format : formats.entrySet()) {
+ DateTimeFormatter parser = format.getValue();
+ try {
+ DateTime dateTime = parser.parseDateTime(srcStringVal);
+ return dateTime.withZone(DateTimeZone.UTC).toDate();
+ } catch (IllegalArgumentException e) {
+ log.debug("value '{}' is not parseable with format '{}'",
+ new Object[] { srcStringVal, format.getKey() });
+ }
+ }
+ log.debug("value '{}' was not parsed by any configured format, thus was not mutated", srcStringVal);
+ return SKIP_FIELD_VALUE_LIST_SINGLETON;
+ }
+ if (srcVal instanceof Date) {
+ return srcVal;
+ }
+ return SKIP_FIELD_VALUE_LIST_SINGLETON;
+ }
+ };
+ }
+
+ @Override
+ public void init(NamedList args) {
+
+ Locale locale = Locale.ROOT;
+
+ String localeParam = (String)args.remove(LOCALE_PARAM);
+ if (null != localeParam) {
+ locale = LocaleUtils.toLocale(localeParam);
+ }
+
+ Object defaultTimeZoneParam = args.remove(DEFAULT_TIME_ZONE_PARAM);
+ DateTimeZone defaultTimeZone = DateTimeZone.UTC;
+ if (null != defaultTimeZoneParam) {
+ defaultTimeZone = DateTimeZone.forID(defaultTimeZoneParam.toString());
+ }
+
+ Collection formatsParam = oneOrMany(args, FORMATS_PARAM);
+ if (null != formatsParam) {
+ for (String value : formatsParam) {
+ formats.put(value, DateTimeFormat.forPattern(value).withZone(defaultTimeZone).withLocale(locale));
+ }
+ }
+ super.init(args);
+ }
+
+ /**
+ * Returns true if the field doesn't match any schema field or dynamic field,
+ * or if the matched field's type is BoolField
+ */
+ @Override
+ public FieldMutatingUpdateProcessor.FieldNameSelector
+ getDefaultSelector(final SolrCore core) {
+
+ return new FieldMutatingUpdateProcessor.FieldNameSelector() {
+ @Override
+ public boolean shouldMutate(final String fieldName) {
+ final IndexSchema schema = core.getLatestSchema();
+ FieldType type = schema.getFieldTypeNoEx(fieldName);
+ return (null == type) || type instanceof DateValueFieldType;
+ }
+ };
+ }
+}
diff --git a/solr/core/src/java/org/apache/solr/update/processor/ParseDoubleFieldUpdateProcessorFactory.java b/solr/core/src/java/org/apache/solr/update/processor/ParseDoubleFieldUpdateProcessorFactory.java
new file mode 100644
index 00000000000..07c984caf73
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/update/processor/ParseDoubleFieldUpdateProcessorFactory.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.solr.update.processor;
+
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.schema.DoubleValueFieldType;
+import org.apache.solr.schema.FieldType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.RoundingMode;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+/**
+ *
+ * Attempts to mutate selected fields that have only CharSequence-typed values
+ * into Double values. If required, rounding uses ceiling mode:
+ * {@link RoundingMode#CEILING}. Grouping separators (',' in the ROOT locale)
+ * are parsed.
+ *
+ *
+ * The default selection behavior is to mutate both those fields that don't match
+ * a schema field, as well as those fields that match a schema field with a field
+ * type that uses class solr.DoubleField, solr.TrieDoubleField, or
+ * solr.SortableDoubleField.
+ *
+ *
+ * If all values are parseable as double (or are already Double), then the field
+ * will be mutated, replacing each value with its parsed Double equivalent;
+ * otherwise, no mutation will occur.
+ *
+ *
+ * The locale to use when parsing field values, which will affect the recognized
+ * grouping separator and decimal characters, may optionally be specified. If
+ * no locale is configured, then {@link Locale#ROOT} will be used. The following
+ * configuration specifies the Russian/Russia locale, which will parse the string
+ * string "12 345,899" as double value 12345.899 (the grouping separator
+ * character is U+00AO NO-BREAK SPACE).
+ *
+ * See {@link Locale} for a description of acceptable language, country (optional)
+ * and variant (optional) values, joined with underscore(s).
+ *
+ */
+public class ParseDoubleFieldUpdateProcessorFactory extends ParseNumericFieldUpdateProcessorFactory {
+
+ private static final Logger log = LoggerFactory.getLogger(ParseDoubleFieldUpdateProcessorFactory.class);
+
+ @Override
+ public UpdateRequestProcessor getInstance(SolrQueryRequest req,
+ SolrQueryResponse rsp,
+ UpdateRequestProcessor next) {
+ return new ParseDoubleFieldUpdateProcessor(getSelector(), locale, next);
+ }
+
+ private static final class ParseDoubleFieldUpdateProcessor extends AllValuesOrNoneFieldMutatingUpdateProcessor {
+ private final Locale locale;
+ // NumberFormat instances are not thread safe
+ private final ThreadLocal numberFormat = new ThreadLocal() {
+ @Override
+ protected NumberFormat initialValue() {
+ NumberFormat format = NumberFormat.getInstance(locale);
+ format.setParseIntegerOnly(false);
+ format.setRoundingMode(RoundingMode.CEILING);
+ return format;
+ }
+ };
+
+ ParseDoubleFieldUpdateProcessor(FieldNameSelector selector, Locale locale, UpdateRequestProcessor next) {
+ super(selector, next);
+ this.locale = locale;
+ }
+
+ @Override
+ protected Object mutateValue(Object srcVal) {
+ if (srcVal instanceof CharSequence) {
+ String stringVal = srcVal.toString();
+ ParsePosition pos = new ParsePosition(0);
+ Number number = numberFormat.get().parse(stringVal, pos);
+ if (pos.getIndex() != stringVal.length()) {
+ log.debug("value '{}' is not parseable, thus not mutated; unparsed chars: '{}'",
+ new Object[] { srcVal, stringVal.substring(pos.getIndex())});
+ return SKIP_FIELD_VALUE_LIST_SINGLETON;
+ }
+ return number.doubleValue();
+ }
+ if (srcVal instanceof Double) {
+ return srcVal;
+ }
+ return SKIP_FIELD_VALUE_LIST_SINGLETON;
+ }
+ }
+
+ @Override
+ protected boolean isSchemaFieldTypeCompatible(FieldType type) {
+ return type instanceof DoubleValueFieldType;
+ }
+}
diff --git a/solr/core/src/java/org/apache/solr/update/processor/ParseFloatFieldUpdateProcessorFactory.java b/solr/core/src/java/org/apache/solr/update/processor/ParseFloatFieldUpdateProcessorFactory.java
new file mode 100644
index 00000000000..b085b8067c5
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/update/processor/ParseFloatFieldUpdateProcessorFactory.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.solr.update.processor;
+
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.schema.FieldType;
+import org.apache.solr.schema.FloatValueFieldType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.RoundingMode;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+/**
+ *
+ * Attempts to mutate selected fields that have only CharSequence-typed values
+ * into Float values. If required, rounding uses ceiling mode:
+ * {@link RoundingMode#CEILING}. Grouping separators (',' in the ROOT locale)
+ * are parsed.
+ *
+ *
+ * The default selection behavior is to mutate both those fields that don't match
+ * a schema field, as well as those fields that match a schema field with a field
+ * type that uses class solr.FloatField, solr.TrieFloatField, or
+ * solr.SortableFloatField.
+ *
+ *
+ * If all values are parseable as float (or are already Float), then the field
+ * will be mutated, replacing each value with its parsed Float equivalent;
+ * otherwise, no mutation will occur.
+ *
+ *
+ * The locale to use when parsing field values, which will affect the recognized
+ * grouping separator and decimal characters, may optionally be specified. If
+ * no locale is configured, then {@link Locale#ROOT} will be used. The following
+ * configuration specifies the Russian/Russia locale, which will parse the string
+ * "12 345,899" as 12345.899f (the grouping separator character is U+00AO NO-BREAK
+ * SPACE).
+ *
+ * See {@link Locale} for a description of acceptable language, country (optional)
+ * and variant (optional) values, joined with underscore(s).
+ *
+ */
+public class ParseFloatFieldUpdateProcessorFactory extends ParseNumericFieldUpdateProcessorFactory {
+
+ private static final Logger log = LoggerFactory.getLogger(ParseFloatFieldUpdateProcessorFactory.class);
+
+ @Override
+ public UpdateRequestProcessor getInstance(SolrQueryRequest req,
+ SolrQueryResponse rsp,
+ UpdateRequestProcessor next) {
+ return new ParseFloatFieldUpdateProcessor(getSelector(), locale, next);
+ }
+
+ private static class ParseFloatFieldUpdateProcessor extends AllValuesOrNoneFieldMutatingUpdateProcessor {
+ private final Locale locale;
+
+ // NumberFormat instances are not thread safe
+ private final ThreadLocal numberFormat = new ThreadLocal() {
+ @Override
+ protected NumberFormat initialValue() {
+ NumberFormat format = NumberFormat.getInstance(locale);
+ format.setParseIntegerOnly(false);
+ format.setRoundingMode(RoundingMode.CEILING);
+ return format;
+ }
+ };
+
+ ParseFloatFieldUpdateProcessor(FieldNameSelector selector, Locale locale, UpdateRequestProcessor next) {
+ super(selector, next);
+ this.locale = locale;
+ }
+
+ @Override
+ protected Object mutateValue(Object srcVal) {
+ if (srcVal instanceof CharSequence) {
+ String stringVal = srcVal.toString();
+ ParsePosition pos = new ParsePosition(0);
+ Number number = numberFormat.get().parse(stringVal, pos);
+ if (pos.getIndex() != stringVal.length()) {
+ log.debug("value '{}' is not parseable, thus not mutated; unparsed chars: '{}'",
+ new Object[] { srcVal, stringVal.substring(pos.getIndex())});
+ return SKIP_FIELD_VALUE_LIST_SINGLETON;
+ }
+ return number.floatValue();
+ }
+ if (srcVal instanceof Float) {
+ return srcVal;
+ }
+ return SKIP_FIELD_VALUE_LIST_SINGLETON;
+ }
+ }
+
+ @Override
+ protected boolean isSchemaFieldTypeCompatible(FieldType type) {
+ return type instanceof FloatValueFieldType;
+ }
+}
diff --git a/solr/core/src/java/org/apache/solr/update/processor/ParseIntFieldUpdateProcessorFactory.java b/solr/core/src/java/org/apache/solr/update/processor/ParseIntFieldUpdateProcessorFactory.java
new file mode 100644
index 00000000000..51faad7038c
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/update/processor/ParseIntFieldUpdateProcessorFactory.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.solr.update.processor;
+
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.schema.FieldType;
+import org.apache.solr.schema.IntValueFieldType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+/**
+ *
+ * Attempts to mutate selected fields that have only CharSequence-typed values
+ * into Integer values. Grouping separators (',' in the ROOT locale) are parsed.
+ *
+ *
+ * The default selection behavior is to mutate both those fields that don't match
+ * a schema field, as well as those fields that match a schema field with a field
+ * type that uses class solr.IntField, solr.TrieIntField, or
+ * solr.SortableIntField.
+ *
+ *
+ * If all values are parseable as int (or are already Integer), then the field
+ * will be mutated, replacing each value with its parsed Integer equivalent;
+ * otherwise, no mutation will occur.
+ *
+ *
+ * The locale to use when parsing field values, which will affect the recognized
+ * grouping separator character, may optionally be specified. If no locale is
+ * configured, then {@link Locale#ROOT} will be used. The following configuration
+ * specifies the Russian/Russia locale, which will parse the string "12 345 899"
+ * as 12345899L (the grouping separator character is U+00AO NO-BREAK SPACE).
+ *
+ * See {@link Locale} for a description of acceptable language, country (optional)
+ * and variant (optional) values, joined with underscore(s).
+ *
+ */
+public class ParseIntFieldUpdateProcessorFactory extends ParseNumericFieldUpdateProcessorFactory {
+
+ private static final Logger log = LoggerFactory.getLogger(ParseIntFieldUpdateProcessorFactory.class);
+
+ @Override
+ public UpdateRequestProcessor getInstance(SolrQueryRequest req,
+ SolrQueryResponse rsp,
+ UpdateRequestProcessor next) {
+ return new ParseIntFieldUpdateProcessor(getSelector(), locale, next);
+ }
+
+ private static final class ParseIntFieldUpdateProcessor extends AllValuesOrNoneFieldMutatingUpdateProcessor {
+ private final Locale locale;
+
+ // NumberFormat instances are not thread safe
+ private final ThreadLocal numberFormat = new ThreadLocal() {
+ @Override
+ protected NumberFormat initialValue() {
+ NumberFormat format = NumberFormat.getInstance(locale);
+ format.setParseIntegerOnly(true);
+ return format;
+ }
+ };
+
+ ParseIntFieldUpdateProcessor(FieldNameSelector selector, Locale locale, UpdateRequestProcessor next) {
+ super(selector, next);
+ this.locale = locale;
+ }
+
+ @Override
+ protected Object mutateValue(Object srcVal) {
+ if (srcVal instanceof CharSequence) {
+ String stringVal = srcVal.toString();
+ ParsePosition pos = new ParsePosition(0);
+ Number number = numberFormat.get().parse(stringVal, pos);
+ if (pos.getIndex() != stringVal.length()) {
+ log.debug("value '{}' is not parseable, thus not mutated; unparsed chars: '{}'",
+ new Object[] { srcVal, stringVal.substring(pos.getIndex())});
+ return SKIP_FIELD_VALUE_LIST_SINGLETON;
+ }
+ int intValue = number.intValue();
+ if (number.longValue() == (long)intValue) {
+ // If the high bits don't get truncated by number.intValue()
+ return intValue;
+ }
+ log.debug("value '{}' doesn't fit into an Integer, thus was not mutated", srcVal);
+ return SKIP_FIELD_VALUE_LIST_SINGLETON;
+ }
+ if (srcVal instanceof Integer) {
+ return srcVal;
+ }
+ return SKIP_FIELD_VALUE_LIST_SINGLETON;
+ }
+ }
+
+ @Override
+ protected boolean isSchemaFieldTypeCompatible(FieldType type) {
+ return type instanceof IntValueFieldType;
+ }
+}
diff --git a/solr/core/src/java/org/apache/solr/update/processor/ParseLongFieldUpdateProcessorFactory.java b/solr/core/src/java/org/apache/solr/update/processor/ParseLongFieldUpdateProcessorFactory.java
new file mode 100644
index 00000000000..1b0ceb5abce
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/update/processor/ParseLongFieldUpdateProcessorFactory.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.solr.update.processor;
+
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.schema.FieldType;
+import org.apache.solr.schema.LongValueFieldType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+/**
+ *
+ * Attempts to mutate selected fields that have only CharSequence-typed values
+ * into Long values. Grouping separators (',' in the ROOT locale) are parsed.
+ *
+ *
+ * The default selection behavior is to mutate both those fields that don't match
+ * a schema field, as well as those fields that match a schema field with a field
+ * type that uses class solr.LongField, solr.TrieLongField, or
+ * solr.SortableLongField.
+ *
+ *
+ * If all values are parseable as long (or are already Long), then the field
+ * will be mutated, replacing each value with its parsed Long equivalent;
+ * otherwise, no mutation will occur.
+ *
+ *
+ * The locale to use when parsing field values, which will affect the recognized
+ * grouping separator character, may optionally be specified. If no locale is
+ * configured, then {@link Locale#ROOT} will be used. The following configuration
+ * specifies the Russian/Russia locale, which will parse the string "12 345 899"
+ * as 12345899L (the grouping separator character is U+00AO NO-BREAK SPACE).
+ *
+ * See {@link Locale} for a description of acceptable language, country (optional)
+ * and variant (optional) values, joined with underscore(s).
+ *
+ */
+public class ParseLongFieldUpdateProcessorFactory extends ParseNumericFieldUpdateProcessorFactory {
+
+ private static final Logger log = LoggerFactory.getLogger(ParseLongFieldUpdateProcessorFactory.class);
+
+ @Override
+ public UpdateRequestProcessor getInstance(SolrQueryRequest req,
+ SolrQueryResponse rsp,
+ UpdateRequestProcessor next) {
+ return new ParseLongFieldUpdateProcessor(getSelector(), locale, next);
+ }
+
+ private static class ParseLongFieldUpdateProcessor extends AllValuesOrNoneFieldMutatingUpdateProcessor {
+ private final Locale locale;
+
+ // NumberFormat instances are not thread safe
+ private final ThreadLocal numberFormat = new ThreadLocal() {
+ @Override
+ protected NumberFormat initialValue() {
+ NumberFormat format = NumberFormat.getInstance(locale);
+ format.setParseIntegerOnly(true);
+ return format;
+ }
+ };
+
+ ParseLongFieldUpdateProcessor(FieldNameSelector selector, Locale locale, UpdateRequestProcessor next) {
+ super(selector, next);
+ this.locale = locale;
+ }
+
+ @Override
+ protected Object mutateValue(Object srcVal) {
+ if (srcVal instanceof CharSequence) {
+ String stringVal = srcVal.toString();
+ ParsePosition pos = new ParsePosition(0);
+ Number number = numberFormat.get().parse(stringVal, pos);
+ if (pos.getIndex() != stringVal.length()) {
+ log.debug("value '{}' is not parseable, thus not mutated; unparsed chars: '{}'",
+ new Object[] { srcVal, stringVal.substring(pos.getIndex())});
+ return SKIP_FIELD_VALUE_LIST_SINGLETON;
+ }
+ return number.longValue();
+ }
+ if (srcVal instanceof Long) {
+ return srcVal;
+ }
+ return SKIP_FIELD_VALUE_LIST_SINGLETON;
+ }
+ }
+
+ @Override
+ protected boolean isSchemaFieldTypeCompatible(FieldType type) {
+ return type instanceof LongValueFieldType;
+ }
+}
diff --git a/solr/core/src/java/org/apache/solr/update/processor/ParseNumericFieldUpdateProcessorFactory.java b/solr/core/src/java/org/apache/solr/update/processor/ParseNumericFieldUpdateProcessorFactory.java
new file mode 100644
index 00000000000..20bc67e91ee
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/update/processor/ParseNumericFieldUpdateProcessorFactory.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.solr.update.processor;
+
+import org.apache.commons.lang.LocaleUtils;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.schema.FieldType;
+import org.apache.solr.schema.IndexSchema;
+
+import java.util.Locale;
+
+/**
+ * Abstract base class for numeric parsing update processor factories.
+ * Subclasses can optionally configure a locale. If no locale is configured,
+ * then {@link Locale#ROOT} will be used. E.g. to configure the French/France
+ * locale:
+ *
+ *