LuceneDAO

/*
 * Created on Mar 26, 2013
 *
 * Copyright 2013 ATPCO Confidential and Proprietary. All Rights Reserved.
 */
package net.atpco.dds.offline.filing.common.dao;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import net.atpco.batch.sterotype.LogIt;
import net.atpco.common.exception.ApplicationException;
import net.atpco.dds.common.DDSLogUtil;
import net.atpco.dds.offline.filing.common.exception.IndexFilesNotFoundException;
import net.atpco.dds.offline.filing.common.model.Entity;
import net.atpco.dds.offline.filing.constants.LuceneConstants;
import net.atpco.dds.offline.filing.model.DataType;
import net.atpco.dds.offline.filing.model.SearchCriteria;
import net.atpco.dds.offline.filing.model.SortCriteria;

import org.apache.log4j.Logger;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.TermVector;
import org.apache.lucene.document.FieldSelector;
import org.apache.lucene.document.FieldSelectorResult;
import org.apache.lucene.document.Fieldable;
import org.apache.lucene.document.NumericField;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexNotFoundException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.PersistentSnapshotDeletionPolicy;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TermRangeQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.WildcardQuery;
import org.apache.lucene.store.FSDirectory;

public class LuceneDAO {
 /** This LOGGER is used to log information related to LuceneSearchDAO. */
 private static final Logger LOGGER = DDSLogUtil.getLogger();

 /** The reader is used to read data from lucene index file. */
 protected transient IndexReader reader;

 /** The searcher is used to search data from lucene index file. */
 protected transient IndexSearcher searcher;

 /** The indexFilePath is used to locate the lucene index file path. */
 protected transient IndexWriter writer;

 /** The directory is used to open index file. */
 protected transient FSDirectory directory;

 protected transient String indexFilePath;

 protected transient FSDirectory snapShotDir = null;

 protected transient PersistentSnapshotDeletionPolicy snapshotter = null;
 
 protected transient Document document = new Document();

 /** A constructor without any parameter. */
 public LuceneDAO() {
  super();
 }

 /** A parameterized constructor without any parameter. */
 public LuceneDAO(final String indexFilePath) {
  super();
  this.indexFilePath = indexFilePath;
 }

 /**
  * This method is used to create instance of IndexReader&IndexSearcher.
  *
  * @param
  * */
 @LogIt
 protected void createReaderAndSearcher() {
  if (reader != null && searcher != null) {
   return;
  }

  if (indexFilePath != null) {
   final File indexFile = new File(indexFilePath);

   if (!indexFile.exists()) {
    throw new ApplicationException("Index Path does not exist. Path: " + indexFilePath,
      LOGGER);
   }

   try {
    directory = FSDirectory.open(indexFile);
    
    reader = IndexReader.open(directory, true);
    searcher = new IndexSearcher(reader);
   } catch (final IndexNotFoundException e) {
    throw new IndexFilesNotFoundException("No Index files exists in the path : " +indexFilePath,
      LOGGER, e);
   } catch (final IOException e) {
    throw new ApplicationException("IOException occures while reading index file" + indexFilePath, LOGGER,
      e);
   }
  } else {
   throw new ApplicationException(
     "indexFilePath is null in method createReaderAndSearcher.",
     LOGGER);
  }
 }

 /**
  * This method is used to populate an instance of BooleanQuery base on search criteria.
  * If the SearchCriteria List is null or empty, return BooleanQuery with MatchAllDocsQuery.
  *
  * @param searchCriteria - List<SearchCriteria>
  * @return BooleanQuery
  */
 @LogIt
 protected BooleanQuery getBooleanQuery(final List<SearchCriteria> searchCriteria) {
  if (searchCriteria != null && !searchCriteria.isEmpty()) {
   return populateMatchedQueryViaCriteria(searchCriteria);
  } else {
   final BooleanQuery bQuery = new BooleanQuery();
   bQuery.add(new MatchAllDocsQuery(), Occur.SHOULD);
   
   return bQuery;
  }
 }
 
