package com.lucidworks.storm.solr; import org.apache.log4j.Logger; import org.apache.solr.client.solrj.request.UpdateRequest; import org.apache.solr.common.SolrInputDocument; import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Date; import java.util.HashSet; import java.util.Map; import java.util.Set; public class DefaultSolrInputDocumentMapper implements SolrInputDocumentMapper { public static Logger log = Logger.getLogger(DefaultSolrInputDocumentMapper.class); protected boolean fieldGuessingEnabled = false; protected String idFieldName = "id"; protected Map<String, String> dynamicFieldOverrides = null; public Map<String, String> getDynamicFieldOverrides() { return dynamicFieldOverrides; } public void setDynamicFieldOverrides(Map<String, String> dynamicFieldOverrides) { this.dynamicFieldOverrides = dynamicFieldOverrides; } public String getIdFieldName() { return idFieldName; } public void setIdFieldName(String idFieldName) { this.idFieldName = idFieldName; } public boolean isFieldGuessingEnabled() { return fieldGuessingEnabled; } public void setFieldGuessingEnabled(boolean fieldGuessingEnabled) { this.fieldGuessingEnabled = fieldGuessingEnabled; } public SolrInputDocument toInputDoc(String docId, Object obj) { return (obj instanceof SolrInputDocument) ? (SolrInputDocument) obj : autoMapToSolrInputDoc(docId, obj); } protected SolrInputDocument map2doc(SolrInputDocument doc, Map map) { for (Object key : map.keySet()) { doc.setField((String)key, map.get(key)); } return doc; } protected SolrInputDocument autoMapToSolrInputDoc(String docId, Object obj) { SolrInputDocument doc = new SolrInputDocument(); doc.setField(idFieldName, docId); if (obj == null) return doc; if (obj instanceof Map) return map2doc(doc, (Map)obj); Class objClass = obj.getClass(); Set<String> fields = new HashSet<String>(); Field[] publicFields = obj.getClass().getFields(); if (publicFields != null) { for (Field f : publicFields) { // only non-static public if (Modifier.isStatic(f.getModifiers()) || !Modifier.isPublic(f.getModifiers())) continue; Object value = null; try { value = f.get(obj); } catch (IllegalAccessException e) { } if (value != null) { String fieldName = f.getName(); fields.add(fieldName); if (idFieldName.equals(fieldName)) continue; if (fieldGuessingEnabled) { if (!f.getType().isArray()) doc.addField(fieldName, value); } else { addDynField(doc, fieldName, value, f.getType(), (dynamicFieldOverrides != null) ? dynamicFieldOverrides.get(fieldName) : null); } } } } PropertyDescriptor[] props = null; try { BeanInfo info = Introspector.getBeanInfo(objClass); props = info.getPropertyDescriptors(); } catch (IntrospectionException e) { log.warn("Can't get BeanInfo for class: " + objClass); } if (props != null) { for (PropertyDescriptor pd : props) { String propName = pd.getName(); if ("class".equals(propName) || fields.contains(propName)) continue; Method readMethod = pd.getReadMethod(); if (readMethod != null) { Object value = null; try { value = readMethod.invoke(obj); } catch (Exception e) { log.debug("Failed to invoke read method for property '" + pd.getName() + "' on object of type '" + objClass.getName() + "' due to: " + e); } if (value != null) { fields.add(propName); if (idFieldName.equals(propName)) continue; if (fieldGuessingEnabled) { if (!pd.getPropertyType().isArray()) doc.addField(propName, value); } else { addDynField(doc, propName, value, pd.getPropertyType(), (dynamicFieldOverrides != null) ? dynamicFieldOverrides.get(propName) : null); } } } } } return doc; } protected void addDynField(SolrInputDocument doc, String fieldName, Object value, Class type, String dynamicFieldSuffix) { if (type.isArray()) return; // TODO: Array types not supported yet ... if (dynamicFieldSuffix == null) { dynamicFieldSuffix = getDefaultDynamicFieldMapping(type); // treat strings with multiple terms as text only if using the default! if ("_s".equals(dynamicFieldSuffix)) { String str = (String) value; if (str.indexOf(" ") != -1) dynamicFieldSuffix = "_t"; } } if (dynamicFieldSuffix != null) // don't auto-map if we don't have a type doc.addField(fieldName + dynamicFieldSuffix, value); } protected String getDefaultDynamicFieldMapping(Class clazz) { if (String.class.equals(clazz)) return "_s"; else if (Long.class.equals(clazz) || long.class.equals(clazz)) return "_l"; else if (Integer.class.equals(clazz) || int.class.equals(clazz)) return "_i"; else if (Double.class.equals(clazz) || double.class.equals(clazz)) return "_d"; else if (Float.class.equals(clazz) || float.class.equals(clazz)) return "_f"; else if (Boolean.class.equals(clazz) || boolean.class.equals(clazz)) return "_b"; else if (Date.class.equals(clazz)) return "_tdt"; return null; // default is don't auto-map } }