/*
* 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;
}
}
}