 /**
  * This method is used to generate a Boolean Query in case of SearchCriteria List is coming from
  * Entities' getIndexKeyValue.
  *
  * @param searchCriteria - List<SearchCriteria>
  * @return BooleanQuery
  */
 @LogIt
 protected BooleanQuery getExpressBooleanQuery(final List<SearchCriteria> searchCriterias) {
  final BooleanQuery booleanQuery = new BooleanQuery();
  
  for(final SearchCriteria searchCriteria : searchCriterias){
   final String attributeName = searchCriteria.getAttributeName();
   final Object attributeValue = searchCriteria.getAttributeValue();
   final DataType attrValDataType = searchCriteria.getAttrValDataType();
   
   final SearchCriteria.ConditionType conditionType = searchCriteria.getConditionType();
   final SearchCriteria.RelationType relationType = searchCriteria.getRelationType();
   
   if (relationType.getRelationCategory() == SearchCriteria.LUCENE_QUERY_EQUALS) {
    // Get the matched attribute value
    final Object matchedObj = populateMatchedAttributeValue(attrValDataType, attributeValue);
    final Query query = populateQueryRelationIsEQ(attributeName, matchedObj, attrValDataType);
    // Add middleQuery into booleanQuery
    booleanQuery.add(query, conditionType.getOccur());
   }else{
    throw new ApplicationException("The relation type is not equals.", LOGGER);
   }
  }
  
  return booleanQuery;
 }

 @LogIt
 protected List<String> getColumnValue(final List<Document> documentList, final String fieldName) {
  final List<String> columnValue = new ArrayList<String>();
  /*
   * Loop the list of document and get every value based on columnName
   * from every document. Add every value into list of string based on
   * various judge.
   */
  for (final Document document : documentList) {
   final List<Fieldable> fields = document.getFields();
   for(final Fieldable field : fields){
    if(field.name().equals(fieldName)){
     columnValue.add(field.stringValue());
    }
   }
  }
  return columnValue;
 }
 
 /**
  * This method is used to populate BooleanQuery which matched the input list of search criteria.
  * Here, firstly the list of search criteria would be grouped through the contiguous same attribute
  * name if the size of the list is lesser than 3. After that every grouped search criteria would retrieve
  * the corresponding boolean query result and finally wrap these results into a single matched
  * BooleanQuery.
  *
  * @param searchCriteria
  *            - List<SearchCriteria>
  * @return booleanQuery matched search criteria
  *      - BooleanQuery
  *
  * */
 @LogIt
 protected BooleanQuery populateMatchedQueryViaCriteria(
   final List<SearchCriteria> searchCriteria) {
  BooleanQuery matchedBQuery = null;
  // If the size of searchCriteria is lesser than 3 then get matchedBQuery directly, otherwise
  // group the list as per the contiguous same attribute name
  if (searchCriteria.size() < LuceneConstants.LUCENE_GROUP_MINIMUM) {
   matchedBQuery = getMatchedQueryToRetrieve(searchCriteria);
  } else {
   // criteriaTemp is used to store the grouped SearchCriteria temporarily.
   final List<SearchCriteria> criteriaTemp = new ArrayList<SearchCriteria>();
   for (int i = 0; i < searchCriteria.size(); i++) {
    // If criteriaTemp is empty then put searchCriteria directly
    if (criteriaTemp.isEmpty()) {
     criteriaTemp.add(searchCriteria.get(i));
    } else {
     // Compare the attribute name from specific searchCriteria and the first element from
     // criteriaTemp since the criteriaTemp always stores the searchCriteria with
     // the same attribute name, if they equal then add the current searchCriteria into
     // criteriaTemp
     if (searchCriteria.get(i).getAttributeName().equals(criteriaTemp.get(0).getAttributeName())) {
      criteriaTemp.add(searchCriteria.get(i));
     }else {
      // If they are not equal then get the matched BooleanQuery for the grouped searchCriteria
      matchedBQuery = wrapBQueryAfterGroup(criteriaTemp,matchedBQuery);
      // Clear the criteriaTemp in order to store the newly searchCriteria to be grouped
      criteriaTemp.clear();
      criteriaTemp.add(searchCriteria.get(i));
     }
    }
    // If proceed to the last element of the list and criteriaTemp is not empty then get the
    // matched BooleanQuery for the last grouped searchCriteria
    if(i == searchCriteria.size()-1 && ! criteriaTemp.isEmpty()) {
     matchedBQuery = wrapBQueryAfterGroup(criteriaTemp,matchedBQuery);
     // Clear the criteriaTemp
     criteriaTemp.clear();
    }
   }
  }
  return matchedBQuery;
 }
 
