/*
* JBoss, Home of Professional Open Source
* Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
*/
package org.jboss.elasticsearch.tools.content;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.elasticsearch.common.settings.SettingsException;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
/**
* Content preprocessor which collects values from multiple source fields and store them as List in target field.
* Duplicities are removed during collecting. Example of configuration for this preprocessor:
*
* <pre>
* {
* "name" : "Contributors collector",
* "class" : "org.jboss.elasticsearch.tools.content.ValuesCollectingPreprocessor",
* "settings" : {
* "target_field" : "contributors",
* "source_fields" : ["fields.author","fields.reporter","fields.updater"],
* "deep_copy" : "false"
* }
* }
* </pre>
*
* Options are:
* <ul>
* <li><code>source_fields</code> - array with source fields in input data. Dot notation for nested values can be used
* here, lists can be in path - see {@link XContentMapValues#extractValue(String, Map)}.
* <li><code>target_field</code> - target field in data to store final list. Dot notation can be used here for structure
* nesting. If collected list is empty nothing is stored here.
* <li><code>deep_copy</code> - default value "false". This parameter specifies whether a complete copy of the whole
* source_fields structure should be done. In default case the copy of data will be done only by reference. Switching
* this parameter to true is especially useful when a person is collecting more complicated values like Lists and Maps
* with a plan to modify those without modifying the source instances.
* </ul>
*
* @author Vlastimil Elias (velias at redhat dot com)
* @author Ryszard Kozmik (rkozmik at redhat dot com)
* @see StructuredContentPreprocessorFactory
*/
public class ValuesCollectingPreprocessor extends StructuredContentPreprocessorBase {
protected static final String CFG_SOURCE_FIELDS = "source_fields";
protected static final String CFG_TARGET_FIELD = "target_field";
protected static final String CFG_DEEP_COPY = "deep_copy";
protected String fieldTarget;
protected List<String> fieldsSource;
protected boolean fieldDeepCopy;
@SuppressWarnings("unchecked")
@Override
public void init(Map<String, Object> settings) throws SettingsException {
if (settings == null) {
throw new SettingsException("'settings' section is not defined for preprocessor " + name);
}
fieldsSource = ((List<String>) settings.get(CFG_SOURCE_FIELDS));
validateConfigurationObjectNotEmpty(fieldsSource, CFG_SOURCE_FIELDS);
fieldTarget = XContentMapValues.nodeStringValue(settings.get(CFG_TARGET_FIELD), null);
validateConfigurationStringNotEmpty(fieldTarget, CFG_TARGET_FIELD);
String fieldDeepCopyStr = XContentMapValues.nodeStringValue(settings.get(CFG_DEEP_COPY), "false" );
fieldDeepCopy = fieldDeepCopyStr.compareTo("true")==0 ? true : false;
}
@Override
public Map<String, Object> preprocessData(Map<String, Object> data, PreprocessChainContext chainContext) {
if (data == null)
return null;
Set<Object> vals = new HashSet<Object>();
for (String sourceField : fieldsSource) {
if (ValueUtils.isEmpty(sourceField))
continue;
Object v = XContentMapValues.extractValue(sourceField, data);
collectValue(vals, v);
}
if (vals != null && !vals.isEmpty()) {
StructureUtils.putValueIntoMapOfMaps(data, fieldTarget, new ArrayList<Object>(vals));
} else {
StructureUtils.putValueIntoMapOfMaps(data, fieldTarget, null);
}
return data;
}
@SuppressWarnings("unchecked")
private void collectValue(Set<Object> values, Object value) {
if (value != null) {
if (value instanceof Collection) {
for (Object o : ((Collection<Object>) value))
if ( fieldDeepCopy ) {
collectValue(values, StructureUtils.getADeepStructureCopy(o));
} else {
collectValue(values, o);
}
} else {
if ( fieldDeepCopy ) {
values.add(StructureUtils.getADeepStructureCopy(value));
} else {
values.add(value);
}
}
}
}
public String getFieldTarget() {
return fieldTarget;
}
public List<String> getFieldsSource() {
return fieldsSource;
}
}