MtasUpdateRequestProcessorFactory.java

  1. package mtas.solr.update.processor;

  2. import java.io.IOException;
  3. import java.io.Reader;
  4. import java.io.StringReader;
  5. import java.lang.reflect.Constructor;
  6. import java.lang.reflect.InvocationTargetException;
  7. import java.util.ArrayList;
  8. import java.util.HashMap;
  9. import java.util.HashSet;
  10. import java.util.Iterator;
  11. import java.util.Map;
  12. import java.util.Map.Entry;
  13. import java.util.Set;

  14. import org.apache.commons.logging.Log;
  15. import org.apache.commons.logging.LogFactory;
  16. import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
  17. import org.apache.lucene.analysis.tokenattributes.FlagsAttribute;
  18. import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
  19. import org.apache.lucene.analysis.tokenattributes.PayloadAttribute;
  20. import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
  21. import org.apache.lucene.analysis.util.CharFilterFactory;
  22. import org.apache.lucene.analysis.util.ResourceLoader;
  23. import org.apache.lucene.util.BytesRef;
  24. import org.apache.solr.common.SolrInputDocument;
  25. import org.apache.solr.common.SolrInputField;
  26. import org.apache.solr.common.util.NamedList;
  27. import org.apache.solr.common.util.SimpleOrderedMap;
  28. import org.apache.solr.core.SolrResourceLoader;
  29. import org.apache.solr.request.SolrQueryRequest;
  30. import org.apache.solr.response.SolrQueryResponse;
  31. import org.apache.solr.schema.FieldType;
  32. import org.apache.solr.schema.SchemaField;
  33. import org.apache.solr.update.AddUpdateCommand;
  34. import org.apache.solr.update.processor.UpdateRequestProcessor;
  35. import org.apache.solr.update.processor.UpdateRequestProcessorFactory;

  36. import mtas.analysis.MtasTokenizer;
  37. import mtas.analysis.util.MtasCharFilterFactory;
  38. import mtas.analysis.util.MtasTokenizerFactory;
  39. import mtas.codec.util.CodecUtil;
  40. import mtas.solr.schema.MtasPreAnalyzedField;

  41. /**
  42.  * A factory for creating MtasUpdateRequestProcessor objects.
  43.  */
  44. public class MtasUpdateRequestProcessorFactory
  45.     extends UpdateRequestProcessorFactory {

  46.   /** The Constant log. */
  47.   private static final Log log = LogFactory
  48.       .getLog(MtasUpdateRequestProcessorFactory.class);

  49.   /** The config. */
  50.   private MtasUpdateRequestProcessorConfig config = null;

  51.   /*
  52.    * (non-Javadoc)
  53.    *
  54.    * @see
  55.    * org.apache.solr.update.processor.UpdateRequestProcessorFactory#init(org.
  56.    * apache.solr.common.util.NamedList)
  57.    */
  58.   @Override
  59.   @SuppressWarnings("rawtypes")
  60.   public void init(NamedList args) {
  61.     super.init(args);
  62.   }

  63.   /**
  64.    * Inits the.
  65.    *
  66.    * @param req the req
  67.    * @throws IOException Signals that an I/O exception has occurred.
  68.    */
  69.   @SuppressWarnings("unchecked")
  70.   private void init(SolrQueryRequest req) throws IOException {
  71.     if (config == null) {
  72.       // initialise
  73.       config = new MtasUpdateRequestProcessorConfig();
  74.       // required info
  75.       Map<String, FieldType> fieldTypes = req.getSchema().getFieldTypes();
  76.       Map<String, SchemaField> fields = req.getSchema().getFields();
  77.       SolrResourceLoader resourceLoader = req.getCore().getSolrConfig()
  78.           .getResourceLoader();
  79.       // check fieldTypes
  80.       // for (String name : fieldTypes.keySet()) {
  81.       for (Entry<String, FieldType> entry : fieldTypes.entrySet()) {
  82.         // only for MtasPreAnalyzedField
  83.         if (entry.getValue() instanceof MtasPreAnalyzedField) {
  84.           MtasPreAnalyzedField mpaf = (MtasPreAnalyzedField) entry.getValue();
  85.           config.fieldTypeDefaultConfiguration.put(entry.getKey(),
  86.               mpaf.defaultConfiguration);
  87.           config.fieldTypeConfigurationFromField.put(entry.getKey(),
  88.               mpaf.configurationFromField);
  89.           config.fieldTypeNumberOfTokensField.put(entry.getKey(),
  90.               mpaf.setNumberOfTokens);
  91.           config.fieldTypeNumberOfPositionsField.put(entry.getKey(),
  92.               mpaf.setNumberOfPositions);
  93.           config.fieldTypeSizeField.put(entry.getKey(), mpaf.setSize);
  94.           config.fieldTypeErrorField.put(entry.getKey(), mpaf.setError);
  95.           config.fieldTypePrefixField.put(entry.getKey(), mpaf.setPrefix);
  96.           if (mpaf.followIndexAnalyzer == null
  97.               || !fieldTypes.containsKey(mpaf.followIndexAnalyzer)) {
  98.             throw new IOException(
  99.                 entry.getKey() + " can't follow " + mpaf.followIndexAnalyzer);
  100.           } else {
  101.             FieldType fieldType = fieldTypes.get(mpaf.followIndexAnalyzer);
  102.             SimpleOrderedMap<?> analyzer = null;
  103.             Object tmpObj1 = fieldType.getNamedPropertyValues(false)
  104.                 .get(FieldType.INDEX_ANALYZER);
  105.             if (tmpObj1 != null && tmpObj1 instanceof SimpleOrderedMap) {
  106.               analyzer = (SimpleOrderedMap<?>) tmpObj1;
  107.             }
  108.             if (analyzer == null) {
  109.               Object tmpObj2 = fieldType.getNamedPropertyValues(false)
  110.                   .get(FieldType.ANALYZER);
  111.               if (tmpObj2 != null && tmpObj2 instanceof SimpleOrderedMap) {
  112.                 analyzer = (SimpleOrderedMap<?>) tmpObj2;
  113.               }
  114.             }
  115.             if (analyzer == null) {
  116.               throw new IOException("no analyzer");
  117.             } else {
  118.               // charfilters
  119.               ArrayList<SimpleOrderedMap<Object>> listCharFilters = null;
  120.               SimpleOrderedMap<Object> configTokenizer = null;
  121.               try {
  122.                 listCharFilters = (ArrayList<SimpleOrderedMap<Object>>) analyzer
  123.                     .findRecursive(FieldType.CHAR_FILTERS);
  124.                 ;
  125.                 configTokenizer = (SimpleOrderedMap<Object>) analyzer
  126.                     .findRecursive(FieldType.TOKENIZER);
  127.               } catch (ClassCastException e) {
  128.                 throw new IOException(
  129.                     "could not cast charFilters and/or tokenizer from analyzer",
  130.                     e);
  131.               }
  132.               if (listCharFilters != null && !listCharFilters.isEmpty()) {
  133.                 CharFilterFactory[] charFilterFactories = new CharFilterFactory[listCharFilters
  134.                     .size()];
  135.                 int number = 0;
  136.                 for (SimpleOrderedMap<Object> configCharFilter : listCharFilters) {
  137.                   String className = null;
  138.                   Map<String, String> args = new HashMap<>();
  139.                   Iterator<Map.Entry<String, Object>> it = configCharFilter
  140.                       .iterator();
  141.                   // get className and args
  142.                   while (it.hasNext()) {
  143.                     Map.Entry<String, Object> obj = it.next();
  144.                     if (obj.getValue() instanceof String) {
  145.                       if (obj.getKey().equals(FieldType.CLASS_NAME)) {
  146.                         className = (String) obj.getValue();
  147.                       } else {
  148.                         args.put(obj.getKey(), (String) obj.getValue());
  149.                       }
  150.                     }
  151.                   }
  152.                   if (className != null) {
  153.                     try {
  154.                       Class<?> cls = Class.forName((String) className);
  155.                       if (cls.isAssignableFrom(MtasCharFilterFactory.class)) {
  156.                         Class<?>[] types = { Map.class, ResourceLoader.class };
  157.                         Constructor<?> cnstr = cls.getConstructor(types);
  158.                         Object cff = cnstr.newInstance(args, resourceLoader);
  159.                         if (cff instanceof MtasCharFilterFactory) {
  160.                           charFilterFactories[number] = (MtasCharFilterFactory) cff;
  161.                           number++;
  162.                         } else {
  163.                           throw new IOException(
  164.                               className + " is no MtasCharFilterFactory");
  165.                         }
  166.                       } else {
  167.                         Class<?>[] types = { Map.class };
  168.                         Constructor<?> cnstr = cls.getConstructor(types);
  169.                         Object cff = cnstr.newInstance(args);
  170.                         if (cff instanceof CharFilterFactory) {
  171.                           charFilterFactories[number] = (CharFilterFactory) cff;
  172.                           number++;
  173.                         } else {
  174.                           throw new IOException(
  175.                               className + " is no CharFilterFactory");
  176.                         }
  177.                       }
  178.                     } catch (ClassNotFoundException | InstantiationException
  179.                         | IllegalAccessException | IllegalArgumentException
  180.                         | InvocationTargetException | NoSuchMethodException e) {
  181.                       throw new IOException(e);
  182.                     }
  183.                   } else {
  184.                     throw new IOException("no className");
  185.                   }
  186.                 }
  187.                 config.fieldTypeCharFilterFactories.put(entry.getKey(),
  188.                     charFilterFactories);
  189.               } else {
  190.                 config.fieldTypeCharFilterFactories.put(entry.getKey(), null);
  191.               }
  192.               if (configTokenizer != null) {
  193.                 String className = null;
  194.                 Map<String, String> args = new HashMap<>();
  195.                 Iterator<Map.Entry<String, Object>> it = configTokenizer
  196.                     .iterator();
  197.                 // get className and args
  198.                 while (it.hasNext()) {
  199.                   Map.Entry<String, Object> obj = it.next();
  200.                   if (obj.getValue() instanceof String) {
  201.                     if (obj.getKey().equals(FieldType.CLASS_NAME)) {
  202.                       className = (String) obj.getValue();
  203.                     } else {
  204.                       args.put(obj.getKey(), (String) obj.getValue());
  205.                     }
  206.                   }
  207.                 }
  208.                 if (className != null) {
  209.                   try {
  210.                     Class<?> cls = Class.forName((String) className);
  211.                     Class<?>[] types = { Map.class, ResourceLoader.class };
  212.                     Constructor<?> cnstr = cls.getConstructor(types);
  213.                     Object cff = cnstr.newInstance(args, resourceLoader);
  214.                     if (cff instanceof MtasTokenizerFactory) {
  215.                       config.fieldTypeTokenizerFactory.put(entry.getKey(),
  216.                           (MtasTokenizerFactory) cff);
  217.                     } else {
  218.                       throw new IOException(
  219.                           className + " is no MtasTokenizerFactory");
  220.                     }
  221.                   } catch (ClassNotFoundException | InstantiationException
  222.                       | IllegalAccessException | IllegalArgumentException
  223.                       | InvocationTargetException | NoSuchMethodException e) {
  224.                     throw new IOException(e);
  225.                   }
  226.                 } else {
  227.                   throw new IOException("no className");
  228.                 }
  229.               }

  230.             }
  231.           }
  232.         }
  233.       }
  234.       for (Entry<String, SchemaField> entry : fields.entrySet()) {
  235.         if (entry.getValue().getType() != null
  236.             && config.fieldTypeTokenizerFactory
  237.                 .containsKey(entry.getValue().getType().getTypeName())) {
  238.           config.fieldMapping.put(entry.getKey(),
  239.               entry.getValue().getType().getTypeName());
  240.         }
  241.       }
  242.     }
  243.   }

  244.   /*
  245.    * (non-Javadoc)
  246.    *
  247.    * @see
  248.    * org.apache.solr.update.processor.UpdateRequestProcessorFactory#getInstance(
  249.    * org.apache.solr.request.SolrQueryRequest,
  250.    * org.apache.solr.response.SolrQueryResponse,
  251.    * org.apache.solr.update.processor.UpdateRequestProcessor)
  252.    */
  253.   @Override
  254.   public UpdateRequestProcessor getInstance(SolrQueryRequest req,
  255.       SolrQueryResponse rsp, UpdateRequestProcessor next) {
  256.     try {
  257.       init(req);
  258.     } catch (IOException e) {
  259.       log.error(e);
  260.     }
  261.     return new MtasUpdateRequestProcessor(next, config);
  262.   }

  263. }

  264. class MtasUpdateRequestProcessor extends UpdateRequestProcessor {

  265.   /** The log. */
  266.   private static Log log = LogFactory.getLog(MtasUpdateRequestProcessor.class);

  267.   private MtasUpdateRequestProcessorConfig config;

  268.   public MtasUpdateRequestProcessor(UpdateRequestProcessor next,
  269.       MtasUpdateRequestProcessorConfig config) {
  270.     super(next);
  271.     this.config = config;
  272.   }

  273.   @Override
  274.   public void processAdd(AddUpdateCommand cmd) throws IOException {
  275.     if (config != null && config.fieldMapping.size() > 0) {
  276.       // get document
  277.       SolrInputDocument doc = cmd.getSolrInputDocument();
  278.       // loop over configurations
  279.       for (String field : config.fieldMapping.keySet()) {
  280.         SolrInputField originalValue = doc.get(field);
  281.         String fieldType = config.fieldMapping.get(field);
  282.         CharFilterFactory[] charFilterFactories = config.fieldTypeCharFilterFactories
  283.             .get(fieldType);
  284.         MtasTokenizerFactory tokenizerFactory = config.fieldTypeTokenizerFactory
  285.             .get(config.fieldMapping.get(field));
  286.         MtasUpdateRequestProcessorSizeReader sizeReader;
  287.         if (originalValue != null
  288.             && originalValue.getValue() instanceof String) {
  289.           MtasUpdateRequestProcessorResultWriter result = null;
  290.           try {
  291.             String storedValue = (String) originalValue.getValue();
  292.             // create reader
  293.             Reader reader = new StringReader(storedValue);
  294.             // configuration
  295.             String configuration = null;
  296.             String defaultConfiguration = config.fieldTypeDefaultConfiguration
  297.                 .get(fieldType);
  298.             if (config.fieldTypeConfigurationFromField.get(fieldType) != null) {
  299.               Object obj = doc.getFieldValue(
  300.                   config.fieldTypeConfigurationFromField.get(fieldType));
  301.               if (obj != null) {
  302.                 configuration = obj.toString();
  303.               }
  304.             }
  305.             // charFilterFactories
  306.             if (charFilterFactories != null) {
  307.               for (CharFilterFactory charFilterFactory : charFilterFactories) {
  308.                 if (charFilterFactory instanceof MtasCharFilterFactory) {
  309.                   reader = ((MtasCharFilterFactory) charFilterFactory)
  310.                       .create(reader, configuration, defaultConfiguration);
  311.                 } else {
  312.                   reader = charFilterFactory.create(reader);
  313.                 }
  314.                 if (reader == null) {
  315.                   throw new IOException(
  316.                       "charFilter " + charFilterFactory.getClass().getName()
  317.                           + " returns null");
  318.                 }
  319.               }
  320.             }
  321.            
  322.             sizeReader = new MtasUpdateRequestProcessorSizeReader(reader);

  323.             // tokenizerFactory
  324.             result = new MtasUpdateRequestProcessorResultWriter(storedValue);
  325.             int numberOfPositions = 0;
  326.             int numberOfTokens = 0;
  327.             Set<String> prefixes = new HashSet<>();
  328.             try (MtasTokenizer tokenizer = tokenizerFactory.create(configuration, defaultConfiguration)) {              
  329.               tokenizer.setReader(sizeReader);
  330.               tokenizer.reset();
  331.               // attributes
  332.               CharTermAttribute termAttribute = tokenizer
  333.                   .getAttribute(CharTermAttribute.class);
  334.               OffsetAttribute offsetAttribute = tokenizer
  335.                   .getAttribute(OffsetAttribute.class);
  336.               PositionIncrementAttribute positionIncrementAttribute = tokenizer
  337.                   .getAttribute(PositionIncrementAttribute.class);
  338.               PayloadAttribute payloadAttribute = tokenizer
  339.                   .getAttribute(PayloadAttribute.class);
  340.               FlagsAttribute flagsAttribute = tokenizer
  341.                   .getAttribute(FlagsAttribute.class);

  342.               while (tokenizer.incrementToken()) {
  343.                 String term = null;
  344.                 Integer offsetStart = null;
  345.                 Integer offsetEnd = null;
  346.                 Integer posIncr = null;
  347.                 Integer flags = null;
  348.                 BytesRef payload = null;
  349.                 if (termAttribute != null) {
  350.                   term = termAttribute.toString();
  351.                   prefixes.add(CodecUtil.termPrefix(term));
  352.                 }
  353.                 if (offsetAttribute != null) {
  354.                   offsetStart = offsetAttribute.startOffset();
  355.                   offsetEnd = offsetAttribute.endOffset();
  356.                 }
  357.                 if (positionIncrementAttribute != null) {
  358.                   posIncr = positionIncrementAttribute.getPositionIncrement();
  359.                 } else {
  360.                   posIncr = 0;
  361.                 }
  362.                 if (payloadAttribute != null) {
  363.                   payload = payloadAttribute.getPayload();
  364.                 }
  365.                 if (flagsAttribute != null) {
  366.                   flags = flagsAttribute.getFlags();
  367.                 }
  368.                 numberOfTokens++;
  369.                 numberOfPositions += posIncr;
  370.                 result.addItem(term, offsetStart, offsetEnd, posIncr, payload,
  371.                     flags);
  372.                 // System.out.print(term+" ");
  373.               }

  374.               // update field
  375.               doc.remove(field);
  376.               if (result.getTokenNumber() > 0) {
  377.                 doc.addField(field, result.getFileName());
  378.               }
  379.             } finally {
  380.               result.close();
  381.             }
  382.             // update size
  383.             setFields(doc, config.fieldTypeSizeField.get(fieldType),
  384.                 sizeReader.getTotalReadSize());
  385.             // update numberOfPositions
  386.             setFields(doc,
  387.                 config.fieldTypeNumberOfPositionsField.get(fieldType),
  388.                 numberOfPositions);
  389.             // update numberOfTokens
  390.             setFields(doc, config.fieldTypeNumberOfTokensField.get(fieldType),
  391.                 numberOfTokens);
  392.             // update prefixes
  393.             setFields(doc, config.fieldTypePrefixField.get(fieldType),
  394.                 prefixes);
  395.           } catch (IOException e) {
  396.             log.info(e);
  397.             // update error
  398.             doc.addField(config.fieldTypeErrorField.get(fieldType),
  399.                 e.getMessage());
  400.             // update size
  401.             setFields(doc, config.fieldTypeSizeField.get(fieldType), 0);
  402.             // update numberOfPositions
  403.             setFields(doc,
  404.                 config.fieldTypeNumberOfPositionsField.get(fieldType), 0);
  405.             // update numberOfTokens
  406.             setFields(doc, config.fieldTypeNumberOfTokensField.get(fieldType),
  407.                 0);
  408.             // update prefixes
  409.             removeFields(doc, config.fieldTypePrefixField.get(fieldType));
  410.             if (result != null) {
  411.               result.forceCloseAndDelete();
  412.               doc.remove(field);
  413.             }
  414.           }
  415.         }
  416.       }

  417.     }
  418.     // pass it up the chain
  419.     super.processAdd(cmd);
  420.   }

  421.   private void removeFields(SolrInputDocument doc, String fieldNames) {
  422.     if (fieldNames != null) {
  423.       String[] tmpFields = fieldNames.split(",");
  424.       for (int i = 0; i < tmpFields.length; i++) {
  425.         doc.removeField(tmpFields[i]);
  426.       }
  427.     }
  428.   }

  429.   private void setFields(SolrInputDocument doc, String fieldNames,
  430.       Object value) {
  431.     if (fieldNames != null) {
  432.       String[] tmpFields = fieldNames.split(",");
  433.       for (int i = 0; i < tmpFields.length; i++) {
  434.         if (!tmpFields[i].trim().isEmpty()) {
  435.           doc.addField(tmpFields[i].trim(), value);
  436.         }
  437.       }
  438.     }
  439.   }

  440. }

  441. class MtasUpdateRequestProcessorConfig {

  442.   HashMap<String, CharFilterFactory[]> fieldTypeCharFilterFactories;
  443.   HashMap<String, MtasTokenizerFactory> fieldTypeTokenizerFactory;
  444.   HashMap<String, String> fieldMapping;
  445.   HashMap<String, String> fieldTypeDefaultConfiguration;
  446.   HashMap<String, String> fieldTypeConfigurationFromField;
  447.   HashMap<String, String> fieldTypeNumberOfTokensField;
  448.   HashMap<String, String> fieldTypeNumberOfPositionsField;
  449.   HashMap<String, String> fieldTypeSizeField;
  450.   HashMap<String, String> fieldTypeErrorField;
  451.   HashMap<String, String> fieldTypePrefixField;

  452.   MtasUpdateRequestProcessorConfig() {
  453.     fieldMapping = new HashMap<>();
  454.     fieldTypeCharFilterFactories = new HashMap<>();
  455.     fieldTypeTokenizerFactory = new HashMap<>();
  456.     fieldTypeDefaultConfiguration = new HashMap<>();
  457.     fieldTypeConfigurationFromField = new HashMap<>();
  458.     fieldTypeNumberOfTokensField = new HashMap<>();
  459.     fieldTypeNumberOfPositionsField = new HashMap<>();
  460.     fieldTypeSizeField = new HashMap<>();
  461.     fieldTypeErrorField = new HashMap<>();
  462.     fieldTypePrefixField = new HashMap<>();
  463.   }

  464. }

  465. class MtasUpdateRequestProcessorSizeReader extends Reader {

  466.   Reader reader;
  467.   long totalReadSize;

  468.   public MtasUpdateRequestProcessorSizeReader(Reader reader) {
  469.     this.reader = reader;
  470.     totalReadSize = 0;
  471.   }

  472.   public int read(char[] cbuf, int off, int len) throws IOException {
  473.     int read = reader.read(cbuf, off, len);
  474.     totalReadSize += read;
  475.     return read;
  476.   }

  477.   public void close() throws IOException {
  478.     reader.close();
  479.   }

  480.   public long getTotalReadSize() {
  481.     return totalReadSize;
  482.   }

  483. }