 /**
  * This method is used to wrap the BooleanQuery which matched the input list of grouped search criteria.
  *
  * @param criteriaTemp
  *            - List<SearchCriteria>
  * @param matchedBQuery
  *      - BooleanQuery
  * @return wrapped BooleanQuery
  *     - BooleanQuery
  *
  * */
 @LogIt
 private BooleanQuery wrapBQueryAfterGroup(final List<SearchCriteria> criteriaTemp, final BooleanQuery matchedBQuery){
  BooleanQuery matchedBQueryCopy = matchedBQuery;
  // wrapQuery is used to wrap the BooleanQuery which matched the grouped search criteria
  final BooleanQuery wrappedQuery = new BooleanQuery();
  BooleanQuery tempBQuery = null;
  // Fetch the occur condition before getting tempBQuery since it may be changed after invoking
  // getMatchedQueryToRetrieve
  final Occur occurCondition = criteriaTemp.get(0).getConditionType().getOccur();
  tempBQuery = getMatchedQueryToRetrieve(criteriaTemp);
  // wrapping the BooleanQuery which matched the grouped search criteria
  wrappedQuery.add(tempBQuery, occurCondition);
  // If matchedBQueryCopy is null then set wrappedQuery to it directly otherwise add the matchedBQueryCopy into
  // wrappedQuery and then reset wrappedQuery  to matchedBQueryCopy
  if (matchedBQueryCopy == null) {
   matchedBQueryCopy = wrappedQuery;
  } else {
   wrappedQuery.add(matchedBQueryCopy, occurCondition);
   matchedBQueryCopy = wrappedQuery;
  }
  return matchedBQueryCopy;
 }
 

 /**
  * This method is used to get an instance of BooleanQuery based on the input
  * search criteria.
  *
  * @param searchCriteria
  *            - List<SearchCriteria>
  * @return booleanQuery matched search criteria - BooleanQuery
  * */
 @SuppressWarnings("unchecked")
 @LogIt
 private BooleanQuery getMatchedQueryToRetrieve(
   final List<SearchCriteria> searchCriteria) {
  BooleanQuery matchedBQuery = null;
  String attributeName = null;
  Object attributeValue = null;
  DataType attrValDataType = null;
  SearchCriteria.ConditionType conditionType = null;
  SearchCriteria.RelationType relationType = null;
  Query middleQuery = null;
  Object matchedObj = null;
  // Reset the condition type of first search criteria if the size of
  // searchCriteria is greater than 1.
  if (searchCriteria.size() > LuceneConstants.TOTAL_SIZE) {
   searchCriteria.get(LuceneConstants.SCRIPT_ZERO).setConditionType(
     searchCriteria.get(LuceneConstants.SCRIPT_ONE)
       .getConditionType());
  }
  // Loop the list of searchCriteria
  for (final SearchCriteria searchCriterion : searchCriteria) {
   final BooleanQuery booleanQuery = new BooleanQuery();
   attributeName = searchCriterion.getAttributeName();
   attributeValue = searchCriterion.getAttributeValue();
   conditionType = searchCriterion.getConditionType();
   attrValDataType = searchCriterion.getAttrValDataType();
   relationType = searchCriterion.getRelationType();
   //Check if attributeName or conditionType or attrValDataType or relationType is valid.
   if (attributeName != null && !attributeName.isEmpty()
     && conditionType != null && attrValDataType != null
     && relationType != null) {

    // relation category is zero, and the corresponding relationType
    // is EQ,namely equal
    if (relationType.getRelationCategory() == SearchCriteria.LUCENE_QUERY_EQUALS) {
     // Get the matched attribute value
     matchedObj = checkAttrValForRelTypeIsEQOrNE(attributeValue, attrValDataType);
     middleQuery = populateQueryRelationIsEQ(attributeName,
       matchedObj, attrValDataType);
     // Add middleQuery into booleanQuery
     booleanQuery.add(middleQuery, conditionType.getOccur());
    }
    // relation category is one, and the corresponding relationType
    // is NE,namely NOT equal
    else if (relationType.getRelationCategory() == SearchCriteria.LUCENE_QUERY_NOT_EQUALS) {
     // Get the matched attribute value
     matchedObj = checkAttrValForRelTypeIsEQOrNE(attributeValue, attrValDataType);
     // tempBQuery is used to be added into booleanQuery
     middleQuery = populateBQueryRelationIsNE(attributeName,
       matchedObj, attrValDataType);
     booleanQuery.add(middleQuery, conditionType.getOccur());
    }
    // relation category is two, and the corresponding relationType
    // may be GT,GTEQ,LT,LTEQ,namely, >, >=, < and <=
    else if (relationType.getRelationCategory() == SearchCriteria.LUCENE_QUERY_RANGE) {
     // Check if attributeValue is null
     if (attributeValue == null) {
      throw new ApplicationException(
        "attributeValue is null, it's not suitable for half-open ranges search .",
        LOGGER);
     }
     
     boolean minInclusive = false;
     boolean maxInclusive = false;
     
     Object minValue = null;
     Object maxValue = null;
     Object searchValue = null;
     
     if (attrValDataType.equals(DataType.DATE)) {
      // If attribute value data type is DATE.
      searchValue = Long.valueOf(((Date) attributeValue).getTime());
     }else if (attrValDataType.equals(DataType.INTEGER)) {
      // If attribute value data type is INTEGER
      searchValue = attributeValue;
     }else {
      // If attribute value data type is STRING
      searchValue = attributeValue;
     }
     
     switch (relationType) {
      case GT:
       minValue = searchValue;
       break;
      case GTEQ:
       minValue = searchValue;
       minInclusive = true;
       break;
      case LT:
       maxValue = searchValue;
       break;
      case LTEQ:
       maxValue = searchValue;
       maxInclusive = true;
       break;
      default:
       break;
     }
     
     if (attrValDataType.equals(DataType.DATE)) {
      // Add NumericRangeQuery to BooleanQuery
      booleanQuery.add(NumericRangeQuery.newLongRange(attributeName, (Long)minValue, (Long)maxValue, minInclusive, maxInclusive), conditionType.getOccur());
     } else if (attrValDataType.equals(DataType.INTEGER)) {
      // Add NumericRangeQuery to booleanQuery
      booleanQuery.add(NumericRangeQuery.newIntRange(attributeName, (Integer)minValue, (Integer)maxValue, minInclusive, maxInclusive), conditionType.getOccur());
     } else {
      // Add TermRangeQuery to booleanQuery
      booleanQuery.add(new TermRangeQuery(attributeName, (String)minValue, (String)maxValue, minInclusive, maxInclusive), conditionType.getOccur());
     }     
     
    }
    // relation category is three, and the corresponding relationType may be IN or NOTIN
    else if (relationType.getRelationCategory() == SearchCriteria.LUCENE_QUERY_INCLUSION) {
     // match the attribute value with attribute value data type
     final List<Object> matchedList = (List<Object>) matchAttrValWithDataType(
       attrValDataType, attributeValue, relationType);
     // inclusionQuery is used to contain queries for matchedList
     final BooleanQuery inclusionQuery = new BooleanQuery();
     // Iterate each single object from matchedList and populate
     // the corresponding needed query
     for (final Object matchedAttributeValue : matchedList) {
      // relationType is IN then populate query and relation
      // type is EQ otherwise relationType is NOTIN then populate query and
      // relation type is NE
      if(relationType.equals(SearchCriteria.RelationType.IN)){
       middleQuery = populateQueryRelationIsEQ(
         attributeName, matchedAttributeValue,
         attrValDataType);
       inclusionQuery.add(middleQuery, Occur.SHOULD);
      } else {
       middleQuery = populateBQueryRelationIsNE(
         attributeName, matchedAttributeValue,
         attrValDataType);
       inclusionQuery.add(middleQuery, Occur.MUST);
      }
     }
     // Add inclusionQuery into booleanQuery
     booleanQuery.add(inclusionQuery, conditionType.getOccur());
    } else {
     throw new ApplicationException(
       "Other relation category of relation type is invalid now.",
       LOGGER);
    }
   } else {
    throw new ApplicationException(
      "attributeName or conditionType or attrValDataType or relationType is null in method getMatchedQueryToRetrieve.",
      LOGGER);
   }
   // If matchedBQuery is null then set booleanQuery to it, or add
   // matchedBQuery into the newly booleanQuery and finally reset newly
   // booleanQuery to matchedBQuery
   if (matchedBQuery == null) {
    matchedBQuery = booleanQuery;
   } else {
    booleanQuery.add(matchedBQuery, conditionType.getOccur());
    matchedBQuery = booleanQuery;
   }
  }
  return matchedBQuery;
 }
 
