package org.elasticsearch.index.fields;
import java.io.IOException;
import java.util.Map;
import org.apache.lucene.document.FieldType;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.helpers.fields.CompletionFieldHelper;
import org.elasticsearch.helpers.fields.SettingsHelper;
import org.elasticsearch.index.mapper.*;
import org.elasticsearch.index.mapper.core.*;
import org.elasticsearch.index.mapper.geo.GeoPointFieldMapper;
import org.elasticsearch.index.mapper.ip.IpFieldMapper;
import org.elasticsearch.script.CompiledScript;
import org.elasticsearch.script.ScriptService;
public class ComputedFieldMapper implements Mapper
{
private final String _name;
private final ScriptService _scriptService;
private Map<String, Object> _mappings;
private Mapper _resultMapper;
private CompiledScript _script;
private boolean _externalValueSupported;
private boolean _deleted;
private ComputedFieldRootMapper _root;
private final ESLogger _logger;
protected ComputedFieldMapper(String name, ScriptService scriptService, Map<String, Object> mappings, Mapper resultMapper)
{
_logger = Loggers.getLogger("computed-fields", SettingsHelper.GetSettings(), name);
_name = name;
_scriptService = scriptService;
_mappings = mappings;
_resultMapper = resultMapper;
String lang = XContentMapValues.nodeStringValue(mappings.get("lang"), null);
String script = XContentMapValues.nodeStringValue(mappings.get("script"), null);
if (script != null) _script = scriptService.compile(lang, script);
else _script = null;
boolean externalValueSupported = false;
if (_resultMapper instanceof StringFieldMapper) externalValueSupported = true;
else if (_resultMapper instanceof ByteFieldMapper) externalValueSupported = true;
else if (_resultMapper instanceof ShortFieldMapper) externalValueSupported = true;
else if (_resultMapper instanceof IntegerFieldMapper) externalValueSupported = true;
else if (_resultMapper instanceof LongFieldMapper) externalValueSupported = true;
else if (_resultMapper instanceof FloatFieldMapper) externalValueSupported = true;
else if (_resultMapper instanceof DoubleFieldMapper) externalValueSupported = true;
//else if (_resultMapper instanceof BooleanFieldMapper) externalValueSupported = true;
//else if (_resultMapper instanceof BinaryFieldMapper) externalValueSupported = true;
else if (_resultMapper instanceof DateFieldMapper) externalValueSupported = true;
else if (_resultMapper instanceof IpFieldMapper) externalValueSupported = true;
//else if (_resultMapper instanceof ObjectMapper) externalValueSupported = true;
//else if (_resultMapper instanceof CompletionFieldMapper) externalValueSupported = true;
//else if (_resultMapper instanceof MultiFieldMapper) externalValueSupported = true;
//else if (_resultMapper instanceof GeoPointFieldMapper) externalValueSupported = true;
_externalValueSupported = externalValueSupported;
}
@Override
public String name()
{
return _name;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException
{
if (_deleted) return builder;
if (_root == null) ComputedFieldRootMapper.Register(builder, this);
builder.field(_name, _mappings);
return builder;
}
@Override
public void parse(ParseContext context) throws IOException
{
}
@Override
public void merge(Mapper mergeWith, MergeContext mergeContext) throws MergeMappingException
{
try
{
ComputedFieldMapper m = (ComputedFieldMapper)mergeWith;
if (!mergeContext.mergeFlags().simulate())
{
_mappings = m._mappings;
Mapper mm = _resultMapper;
_resultMapper = m._resultMapper;
m._resultMapper = mm;
_script = m._script;
_externalValueSupported = m._externalValueSupported;
_deleted = m._deleted;
reset();
}
m.close();
}
catch (Throwable ex)
{
_logger.error("field mapper merge", ex);
if (ex instanceof RuntimeException) throw (RuntimeException)ex;
else throw new RuntimeException(ex);
}
}
@Override
public void traverse(FieldMapperListener fieldMapperListener)
{
try
{
if (_resultMapper == null) return;
if (_deleted) return;
_resultMapper.traverse(fieldMapperListener);
}
catch (Throwable ex)
{
_logger.error("field mapper traverse", ex);
if (ex instanceof RuntimeException) throw (RuntimeException)ex;
else throw new RuntimeException(ex);
}
}
@Override
public void traverse(ObjectMapperListener objectMapperListener)
{
}
@Override
public void close()
{
try
{
if (_resultMapper != null) _resultMapper.close();
reset();
}
catch (Throwable ex)
{
_logger.error("field mapper close", ex);
if (ex instanceof RuntimeException) throw (RuntimeException)ex;
else throw new RuntimeException(ex);
}
}
public void reset()
{
if (_root != null)
{
_root.removeChild(this);
_root = null;
}
_deleted = false;
}
public void execute(ParseContext context, Map<String, Object> vars) throws IOException
{
try
{
if (!enabled()) return;
Object value =_scriptService.execute(_script, vars);
if (value == null) return;
if (_externalValueSupported)
{
context.externalValue(value);
_resultMapper.parse(context);
}
else if (_resultMapper instanceof GeoPointFieldMapper)
{
ParseContextWrapper wrapper = new ParseContextWrapper(context);
wrapper.reset(value);
_resultMapper.parse(wrapper);
}
else
{
FieldType fieldType = AbstractFieldMapper.Defaults.FIELD_TYPE;
float boost = 1.0f;
if (_resultMapper instanceof CompletionFieldMapper)
{
CompletionFieldMapper cfm = (CompletionFieldMapper)_resultMapper;
CompletionFieldHelper.parse(cfm.value(value), cfm, context.doc());
return;
}
else if (_resultMapper instanceof FieldMapper<?>)
{
FieldMapper<?> fm = (FieldMapper<?>)_resultMapper;
value = fm.value(value);
fieldType = fm.fieldType();
boost = fm.boost();
}
ComputedField field = new ComputedField(_name, value, fieldType);
field.setBoost(boost);
context.doc().add(field);
}
}
catch (Throwable ex)
{
_logger.error("field mapper execute", ex);
if (ex instanceof RuntimeException) throw (RuntimeException)ex;
else throw new RuntimeException(ex);
}
}
public void root(ComputedFieldRootMapper root)
{
_root = root;
}
public void deleted(boolean deleted)
{
_deleted = deleted;
}
public boolean enabled()
{
if (_root == null) return false;
if (!_root.enabled()) return false;
if (_resultMapper == null) return false;
if (_script == null) return false;
return !_deleted;
}
public static class TypeParser implements Mapper.TypeParser
{
private final ScriptService _scriptService;
public TypeParser(ScriptService scriptService)
{
_scriptService = scriptService;
}
@SuppressWarnings("unchecked")
@Override
public Mapper.Builder<?, ?> parse(String name, Map<String, Object> node, ParserContext parserContext) throws MapperParsingException
{
Builder builder = new Builder(name, _scriptService, node);
Map<String, Object> result = (Map<String, Object>)node.get("result");
if (result != null)
{
String type = XContentMapValues.nodeStringValue(result.get("type"), "string");
builder.resultTypeParser(parserContext.typeParser(type).parse(name, result, parserContext));
}
return builder;
}
}
public static class Builder extends Mapper.Builder<Builder, ComputedFieldMapper>
{
private ScriptService _scriptService;
private Mapper.Builder<?, ?> _resultBuilder;
private Map<String, Object> _mappings;
protected Builder(String name, ScriptService scriptService, Map<String, Object> mappings)
{
super(name);
_scriptService = scriptService;
_mappings = mappings;
}
public Builder resultTypeParser(Mapper.Builder<?, ?> resultBuilder)
{
_resultBuilder = resultBuilder;
return this;
}
@Override
public ComputedFieldMapper build(BuilderContext context)
{
return new ComputedFieldMapper(name,
_scriptService,
_mappings,
_resultBuilder != null ? _resultBuilder.build(context) : null);
}
}
}