package org.tynamo.model.elasticsearch.mapping.impl;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.tynamo.descriptor.CollectionDescriptor;
import org.tynamo.descriptor.TynamoPropertyDescriptor;
import org.tynamo.model.elasticsearch.annotations.ElasticSearchEmbedded;
import org.tynamo.model.elasticsearch.mapping.FieldMapper;
import org.tynamo.model.elasticsearch.mapping.MapperFactory;
import org.tynamo.model.elasticsearch.mapping.MappingException;
import org.tynamo.model.elasticsearch.mapping.MappingUtil;
/**
* Field mapper for collection type; maps to array by default
*
* @param <M>
* the generic model type which owns this field
*/
public class CollectionFieldMapper<M> extends AbstractFieldMapper<M> {
private final boolean nestedMode;
private final String type;
private final List<FieldMapper<Object>> fields;
public CollectionFieldMapper(MapperFactory factory, TynamoPropertyDescriptor field, String prefix) {
super(field, prefix);
if (!Collection.class.isAssignableFrom(field.getPropertyType())) {
throw new MappingException("field must be of Collection type");
}
// FIXME not support for ElasticSearchEmbedded at the moment
// ElasticSearchEmbedded embed = field.getAnnotation(ElasticSearchEmbedded.class);
ElasticSearchEmbedded embed = null;
nestedMode = (embed != null);
// Detect object type in collection
type = MappingUtil.detectFieldType(getCollectionType());
// Find fields to use for embedded objects
if (nestedMode) {
Class<?> itemClass = getCollectionType();
List<Field> fieldsToIndex = EmbeddedFieldMapper.getFieldsToIndex(itemClass, embed);
fields = new ArrayList<FieldMapper<Object>>();
for (Field embeddedField : fieldsToIndex) {
// FIXME not support for ElasticSearchEmbedded at the moment
// fields.add(factory.getMapper(embeddedField));
}
} else {
fields = null;
}
}
private Class<?> getCollectionType() {
return ((CollectionDescriptor) field).getElementType();
// ParameterizedType type = (ParameterizedType) field.getGenericType();
// return (Class<?>) type.getActualTypeArguments()[0];
}
@Override
public void addToMapping(XContentBuilder builder) throws IOException {
String indexFieldName = getIndexField();
if (nestedMode) {
// Embedded mode
builder.startObject(indexFieldName);
builder.startObject("properties");
for (FieldMapper<?> mapper : fields) {
mapper.addToMapping(builder);
}
builder.endObject();
builder.endObject();
} else {
// Flat mode (array of primitives)
MappingUtil.addField(builder, indexFieldName, type, meta);
}
}
@Override
public void addToDocument(Object o, XContentBuilder builder) throws IOException {
String indexFieldName = getIndexField();
Collection<?> value = (Collection<?>) o;
if (value != null) {
builder.startArray(indexFieldName);
if (nestedMode) {
// Embedded mode uses mapping
for (Object object : (Collection<?>) value) {
builder.startObject();
for (FieldMapper<Object> mapper : fields) {
mapper.addToDocument(object, builder);
}
builder.endObject();
}
} else {
boolean isStringType = type.equals("string");
// Flat mode uses primitive values or toString
for (Object object : (Collection<?>) value) {
// Use toString for string type
if (isStringType) {
builder.value(object.toString());
} else {
builder.value(object);
}
}
}
builder.endArray();
}
}
}