 /**
  * This method is used to check if attributeValue is null if it is then set it as matchedObj
  * otherwise get the matched attributeValue through attrValDataType.
  *
  * @param attributeValue
  *            - Object
  * @param attrValDataType
  *            - DataType
  * @return matched Object
  *      - Object
  *
  * */
 @LogIt
 private Object checkAttrValForRelTypeIsEQOrNE(final Object attributeValue, final DataType attrValDataType){
  Object matchedObj = null;
  // If attributeValue is null then set it to matchedObj,
  // otherwise match the attribute value with data type to matchedObj
  if(attributeValue == null){
   matchedObj = attributeValue;
  } else {
   matchedObj = matchAttrValWithDataType(attrValDataType,
     attributeValue, null);
  }
  return matchedObj;
 }
 
 /**
  * This method is used to match the attribute value with attribute value
  * data type via relation type.
  *
  * @param dataType
  *            - DataType
  * @param attributeValue
  *            - Object
  * @param relationType
  *            - SearchCriteria.RelationType
  * @return matched Object
  *      - Object
  *
  * */
 @SuppressWarnings("rawtypes")
 @LogIt
 private Object matchAttrValWithDataType(final DataType dataType,
   final Object attributeValue,
   final SearchCriteria.RelationType relationType) {
  Object matchedObj = null;
  try {
   // If relationType is null then populate matched attribute value
   // directly otherwise iterate every single value from passed attribute value
   if (relationType == null) {
    matchedObj = populateMatchedAttributeValue(dataType,
      attributeValue);
   } else {
    // Cast attributeValue into Collection and iterate it
    final Collection collection = (Collection) attributeValue;
    final Iterator it = collection.iterator();
    final List<Object> matchedList = new ArrayList<Object>();
    while (it.hasNext()) {
     matchedObj = populateMatchedAttributeValue(dataType,
       it.next());
     // Add every matchedObj from iterator into matchedList
     matchedList.add(matchedObj);
    }
    // Reset the matchedList after iterator to matchedObj
    matchedObj = matchedList;
   }
  } catch (final ClassCastException e) {
   throw new ApplicationException(
     "attributeValue is not matched the dataType.", LOGGER, e);
  }
  return matchedObj;
 }

