/* * 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 java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Locale; import java.util.Set; 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 org.apache.solr.update.processor.FieldMutatingUpdateProcessor.FieldNameSelector; /** * <p> * Attempts to mutate selected fields that have only CharSequence-typed values * into Boolean values. * </p> * <p> * 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. * </p> * <p> * 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. * </p> * <p> * 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: * </p> * * <pre class="prettyprint"> * <processor class="solr.ParseBooleanFieldUpdateProcessorFactory"> * <str name="caseSensitive">true</str> * <str name="trueValue">True</str> * <str name="trueValue">Yes</str> * <arr name="falseValue"> * <str>False</str> * <str>No</str> * </arr> * </processor></pre> */ 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<String> trueValues = new HashSet<>(Arrays.asList(new String[] { "true" })); private Set<String> 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<String> trueValuesParam = args.removeConfigArgs(TRUE_VALUES_PARAM); if ( ! trueValuesParam.isEmpty()) { trueValues.clear(); for (String trueVal : trueValuesParam) { trueValues.add(caseSensitive ? trueVal : trueVal.toLowerCase(Locale.ROOT)); } } Collection<String> falseValuesParam = args.removeConfigArgs(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 FieldNameSelector getDefaultSelector(final SolrCore core) { return fieldName -> { final IndexSchema schema = core.getLatestSchema(); FieldType type = schema.getFieldTypeNoEx(fieldName); return (null == type) || (type instanceof BoolField); }; } }