package org.tynamo.model.elasticsearch.mapping.impl; import java.io.IOException; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.elasticsearch.common.xcontent.XContentBuilder; import org.tynamo.descriptor.TynamoPropertyDescriptor; import org.tynamo.model.elasticsearch.annotations.ElasticSearchEmbedded; import org.tynamo.model.elasticsearch.annotations.ElasticSearchEmbedded.Mode; 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.util.ReflectionUtil; /** * Field mapper for embedded objects * * @param <M> * the generic model type which owns this field */ public class EmbeddedFieldMapper<M> extends AbstractFieldMapper<M> { private final ElasticSearchEmbedded embed; private final List<FieldMapper<Object>> fields; public EmbeddedFieldMapper(MapperFactory factory, TynamoPropertyDescriptor field, String prefix) { super(field, prefix); // FIXME no support for EmbeddedFieldMapper at the moment // embed = field.getAnnotation(ElasticSearchEmbedded.class); embed = null; // Set correct prefix in case we are in embedded mode String embedPrefix = null; if (embed.mode() == Mode.embedded) { if (embed.prefix().length() > 0) { embedPrefix = prefix(embed.prefix()); } else { embedPrefix = getFieldName() + "."; } } // Add fieldmappers for embedded fields fields = new ArrayList<FieldMapper<Object>>(); for (Field embeddedField : getFieldsToIndex(field.getPropertyType(), embed)) { // FIXME no support for EmbeddedFieldMapper at the moment // fields.add(factory.getMapper(embeddedField, embedPrefix)); } } static List<Field> getFieldsToIndex(Class<?> clazz, ElasticSearchEmbedded meta) { @SuppressWarnings("unchecked") List<String> fieldsToIndex = Arrays.asList(meta.fields()); List<Field> clazzFields = ReflectionUtil.getAllFields(clazz); List<Field> fields = new ArrayList<Field>(); // Make sure the user has not requested unknown fields if (fieldsToIndex.size() > 0) { for (String fieldName : fieldsToIndex) { boolean knownField = false; for (Field clazzField : clazzFields) { if (clazzField.getName().equals(fieldName)) { knownField = true; break; } } if (!knownField) { throw new MappingException("Unknown field specified in " + meta); } } } // Set up fields for (Field embeddedField : clazzFields) { // // FIXME questionable, decide later how to deal properly // // shouldIgnoreField is true when @Transient // if (PlayModelMapper.shouldIgnoreField(embeddedField)) { // continue; // } // // // If no fields were requested and it's marked, ignore it // // userRequestedIgnoreField is true when @ElasticSearchIgnore // if (fieldsToIndex.size() == 0 // && PlayModelMapper.userRequestedIgnoreField(embeddedField)) { // continue; // } // If specific fields are requested, and this is not one of them, // ignore it if (fieldsToIndex.size() > 0 && !fieldsToIndex.contains(embeddedField.getName())) { continue; } // Add it fields.add(embeddedField); } return fields; } @Override public void addToMapping(XContentBuilder builder) throws IOException { String indexFieldName = getIndexField(); switch (embed.mode()) { case embedded: for (FieldMapper<?> mapper : fields) { mapper.addToMapping(builder); } break; case object: case nested: builder.startObject(indexFieldName); builder.field("type", embed.mode().toString()); builder.startObject("properties"); for (FieldMapper<?> mapper : fields) { mapper.addToMapping(builder); } builder.endObject(); builder.endObject(); break; } } @Override public void addToDocument(Object value, XContentBuilder builder) throws IOException { String name = getIndexField(); if (value != null) { switch (embed.mode()) { case embedded: for (FieldMapper<Object> mapper : fields) { mapper.addToDocument(value, builder); } break; case object: case nested: builder.startObject(name); for (FieldMapper<Object> mapper : fields) { mapper.addToDocument(value, builder); } builder.endObject(); break; } } } }