 /**
  * This method is used to populate matched object through data type and
  * attribute value and it would throw ClassCastException if the data type
  * and attribute value are unmatched.
  *
  * @param dataType
  *            - DataType
  * @param attributeValue
  *            - Object
  * @return matched Object
  *      - Object
  * @throws ClassCastException
  *
  * */
 @LogIt
 private Object populateMatchedAttributeValue(final DataType dataType, final Object attributeValue) throws ClassCastException {
  Object matchedObj = null;
  // If dataType is DATE then cast the object into Date and then get time
  if (dataType.equals(DataType.DATE)) {
   matchedObj = ((Date) attributeValue).getTime();
  } else if (dataType.equals(DataType.INTEGER)) {
   // If dataType is DATE then cast the object into Integer
   matchedObj = attributeValue;
  } else {
   // Otherwise convert attribute value to String
   matchedObj = attributeValue.toString();
  }
  return matchedObj;
 }
 
 /**
  * This method is used to populate the needed query for the relation type is
  * EQ as per the specific attribute name, attribute value and attribute data
  * type.
  *
  * @param attributeName
  *            - String
  * @param attributeValue
  *            - Object
  * @param attrValDataType
  *            - DataType
  * @return middleQuery
  *      - Query
  *
  * */
 @LogIt
 private Query populateQueryRelationIsEQ(final String attributeName,
   final Object attributeValue, final DataType attrValDataType) {
  Query middleQuery = null;
  // If attributeValue is null then replace attributeValue with
  // NULL_SUBSTITUTION
  if (attributeValue == null) {
   middleQuery = new TermQuery(new Term(attributeName,
     LuceneConstants.NULL_SUBSTITUTION));
  } else {
   // If attribute value data type is DATE then cast attributeValue into
   // Long
   if (attrValDataType.equals(DataType.DATE)) {
    final Long numRange = (Long) attributeValue;
    middleQuery = NumericRangeQuery.newLongRange(attributeName,
      LuceneConstants.PRECISIONSTEP, numRange, numRange, true,
      true);
   } else if (attrValDataType.equals(DataType.INTEGER)) {
    // If attribute value data type is INTEGER then cast attributeValue
    // into Integer
    final Integer numRange = (Integer) attributeValue;
    middleQuery = NumericRangeQuery.newIntRange(attributeName,
      LuceneConstants.PRECISIONSTEP, numRange, numRange, true,
      true);
   } else {
    // If attribute value data type is STRING then cast attributeValue
    // into String
    final String matchedAttributeValue = (String) attributeValue;
    middleQuery = new TermQuery(new Term(attributeName,
      matchedAttributeValue));
   }
  }
  return middleQuery;
 }

 /**
  * This method is used to populate the needed query for the relation type is
  * NE as per the specific attribute name, attribute value and attribute data
  * type.
  *
  * @param attributeName
  *            - String
  * @param attributeValue
  *            - Object
  * @param attrValDataType
  *            - DataType
  * @return middleQuery - BooleanQuery
  *
  * */
 @LogIt
 private BooleanQuery populateBQueryRelationIsNE(final String attributeName,
   final Object attributeValue, final DataType attrValDataType) {
  Query middleQuery = null;
  // tempBQuery is used to contain middleQuery and WildcardQuery
  final BooleanQuery tempBQuery = new BooleanQuery();
  // wildCardQuery is used to get all records from the index file
  final WildcardQuery wildCardQuery = new WildcardQuery(new Term(
    attributeName, LuceneConstants.STARSIGN));
  middleQuery = populateQueryRelationIsEQ(attributeName, attributeValue, attrValDataType );
  // Add wildCardQuery and middleQuery into booleanQuery
  tempBQuery.add(middleQuery, Occur.MUST_NOT);
  tempBQuery.add(wildCardQuery, Occur.MUST);
  return tempBQuery;
 }

 /**
  * This method is used to get a list of Document based on the input query.
  *
  * @param query - BooleanQuery
  * @return list of document - List<Document>
  * @throws IOException
  * */
 @LogIt
 protected List<Document> getDocumentMatchingSearchCriteria(final BooleanQuery query) throws IOException {
  final List<Document> documentList = new ArrayList<Document>();
  final ScoreDoc[] scoreDocs = getScoreDocArray(query);
  Document document = null;
  // Loop the matched documents and add every document into documentList.
  
  final SpecialFieldSelector sfs = new SpecialFieldSelector(LuceneConstants.FIELD_NAME_FOR_OBJECT);
  for (final ScoreDoc scoreDoc : scoreDocs) {
   document = searcher.doc(scoreDoc.doc, sfs);
   documentList.add(document);
  }
  return documentList;
 }

 /**
  * This overload method is used to get a list of Document based on the input query.
  *
  * @param query - BooleanQuery
  * @return list of document - List<Document>
  * @throws IOException
  * */
 @LogIt
 protected List<Document> getDocumentMatchingSearchCriteria(final BooleanQuery query, final String fieldName) throws IOException {
  final List<Document> documentList = new ArrayList<Document>();
  final ScoreDoc[] scoreDocs = getScoreDocArray(query);
  final FieldSelector fieldSelector = new SpecialFieldSelector(fieldName);

  // Loop the matched documents and add every document into documentList.
  for (final ScoreDoc scoreDoc : scoreDocs) {
   documentList.add(searcher.doc(scoreDoc.doc, fieldSelector));
  }
  return documentList;
 }
 /**
  * This method is used to get array of ScoreDoc based on the input query.
  *
  * @param query
  *            - BooleanQuery
  * @return array of ScoreDoc
  *    - ScoreDoc[]
  * @throws IOException
  * */
 @LogIt
 public ScoreDoc[] getScoreDocArray(final BooleanQuery query)
   throws IOException {
  // Search the matched documents based on the query.
  
  return searcher.search(query, 100000).scoreDocs;
 }
 
 /**
  * This method is used to get array of ScoreDoc based on the input query
  * and sort the result based on the sort criteria.
  *
  * @param query
  *            - BooleanQuery
  * @param sortCriteria
  *     - List<SortCriteria>
  * @return array of ScoreDoc
  *     - ScoreDoc[]
  * @throws IOException
  * */
 @LogIt
 public ScoreDoc[] getScoreDocArrayWithSorting(final BooleanQuery query, final List<SortCriteria> sortCriteria)
   throws IOException {
  final SortField[] sortFieldArray = getSortFieldArray(sortCriteria);
  // Search the matched documents based on the query.
  
  return searcher.search(query, Integer.MAX_VALUE, new Sort(sortFieldArray)).scoreDocs;
 }

 /**
  * This method is used to return a SortField based on the input sortCrteria.
  *
  * @param sortCriteria
  *            List<SortCriteria>
  * @return an array of SortField - SortField[]
  * */
 @LogIt
 private SortField[] getSortFieldArray(final List<SortCriteria> sortCriteria) {
  final SortField[] sortFieldArray = new SortField[sortCriteria.size()];
  String fieldToSort = null;
  DataType sortType = null;
  boolean sortDirection = false;
  /*
   * Loop the sortFieldArray and initiate it based on the different sort
   * type.
   */
  for (int i = 0; i < sortFieldArray.length; i++) {
   fieldToSort = sortCriteria.get(i).getFieldToSort();
   sortType = sortCriteria.get(i).getSortType();
   sortDirection = sortCriteria.get(i).isSortDirection();
   /*
    * fieldToSort and sortType should not be null and sortType should
    * be in STRING, INT or DATE.
    */
   // Removed the duplicate boolean check
   if (fieldToSort != null && sortType != null) {
    // SortType here is STRING.
    if (sortType.equals(DataType.STRING)) {
     sortFieldArray[i] = new SortField(fieldToSort, SortField.STRING,
       sortDirection);

    } // SortType here is INT.
    else if (sortType.equals(DataType.INTEGER)) {
     sortFieldArray[i] = new SortField(fieldToSort, SortField.INT,
       sortDirection);
    } else if (sortType.equals(DataType.DATE)) {
     // SortType here is DATE.
     sortFieldArray[i] = new SortField(fieldToSort, SortField.LONG,
       sortDirection);
    }
   } else {
    throw new ApplicationException(
      "fieldToSort or sortType is null or sortType is unequal with STRING, INT and DATE in method getSortFieldArray.",
      LOGGER);
   }
  }
  return sortFieldArray;
 }

 /**
  * This method is used to get a list of Document based on the input query
  * and sort criteria.
  *
  * @param query
  *            - BooleanQuery
  * @param sortCriteria
  *            - List<SortCriteria>
  * @throws IOException
  * */
 @LogIt
 public List<Document> getDocumentMatchingSearchCriteriaWithSorting(
   final BooleanQuery query, final List<SortCriteria> sortCriteria)
   throws IOException {
  final SortField[] sortFieldArray = getSortFieldArray(sortCriteria);
  final List<Document> documentList = new ArrayList<Document>();
  // Search the matched documents based on the query.
  final TopDocs topDocs = searcher.search(query, Integer.MAX_VALUE,
    new Sort(sortFieldArray));
  final ScoreDoc[] scoreDocs = topDocs.scoreDocs;
  Document document = null;
  // Loop the matched documents and add every document into documentList.
  for (final ScoreDoc scoreDoc : scoreDocs) {
   document = searcher.doc(scoreDoc.doc);
   documentList.add(document);
  }
  return documentList;
 }

 /**
  * This method is used to convert an byte array to Object based on the input
  * byteData.
  *
  * @param byteData
  *            - byte[]
  * @return object converted from byte array - Object
  * */
 @LogIt
 private Object convertByteArrayToObject(final byte[] byteData) {
  Object object = null;
  ObjectInputStream inStream = null;
  try {
   // Instantiate inStream using constructor with ByteArrayInputStream.
   inStream = new ObjectInputStream(new ByteArrayInputStream(byteData));
   object = inStream.readObject();
  } catch (final ClassNotFoundException e) {
   throw new ApplicationException(
     "Ocuured ClassNotFoundException in method convertByteArrayToObject",
     LOGGER, e);
  } catch (final IOException e) {
   throw new ApplicationException(
     "Ocuured IOException in method convertByteArrayToObject",
     LOGGER, e);
  } finally {
   // Close inStream if it is null.
   if (inStream != null) {
    try {
     inStream.close();
    } catch (final IOException e) {
     LOGGER.error("Exception occured while trying to close "
       + "the input stream in finally block", e);
    }
   }
  }
  return object;
 }

 /**
  * This method is used to get a list of Object based on the input
  * documentList.
  *
  * @param documentList - List<Document>
  * @return list of entity - List<Entity>
  * */
 @LogIt
 public List<Entity> getDesiredObject(final List<Document> documentList) {
  final List<Entity> objectList = new ArrayList<Entity>();
  byte[] byteData = null;
  Object object = null;
  // Loop the documentList and invoke method convertByteArrayToObject.
  for (final Document document : documentList) {
   byteData = document.getFieldable(
     LuceneConstants.FIELD_NAME_FOR_OBJECT).getBinaryValue();
   object = convertByteArrayToObject(byteData);
   objectList.add((Entity) object);
  }
  return objectList;
 }

 /**
  * This method is used to get all documents from lucene index file.
  *
  * @return List<Document> - List of "Lucene" document
  * @throws IOException
  * @throws CorruptIndexException
  * */
 @LogIt
 protected List<Document> getAllDocuments() throws CorruptIndexException, IOException {
  final List<Document> documentList = new ArrayList<Document>();
  final int totalNumDocs = reader.numDocs();
  Document document = null;
  // Loop totalNumDocs and add every document in to documentList.
  for (int i = 0; i < totalNumDocs; i++) {
   document = reader.document(i);
   documentList.add(document);
  }
  return documentList;
 }

 /**
  * This overload method is used to get all documents only with specific field from lucene index file.
  *
  * @param fieldName - String
  * @return List<Document> - List of "Lucene" document
  * @throws CorruptIndexException
  * @throws IOException
  */
 @LogIt
 protected List<Document> getAllDocuments(final String fieldName) throws CorruptIndexException, IOException {
  final List<Document> documentList = new ArrayList<Document>();
  final int totalNumDocs = reader.numDocs();
  // Loop totalNumDocs and add every document in to documentList.
  
  final FieldSelector fieldSelector = new SpecialFieldSelector(fieldName);
  
  for (int i = 0; i < totalNumDocs; i++) {
   documentList.add(reader.document(i, fieldSelector));
  }
  return documentList;
 }

 /**
  * This method is used to create Document related to the input object.
  *
  * @param objectToStore
  *            - Entity
  *
  * @return document consists of matched fields - Document
  * */
 public Document createDocument(final Entity objectToStore) {
  final Map<String, Object> indexKeys = objectToStore.getIndexKeyValue();
  // If indexKeys is not null and is not empty then instantiate document.
  if (indexKeys != null && !indexKeys.isEmpty()) {
   // Convert objectToStore into byte array.
   final byte[] objectByte = convertObjectToByteArray(objectToStore);
   Field field = null;
   // add objectField into document.

   if(document.getFieldable(LuceneConstants.FIELD_NAME_FOR_OBJECT) == null){
    field = new Field(LuceneConstants.FIELD_NAME_FOR_OBJECT, objectByte);
    document.add(field);
   }else{
    field = (Field) document.getFieldable(LuceneConstants.FIELD_NAME_FOR_OBJECT);
    field.setValue(objectByte);
   }
   
   String keyName = null;
   Object keyValue = null;
   
   NumericField numericField = null;
   for (final Map.Entry<String, Object> indexKey : indexKeys.entrySet()) {
    keyName = indexKey.getKey();
    keyValue = indexKey.getValue();
    if (keyName != null && !keyName.isEmpty()) {
     /*
      * Check if the keyValue is null or an instance of Date, if
      * it is null then set the value of field into "NuLl_", if
      * it is an instance of date then set the value of field to
      * one Date.
      */
     if (keyValue == null) {
      if(document.getFieldable(keyName) == null){
       field = new Field(keyName, LuceneConstants.NULL_SUBSTITUTION, Field.Store.YES, Field.Index.NOT_ANALYZED, TermVector.NO);
       document.add(field);
      }else{
       field = (Field) document.getFieldable(keyName);
       field.setValue(LuceneConstants.NULL_SUBSTITUTION);
      }
     } else {
      if (keyValue instanceof Date) {
       if(document.getFieldable(keyName) == null){
        numericField = new NumericField(keyName, Field.Store.YES, true);
        numericField.setLongValue(((Date) keyValue).getTime());
        
        document.add(numericField);
       }else{
        numericField = (NumericField)document.getFieldable(keyName);
        numericField.setLongValue(((Date) keyValue).getTime());
       }
      } else if (keyValue instanceof Integer) {
       if(document.getFieldable(keyName) == null){
        numericField = new NumericField(keyName, Field.Store.YES, true);
        numericField.setIntValue((Integer)keyValue);
        
        document.add(numericField);
       }else{
        numericField = (NumericField)document.getFieldable(keyName);
        numericField.setIntValue((Integer) keyValue);
       }
      } else {
       if(document.getFieldable(keyName) == null){
        field = new Field(keyName, keyValue.toString(), Field.Store.YES, Field.Index.NOT_ANALYZED, TermVector.NO);
        
        document.add(field);
       }else{
        field = (Field)document.getFieldable(keyName);
        field.setValue(keyValue.toString());
       }
      }
     }
    } else {
     throw new ApplicationException(
       "keyName should not be null or empty."+ objectToStore.toString(), LOGGER);
    }
   }
  } else {
   throw new ApplicationException(
     "columnList is null in method createDocument." +objectToStore.toString(), LOGGER);
  }
  return document;
 }

 /**
  * This method is used to convert object to byte array.
  *
  * @param objectToStore
  *            - Object
  * @return an array of byte - byte[]
  * */
 public byte[] convertObjectToByteArray(final Object objectToStore) {
  final ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
  ObjectOutput outStream = null;
  try {
   // Instantiate outStream using constructor with byteStream.
   outStream = new ObjectOutputStream(byteStream);
   outStream.writeObject(objectToStore);
  } catch (final IOException e) {
   throw new ApplicationException(
     "Occured IOException in method convertObjectToByteArray" + objectToStore.toString(),
     LOGGER, e);
  } finally {
   try {
    // Close outStream if it is not null.
    if (outStream != null) {
     outStream.close();
    }
    // Close byteStream.
    byteStream.close();
   } catch (final IOException e) {
    LOGGER.error("Error occured in finally while closing"+objectToStore.toString(), e);
   }
  }
  return byteStream.toByteArray();
 }
}


class SpecialFieldSelector implements FieldSelector{
 /**
  * serialVersionUID
  */
 private static final long serialVersionUID = 1L;
 
 protected String selectFieldName;
 

 public SpecialFieldSelector(final String m_szFieldName) {
  this.selectFieldName = m_szFieldName;
 }

 @Override
 public FieldSelectorResult accept(final String fieldName) {
  if(fieldName.equalsIgnoreCase(selectFieldName)){
   return FieldSelectorResult.LOAD;
  }else{
   return FieldSelectorResult.NO_LOAD;
  }
 }
}

你可能感兴趣的:(DAO,技术,Lucene)