/*******************************************************************************
 * Copyright 2014 Centre for Development of Advanced Computing(C-DAC), Pune
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package in.cdac.medinfo.csnotk.csnolib.agents;

import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.ACCEPTABILITY;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.ACTIVE;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.ATTRIBUTE_TYPE;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.CHARACTERISTICTYPEID;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.CLASS_NAME;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.CONCEPTID;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.CONCEPT_CHECKPONT;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.CONCEPT_FSN;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.CONCEPT_STATE;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.CONCRETE_RELATIONSHIP_ALL_RECORDS;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.CONCRETE_RELATIONSHIP_EXTENSION_RECORDS_COUNT;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.COUNTOFCHILDREN;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.DATA_DIR;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.DEFINITIONSTATUS;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.DESCRIPTION_ALL_RECORDS;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.DESCRIPTION_MODID;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.DESC_CHECKPONT;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.DESTINATION;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.DISTINCT;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.EFFECTIVETIME;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.HIERARCHY;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.ID;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.INDEX_OFFSET;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.ISIMMEDIATECHILDREN;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.IS_PREFERRED_TERM;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.MAP_INDEX;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.MAX_RECORDS;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.MODIFIERID;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.MODULEID;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.NEW_LINE;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.PARENTID;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.PREFERRED_NAME;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.REFERENCEDCOMPONENT;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.REFINDEX;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.REFSETCOUNT;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.REFSETTERM;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.REFSET_ID;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.RELATIONSHIPGROUP;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.RELATIONSHIP_ALL_RECORDS;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.RELATIONSHIP_EXTENSION_RECORDS_COUNT;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.RELATIONSHIP_TC_ALL_RECORDS;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.RELATION_INDEX;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.RELATION_TYPE;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.RELATION_TYPE_ALL;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.RELATION_TYPE_ALL_RECORDS;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.REL_TYPE_INDEX;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.SEARCH_TERM;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.SEMANTIC_ALL_RECORDS;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.SEMANTIC_TAG;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.SEMINDEX;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.SIMPLEMAP_RECORDS_COUNT;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.SIMPLE_MAP_INDEX;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.SIMPLE_MAP_TARGET;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.SNOINDEX;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.SOURCE;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.STATE;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.TCINDEX;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.TERM;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.TOTAL_HIT_THRESHOLD;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.TYPEID;
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.TYPE;
//INDIA_MODULE_ID
import static in.cdac.medinfo.csnotk.csnolib.commons.Constants.INDIA_MODULE_ID;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.TopScoreDocCollector;
import org.apache.lucene.search.WildcardQuery;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;

import in.cdac.medinfo.csnotk.csnolib.api.ISearchAgent;
import in.cdac.medinfo.csnotk.csnolib.commons.DBSchemaMapping;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumAcceptability;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumAcceptabilityId;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumCaseSignificance;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumCharacteristicType;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumComponent;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumDefStatus;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumDescType;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumDirection;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumLangRefset;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumModifier;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumProperties;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumRelationshipType;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumSemanticTag;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumState;
import in.cdac.medinfo.csnotk.csnolib.db.ConnectionManager;
import in.cdac.medinfo.csnotk.csnolib.db.DBUtil;
import in.cdac.medinfo.csnotk.csnolib.exception.IndexException;
import in.cdac.medinfo.csnotk.csnolib.factory.IndexReaderFactory;
import in.cdac.medinfo.csnotk.csnolib.model.ComponentFactory;
import in.cdac.medinfo.csnotk.csnolib.model.CompositeDescription;
import in.cdac.medinfo.csnotk.csnolib.model.Concept;
import in.cdac.medinfo.csnotk.csnolib.model.ConcreteRelationship;
import in.cdac.medinfo.csnotk.csnolib.model.RefSetMember;
import in.cdac.medinfo.csnotk.csnolib.model.Refset;
import in.cdac.medinfo.csnotk.csnolib.model.Relationship;
import in.cdac.medinfo.csnotk.csnolib.model.RelationshipTC;
import in.cdac.medinfo.csnotk.csnolib.model.SimpleMapRefset;
import in.cdac.medinfo.csnotk.csnolib.model.SimpleMapRefsetGroup;
import in.cdac.medinfo.csnotk.csnolib.model.SimpleMapRefsetResult;
import in.cdac.medinfo.csnotk.csnolib.util.KeyBasedComparator;
import in.cdac.medinfo.csnotk.csnolib.util.PropertyReader;
import in.cdac.medinfo.csnotk.csnolib.util.SemanticTagBuilder;
import in.cdac.medinfo.csnotk.csnolib.util.SnoDomainUtil;
//Logs
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import in.cdac.medinfo.csnotk.csnolib.util.LoggerUtility;
//BooleanQuery for deletion
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.TermQuery;

/**
 * Implementation of document search functionality with Lucene version 8.7.0.
 * Search functionality and implementation of index creation is provided by this
 * class.
 */
public class LuceneSearchAgent implements ISearchAgent {


	private static final String STATEMENT_IS_NOT_CLOSED = "Statement is not closed";

	private static final String INVALID_PARAMETER = "INVALID PARAMETER";

	private static final String E_MMM_DD_HH_MM_SS_Z_YYYY = "E MMM dd HH:mm:ss Z yyyy";

	private static final String AND = " AND ";
	private static final String PREFERRED_TERM_NOT_FOUND_FOR_SCTID = "preferred term not found for SCTID:";

	private static final String INDEX_GENERATION_PROCESS_COMPLETED = " index generation process completed !!!";

	private static final String INDEX_GENERATION_PROCESS_INTERRUPTED = " index generation process interrupted !!!";

	private static final String NUMBER_OF_RECORDS_WHICH_ARE_INDEXED_TILL_NOW = "Number of records which are indexed till now : ";

	private static final String TOTAL_NUMBER_OF = "Total number of ";

	private static final String INDEX_CREATION_FAILED_FOR  = "Index creation failed for";

	private static final String RECORDS_NEED_TO_BE_INDEXED = " records need to be indexed : ";

	private static final long serialVersionUID = 1L;

	private static final int RESPONSE_SIZE_FOR_EXTENSION = 100;

	/**
	 * static Fields
	 */
	private IndexWriter indexWriter = null;
	private Analyzer analyzer = null;
	private IndexWriterConfig indexConfig = null;
	private Directory indexDirectory = null;
	private static Set<String> semanticTagsForIndexing = new HashSet<>();
	private static List<String> relationTypesForIndexing = new ArrayList<>();
	protected HashMap<String, String> relationTypeMap = new HashMap<>();
    private static HashMap<String,HashMap<Integer,Integer>> extensionIdMap = new HashMap<>();

	private IndexReaderFactory indexReaderFactory = IndexReaderFactory.getInstance();

	/**
	 * Returns {@link IndexWriter} object.
	 *
	 * @param refsetId Identifier of Refset
	 * @return object of {@link IndexWriter}.
	 * @throws IOException if any error occurs while processing.
	 */
	private IndexWriter getIndexWriter(String refsetId) throws IOException {
		if (this.indexWriter == null) {
			this.indexWriter = new IndexWriter(getIndexDirectoryToWrite(refsetId), getIndexConfig());
		}
		return indexWriter;
	}

	/**
	 * Returns {@link IndexReader} object.
	 *
	 * @param refsetId Identifier of Refset
	 * @return instance of {@link IndexReader}.
	 * @throws IOException if any error occurs while processing.
	 */
	private IndexReader getIndexReader(String refsetId) throws IOException {
		return indexReaderFactory.getIndexReader(refsetId);
	}

	/**
	 * Returns {@link IndexSearcher} object.
	 *
	 * @param reader instance of {@link IndexReader} .
	 * @return {@link IndexSearcher}.
	 */
	private IndexSearcher getIndexSearcher(IndexReader reader) {
		return new IndexSearcher(reader);
	}

	/**
	 * Returns {@link Analyzer} object.
	 *
	 * @return {@link Analyzer}.
	 */
	private Analyzer getAnalyzer() {
		if (this.analyzer == null) {
			this.analyzer = new SpecialCharAnalyzer();
		}
		return this.analyzer;
	}

	/**
	 * Returns {@link IndexWriterConfig} object.
	 *
	 * @return {@link IndexWriterConfig}.
	 */
	private IndexWriterConfig getIndexConfig() {
		if (this.indexConfig == null) {
			this.indexConfig = new IndexWriterConfig(this.getAnalyzer());
		}
		return this.indexConfig;
	}

	/**
	 * Returns index directory location to write the files.
	 *
	 * @param refsetId Identifier of Refset
	 * @return {@link Directory}.
	 * @throws IOException if any error occurs while processing.
	 */
	private Directory getIndexDirectoryToWrite(String refsetId) throws IOException {
        Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
		if (indexDirectory == null) {
			PropertyReader.loadSystemProperties();
			try {
				if (refsetId == null)
					indexDirectory = FSDirectory
							.open(Paths.get(new File(PropertyReader.getProperty(DATA_DIR) + File.separator + SNOINDEX)
									.getAbsolutePath()));
				if (SIMPLE_MAP_INDEX.equals(refsetId) || SNOINDEX.equals(refsetId) || RELATION_INDEX.equals(refsetId)
						|| TCINDEX.equals(refsetId) || SEMINDEX.equals(refsetId) || REL_TYPE_INDEX.equals(refsetId))
					indexDirectory = FSDirectory
							.open(Paths.get(new File(PropertyReader.getProperty(DATA_DIR) + File.separator + refsetId)
									.getAbsolutePath()));
				else
					indexDirectory = FSDirectory
							.open(Paths.get(new File(PropertyReader.getProperty(DATA_DIR) + File.separator + REFINDEX)
									.getAbsolutePath()));
			} catch (IOException e) {

				logger.info(LoggerUtility.Log, "Creating sub directory on WinNTFileSystem");
				StackTraceElement[] arrStackTraceElement = e.getStackTrace();
				if (arrStackTraceElement[0].getClassName().equals("java.io.WinNTFileSystem")) {
					try {
						if (refsetId == null || SNOINDEX.equals(refsetId) || RELATION_INDEX.equals(refsetId)
								|| TCINDEX.equals(refsetId) || SEMINDEX.equals(refsetId)
								|| SIMPLE_MAP_INDEX.equals(refsetId) || REL_TYPE_INDEX.equals(refsetId))
							new File(PropertyReader.getProperty(DATA_DIR) + File.separator + refsetId).mkdir();
						else
							new File(PropertyReader.getProperty(DATA_DIR) + File.separator + REFINDEX).mkdir();
						getIndexDirectoryToWrite(refsetId);
					} catch (IOException ie) {
						throw new IOException("Failed to create sub directory on WinNTFileSystem");
					}
				} else
					throw new IOException(e);
			}
		}
		return indexDirectory;
	}

	/**
	 * Adds documents to lucene index.
	 *
	 * @param List of {@link CompositeDescription}, {@link Relationship},
	 *             {@link RelationshipTC}
	 * @return {@link IndexWriter}.
	 * @throws Exception
	 */
	// @SuppressWarnings("deprecation")
	@SuppressWarnings("unchecked")
	private <T> IndexWriter addLuceneDocument(List<T> list) throws Exception {
		Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
		if (list != null) {
			Document document = null;
			if (!list.isEmpty() && list.get(0) instanceof CompositeDescription) {
				Field id = new StringField(ID, "", Field.Store.YES);
				Field active = new StringField(ACTIVE, "", Field.Store.YES);
				Field conceptId = new StringField(CONCEPTID, "", Field.Store.YES);
				Field typeId = new StringField(TYPEID, "", Field.Store.YES);
				Field term = new TextField(TERM, "", Field.Store.YES);
				Field hierarchy = new StringField(HIERARCHY, "", Field.Store.YES);
				Field preferredTerm = new StringField(IS_PREFERRED_TERM, "", Field.Store.YES);
				Field conceptState = new StringField(CONCEPT_STATE, "", Field.Store.YES);
				Field conceptFSN = new StringField(CONCEPT_FSN, "", Field.Store.YES);
				Field definitionStatus = new StringField(DEFINITIONSTATUS, "", Field.Store.YES);
				Field moduleId = new StringField(DESCRIPTION_MODID, "", Field.Store.YES);
				Field caseSignificanceId = new StringField(DBSchemaMapping.DESCRIPTION_CASE_SIGNIFICANCE_ID, "",
						Field.Store.YES);
				Field languageCode = new StringField(DBSchemaMapping.DESCRIPTION_LANGUAGE_CODE, "", Field.Store.YES);
				Field descriptionEffectiveTime = new StringField(DBSchemaMapping.DESCRIPTION_EFFECTIVE_TIME, "",
						Field.Store.YES);
				Field refsetLangReferenceComponentId = new StringField(
						DBSchemaMapping.REFSETLANG_REFERENCED_COMPONENT_ID, "", Field.Store.YES);
				Field refsetLangReferenceId = new StringField(DBSchemaMapping.REFSETLANG_REFSET_ID, "",
						Field.Store.YES);
				Field refsetLangActive = new StringField(DBSchemaMapping.REFSETLANG_ACTIVE, "", Field.Store.YES);
				Field refsetLangAcceptability = new StringField(DBSchemaMapping.REFSETLANG_ACCEPTABILITY_ID, "",
						Field.Store.YES);
				for (CompositeDescription compositeDescription : (List<CompositeDescription>) list) {
					/* create document to index */

					/*
					 * Create fields to be indexed for document As term need be indexed and to be
					 * shown in results , so use it as a text field. Add fields to document
					 */
					try {
						document = new Document();
						id.setStringValue(compositeDescription.getId());
						document.add(id);
						active.setStringValue(compositeDescription.getActiveStatus() + "");
						document.add(active);
						conceptId.setStringValue(compositeDescription.getConceptId());
						document.add(conceptId);
						typeId.setStringValue(compositeDescription.getType());
						document.add(typeId);
						term.setStringValue(compositeDescription.getTerm());
						document.add(term);
						hierarchy.setStringValue(compositeDescription.getHierarchy());
						document.add(hierarchy);
						preferredTerm.setStringValue(compositeDescription.getIsPreferredTerm());
						document.add(preferredTerm);
						conceptState.setStringValue(compositeDescription.getConceptState());
						document.add(conceptState);
						conceptFSN.setStringValue(compositeDescription.getConceptFsn());
						document.add(conceptFSN);
						definitionStatus.setStringValue(compositeDescription.getDefinitionStatus());
						document.add(definitionStatus);
						moduleId.setStringValue(compositeDescription.getModuleId());
						document.add(moduleId);
						caseSignificanceId.setStringValue(compositeDescription.getCaseSId());
						document.add(caseSignificanceId);
						languageCode.setStringValue(compositeDescription.getLangCode());
						document.add(languageCode);
						descriptionEffectiveTime.setStringValue(compositeDescription.getTime() + "");
						document.add(descriptionEffectiveTime);
						refsetLangReferenceComponentId
								.setStringValue(compositeDescription.getRefSetLangMember().getReferencedComponentId());
						document.add(refsetLangReferenceComponentId);
						refsetLangReferenceId.setStringValue(compositeDescription.getRefSetLangMember().getRefSetId());
						document.add(refsetLangReferenceId);
						refsetLangActive
								.setStringValue(compositeDescription.getRefSetLangMember().getActiveStatus() + "");
						document.add(refsetLangActive);
						refsetLangAcceptability
								.setStringValue(compositeDescription.getRefSetLangMember().getAcceptabilityId());
						document.add(refsetLangAcceptability);
						this.indexWriter.addDocument(document);
						// adding semantic tags into Set for storing index
						if (!compositeDescription.getHierarchy().isEmpty())
							semanticTagsForIndexing.add(compositeDescription.getHierarchy());
					} catch (Exception e) {
						if (compositeDescription.getConceptId() != null && compositeDescription.getId() != null) {
							String msg = "";
							if (e.getMessage() != null)
								msg = e.getMessage() + ", ";
							msg = msg + "conceptId: " + compositeDescription.getConceptId() + " , descriptionId: "
									+ compositeDescription.getId();
							logger.error(LoggerUtility.ConsoleWithException, "{}", msg);
							throw new IndexException(msg);
						} else
							throw new Exception(e);
					}
				}
			} else if (!list.isEmpty() && list.get(0) instanceof Relationship) {
				Field effectiveTime = new StringField(EFFECTIVETIME, "", Field.Store.YES);
				Field active = new StringField(ACTIVE, "", Field.Store.YES);
				Field moduleId = new StringField(MODULEID, "", Field.Store.YES);
				Field sourceId = new StringField(SOURCE + CONCEPTID, "", Field.Store.YES);
				Field sourceActive = new StringField(SOURCE + ACTIVE, "", Field.Store.YES);
				Field sourceFSN = new StringField(SOURCE + CONCEPT_FSN, "", Field.Store.YES);
				Field sourcePreferredName = new StringField(SOURCE   + PREFERRED_NAME, "", Field.Store.YES);
				Field sourceDefinitionStatusId = new StringField(SOURCE + DEFINITIONSTATUS, "", Field.Store.YES);
				Field sourceEffectiveTime = new StringField(SOURCE + EFFECTIVETIME, "", Field.Store.YES);
				Field sourceModuleId = new StringField(SOURCE + MODULEID, "", Field.Store.YES);

				Field hierarchy = new StringField(HIERARCHY, "", Field.Store.YES);

				Field destinationId = new StringField(DESTINATION + CONCEPTID, "", Field.Store.YES);
				Field destionationActive = new StringField(DESTINATION + ACTIVE, "", Field.Store.YES);
				Field destionationFSN = new StringField(DESTINATION + CONCEPT_FSN, "", Field.Store.YES);
				Field destionationPreferredName = new StringField(DESTINATION   + PREFERRED_NAME, "", Field.Store.YES);
				Field destionationDefinitionStatusId = new StringField(DESTINATION + DEFINITIONSTATUS, "",
						Field.Store.YES);
				Field destionationEffectiveTime = new StringField(DESTINATION + EFFECTIVETIME, "", Field.Store.YES);
				Field destionationModuleId = new StringField(DESTINATION + MODULEID, "", Field.Store.YES);
				Field relationshipGroup = new StringField(RELATIONSHIPGROUP, "", Field.Store.YES);

				Field typeId = new StringField(TYPE + CONCEPTID, "", Field.Store.YES);
				Field typeActive = new StringField(TYPE + ACTIVE, "", Field.Store.YES);
				Field typeFSN = new StringField(TYPE + CONCEPT_FSN, "", Field.Store.YES);
				Field typePreferredName = new StringField(TYPE   + PREFERRED_NAME, "", Field.Store.YES);
				Field typeDefinitionStatusId = new StringField(TYPE + DEFINITIONSTATUS, "", Field.Store.YES);
				Field typeEffectiveTime = new StringField(TYPE + EFFECTIVETIME, "", Field.Store.YES);
				Field typeModuleId = new StringField(TYPE + MODULEID, "", Field.Store.YES);
				Field characteristicTypeId = new StringField(CHARACTERISTICTYPEID, "", Field.Store.YES);
				Field modifierId = new StringField(MODIFIERID, "", Field.Store.YES);
				for (Relationship relationship : (List<Relationship>) list) {
					try {
						document = new Document();
						effectiveTime.setStringValue(relationship.getTime() + "");
						document.add(effectiveTime);
						active.setStringValue(relationship.getActiveStatus() + "");
						document.add(active);
						moduleId.setStringValue(relationship.getModuleId());
						document.add(moduleId);
						sourceId.setStringValue(relationship.getSourceConcept().getId());
						document.add(sourceId);
						sourceActive.setStringValue(relationship.getSourceConcept().getActiveStatus() + "");
						document.add(sourceActive);
						sourceFSN.setStringValue(relationship.getSourceConcept().getFullySpecifiedName());
						document.add(sourceFSN);
						try {
							sourcePreferredName.setStringValue(relationship.getSourceConcept().getPreferredName(EnumLangRefset.US));
						}catch(SQLException e) {
							logger.error(LoggerUtility.Exception, "{}{}",PREFERRED_TERM_NOT_FOUND_FOR_SCTID,relationship.getSourceConcept().getId());
							sourcePreferredName.setStringValue("N/A");
						}
						document.add(sourcePreferredName);
						sourceDefinitionStatusId
								.setStringValue(relationship.getSourceConcept().getDefinitionStatusId());
						document.add(sourceDefinitionStatusId);
						sourceEffectiveTime.setStringValue(relationship.getSourceConcept().getTime() + "");
						document.add(sourceEffectiveTime);
						sourceModuleId.setStringValue(relationship.getSourceConcept().getModuleId());
						document.add(sourceModuleId);

						// adding hierarchy/ semantic tag in indexing
						hierarchy.setStringValue(SemanticTagBuilder
								.buildSemanticTagFromString(relationship.getSourceConcept().getFullySpecifiedName()));
						document.add(hierarchy);

						destinationId.setStringValue(relationship.getDestinationConcept().getId());
						document.add(destinationId);
						destionationActive.setStringValue(relationship.getDestinationConcept().getActiveStatus() + "");
						document.add(destionationActive);
						destionationFSN.setStringValue(relationship.getDestinationConcept().getFullySpecifiedName());
						try {
							destionationPreferredName.setStringValue(relationship.getDestinationConcept().getPreferredName(EnumLangRefset.US));
						}catch(SQLException e) {
							logger.error(LoggerUtility.Exception, "{}{}",PREFERRED_TERM_NOT_FOUND_FOR_SCTID,relationship.getDestinationConcept().getId());
							destionationPreferredName.setStringValue("N/A");
						}
						document.add(destionationPreferredName);
						document.add(destionationFSN);
						destionationDefinitionStatusId
								.setStringValue(relationship.getDestinationConcept().getDefinitionStatusId());
						document.add(destionationDefinitionStatusId);
						destionationEffectiveTime.setStringValue(relationship.getDestinationConcept().getTime() + "");
						document.add(destionationEffectiveTime);
						destionationModuleId.setStringValue(relationship.getDestinationConcept().getModuleId());
						document.add(destionationModuleId);
						relationshipGroup.setStringValue(relationship.getRelationshipGroup() + "");
						document.add(relationshipGroup);

						typeId.setStringValue(relationship.getType().getId());
						document.add(typeId);
						typeActive.setStringValue(relationship.getType().getActiveStatus() + "");
						document.add(typeActive);
						typeFSN.setStringValue(relationship.getType().getFullySpecifiedName());
						document.add(typeFSN);
						try {
							typePreferredName.setStringValue(relationship.getType().getPreferredName(EnumLangRefset.US));
						}catch(SQLException e) {
							logger.error(LoggerUtility.Exception, "{}{}",PREFERRED_TERM_NOT_FOUND_FOR_SCTID,relationship.getType().getId());
							typePreferredName.setStringValue("N/A");
						}
						document.add(typePreferredName);
						typeDefinitionStatusId.setStringValue(relationship.getType().getDefinitionStatusId());
						document.add(typeDefinitionStatusId);
						typeEffectiveTime.setStringValue(relationship.getType().getTime() + "");
						document.add(typeEffectiveTime);
						typeModuleId.setStringValue(relationship.getType().getModuleId());
						document.add(typeModuleId);
						characteristicTypeId.setStringValue(relationship.getCharacteristicTypeId());
						document.add(characteristicTypeId);
						modifierId.setStringValue(relationship.getModifierId());
						document.add(modifierId);
						this.indexWriter.addDocument(document);
					}catch (Exception e) {
						String msg = "";
						if (relationship.getId() != null) {
							if (relationship.getSourceConcept().getId() == null) {
								if (e.getMessage() != null)
									msg = e.getMessage() + ", ";
								msg = msg + "SourceId is missing from concept/ description file for RelationshipId:"
										+ relationship.getId();
							} else if (relationship.getDestinationConcept().getId() == null) {
								if (e.getMessage() != null)
									msg = e.getMessage() + ", ";
								msg = msg
										+ "DestinationId is missing from concept/ description file for RelationshipId:"
										+ relationship.getId();
							} else if (relationship.getType().getId() == null) {
								if (e.getMessage() != null)
									msg = e.getMessage() + ", ";
								msg = msg + "TypeId is missing from concept/ description file for RelationshipId:"
										+ relationship.getId();
							}
							logger.error(LoggerUtility.ConsoleWithException, "{}", msg);
							throw new IndexException(msg);
						} else
							throw new Exception(e);
					}
				}
			} else if (!list.isEmpty() && list.get(0) instanceof ConcreteRelationship) {
				Field effectiveTime = new StringField(EFFECTIVETIME, "", Field.Store.YES);
				Field active = new StringField(ACTIVE, "", Field.Store.YES);
				Field moduleId = new StringField(MODULEID, "", Field.Store.YES);
				Field sourceId = new StringField(SOURCE + CONCEPTID, "", Field.Store.YES);
				Field sourceActive = new StringField(SOURCE + ACTIVE, "", Field.Store.YES);
				Field sourceFSN = new StringField(SOURCE + CONCEPT_FSN, "", Field.Store.YES);
				Field sourceDefinitionStatusId = new StringField(SOURCE + DEFINITIONSTATUS, "", Field.Store.YES);
				Field sourceEffectiveTime = new StringField(SOURCE + EFFECTIVETIME, "", Field.Store.YES);
				Field sourceModuleId = new StringField(SOURCE + MODULEID, "", Field.Store.YES);

				Field hierarchy = new StringField(HIERARCHY, "", Field.Store.YES);
				Field destinationId = new StringField(DESTINATION + CONCEPTID, "", Field.Store.YES);
				Field concreteValuePrefix = new StringField("valueWithPrefix", "", Field.Store.YES);
				Field dataType = new StringField("dataType", "", Field.Store.YES);

				Field relationshipGroup = new StringField(RELATIONSHIPGROUP, "", Field.Store.YES);

				Field typeId = new StringField(TYPE + CONCEPTID, "", Field.Store.YES);
				Field typeActive = new StringField(TYPE + ACTIVE, "", Field.Store.YES);
				Field typeFSN = new StringField(TYPE + CONCEPT_FSN, "", Field.Store.YES);
				Field typeDefinitionStatusId = new StringField(TYPE + DEFINITIONSTATUS, "", Field.Store.YES);
				Field typeEffectiveTime = new StringField(TYPE + EFFECTIVETIME, "", Field.Store.YES);
				Field typeModuleId = new StringField(TYPE + MODULEID, "", Field.Store.YES);
				Field characteristicTypeId = new StringField(CHARACTERISTICTYPEID, "", Field.Store.YES);
				Field modifierId = new StringField(MODIFIERID, "", Field.Store.YES);
				for (ConcreteRelationship relationship : (List<ConcreteRelationship>) list) {
					try {
						document = new Document();
						effectiveTime.setStringValue(relationship.getTime() + "");
						document.add(effectiveTime);
						active.setStringValue(relationship.getActiveStatus() + "");
						document.add(active);
						moduleId.setStringValue(relationship.getModuleId());
						document.add(moduleId);
						sourceId.setStringValue(relationship.getSourceConcept().getId());
						document.add(sourceId);
						sourceActive.setStringValue(relationship.getSourceConcept().getActiveStatus() + "");
						document.add(sourceActive);
						sourceFSN.setStringValue(relationship.getSourceConcept().getFullySpecifiedName());
						document.add(sourceFSN);
						sourceDefinitionStatusId
								.setStringValue(relationship.getSourceConcept().getDefinitionStatusId());
						document.add(sourceDefinitionStatusId);
						sourceEffectiveTime.setStringValue(relationship.getSourceConcept().getTime() + "");
						document.add(sourceEffectiveTime);
						sourceModuleId.setStringValue(relationship.getSourceConcept().getModuleId());
						document.add(sourceModuleId);

						// adding hierarchy/ semantic tag in indexing
						hierarchy.setStringValue(SemanticTagBuilder
								.buildSemanticTagFromString(relationship.getSourceConcept().getFullySpecifiedName()));
						document.add(hierarchy);

						destinationId.setStringValue(relationship.getDestinationValue());
						document.add(destinationId);

						if (relationship.getDestinationValue().contains("#"))
							dataType.setStringValue("DECIMAL");
						else
							dataType.setStringValue("STRING");
						document.add(dataType);

						concreteValuePrefix.setStringValue(relationship.getDestinationValueWithPrefix());
						document.add(concreteValuePrefix);

						relationshipGroup.setStringValue(relationship.getRelationshipGroup() + "");
						document.add(relationshipGroup);

						typeId.setStringValue(relationship.getType().getId());
						document.add(typeId);
						typeActive.setStringValue(relationship.getType().getActiveStatus() + "");
						document.add(typeActive);
						typeFSN.setStringValue(relationship.getType().getFullySpecifiedName());
						document.add(typeFSN);
						typeDefinitionStatusId.setStringValue(relationship.getType().getDefinitionStatusId());
						document.add(typeDefinitionStatusId);
						typeEffectiveTime.setStringValue(relationship.getType().getTime() + "");
						document.add(typeEffectiveTime);
						typeModuleId.setStringValue(relationship.getType().getModuleId());
						document.add(typeModuleId);
						characteristicTypeId.setStringValue(relationship.getCharacteristicTypeId());
						document.add(characteristicTypeId);
						modifierId.setStringValue(relationship.getModifierId());
						document.add(modifierId);
						this.indexWriter.addDocument(document);
					} catch (Exception e) {
						String msg = "";
						if (relationship.getId() != null) {
							if (relationship.getSourceConcept().getId() == null) {
								if (e.getMessage() != null)
									msg = e.getMessage() + ", ";
								msg = msg + "SourceId is missing from concept/ description file for RelationshipId:"
										+ relationship.getId();
							} else if (relationship.getDestinationValue() == null) {
								if (e.getMessage() != null)
									msg = e.getMessage() + ", ";
								msg = msg
										+ "DestinationValue is missing from concept/ description file for RelationshipId:"
										+ relationship.getId();
							} else if (relationship.getType().getId() == null) {
								if (e.getMessage() != null)
									msg = e.getMessage() + ", ";
								msg = msg + "TypeId is missing from concept/ description file for RelationshipId:"
										+ relationship.getId();
							}
							logger.error(LoggerUtility.ConsoleWithException, "{}", msg);
							throw new IndexException(msg + "caused By" + e.getMessage());
						} else
							throw new Exception(e);
					}
				}
			}
			// *******************************
			else if (!list.isEmpty() && list.get(0) instanceof SimpleMapRefset) {
				Field id = new StringField(ID, "", Field.Store.YES);
				Field effectiveTime = new StringField(EFFECTIVETIME, "", Field.Store.YES);
				Field active = new StringField(ACTIVE, "", Field.Store.YES);
				Field moduleId = new StringField(MODULEID, "", Field.Store.YES);
				Field refsetId = new StringField(REFSET_ID, "", Field.Store.YES);
				Field referencedComponentId = new StringField(REFERENCEDCOMPONENT + CONCEPTID, "", Field.Store.YES);
				Field referencedComponentActive = new StringField(REFERENCEDCOMPONENT + ACTIVE, "", Field.Store.YES);
				Field referencedComponentFSN = new StringField(REFERENCEDCOMPONENT + CONCEPT_FSN, "", Field.Store.YES);
				Field referencedComponentDefinitionStatusId = new StringField(REFERENCEDCOMPONENT + DEFINITIONSTATUS,
						"", Field.Store.YES);
				Field referencedComponentEffectiveTime = new StringField(REFERENCEDCOMPONENT + EFFECTIVETIME, "",
						Field.Store.YES);
				Field referencedComponentModuleId = new StringField(REFERENCEDCOMPONENT + MODULEID, "",
						Field.Store.YES);
				Field mapTarget = new StringField(SIMPLE_MAP_TARGET, "", Field.Store.YES);
				for (SimpleMapRefset simpleMapRefset : (List<SimpleMapRefset>) list) {
					document = new Document();
					id.setStringValue(simpleMapRefset.getId() + "");
					document.add(id);
					effectiveTime.setStringValue(simpleMapRefset.getTime() + "");
					document.add(effectiveTime);
					active.setStringValue(simpleMapRefset.getActiveStatus() + "");
					document.add(active);
					moduleId.setStringValue(simpleMapRefset.getModuleId());
					document.add(moduleId);
					refsetId.setStringValue(simpleMapRefset.getRefSetId());
					document.add(refsetId);
					referencedComponentId.setStringValue(simpleMapRefset.getreferencedComponent().getId());
					document.add(referencedComponentId);
					referencedComponentActive
							.setStringValue(simpleMapRefset.getreferencedComponent().getActiveStatus() + "");
					document.add(referencedComponentActive);
					referencedComponentFSN
							.setStringValue(simpleMapRefset.getreferencedComponent().getFullySpecifiedName());
					document.add(referencedComponentFSN);
					referencedComponentDefinitionStatusId
							.setStringValue(simpleMapRefset.getreferencedComponent().getDefinitionStatusId());
					document.add(referencedComponentDefinitionStatusId);
					referencedComponentEffectiveTime
							.setStringValue(simpleMapRefset.getreferencedComponent().getTime() + "");
					document.add(referencedComponentEffectiveTime);
					referencedComponentModuleId.setStringValue(simpleMapRefset.getreferencedComponent().getModuleId());
					document.add(referencedComponentModuleId);
					mapTarget.setStringValue(simpleMapRefset.getMapTarget() + "");
					document.add(mapTarget);

					this.indexWriter.addDocument(document);
				}
			} else if (!list.isEmpty() && list.get(0) instanceof RelationshipTC) {
				Field parentId = new StringField(PARENTID, "", Field.Store.YES);
				Field childId = new StringField(ID, "", Field.Store.YES);
				Field childFSN = new TextField(CONCEPT_FSN, "", Field.Store.YES);
				Field childPreferredName = new TextField(PREFERRED_NAME, "", Field.Store.YES);
				Field childDefinitionStatusId = new StringField(DEFINITIONSTATUS, "", Field.Store.YES);
				Field childActiveStatus = new StringField(ACTIVE, "", Field.Store.YES);
				Field childEffectiveTime = new StringField(EFFECTIVETIME, "", Field.Store.YES);
				Field childModuleId = new StringField(DESCRIPTION_MODID, "", Field.Store.YES);
				Field countOfChildrens = new StringField(COUNTOFCHILDREN, "", Field.Store.YES);
				Field isImmediateChildren = new StringField(ISIMMEDIATECHILDREN, "", Field.Store.YES);
				for (RelationshipTC relationship : (List<RelationshipTC>) list) {
					document = new Document();
					parentId.setStringValue(relationship.getParentId());
					document.add(parentId);
					childId.setStringValue(relationship.getChildConcept().getId());
					document.add(childId);
					childFSN.setStringValue(relationship.getChildConcept().getFullySpecifiedName());
					document.add(childFSN);
                    try {
                        childPreferredName.setStringValue(relationship.getChildConcept().getPreferredName(EnumLangRefset.US));
                    }catch(SQLException e) {
                        logger.error(LoggerUtility.Exception, "{}{}", PREFERRED_TERM_NOT_FOUND_FOR_SCTID,relationship.getChildConcept().getId());
                        childPreferredName.setStringValue("N/A");
                    }
					document.add(childPreferredName);
					childDefinitionStatusId.setStringValue(relationship.getChildConcept().getDefinitionStatusId());
					document.add(childDefinitionStatusId);
					childActiveStatus.setStringValue(relationship.getChildConcept().getActiveStatus() + "");
					document.add(childActiveStatus);
					childEffectiveTime.setStringValue(relationship.getChildConcept().getTime() + "");
					document.add(childEffectiveTime);
					childModuleId.setStringValue(relationship.getChildConcept().getModuleId());
					document.add(childModuleId);
					countOfChildrens.setStringValue(relationship.getCountOfChildrens() + "");
					document.add(countOfChildrens);
					isImmediateChildren.setStringValue(relationship.getIsImmediateChildren() + "");
					document.add(isImmediateChildren);
					this.indexWriter.addDocument(document);
				}
			}
		}
		return this.indexWriter;
	}

	/**
	 * Searches for a particular input query in the index.
	 *
	 * @param query              input query i.e. the term to be searched.
	 * @param returnLimitPerPage returnLimit per page.
	 * @param inOrder            whether the results should be in order.
	 * @param refsetId           Identifier of Refset
	 * @return scoreDocs ScoreDoc array that contains list of top scoring results.
	 * @throws IOException if any error occurs while processing.
	 */
	private ScoreDoc[] search(Query query, int returnLimitPerPage, String refsetId) throws IOException {
		int indexOffset = INDEX_OFFSET;

		PropertyReader.loadSystemProperties();
		TopScoreDocCollector topScoreDocCollector = null;
		IndexReader indexReader = null;
		if (returnLimitPerPage == 0) {
			returnLimitPerPage = returnLimitPerPage + indexOffset;
			topScoreDocCollector = TopScoreDocCollector.create(returnLimitPerPage, TOTAL_HIT_THRESHOLD);
			if (refsetId == null)
				topScoreDocCollector = TopScoreDocCollector.create(DESCRIPTION_ALL_RECORDS, TOTAL_HIT_THRESHOLD);
			else if (refsetId.equalsIgnoreCase(RELATION_INDEX))
				topScoreDocCollector = TopScoreDocCollector.create(RELATIONSHIP_ALL_RECORDS, TOTAL_HIT_THRESHOLD);
			else if (refsetId.equalsIgnoreCase(TCINDEX)) {
				topScoreDocCollector = TopScoreDocCollector.create(RELATIONSHIP_TC_ALL_RECORDS, TOTAL_HIT_THRESHOLD);
			} else if (refsetId.equalsIgnoreCase(SEMINDEX)) {
				topScoreDocCollector = TopScoreDocCollector.create(SEMANTIC_ALL_RECORDS, TOTAL_HIT_THRESHOLD);
			}
		} else {
			topScoreDocCollector = TopScoreDocCollector.create(returnLimitPerPage, TOTAL_HIT_THRESHOLD);
		}
		indexReader = getIndexReader(refsetId);
		if (indexReader != null)
			this.getIndexSearcher(indexReader).search(query, topScoreDocCollector);
		TopDocs topDocs = topScoreDocCollector.topDocs();
		return topDocs.scoreDocs;
	}

	/**
	 * Searches for the top matching descriptions.
	 *
	 * @param results  array of {@link ScoreDoc} elements.
	 * @param refsetId Identifier of Refset
	 * @return descriptions set of {@link CompositeDescription}s.
	 * @throws IOException              if any error occurs while processing.
	 * @throws java.text.ParseException
	 */
	private Set<CompositeDescription> getTopDescriptions(ScoreDoc[] results, String term, String refsetId,
			boolean fullConcept) throws IOException, java.text.ParseException {
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat(E_MMM_DD_HH_MM_SS_Z_YYYY, Locale.ENGLISH);
		Set<CompositeDescription> topDescriptions = null;
		List<CompositeDescription> list = new ArrayList<>();
		IndexSearcher indexSearcher = null;
		Document document = null;
		indexSearcher = this.getIndexSearcher(getIndexReader(refsetId));

		if (fullConcept == false) {
			for (int i = 0; i < results.length; i++) {
				document = indexSearcher.doc(results[i].doc);
				CompositeDescription description = new CompositeDescription();
				description.setId(document.get(ID));
				description.setTerm(document.get(TERM));
				description.setConceptId(document.get(CONCEPTID));
				description.setConceptFsn(document.get(CONCEPT_FSN));
				list.add(description);
			}
		} else {
			for (int i = 0; i < results.length; i++) {

				document = indexSearcher.doc(results[i].doc);
				CompositeDescription description = new CompositeDescription();
				description.setTerm(document.get(TERM));
				description.setId(document.get(ID));
				description.setActiveStatus(Byte.parseByte(document.get(ACTIVE)));
				description.setConceptId(document.get(CONCEPTID));
				description.setType(EnumDescType.getEnum(document.get(TYPEID)));
				description.setConceptState(document.get(CONCEPT_STATE));
				description.setConceptFsn(document.get(CONCEPT_FSN));
				description.setDefinitionStatus(document.get(DEFINITIONSTATUS));
				description.setLangCode(document.get(DBSchemaMapping.DESCRIPTION_LANGUAGE_CODE));
				description.setModuleId(document.get(DESCRIPTION_MODID));
				description.setHierarchy(document.get(HIERARCHY));
				description.setIsPreferredTerm(document.get(IS_PREFERRED_TERM));
				description.setTime(simpleDateFormat.parse(document.get(DBSchemaMapping.DESCRIPTION_EFFECTIVE_TIME)));
				description.setCaseSId(
						EnumCaseSignificance.getEnum(document.get(DBSchemaMapping.DESCRIPTION_CASE_SIGNIFICANCE_ID)));
				list.add(description);
			}
		}
		if (term != null && !term.isEmpty()) {
			Collections.sort(list, new KeyBasedComparator(term));
		}
		topDescriptions = new LinkedHashSet<>(list);
		return topDescriptions;
	}

	/**
	 * Searches for the matching descriptions on the basis of language preference
	 *
	 * @param results  array of {@link ScoreDoc} elements.
	 * @param refsetId Identifier of Refset
	 * @return descriptions set of {@link CompositeDescription}s.
	 * @throws IOException              if any error occurs while processing.
	 * @throws java.text.ParseException
	 */
	private Set<CompositeDescription> getLangDescriptions(ScoreDoc[] results, String term, String refsetId)
			throws IOException, java.text.ParseException {
		Set<CompositeDescription> topDescriptions = null;
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat(E_MMM_DD_HH_MM_SS_Z_YYYY, Locale.ENGLISH);
		List<CompositeDescription> list = new ArrayList<>();
		IndexSearcher indexSearcher = null;
		Document document = null;
		indexSearcher = this.getIndexSearcher(getIndexReader(refsetId));
		for (int i = 0; i < results.length; i++) {
			document = indexSearcher.doc(results[i].doc);
			CompositeDescription description = new CompositeDescription();
			description.setTerm(document.get(TERM));
			description.setId(document.get(ID));
			description.setActiveStatus(Byte.parseByte(document.get(ACTIVE)));
			description.setConceptId(document.get(CONCEPTID));
			description.setType(EnumDescType.getEnum(document.get(TYPEID)));
			description.setConceptState(document.get(CONCEPT_STATE));
			description.setConceptFsn(document.get(CONCEPT_FSN));
			description.setDefinitionStatus(document.get(DEFINITIONSTATUS));
			description.setLangCode(document.get(DBSchemaMapping.DESCRIPTION_LANGUAGE_CODE));
			description.setModuleId(document.get(DESCRIPTION_MODID));
			description.setHierarchy(document.get(HIERARCHY));
			description.setIsPreferredTerm(document.get(IS_PREFERRED_TERM));
			description.setTime(simpleDateFormat.parse(document.get(DBSchemaMapping.DESCRIPTION_EFFECTIVE_TIME)));
			description.setCaseSId(
					EnumCaseSignificance.getEnum(document.get(DBSchemaMapping.DESCRIPTION_CASE_SIGNIFICANCE_ID)));

			RefSetMember refset = new RefSetMember();
			refset.setRefSetId(document.get(DBSchemaMapping.REFSETLANG_REFSET_ID));
			refset.setActiveStatus(Byte.parseByte(document.get(DBSchemaMapping.REFSETLANG_ACTIVE)));
			description.setRefSetLangMember(refset);

			if (EnumAcceptabilityId.PREFERRED.getValue()
					.equalsIgnoreCase(document.get(DBSchemaMapping.REFSETLANG_ACCEPTABILITY_ID)))
				description.setIsPreferredTerm("1");
			else
				description.setIsPreferredTerm("0");
			list.add(description);
		}
		if (term != null && !term.isEmpty()) {
			Collections.sort(list, new KeyBasedComparator(term));
		}

		topDescriptions = new LinkedHashSet<>(list);

		return topDescriptions;
	}

	/**
	 * Searches for the top matching Descendents description.
	 *
	 * @param refsetId Identifier of Refset
	 * @return descriptions set of {@link Relationship Transitive Closure}s.
	 * @throws IOException              if any error occurs while processing.
	 * @throws java.text.ParseException
	 */
	private Set<Concept> getTopDescriptionsTC(ScoreDoc[] results, String refsetId)
			throws IOException, java.text.ParseException {
		Set<Concept> conceptSet = new HashSet<>();
		ComponentFactory componentFactory = ComponentFactory.getInstance();
		IndexSearcher indexSearcher = null;
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat(E_MMM_DD_HH_MM_SS_Z_YYYY, Locale.ENGLISH);
		Document document = null;
		indexSearcher = this.getIndexSearcher(getIndexReader(refsetId));
		for (int i = 0; i < results.length; i++) {
			document = indexSearcher.doc(results[i].doc);
			Concept child = (Concept) componentFactory.getComponent(EnumComponent.CONCEPT, null);
			child.setId(document.get(ID));
			child.setFullySpecifiedName(document.get(CONCEPT_FSN));
            child.setPreferredName(document.get(PREFERRED_NAME));
			child.setDefinitionStatusId(EnumDefStatus.getEnum(document.get(DEFINITIONSTATUS)));
			child.setActiveStatus(Byte.parseByte(document.get(ACTIVE)));
			child.setModuleId(document.get(DESCRIPTION_MODID));
			child.setTime(simpleDateFormat.parse((document.get(EFFECTIVETIME))));
			conceptSet.add(child);
		}
		return conceptSet;
	}

	/**
	 * Sets the common properties of some of the objects
	 * used in getRelationships().
	 *
	 * @param document           - Object of {@link Document} used to fetch details
	 *                             from the indexes<br>
	 * @param sourceConcept      - Object of {@link Concept} whose properties
	 *                             needs to be set.<br>
	 * @param relationship       - Object of {@link Relationship} whose properties
	 *                             needs to be set.<br>
	 * @param typeConcept        - Object of {@link Concept} whose properties
	 *                             needs to be set only when the parameter is not null<br>
	 * @throws java.text.ParseException
	 *
	 */
    private void commonConfigForGetRelationships(Document document, Concept sourceConcept, Relationship relationship, Concept typeConcept) throws java.text.ParseException {
    	SimpleDateFormat simpleDateFormat = new SimpleDateFormat(E_MMM_DD_HH_MM_SS_Z_YYYY, Locale.ENGLISH);
        sourceConcept.setId(document.get(SOURCE + CONCEPTID));
        sourceConcept.setFullySpecifiedName(document.get(SOURCE + CONCEPT_FSN));
        sourceConcept.setPreferredName(document.get(SOURCE + PREFERRED_NAME));
        sourceConcept.setActiveStatus(Byte.parseByte(document.get(SOURCE + ACTIVE)));
        sourceConcept.setDefinitionStatusId(EnumDefStatus.getEnum(document.get(SOURCE + DEFINITIONSTATUS)));
        sourceConcept.setTime(simpleDateFormat.parse(document.get(SOURCE + EFFECTIVETIME)));
        sourceConcept.setModuleId(document.get(SOURCE + MODULEID));
        relationship.setSourceConcept(sourceConcept);
        relationship.setRelationshipGroup(Integer.parseInt(document.get(RELATIONSHIPGROUP)));
        relationship.setActiveStatus(Byte.parseByte(document.get(ACTIVE)));
        if(typeConcept!=null) {
        	relationship.setModuleId(document.get(MODULEID));
			relationship.setTime(simpleDateFormat.parse(document.get(EFFECTIVETIME)));
            typeConcept.setId(document.get(TYPE + CONCEPTID));
            typeConcept.setFullySpecifiedName(document.get(TYPE + CONCEPT_FSN));
            typeConcept.setPreferredName(document.get(TYPE + PREFERRED_NAME));
            typeConcept.setActiveStatus(Byte.parseByte(document.get(TYPE + ACTIVE)));
            typeConcept.setDefinitionStatusId(EnumDefStatus.getEnum(document.get(TYPE + DEFINITIONSTATUS)));
            typeConcept.setTime(simpleDateFormat.parse(document.get(SOURCE + EFFECTIVETIME)));
            typeConcept.setModuleId(document.get(SOURCE + MODULEID));
            relationship.setType(typeConcept);
            relationship.setModifierId(EnumModifier.getEnum(document.get(MODIFIERID)));
            relationship.setCharacteristicTypeId(EnumCharacteristicType.getEnum(document.get(CHARACTERISTICTYPEID)));
        }
    }

	/**
	 * Searches given concept id in the SNOMED CT repository based on provided
	 * relationship type, direction.
	 *
	 * @param conceptId            - {@link String} Concept ID to be searched in
	 *                             SNOMED CT repository.<br>
	 * @param enumRelationshipType - {@link EnumRelationshipType} Type of
	 *                             relationship.<br>
	 *                             If enumRelationshipType is,<br>
	 *                             is a - It will return only "is a" relation type
	 *                             Components.<br>
	 *                             all - It will return only "all" relation type
	 *                             Components.<br>
	 *                             finding site - It will return only "finding site"
	 *                             relation type Components.<br>
	 * @param direction            - {@link EnumDirection} Relationship direction
	 *                             i.e whether the specified concept Id is the
	 *                             source or destination Possible values for
	 *                             directions is,<br>
	 *                             source - concept id is a source<br>
	 *                             destination - concept id is a destination
	 * @throws IOException If any error occurs while processing.
	 * @return List of {@link Relationship} matching the given parameter.<br>
	 * @throws java.text.ParseException
	 *
	 */
	private List<Relationship> getRelationships(ScoreDoc[] results, String attributeType)
			throws IOException, java.text.ParseException {
		List<Relationship> relationshipList = new ArrayList<>();
		ComponentFactory componentFactory = ComponentFactory.getInstance();
		Document document = null;
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat(E_MMM_DD_HH_MM_SS_Z_YYYY, Locale.ENGLISH);
		IndexSearcher indexSearcher = null;
		indexSearcher = this.getIndexSearcher(getIndexReader(RELATION_INDEX));
		for (int i = 0; i < results.length; i++) {
			document = indexSearcher.doc(results[i].doc);
			if (attributeType.equals(ATTRIBUTE_TYPE) && document.get(ACTIVE).equals("1")) {
				Relationship relationship = (Relationship) componentFactory.getComponent(EnumComponent.RELATIONSHIP,
						null);
				Concept sourceConcept = (Concept) componentFactory.getComponent(EnumComponent.CONCEPT, null);
                commonConfigForGetRelationships(document,sourceConcept, relationship, null);
				relationshipList.add(relationship);
			} else if (document.get(DESTINATION + ACTIVE) == null && document.get(ACTIVE).equals("1")) {
				Relationship relationship = (Relationship) componentFactory.getComponent(EnumComponent.RELATIONSHIP,
						null);

				Concept sourceConcept = (Concept) componentFactory.getComponent(EnumComponent.CONCEPT, null);
				Concept typeConcept = (Concept) componentFactory.getComponent(EnumComponent.CONCEPT, null);
                commonConfigForGetRelationships(document, sourceConcept, relationship, typeConcept);

				Concept destinationConcept = (Concept) componentFactory.getComponent(EnumComponent.CONCEPT, null);
				destinationConcept.setId(document.get(DESTINATION + CONCEPTID));

				relationship.setConcreteRelationship(true);
				relationship.setDestinationConcept(destinationConcept);

				relationshipList.add(relationship);

			}

			else {
				if (document.get(ACTIVE).equals("1")) {
					Relationship relationship = (Relationship) componentFactory.getComponent(EnumComponent.RELATIONSHIP,
							null);

					Concept sourceConcept = (Concept) componentFactory.getComponent(EnumComponent.CONCEPT, null);
					Concept typeConcept = (Concept) componentFactory.getComponent(EnumComponent.CONCEPT, null);
                    commonConfigForGetRelationships(document, sourceConcept, relationship, typeConcept);

					Concept destinationConcept = (Concept) componentFactory.getComponent(EnumComponent.CONCEPT, null);
					destinationConcept.setId(document.get(DESTINATION + CONCEPTID));
					destinationConcept.setFullySpecifiedName(document.get(DESTINATION + CONCEPT_FSN));
					destinationConcept.setPreferredName(document.get(DESTINATION + PREFERRED_NAME));
					destinationConcept.setActiveStatus(Byte.parseByte(document.get(DESTINATION + ACTIVE)));
					destinationConcept
							.setDefinitionStatusId(EnumDefStatus.getEnum(document.get(DESTINATION + DEFINITIONSTATUS)));
					destinationConcept.setTime(simpleDateFormat.parse(document.get(SOURCE + EFFECTIVETIME)));
					destinationConcept.setModuleId(document.get(SOURCE + MODULEID));
					relationship.setDestinationConcept(destinationConcept);

					relationshipList.add(relationship);
				}
			}
		}
		return relationshipList;
	}

	/**
	 * Delete data directory content at the time Creating International indexing
	 *
	 * @param dataDir -{@link String} dataDir path location
	 * @return {@link Boolean} True/False on the basis of job done
	 * @throws SecurityException If file/directory does not allow to delete.
	 * @throws IOException
	 */
	public boolean deleteDataDirectory(String dataDir) throws SecurityException, IOException {
		Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
		boolean flag = false;
		PropertyReader.loadSystemProperties();
		if (null == dataDir)
			dataDir = EnumProperties.DATA_PATH.getValue();
		File[] directoryData = new File(dataDir).listFiles();
		if (directoryData != null) {
			for (File directory : directoryData) {
				if (TCINDEX.equalsIgnoreCase(directory.getName()) || REFINDEX.equalsIgnoreCase(directory.getName())
						|| SNOINDEX.equalsIgnoreCase(directory.getName())
						|| RELATION_INDEX.equalsIgnoreCase(directory.getName())
						|| MAP_INDEX.equalsIgnoreCase(directory.getName())
						|| SEMINDEX.equalsIgnoreCase(directory.getName())
						|| REL_TYPE_INDEX.equalsIgnoreCase(directory.getName())
						|| SIMPLE_MAP_INDEX.equalsIgnoreCase(directory.getName())) {
					if (directory.isDirectory()) {
						flag = deleteSubDirectory(directory);
						if (!flag) {
							logger.error(LoggerUtility.Exception, "Unable to delete {} directory from data directory is not deleted" , directory.getName());
							return false;
						} else
							logger.info(LoggerUtility.ConsoleWithLog, "Directory: {} deleted successfully " , directory.getName());
					} else
						logger.error(LoggerUtility.Exception, "Unable to delete {}, it is not a directory." , directory.getName());
				}
			}

		}
		return true;
	}

	/**
	 * For Deleting the old index directory.
	 *
	 * @param file is file path for deleting the old created index directory.
	 * @throws IOException
	 */
	private boolean deleteSubDirectory(File subDirectory) throws SecurityException, IOException {
		File[] subDirectoryContent = subDirectory.listFiles();
		// This is needed for refsets, because RefIndex folder may contains multiple
		// directories with directory name as RefsetId
		for (File path : subDirectoryContent) {
			Files.delete(path.toPath());
		}
		return subDirectory.delete();
	}

	/**
	 * *@param refsetId Identifier of Refset Lucene index creation utility.
	 */
	public boolean createIndex(String refsetId, Set<String> moduleIdSet, boolean flag, boolean isconcreteRelPresent) {
		Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
		Statement statement = null;
		Connection connection = null;
		try {
			if (flag) {
				logger.info(LoggerUtility.ConsoleWithLog, "Loading System Properties ...");
			}
			PropertyReader.loadSystemProperties();
			SnoDomainUtil domainUtil = new SnoDomainUtil();
			if (flag) {
				logger.info(LoggerUtility.ConsoleWithLog, "Reading database properties ...");
			}
			try {
				connection = ConnectionManager.getConnection();
				statement = connection.createStatement();
				if (flag) {
					logger.info(LoggerUtility.ConsoleWithLog, "Database Path : {}" , PropertyReader.getProperty(DATA_DIR));
				}
			} catch (SQLException e) {
				logger.error(LoggerUtility.ConsoleWithException, "Database connection Failed ...");
				return false;
			}
            //Index creation begins
			if (!"".equalsIgnoreCase(refsetId) && refsetId != null) {
				boolean indexCheck = this.createIndex(refsetId, statement, domainUtil, moduleIdSet);
                if(indexCheck)
                    return true;
                else {
                    logger.error(LoggerUtility.ConsoleWithException, "{} {}", INDEX_CREATION_FAILED_FOR, refsetId );
                    return false;
                }
			} else {

				boolean indexCheck = true;
                //Text Definition
				if (moduleIdSet == null)
					indexCheck = this.createIndex(DBSchemaMapping.TEXTDEFINITION, statement, domainUtil, null);

                //Description
                if(indexCheck)
                    indexCheck = this.createIndex(DBSchemaMapping.DESCRIPTION, statement, domainUtil, moduleIdSet);
                else {
                    logger.error(LoggerUtility.ConsoleWithException, "{} {}", INDEX_CREATION_FAILED_FOR, DBSchemaMapping.TEXTDEFINITION );
                    return false;
                }
                //Semantic Tags
                if(indexCheck)
                    indexCheck = this.createSemanticTagsIndex();
                else {
                    logger.error(LoggerUtility.ConsoleWithException, "{} {}", INDEX_CREATION_FAILED_FOR, DBSchemaMapping.DESCRIPTION );
                    return false;
                }
                //Relationship
                if(indexCheck)
                    indexCheck = this.createIndex(DBSchemaMapping.RELATIONSHIP, statement, domainUtil, moduleIdSet);
                else {
                    logger.error(LoggerUtility.ConsoleWithException, "{} Semantic Tags", INDEX_CREATION_FAILED_FOR );
                    return false;
                }
                if(!indexCheck) {
                    logger.error(LoggerUtility.ConsoleWithException, "{} {}", INDEX_CREATION_FAILED_FOR, DBSchemaMapping.RELATIONSHIP );
                    return false;
                }
                else {
                    if (isconcreteRelPresent) {
                        //Concrete Relationship
                        indexCheck = this.createIndex(DBSchemaMapping.CONCRETE_RELATIONSHIP, statement, domainUtil, moduleIdSet);
                        if(!indexCheck) {
                            logger.error(LoggerUtility.ConsoleWithException, "{} {}", INDEX_CREATION_FAILED_FOR, DBSchemaMapping.CONCRETE_RELATIONSHIP );
                            return false;
                        }
                    }
                    //Transclosure
                    indexCheck = this.createIndex(DBSchemaMapping.TRANSCLOSURE, statement, domainUtil, moduleIdSet);
                }
                //Relation Types
                if(indexCheck)
                    indexCheck = this.createRelationTypesIndex();
                else {
                    logger.error(LoggerUtility.ConsoleWithException, "{} {}", INDEX_CREATION_FAILED_FOR, DBSchemaMapping.TRANSCLOSURE );
                    return false;
                }
                if(!indexCheck) {
                    logger.error(LoggerUtility.ConsoleWithException, "{} Relation Types", INDEX_CREATION_FAILED_FOR );
                    return false;
                }
                //Index creation successful
                return true;

			}
		} catch (IOException | SQLException | java.text.ParseException e) {
			if (e instanceof IOException) {
				logger.error(LoggerUtility.ConsoleWithException, "Reading writing issue ...");
			} else if (e instanceof SQLException) {
				logger.error(LoggerUtility.ConsoleWithException, "Database connection issue, Database may already be in use.. ");
			} else if (e instanceof java.text.ParseException) {
				logger.error(LoggerUtility.ConsoleWithException, "Date parsing error occurred... ");
			}
			logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
			return false;
		} catch (Exception e) {
			logger.error(LoggerUtility.ConsoleWithException, LoggerUtility.createErrorMessage(e));
			logger.error(LoggerUtility.ConsoleWithException, "Exceptions occurred during indexing... {}",e.getMessage());
			return false;
		} finally {
			try {
				if (statement != null)
					DBUtil.closeStatement(statement);
				if (connection != null)
					connection.close();
			} catch (SQLException e) {
				logger.error(LoggerUtility.Exception, STATEMENT_IS_NOT_CLOSED);
				logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
			}
		}
	}

	/**
	 * For Deleting the old index.
	 *
	 * @param refsetId Identifier of Refset/RelationIndex/TCIndex for deleting the
	 *                 old created index.
	 **/
	public boolean deleteIndex(String refsetId) {
		Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
		try {
			this.setIndexDirectory(null);
			indexDirectory = getIndexDirectoryToWrite(refsetId);
			if (indexDirectory != null) {
				String[] list = indexDirectory.listAll();
				if (list != null) {
					for (int i = 0; i < list.length; i++) {
						indexDirectory.deleteFile(list[i]);
					}
				}
			} else {
				logger.info(LoggerUtility.ConsoleWithLog, "Path is invalid");
				return false;
			}
		} catch (IOException e) {
			logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
		} catch (Exception e) {
			logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
		}
		this.setIndexDirectory(null);
		return true;
	}

	/**
	 * Searches given term/text or concept id in the SNOMED CT repository based on
	 * provided component state, semantic tag, acceptability.
	 *
	 * @param queryParams Hashmap containing key value pair of search parameters on
	 *                    the basis of which search criteria will be decided.
	 *                    Parameters are as follows, <br>
	 *                    SearchTerm - {@link String} term/text or Concept ID to be
	 *                    searched in SNOMED CT repository.<br>
	 *                    <br>
	 *                    State - {@link EnumState} State of a component.<br>
	 *                    If enumState is,<br>
	 *                    active - It will return only active Components.<br>
	 *                    inactive - It will return only inactive Components.<br>
	 *                    both - It will return both active and inactive
	 *                    Components.<br>
	 *                    <br>
	 *                    SemanticTag - {@link EnumSemanticTag} Refers to the
	 *                    Semantic Tag in which SNOMED CT term/text or concept id
	 *                    will be searched. <br>
	 *                    If enumSemanticTag is,<br>
	 *                    all - It will search SNOMED CT term in all the Semantic
	 *                    Tags.<br>
	 *                    For other possible values please refer
	 *                    {@link EnumSemanticTag}.<br>
	 *                    <br>
	 *                    A list of {@link EnumSemanticTag} can also be provide in
	 *                    which search/suggest is to be perform.<br>
	 *                    Acceptability - {@link EnumAcceptability} Represents the
	 *                    type of description i.e. whether the search results
	 *                    contains preferred terms or preferred terms other than FSN
	 *                    or only acceptable terms and so on.<br>
	 *                    If enumAcceptability is, <br>
	 *                    all - refers to all description for a concept (both
	 *                    preferred and acceptable).<br>
	 *                    preferred - refers to all preferred description for a
	 *                    concept (including FSN). <br>
	 *                    preferredexcludingfsn - refers to SNOMEDCT description for
	 *                    preferred terms excluding FSN.<br>
	 *                    synonyms - refers to all description for a concept
	 *                    (excluding FSN).<br>
	 *                    acceptable- refers to all description for a concept
	 *                    excluding preferred.<br>
	 *                    <br>
	 *                    returnLimit/MaxRecords - Maximum number of matching terms
	 *                    to be fetched.<br>
	 *                    <br>
	 *                    groupByConcept - boolean value, if true, then method will
	 *                    return set of {@link CompositeDescription} which will
	 *                    contain one description from one concept.<br>
	 *                    <br>
	 *                    refsetId - Identifier of Refset from which search is to be
	 *                    done and null if search is to be done from whole SNOMED CT
	 *                    Terminology <br>
	 *                    <br>
	 * @return Set of {@link CompositeDescription} matching the given term/text or
	 *         Concept ID.<br>
	 */
	public Set<CompositeDescription> search(HashMap<String, List<String>> queryParams, boolean fullConcept) {
		Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
		boolean searchOnID = false;
		Set<CompositeDescription> emptySet = Collections.emptySet();
		StringBuilder queryCriteria = new StringBuilder();
		String inputQuery = "";
		String strMaxRecords = "";
		String refsetId = null;
		int maxRecords = -1;

		if (queryParams.containsKey(REFSET_ID)) {
			refsetId = queryParams.get(REFSET_ID).get(0);
		}
		if (queryParams.containsKey(SEARCH_TERM)) {
			inputQuery = queryParams.get(SEARCH_TERM).get(0);
			if (null == inputQuery || inputQuery.trim().isEmpty()) {
				String strMessage = INVALID_PARAMETER + NEW_LINE + CLASS_NAME + this.getClass().getName() + NEW_LINE
						+ SEARCH_TERM + ":" + inputQuery;
				logger.error(LoggerUtility.Exception, strMessage);
				return emptySet;
			}
			inputQuery = queryParams.get(SEARCH_TERM).get(0); // repeated code as line 1005
		}

		if (queryParams.containsKey(MAX_RECORDS)) {
			strMaxRecords = queryParams.get(MAX_RECORDS).get(0);
			int allReturnLimit = 10000;
			if (null == strMaxRecords || strMaxRecords.trim().isEmpty()) {
				String strMessage = INVALID_PARAMETER + NEW_LINE + CLASS_NAME + this.getClass().getName() + NEW_LINE
						+ MAX_RECORDS + ":" + strMaxRecords;
				logger.error(LoggerUtility.Exception, strMessage);
				return emptySet;

			}
			try {
				maxRecords = Integer.parseInt(strMaxRecords);
				PropertyReader.loadSystemProperties();
				allReturnLimit = Integer.parseInt(PropertyReader.getProperty("search.returnlimit"));
				if (maxRecords < 0 || maxRecords > allReturnLimit) {
					throw new ArithmeticException();
				}

			} catch (ArithmeticException e) {
				// Log the Exception
				String strMessage = LoggerUtility.createErrorMessage(e) + NEW_LINE + "INVALID PARAMETER : " + MAX_RECORDS + " should be between 0 to " + allReturnLimit
						+ NEW_LINE + MAX_RECORDS + ":" + strMaxRecords;
				logger.error(LoggerUtility.Exception, strMessage);
				return emptySet;
			} catch (NumberFormatException e) {
				// Log the Exception
				String strMessage = LoggerUtility.createErrorMessage(e) + NEW_LINE + INVALID_PARAMETER + NEW_LINE + CLASS_NAME + this.getClass().getName() + NEW_LINE
						+ MAX_RECORDS + ":" + strMaxRecords;
				logger.error(LoggerUtility.Exception, strMessage);
				return emptySet;
			}
		}

		if (queryParams.containsKey(STATE)) {
			String state = queryParams.get(STATE).get(0);
			if (null == state || state.trim().isEmpty()) {
				String strMessage = INVALID_PARAMETER + NEW_LINE + CLASS_NAME + this.getClass().getName() + NEW_LINE
						+ STATE + ":" + state;
				logger.error(LoggerUtility.Exception, strMessage);
				return emptySet;
			}

			if (!state.equalsIgnoreCase(EnumState.BOTH.name())) {
				if (EnumState.ACTIVE.name().equals(state)) {
					queryCriteria.append(" AND (" + ACTIVE + ":1" + AND + CONCEPT_STATE + ":1)");
				} else if (EnumState.INACTIVE.name().equals(state)) {
					queryCriteria.append(" AND (" + ACTIVE + ":0" + " OR " + CONCEPT_STATE + ":0)");
				}
			}
		}
		if (queryParams.containsKey(SEMANTIC_TAG)) {
			List<String> semantictagList = queryParams.get(SEMANTIC_TAG);

			if (null == semantictagList || semantictagList.isEmpty()) {
				String strMessage = INVALID_PARAMETER + NEW_LINE + CLASS_NAME + this.getClass().getName() + NEW_LINE
						+ SEMANTIC_TAG + ":" + semantictagList;
				logger.error(LoggerUtility.Exception, strMessage);
				return emptySet;
			}
			for (int i = 0; i < semantictagList.size(); i++)
				semantictagList.set(i, semantictagList.get(i).toLowerCase());
			if (!semantictagList.contains("all")) {
				String semantictag = "";
				queryCriteria.append(AND + "(");
				for (int i = 0; i < semantictagList.size(); i++) {
					semantictag = semantictagList.get(i);

					String[] semanticTagWords = null;
					Boolean isMultiWordSemanticTag = false;
					if (semantictag.split("\\s+").length > 1) {
						semanticTagWords = semantictag.split("\\s+");
						isMultiWordSemanticTag = true;
					} else if (semantictag.split("/").length > 1) {
						semanticTagWords = semantictag.split("/");
						isMultiWordSemanticTag = true;
					}

					if (0 == i) {

						if (isMultiWordSemanticTag) {
							queryCriteria.append("(");
							for (int count = 0; count < semanticTagWords.length; count++) {

								if (count == 0) {
									queryCriteria.append(HIERARCHY + ":" + semanticTagWords[count] + "*");
								} else if (count == semanticTagWords.length - 1) {
									queryCriteria.append(AND + HIERARCHY + ":*" + semanticTagWords[count]);
								} else {
									queryCriteria.append(AND + HIERARCHY + ":*" + semanticTagWords[count] + "*");
								}
							}
							queryCriteria.append(")");
						} else {
							queryCriteria.append(HIERARCHY + ":" + semantictag);
						}
					} else {
						if (isMultiWordSemanticTag) {
							queryCriteria.append(" OR " + "(");
							for (int count = 0; count < semanticTagWords.length; count++) {

								if (count == 0) {
									queryCriteria.append(HIERARCHY + ":" + semanticTagWords[count] + "*");
								} else if (count == semanticTagWords.length - 1) {
									queryCriteria.append(AND + HIERARCHY + ":*" + semanticTagWords[count]);
								} else {
									queryCriteria.append(AND + HIERARCHY + ":*" + semanticTagWords[count] + "*");
								}
							}
							queryCriteria.append(")");
						} else {
							queryCriteria.append(" OR " + HIERARCHY + ":" + semantictag);
						}
					}
				}
				queryCriteria.append(")");

			}
		}
		if (queryParams.containsKey(ACCEPTABILITY)) {
			String acceptability = queryParams.get(ACCEPTABILITY).get(0);
			if (null == acceptability || acceptability.trim().isEmpty()) {
				String strMessage = INVALID_PARAMETER + NEW_LINE + CLASS_NAME + this.getClass().getName() + NEW_LINE
						+ ACCEPTABILITY + ":" + acceptability;
				logger.error(LoggerUtility.Exception, strMessage);
				return emptySet;

			}
			if (EnumAcceptability.PREFERRED.name().equalsIgnoreCase(acceptability)) {
				queryCriteria.append(AND + IS_PREFERRED_TERM + ":1");
			} else if (EnumAcceptability.PREFERRED_EXCLUDING_FSN.name().equalsIgnoreCase(acceptability)) {
				queryCriteria.append(AND + IS_PREFERRED_TERM + ":1 AND " +
					TYPEID + ":" + EnumDescType.SYNONYM.getValue());
			} else if (EnumAcceptability.SYNONYMS.name().equalsIgnoreCase(acceptability)) {
				queryCriteria.append(AND + TYPEID + ":" + EnumDescType.SYNONYM.getValue());
			} else if (EnumAcceptability.ACCEPTABLE.name().equalsIgnoreCase(acceptability)) {
				queryCriteria.append(AND + IS_PREFERRED_TERM + ":0");
			}
		}
		if (queryParams.containsKey(DBSchemaMapping.REFSETLANG_REFSET_ID)) {
			String langRefset = queryParams.get(DBSchemaMapping.REFSETLANG_REFSET_ID).get(0);
			if (null == langRefset || langRefset.trim().isEmpty()
					|| langRefset.equalsIgnoreCase(EnumLangRefset.getEnum("all").getValue())) {
				queryCriteria.append(AND + DBSchemaMapping.REFSETLANG_ACTIVE + ":1");
			} else {
				queryCriteria.append(AND + DBSchemaMapping.REFSETLANG_REFSET_ID
					+ ":" + EnumLangRefset.getEnum(langRefset).getId() +
					AND + DBSchemaMapping.REFSETLANG_ACTIVE + ":1");
			}
		}

		inputQuery = inputQuery.trim();

		// following logic is top check whether search is on Concept ID or Term
		/**
		 * JAVA SCRIPT LONG LIMIT: +/-9007199254740992 JAVA LONG LIMIT: +/-
		 * 9223372036854775807 Sample SNOMED ID: 900000000000441000
		 */
		try {
			if (null != inputQuery && !inputQuery.isEmpty()) {

				Long.parseLong(inputQuery);
				if (inputQuery.length() < 6) {
					searchOnID = false;
				} else {
					searchOnID = true;
				}

			}
		} catch (NumberFormatException nfe) {
			searchOnID = false;
		}

		String originalTerm = inputQuery;
		ScoreDoc[] results = null;
		QueryParser queryParser = null;

		if (searchOnID) {
			String manipulate = originalTerm;
			// ask here
			String checkPoint = manipulate.substring(manipulate.length() - 2, manipulate.length() - 1);
			if (checkPoint.equalsIgnoreCase(CONCEPT_CHECKPONT)) {
				queryParser = new QueryParser(CONCEPTID, getAnalyzer());
			} else if (checkPoint.equalsIgnoreCase(DESC_CHECKPONT)) {
				queryParser = new QueryParser(ID, getAnalyzer());
			} else {
				return emptySet;
			}
		} else {
			/*
			 * Earlier no results were shown for search terms that included 'of' or 'and'.
			 * for example- if 'tuberculosis of' or 'heart and' were searched, 0 results
			 * were returned. Lucene search also failed for search terms that include
			 * special characters (Example-No results were produced for search term-
			 * 'jaggery(substanc' in CSNOCtrl. But results were shown for
			 * 'jaggery(substance)'.Search did not function properly when special characters
			 * were included). Thus, all these cases are now checked and the input query is
			 * manipulated so as to produce proper results. Stop words like -'and',
			 * 'to','the' and 'of' are replaced by space and special characters are removed
			 * from the search term. Examples- 'x-ray of hand' is replaced by 'x-ray hand'
			 * 'x-ray of' is replaced by 'x-ray' 'sinusitis(&acute)' is replaced by
			 * 'sinusitis acute' Tried with lucene 5.3 also, but it also fails for these
			 * cases
			 */

			try {
				inputQuery = inputQuery.toLowerCase();
				String[] specialCharacters = { "\"", ":", "/", "^", "$", "%", "{", "}", "[", "]", "(", ")", ".", "*",
						"+", "?", "|", "<", ">", "-", "&", ",", "!" };
				for (int i = 0; i < specialCharacters.length; i++) {
					if (inputQuery.contains(specialCharacters[i])) {
						inputQuery = inputQuery.replace(specialCharacters[i], "\\" + specialCharacters[i]);
					}
				}
				String[] searchdKeyword = inputQuery.split(" ");
				String finalQuery = "";
				for (int i = 0; i < searchdKeyword.length; i++) {
					searchdKeyword[i] = searchdKeyword[i] + "*";
				}
				for (String string : searchdKeyword) {
					finalQuery = finalQuery + string + " ";
				}
				inputQuery = finalQuery;
			} catch (Exception ex) {
				// Log the Exception
                logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(ex));
			}
			queryParser = new QueryParser(TERM, getAnalyzer());
		}
		queryParser.setDefaultOperator(QueryParser.Operator.AND);
		queryParser.setAllowLeadingWildcard(true);

		try {
			if (inputQuery != null) // 3DEC2015
			{
				Query query = null;
				// input query and queryCriteria
				query = queryParser.parse(inputQuery + queryCriteria.toString());
				results = this.search(query, Integer.parseInt(strMaxRecords), refsetId);
				if (results.length == 0) {
					String[] searchdKeyword = inputQuery.split(" ");
					String finalQuery = "";
					for (int i = 0; i < searchdKeyword.length; i++) {
						searchdKeyword[i] = "*" + searchdKeyword[i];
					}
					for (String string : searchdKeyword) {
						finalQuery = finalQuery + string + " ";
					}
					inputQuery = finalQuery;

					queryParser = new QueryParser(TERM, getAnalyzer());
					queryParser.setDefaultOperator(QueryParser.Operator.AND);
					queryParser.setAllowLeadingWildcard(true);
					query = queryParser.parse(inputQuery + queryCriteria.toString());
					results = this.search(query, Integer.parseInt(strMaxRecords), refsetId);
				}

				if (!queryParams.containsKey(DBSchemaMapping.REFSETLANG_REFSET_ID))
					return getTopDescriptions(results, originalTerm, refsetId, fullConcept);
				else
					return getLangDescriptions(results, originalTerm, refsetId);

			}
		} catch (ParseException e) {
			// Log the Exception
			logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
		} catch (java.text.ParseException e) {
			// Log the Exception
			logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
		} catch (IOException e) {
			// Log the Exception
			logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
		} catch (Exception e) {
			// Log the Exception
			logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
		}
		return emptySet;
	}

	/**
	 * Searches given conceptId in the SNOMED CT repository<br>
	 *
	 * @param conceptId String it will search on the basis of given conceptId and
	 *                  also use for checking the entered conceptId is valid or
	 *                  not<br>
	 * @return Object of {@link Concept} respected to given conceptId
	 * @throws ParseException
	 */
	@Override
	public Concept search(final String conceptId) throws IOException, java.text.ParseException, ParseException {
		if (conceptId == null)
			return null;
		Concept concept = (Concept) ComponentFactory.getInstance().getComponent(EnumComponent.CONCEPT, null);
		PropertyReader.loadSystemProperties();
		final String inputQuery = CONCEPTID;
		QueryParser queryParser = new QueryParser(inputQuery, getAnalyzer());
		StringBuilder queryCriteria = new StringBuilder(" " + inputQuery + ":" + conceptId);
		if (inputQuery != null) // 3DEC2015
		{
			Query query = null;
			ScoreDoc[] results = null;
			query = queryParser.parse(queryCriteria.toString().trim());
			results = this.search(query, 100, null);
			CompositeDescription compositeDescription = new ArrayList<CompositeDescription>(
					getTopDescriptions(results, conceptId, null, true)).get(0);
			concept.setActiveStatus(Byte.parseByte(compositeDescription.getConceptState()));
			concept.setFullySpecifiedName(compositeDescription.getConceptFsn());
			concept.setModuleId(compositeDescription.getModuleId());
			concept.setTime(compositeDescription.getTime());
			concept.setDefinitionStatusId(EnumDefStatus.getEnum(compositeDescription.getDefinitionStatus()));
			concept.setId(compositeDescription.getConceptId());
		}
		return concept;
	}

	@Override
	public List<Relationship> getRelationships(String conceptId, String relationshipType, EnumDirection directions) {
		Logger logger = LogManager.getLogger(LuceneSearchAgent.class);

		String inputQuery = null;
		StringBuilder queryCriteria = null;
		QueryParser queryParser = null;
		PropertyReader.loadSystemProperties();
		if (directions.getValue().equals(DESTINATION))
			inputQuery = DESTINATION + CONCEPTID;
		else
			inputQuery = SOURCE + CONCEPTID;
		queryParser = new QueryParser(inputQuery, getAnalyzer());
		if (relationshipType.equals(EnumRelationshipType.ALL.getValue()))
			queryCriteria = new StringBuilder(" " + inputQuery + ":" + conceptId);
		else {
			if (relationTypeMap.containsValue(relationshipType)) {
				for (String key : relationTypeMap.keySet()) {
					if (relationshipType.equals(relationTypeMap.get(key))) {
						relationshipType = key;
					}
				}
			} else {
				EnumRelationshipType relation = EnumRelationshipType.getEnumForValue(relationshipType.trim());
				relationshipType = relation.getId();
			}
			queryCriteria = new StringBuilder(
					" " + inputQuery + ":" + conceptId + AND + TYPE + CONCEPTID + ":" + relationshipType);
		}
		queryCriteria.append(AND + ACTIVE + ":1");
		queryParser.setDefaultOperator(QueryParser.Operator.AND);
		queryParser.setAllowLeadingWildcard(true);
		try {
			if (inputQuery != null) // 3DEC2015
			{
				Query query = null;
				ScoreDoc[] results = null;
				query = queryParser.parse(queryCriteria.toString().trim());
				if (directions.getValue().equalsIgnoreCase(DESTINATION))
					results = this.search(query, RELATIONSHIP_ALL_RECORDS, RELATION_INDEX);
				else
					results = this.search(query, Integer.parseInt(EnumProperties.SEARCH_RETURNLIMIT_VAL.getValue()),
							RELATION_INDEX);
				return getRelationships(results, "");
			}
		} catch (java.text.ParseException | ParseException | IOException  e) {
			// Log the Exception
			logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
		}
		return Collections.emptyList();
	}

	public List<Relationship> getRelationships(String conceptId, EnumRelationshipType enumRelationshipType,
			EnumDirection directions) {
		Logger logger = LogManager.getLogger(LuceneSearchAgent.class);

		String inputQuery = null;
		StringBuilder queryCriteria = null;
		QueryParser queryParser = null;
		PropertyReader.loadSystemProperties();
		if (directions.getValue().equals(DESTINATION))
			inputQuery = DESTINATION + CONCEPTID;
		else
			inputQuery = SOURCE + CONCEPTID;
		queryParser = new QueryParser(inputQuery, getAnalyzer());
		if (enumRelationshipType == EnumRelationshipType.ALL)
			queryCriteria = new StringBuilder(" " + inputQuery + ":" + conceptId);
		else
			queryCriteria = new StringBuilder(" " + inputQuery + ":" + conceptId + AND + TYPE + CONCEPTID + ":"
					+ enumRelationshipType.getId());
		queryCriteria.append(AND + ACTIVE + ":1");
		queryParser.setDefaultOperator(QueryParser.Operator.AND);
		queryParser.setAllowLeadingWildcard(true);
		try {
			if (inputQuery != null) // 3DEC2015
			{
				Query query = null;
				ScoreDoc[] results = null;
				query = queryParser.parse(queryCriteria.toString().trim());
				if (directions.getValue().equalsIgnoreCase(DESTINATION))
					results = this.search(query, RELATIONSHIP_ALL_RECORDS, RELATION_INDEX);
				else
					results = this.search(query, Integer.parseInt(EnumProperties.SEARCH_RETURNLIMIT_VAL.getValue()),
							RELATION_INDEX);
				return getRelationships(results, "");
			}
		} catch (java.text.ParseException|ParseException |IOException e) {
			// Log the Exception
			logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
		}
		return Collections.emptyList();
	}

	/**
	 * Searches given concept id in the SNOMED CT repository. returnLimit/MaxRecords
	 * - Maximum number of matching terms to be fetched.<br>
	 * <br>
	 * refsetId - Identifier of Refset from which search is to be done and null if
	 * search is to be done from whole SNOMED CT Terminology <br>
	 * <br>
	 *
	 * @return Set of {@link RelationshipTC} matching the given Concept ID.<br>
	 * @throws java.text.ParseException
	 */
	@Override
	public Set<Concept> searchTC(String id) throws java.text.ParseException {
		Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
		Set<Concept> emptySet = Collections.emptySet();
		String inputQuery = "";
		inputQuery = id.trim();
		if (inputQuery.length() < 6) {
			return emptySet;
		}

		ScoreDoc[] results = null;
		QueryParser queryParser = null;
		queryParser = new QueryParser(PARENTID, getAnalyzer());
		queryParser.setDefaultOperator(QueryParser.Operator.AND);
		queryParser.setAllowLeadingWildcard(true);
		try {
			if (inputQuery != null) { // 3DEC2015
				Query query = null;
				query = queryParser.parse(inputQuery);
				results = this.search(query, 200000, TCINDEX);
				return getTopDescriptionsTC(results, TCINDEX);
			}
		} catch (ParseException |IOException e) {
			// Log the Exception
			logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
		}
		return emptySet;
	}

	private void setIndexDirectory(Directory indexDirectory) {
		this.indexDirectory = indexDirectory;
	}

	/**
	 * Releases Lucene resource like indexWriter, indexDirectory, indexConfig for
	 * multiple read/write operations.
	 */
	private void releaseLuceneResources() {
		this.indexWriter = null;
		this.indexDirectory = null;
		this.indexConfig = null; // we need to make it null otherwise it throws exception
	}

	/**
	 * For Deleting the old created index of extension.
	 *
	 * @param refsetId Identifier of Refset
	 */
	public void deleteExtensionDocument(final String refsetId, final String moduleId, final String queryConstant)
			throws IOException {
		try {
			this.releaseLuceneResources();
			Query query = new WildcardQuery(new Term(queryConstant, moduleId));// module id 13941000189108
			this.indexWriter = getIndexWriter(refsetId);
			this.indexWriter.deleteDocuments(query);
		} finally {
			this.getIndexWriter(refsetId).commit();
			this.getIndexWriter(refsetId).close();
			this.releaseLuceneResources();

		}
	}

	/**
	 * Check index directory empty or not; if not and write.lock present then delete
	 * write.lock it.
	 *
	 * @param indexDir {@link String} pass index dir name
	 */
	boolean checkIndex(final String indexDir) {
		Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
		if (indexDir == null)
			return false;
		PropertyReader.loadSystemProperties();
		StringBuilder indexDataDir = new StringBuilder(PropertyReader.getProperty(DATA_DIR));
		try {
			indexDataDir.append(File.separator + indexDir);
			File[] indexDirectoryData = new File(indexDataDir.toString()).listFiles();
			if (indexDirectoryData != null) {
				for (File indexFile : indexDirectoryData) {
					if ("write.lock".equalsIgnoreCase(indexFile.getName())) {
						indexFile.delete();
						break;
					}
				}
				return true;
			}
		} catch (SecurityException e) {
			logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
			logger.error(LoggerUtility.Exception, "Access denied for {} directory" , indexDataDir);
		} catch (Exception e) {
			logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
		}
		return false;
	}

	/**
	 * Gets count of total number of rows in specified table.
	 *
	 * @param refsetId   pass refsetId/table name.
	 * @param statement  Object of {@link Statement} class
	 * @return totalRecords
	 * 					the total number of rows in a table.
     *                  if -1 is returned, process has encountered some problem.
	 */
    private long getTotalRecords( String refsetId, Statement statement ) {
    	Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
    	long totalRecords = 0;
        try {
			if (DBSchemaMapping.SIMPLEMAP.equalsIgnoreCase(refsetId)
			        || DBSchemaMapping.DESCRIPTION.equalsIgnoreCase(refsetId)
			        || DBSchemaMapping.RELATIONSHIP.equalsIgnoreCase(refsetId)
			        || DBSchemaMapping.TRANSCLOSURE.equalsIgnoreCase(refsetId)
			        || DBSchemaMapping.TEXTDEFINITION.equalsIgnoreCase(refsetId)
			        || DBSchemaMapping.CONCRETE_RELATIONSHIP.equalsIgnoreCase(refsetId))
			    totalRecords = DBUtil.getTotalRows(refsetId, statement, null);
			else
			    totalRecords = DBUtil.getTotalRefsetRows(refsetId, statement);
		} catch (SQLException e) {
			logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
			totalRecords = -1;
		}

        return totalRecords;
    }

	/**
	 * For deleting the old created index of extension and return
     * the number of records updated in database.
	 *
	 * @param moduleIdSet   Object of {@link String} class, pass when updation of index
     *                      is required otherwise null.
	 * @param refsetId      pass refsetId/table name.
	 * @param statement     Object of {@link Statement} class.
	 * @return extensionUpdateCount
     *                      count of records updated in extension.
     *                      if -1 is returned, process has encountered some problem.
	 */
    private int deleteExtensionDocumentGetExtensionUpdateCount( Set<String> moduleIdSet,  String refsetId, Statement statement ) {
    	Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
    	int extensionUpdateCount = 0;
        HashMap<String, String> refsetIdList = null;
        try {
			if (DBSchemaMapping.DESCRIPTION.equalsIgnoreCase(refsetId)
			        || DBSchemaMapping.TEXTDEFINITION.equalsIgnoreCase(refsetId)) {
			    if (moduleIdSet != null && !moduleIdSet.isEmpty()) {
			        for (String moduleId : moduleIdSet) {
			            deleteExtensionDocument(SNOINDEX, moduleId, DESCRIPTION_MODID);
			            extensionUpdateCount = extensionUpdateCount
			                + (int) DBUtil.getTotalRows(refsetId, statement, moduleId);
			        }
			    }
			} else if (DBSchemaMapping.RELATIONSHIP.equalsIgnoreCase(refsetId)) {
			    if (moduleIdSet != null && !moduleIdSet.isEmpty()) {
			        for (String moduleId : moduleIdSet) {
			            deleteExtensionDocument(RELATION_INDEX, moduleId, MODULEID);
			        }
			        extensionUpdateCount = RELATIONSHIP_EXTENSION_RECORDS_COUNT;
			    }
			} else if (DBSchemaMapping.CONCRETE_RELATIONSHIP.equalsIgnoreCase(refsetId)) {
			    if (moduleIdSet != null && !moduleIdSet.isEmpty()) {
			        // for setting ext update count and offset
			        extensionUpdateCount = CONCRETE_RELATIONSHIP_EXTENSION_RECORDS_COUNT;
			    }
			} else if (DBSchemaMapping.TRANSCLOSURE.equalsIgnoreCase(refsetId)) {
			    if (moduleIdSet != null && !moduleIdSet.isEmpty()) {
			        for (String moduleId : moduleIdSet) {
			            deleteExtensionDocument(TCINDEX, moduleId, DESCRIPTION_MODID);
			            extensionUpdateCount = extensionUpdateCount
			                + (int) DBUtil.getTotalRows(DBSchemaMapping.TRANSCLOSURE, statement, moduleId);
			        }
			    }
			} else if (DBSchemaMapping.SIMPLEMAP.equalsIgnoreCase(refsetId)) {
			    if (moduleIdSet != null && !moduleIdSet.isEmpty()) {
			        for (String moduleId : moduleIdSet) {
			            deleteExtensionDocument(SIMPLE_MAP_INDEX, moduleId, MODULEID);
			        }
			        extensionUpdateCount = SIMPLEMAP_RECORDS_COUNT;
			    }
			} else {
			    if (DBSchemaMapping.REFSET.equalsIgnoreCase(refsetId)) {
			        refsetIdList = new HashMap<>();
			        for (String m : moduleIdSet)
			            refsetIdList = DBUtil.getDistinctRefsetId(m, null, statement);
			        if (refsetIdList.isEmpty() || refsetIdList == null)
			            return -1;

			        for (String refset : refsetIdList.keySet())
			            deleteRefsetDocument(refset);
			        this.addDistinctRefsetForIndexing(refsetIdList, REFINDEX);
			    } else if (DBSchemaMapping.cREFSET.equalsIgnoreCase(refsetId)) {
			        refsetIdList = new HashMap<>();
			        for (String m : moduleIdSet)
			            refsetIdList = DBUtil.getDistinctcRefsetId(m, statement);
			        if (refsetIdList.isEmpty() || refsetIdList == null || refsetIdList.size() < 1)
			            return -1;
			        for (String refset : refsetIdList.keySet())
			            deleteRefsetDocument(refset);
			        this.addDistinctRefsetForIndexing(refsetIdList, REFINDEX);
			    } else {
			        refsetIdList = DBUtil.getDistinctRefsetId(null, refsetId, statement);
			        deleteRefsetDocument(refsetId);
			        addDistinctRefsetForIndexing(refsetIdList, refsetId);
			    }
			}
		} catch (IOException e) {
			logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
            return -1;
		} catch (SQLException e) {
			logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
            return -1;
		}
        return extensionUpdateCount;
    }

	/**
	 * Displays total number of records that need to be indexed
     * and returns whether extension is selected or not.
	 *
	 * @param moduleIdSet               Object of {@link String} class, pass when updation of index
     *                                  is required otherwise null.
	 * @param refsetId                  pass refsetId/table name.
	 * @param extensionUpdateCount      count of records updated in extension.
	 * @param totalRecords              count of records in a table.
	 * @return isExtensionSelected
     *                      returns true/false whether extension is selected.
	 */
    private boolean displayTotalIndexGetIsExtensionSelected( Set<String> moduleIdSet, String refsetId, int extensionUpdateCount, long totalRecords ) {
        Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
        boolean isExtensionSelected = false;
        if (moduleIdSet != null && !moduleIdSet.isEmpty() && !refsetId.equalsIgnoreCase(DBSchemaMapping.REFSET)
                && !refsetId.equalsIgnoreCase(DBSchemaMapping.cREFSET)) {
            logger.info(LoggerUtility.ConsoleWithLog, "{}{}{}{}",
                TOTAL_NUMBER_OF , getProcessOrIndexName(refsetId, true), RECORDS_NEED_TO_BE_INDEXED , extensionUpdateCount);
            isExtensionSelected = true;
        } else {
            logger.info(LoggerUtility.ConsoleWithLog, "{}{}{}{}",TOTAL_NUMBER_OF , getProcessOrIndexName(refsetId, true) , RECORDS_NEED_TO_BE_INDEXED , totalRecords);
            isExtensionSelected = checkIsExtension(getProcessOrIndexName( refsetId, true ));
        }
        return isExtensionSelected;
    }

	/**
	 * Maps refsetId with its process name (needed for display purpose)
     * or the index name (needed by getIndexWriter()).
	 *
	 * @param refsetId         pass refsetId/table name.
	 * @param processOrIndex   if true, return process name
     *                         if false, return index name.
	 * @return String          returns either process name or index name.
	 */
    private String getProcessOrIndexName( String refsetId, boolean processOrIndex ) {
        String indexProcessFor, indexForIndexWriter;
        if (DBSchemaMapping.DESCRIPTION.equalsIgnoreCase(refsetId)) {
            indexProcessFor = "Description";
            indexForIndexWriter = SNOINDEX;
        }
        else if( DBSchemaMapping.TEXTDEFINITION.equalsIgnoreCase(refsetId)) {
            indexProcessFor = "Text Definition";
            indexForIndexWriter = SNOINDEX;
        }
        else if (DBSchemaMapping.RELATIONSHIP.equalsIgnoreCase(refsetId)) {
            indexProcessFor = "Relationship";
            indexForIndexWriter = RELATION_INDEX;
        }
        else if (DBSchemaMapping.CONCRETE_RELATIONSHIP.equalsIgnoreCase(refsetId)) {
            indexProcessFor = "Concrete_Relationship";
            indexForIndexWriter = RELATION_INDEX;
        }
        else if (DBSchemaMapping.TRANSCLOSURE.equalsIgnoreCase(refsetId)) {
            indexProcessFor = "Transclosure";
            indexForIndexWriter = TCINDEX;
        }
        else if (DBSchemaMapping.SIMPLEMAP.equalsIgnoreCase(refsetId)) {
            indexProcessFor = "Simple Map";
            indexForIndexWriter = SIMPLE_MAP_INDEX;
        }
        else {
            if (DBSchemaMapping.REFSET.equalsIgnoreCase(refsetId)) {
                indexProcessFor = "Refset";
                indexForIndexWriter = REFINDEX;
            }
            else if (DBSchemaMapping.cREFSET.equalsIgnoreCase(refsetId)) {
                indexProcessFor = "cRefset";
                indexForIndexWriter = REFINDEX;
            }
            else {
                indexProcessFor = refsetId;
                indexForIndexWriter = REFINDEX;
            }
        }

        if( processOrIndex )
            return indexProcessFor;
        else
            return indexForIndexWriter;
    }

	/**
	 * Lucene index creation/updation utility.
	 *
	 * @param refsetId   pass refsetId/table name.
	 * @param statement  Object of {@link Statement} class
	 * @param domainUtil Object of {@link SnoDomainUtil} class
	 * @param moduleId   Object of {@link String} class, pass when updation of index
	 *                   is required otherwise null.
	 * @return boolean return true/false on success/failure respectively
	 * @throws Exception
	 */
	private boolean createIndex(String refsetId, Statement statement, SnoDomainUtil domainUtil, Set<String> moduleIdSet)
			throws Exception {
		Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
		if (refsetId == null || statement == null || domainUtil == null)
			return false;
		long offset = 0;
		long indexOffset = INDEX_OFFSET;
		long totalRecords = 0;
		String indexProcessFor = "";
		int extensionUpdateCount = 0;
		boolean isExtensionSelected= false;
		try {
            //Fetch totalRecords
            totalRecords = getTotalRecords( refsetId, statement );
            if( totalRecords == -1)
            	return false;

            //Delete extension document (if exists) and return extensionUpdateCount
            extensionUpdateCount = deleteExtensionDocumentGetExtensionUpdateCount( moduleIdSet, refsetId, statement);
            if( extensionUpdateCount == -1)
            	return false;
            if( extensionUpdateCount > 0)
                offset = (totalRecords - extensionUpdateCount);
            indexProcessFor = getProcessOrIndexName(refsetId, true);
            this.getIndexWriter(getProcessOrIndexName(refsetId, false));

            //Changes for Transclosure
            if( DBSchemaMapping.TRANSCLOSURE.equalsIgnoreCase(refsetId) && moduleIdSet != null ) {
                //Start index from scratch
                offset = 0;
                //To display correct number of index,i.e,all
                extensionUpdateCount = (int) totalRecords;
            }

            //Display the total no. of records needed to be indexed
            isExtensionSelected = displayTotalIndexGetIsExtensionSelected( moduleIdSet, refsetId, extensionUpdateCount, totalRecords );

            //Changes for Transclosure
            if( DBSchemaMapping.TRANSCLOSURE.equalsIgnoreCase(refsetId) && isExtensionSelected ) {
                //for modifyDBquery() and retrieveColumns()
                isExtensionSelected = false;
                //Deleting TC index
                logger.info(LoggerUtility.ConsoleWithLog, "Deleting old TC index ...");
                this.indexWriter.deleteAll();
                this.getIndexWriter(refsetId).commit();
            }

            //Index Creation Begins
			domainUtil.setOffset(offset);
			domainUtil.setNumberOfRecords(indexOffset);
			logger.info(LoggerUtility.ConsoleWithLog,
				 "{} indexes are going to generate ... Please wait while index generation completes ...",indexProcessFor);

			if (totalRecords > 0) {
				do {
					logger.info(LoggerUtility.ConsoleWithLog, "{}{} / {}",NUMBER_OF_RECORDS_WHICH_ARE_INDEXED_TILL_NOW , offset , totalRecords);
					if (DBSchemaMapping.SIMPLEMAP.equalsIgnoreCase(refsetId)
							|| DBSchemaMapping.DESCRIPTION.equalsIgnoreCase(refsetId)
							|| DBSchemaMapping.TEXTDEFINITION.equalsIgnoreCase(refsetId)
							|| DBSchemaMapping.RELATIONSHIP.equalsIgnoreCase(refsetId)
							|| DBSchemaMapping.CONCRETE_RELATIONSHIP.equalsIgnoreCase(refsetId)
							|| DBSchemaMapping.TRANSCLOSURE.equalsIgnoreCase(refsetId)) {
						this.addLuceneDocument(domainUtil.getDomain(refsetId, offset, indexOffset,isExtensionSelected));
						}
					else if (DBSchemaMapping.REFSET.equalsIgnoreCase(refsetId) || DBSchemaMapping.cREFSET.equalsIgnoreCase(refsetId)) {
						this.addRefsetLuceneDocument(
								domainUtil.getDomaincRefset(getProcessOrIndexName(refsetId, true), offset, indexOffset, moduleIdSet));
					} else
						this.addRefsetLuceneDocument(domainUtil.getDomainRefset(refsetId, offset, totalRecords));
					offset = offset + indexOffset;
					domainUtil.setOffset(offset);
					domainUtil.setNumberOfRecords(indexOffset);
				} while (totalRecords > offset);
				if (offset > totalRecords) {
					logger.info(LoggerUtility.ConsoleWithLog, "{}{} / {}",
							NUMBER_OF_RECORDS_WHICH_ARE_INDEXED_TILL_NOW , totalRecords , totalRecords);
				} else {
					logger.info(LoggerUtility.ConsoleWithLog, "{}{}",indexProcessFor , INDEX_GENERATION_PROCESS_INTERRUPTED);
					return false;
				}
			}
			if (DBSchemaMapping.DESCRIPTION.equalsIgnoreCase(refsetId))
				DESCRIPTION_ALL_RECORDS = (int) totalRecords;
			else if (DBSchemaMapping.RELATIONSHIP.equalsIgnoreCase(refsetId))
				RELATIONSHIP_ALL_RECORDS = (int) totalRecords;
			else if (DBSchemaMapping.CONCRETE_RELATIONSHIP.equalsIgnoreCase(refsetId))
				CONCRETE_RELATIONSHIP_ALL_RECORDS = (int) totalRecords;
			else if (DBSchemaMapping.TRANSCLOSURE.equalsIgnoreCase(refsetId))
				RELATIONSHIP_TC_ALL_RECORDS = (int) totalRecords;
			this.getIndexWriter(refsetId).commit();
		} finally {
			updateOffset(); // reset offset value after index generation
			this.getIndexWriter(refsetId).close();
			this.releaseLuceneResources();
		}
		logger.info(LoggerUtility.ConsoleWithLog, "{}{}", indexProcessFor , INDEX_GENERATION_PROCESS_COMPLETED);
		return true;
	}

	/**
	 * The method reset the offset value if international DB is being updated
	 *
	 * @return void
	 *
	 **/
	private void updateOffset() {
		// the block reset the offset value
		DBSchemaMapping.DESCRIPTION_TEXTDEFINITION_CURRENTID = "0";
		DBSchemaMapping.RELATIONSHIP_CURRENTID = "0";
		DBSchemaMapping.CONCRETE_RELATIONSHIP_CURRENTID = "0";
		DBSchemaMapping.TRANSCLOSURE_CURRENTID = "0";
	}

	private boolean checkIsExtension(String indexProcessFor) {
		boolean checkFlag = false;
		if(indexProcessFor.equalsIgnoreCase("Refset"))
			checkFlag = true;
		return checkFlag;
	}

	/**
	 * Searches the concepts of attribute name and attribute value.
	 *
	 * @param queryParams
	 * @return List of {@link Relationship} matching the given Concept ID.
	 * @throws IOException
	 * @throws ParseException
	 * @throws org.apache.lucene.queryparser.classic.ParseException
	 */
	@Override
	public List<Relationship> getConcepts(HashMap<String, List<String>> queryParams)
			throws IOException, java.text.ParseException, ParseException {
		Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
		PropertyReader.loadSystemProperties();
		String relationshipType = "";
		if (relationTypeMap.containsValue(queryParams.get(TYPE + CONCEPTID).get(0))) {
			for (String key : relationTypeMap.keySet()) {
				if (queryParams.get(TYPE + CONCEPTID).get(0).equals(relationTypeMap.get(key))) {
					relationshipType = key;
				}
			}
		}

		String inputQuery = relationshipType;
		QueryParser queryParser = new QueryParser(TYPE + CONCEPTID, getAnalyzer());
		StringBuilder queryCriteria = new StringBuilder();
		if (queryParams.containsKey(SEMANTIC_TAG)) {
			List<String> semantictagList = queryParams.get(SEMANTIC_TAG);

			if (null == semantictagList || semantictagList.isEmpty()) {
				String strMessage = INVALID_PARAMETER + NEW_LINE + CLASS_NAME + this.getClass().getName() + NEW_LINE
						+ SEMANTIC_TAG + ":" + semantictagList;
				logger.error(LoggerUtility.Exception, strMessage);
				return Collections.emptyList();
			}

			// make all elements to lowercase
			for (int i = 0; i < semantictagList.size(); i++)
				semantictagList.set(i, semantictagList.get(i).toLowerCase());

			if (!semantictagList.contains(EnumSemanticTag.ALL.getValue())) {
				String semantictag = "";
				queryCriteria.append(" " + "(");
				for (int i = 0; i < semantictagList.size(); i++) {
					semantictag = semantictagList.get(i);
					String[] semanticTagWords = null;
					Boolean isMultiWordSemanticTag = false;
					if (semantictag.split("\\s+").length > 1) {
						semanticTagWords = semantictag.split("\\s+");
						isMultiWordSemanticTag = true;
					} else if (semantictag.split("/").length > 1) {
						semanticTagWords = semantictag.split("/");
						isMultiWordSemanticTag = true;
					}

					if (0 == i) {
						if (isMultiWordSemanticTag) {
							queryCriteria.append("(");
							for (int count = 0; count < semanticTagWords.length; count++) {

								if (count == 0) {
									queryCriteria.append(HIERARCHY + ":" + semanticTagWords[count] + "*");
								} else if (count == semanticTagWords.length - 1) {
									queryCriteria.append(AND + HIERARCHY + ":*" + semanticTagWords[count]);
								} else {
									queryCriteria.append(AND + HIERARCHY + ":*" + semanticTagWords[count] + "*");
								}
							}
							queryCriteria.append(")");
						} else {
							queryCriteria.append(HIERARCHY + ":" + semantictag);
						}
					} else {
						if (isMultiWordSemanticTag) {
							queryCriteria.append(" OR " + "(");
							for (int count = 0; count < semanticTagWords.length; count++) {

								if (count == 0) {
									queryCriteria.append(HIERARCHY + ":" + semanticTagWords[count] + "*");
								} else if (count == semanticTagWords.length - 1) {
									queryCriteria.append(AND + HIERARCHY + ":*" + semanticTagWords[count]);
								} else {
									queryCriteria.append(AND + HIERARCHY + ":*" + semanticTagWords[count] + "*");
								}
							}
							queryCriteria.append(")");
						} else {
							queryCriteria.append(" OR " + HIERARCHY + ":" + semantictag);
						}
					}
				}
				queryCriteria.append(")");

			}
		}

		if (queryParams.containsKey(DESTINATION + CONCEPTID)) {
			queryCriteria.append(" AND (" + DESTINATION + CONCEPTID + ":" +
				queryParams.get(DESTINATION + CONCEPTID).get(0) + ")");
		}
		queryParser.setDefaultOperator(org.apache.lucene.queryparser.classic.QueryParser.Operator.AND);
		queryParser.setAllowLeadingWildcard(true);
		return getRelationships(
				search(queryParser.parse(inputQuery + queryCriteria.toString()), RELATIONSHIP_ALL_RECORDS, RELATION_INDEX),
				ATTRIBUTE_TYPE);
	}

	/**
     * Checks whether the given (index) directory exists.
	 *
	 * @param indexDirectory
	 * @return boolean value true/false
	 */
	public boolean checkDirectory(String indexDirectory) {
        boolean dirCheck = true;
		Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
		PropertyReader.loadSystemProperties();
		String dataDir = PropertyReader.getProperty(DATA_DIR);
		if (dataDir != null && !dataDir.isEmpty()) {
			dataDir = dataDir + File.separator + indexDirectory;
			File indexDir = new File(dataDir);
			if (!indexDir.exists()) {
                dirCheck = false;
                logger.info(LoggerUtility.CONSOLE, "{} directory is not present", indexDirectory);
            } else if (!indexDir.isDirectory()) {
                dirCheck = false;
                logger.info(LoggerUtility.CONSOLE, "{} is not a directory", indexDirectory);
            }
		}
        return dirCheck;
    }

	@Override
	public Set<String> getSemanticTags() throws ParseException, IOException {
		PropertyReader.loadSystemProperties();
		Set<String> semanticTags = new HashSet<>();
		IndexSearcher indexSearcher = null;
		String dataDir = PropertyReader.getProperty(DATA_DIR);
		if (dataDir != null && !dataDir.isEmpty()) {
			dataDir = dataDir + File.separator + SEMINDEX;
			File sematicIndexDir = new File(dataDir);
			if (!sematicIndexDir.exists())
				throw new IOException("Semantic index directory is not present");
			else if (!sematicIndexDir.isDirectory())
				throw new IOException("Semantic index is not a directory");
		}

		final String inputQuery = SEMANTIC_TAG + ":" + SEMANTIC_TAG;
		QueryParser queryParser = new QueryParser(inputQuery, getAnalyzer());
		Document document = null;
		if (inputQuery != null) {
			Query query = null;
			ScoreDoc[] results = null;
			query = queryParser.parse(inputQuery.trim());
			results = this.search(query, SEMANTIC_ALL_RECORDS, SEMINDEX);
			indexSearcher = this.getIndexSearcher(getIndexReader(SEMINDEX));
			for (int i = 0; i < results.length; i++) {
				document = indexSearcher.doc(results[i].doc);
				semanticTags.add(document.get(HIERARCHY));
			}
		}

		return semanticTags;
	}

	@Override
	public List<String> getRelationType() throws ParseException, IOException {
		List<String> relation = null;
		PropertyReader.loadSystemProperties();
		IndexSearcher indexSearcher = null;
		String dataDir = PropertyReader.getProperty(DATA_DIR);
		if (dataDir != null && !dataDir.isEmpty()) {
			dataDir = dataDir + File.separator + REL_TYPE_INDEX;
			File relIndexDir = new File(dataDir);
			if (!relIndexDir.exists())
				throw new IOException("RelationType index directory is not present");
			else if (!relIndexDir.isDirectory())
				throw new IOException("RelationType index is not a directory");
		}
		final String inputQuery = RELATION_TYPE_ALL + ":" + RELATION_TYPE_ALL;
		QueryParser queryParser = new QueryParser(inputQuery, getAnalyzer());
		Document document = null;
		if (inputQuery != null) {
			Query query = null;
			ScoreDoc[] results = null;
			query = queryParser.parse(inputQuery.trim());
			results = this.search(query, RELATION_TYPE_ALL_RECORDS, REL_TYPE_INDEX);
			relation = new ArrayList<>();
			indexSearcher = this.getIndexSearcher(getIndexReader(REL_TYPE_INDEX));
			for (int i = 0; i < results.length; i++) {
				document = indexSearcher.doc(results[i].doc);
				relation.add(document.get(RELATION_TYPE).toLowerCase());
				relationTypeMap.put(document.get(CONCEPTID), document.get(RELATION_TYPE).toLowerCase());
			}
		}
		return relation;
	}

	private boolean createRelationTypesIndex() throws IOException, java.text.ParseException {
		Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
		List<String> indexedrelation = new ArrayList<>();

		IndexWriter indexWriter = null;
        if (checkDirectory(REL_TYPE_INDEX)) {
            try {
                indexedrelation = getRelationType();
                if (indexedrelation != null && relationTypesForIndexing != null) {
                    relationTypesForIndexing.removeAll(indexedrelation);
                    for (String i : indexedrelation)
                        relationTypeMap.values().remove(i);
                }

            } catch (ParseException | IOException e) {
                // Log the Exception
                logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
            }
        }

		boolean flag = false;
		Set<Concept> descAttribute = searchTC(EnumRelationshipType.ATTRIBUTE.getId());
		for (Concept c : descAttribute) {
			String fsn = c.getFullySpecifiedName().toLowerCase();
			if (fsn.contains(")") && fsn.contains("(")) {
				int i = fsn.indexOf("(") - 1;
				fsn = fsn.substring(0, i);
			}
			if (!indexedrelation.contains(fsn)) {
				relationTypeMap.put(c.getId(), fsn);
				relationTypesForIndexing.add(fsn);
			}
		}
		if (!relationTypesForIndexing.isEmpty() && !relationTypeMap.isEmpty()) {
			String indexDirectory = REL_TYPE_INDEX;
			try {
				indexWriter = this.getIndexWriter(indexDirectory);
				Document document = null;
				for (Map.Entry<String, String> entry : relationTypeMap.entrySet()) {
					String term = entry.getValue();
					term = term.trim();
					document = new Document();
					document.add(new StringField(RELATION_TYPE, term, Field.Store.YES));
					document.add(new StringField(CONCEPTID, entry.getKey(), Field.Store.YES));
					document.add(new StringField(RELATION_TYPE_ALL, RELATION_TYPE_ALL, Field.Store.YES));
					indexWriter.addDocument(document);
				}
				flag = true;
				SNOMEDAgent.updateRelationType = true;
			} finally {
				if (indexWriter != null) {
					indexWriter.commit();
					indexWriter.close();
				}
				this.releaseLuceneResources();
			}
		} else {
			flag = true;
		}
		return flag;

	}

	/**
	 * Creating semantic tags based on provided SNOMED CT RF2 at time of index
	 * creation
	 *
	 * @param indexDirectory
	 * @param semanticTags
	 * @return boolean value true/false
	 * @throws IOException
	 */
	private boolean createSemanticTagsIndex() throws IOException {
		Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
		// identify and remove duplicate semantic tags before indexing
		Set<String> indexedSemanticTags = new HashSet<>();
		IndexWriter indexWriter = null;
        if (checkDirectory(SEMINDEX)) {
            try {
                indexedSemanticTags = getSemanticTags();
                if (!indexedSemanticTags.isEmpty() && (!semanticTagsForIndexing.isEmpty()))
                    semanticTagsForIndexing.removeAll(indexedSemanticTags);
            } catch (ParseException | IOException e) {
                // Log the Exception
                logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
            }
        }

		boolean flag = false;
		// adding semantic tags to index directory
		if (!semanticTagsForIndexing.isEmpty()) {

			this.removeInvalidSemanticTags(semanticTagsForIndexing);
			String indexDirectory = SEMINDEX;
			try {
				indexWriter = this.getIndexWriter(indexDirectory);
				Document document = null;
				for (String semanticTag : semanticTagsForIndexing) {
					semanticTag = semanticTag.trim();
					document = new Document();
					document.add(new StringField(HIERARCHY, semanticTag, Field.Store.YES));
					document.add(new StringField(SEMANTIC_TAG, SEMANTIC_TAG, Field.Store.YES));
					indexWriter.addDocument(document);
				}
				flag = true;
				SNOMEDAgent.updateSematicTag = true;
			} finally {
				if (indexWriter != null) {
					indexWriter.commit();
					indexWriter.close();
				}
				this.releaseLuceneResources();
			}
		} else {
			flag = true;
		}
		return flag;
	}

	/**
	 * removeInvalidSemanticTags method will be used for removing irrelevant/ not
	 * properly defined semantic tags
	 *
	 * @param semanticTagsForIndexing
	 * @return Set of {@link String} having semantic tags init for indexing.
	 */
	private Set<String> removeInvalidSemanticTags(Set<String> semanticTagsForIndexing) {
		Set<String> semanticTagsForRemove = new HashSet<>();
		String[] specialCharacters = { "&", ":", "[", "]", "(", ")", "+", "-", ",", "!" };
		boolean discardSemanticTag = false;
		String[] semArray = null;
		for (String semanticTag : semanticTagsForIndexing) {
			discardSemanticTag = false;
			if (!semanticTag.equalsIgnoreCase("snomed rt+ctv3"))
				for (int i = 0; i < specialCharacters.length; i++) {
					if (semanticTag.contains(specialCharacters[i])) {
						discardSemanticTag = true;
						break;
					}
				}
			if (discardSemanticTag)
				semanticTagsForRemove.add(semanticTag);
			else if (!semanticTag.equalsIgnoreCase("snomed rt+ctv3")) {
				/*
				 * check semantic tag for nos word init ex. pneumonitis, allergic nos, ca
				 * ovary/uterine adnexa nos
				 */
				semArray = semanticTag.split(" ");
				for (String word : semArray) {
					if (word.equalsIgnoreCase("nos")) {
						semanticTagsForRemove.add(semanticTag);
						break;
					}
				}
			}

		}
		LuceneSearchAgent.semanticTagsForIndexing.removeAll(semanticTagsForRemove);

		return LuceneSearchAgent.semanticTagsForIndexing;
	}

	/**
	 * Searches the simpleMapTarget of refsetComponentId provided .
	 *
	 * @param queryParams
	 * @return SimpleMapRefsetResult mapped to the given Refset Component ID.
	 * @throws IOException
	 * @throws ParseException
	 * @throws org.apache.lucene.queryparser.classic.ParseException
	 */
	@Override
	public SimpleMapRefsetResult searchSimpleMap(String refsetComponentId)
			throws java.text.ParseException, IOException, ParseException {

		if (refsetComponentId == null)
			return null;

		SimpleMapRefsetResult SimpleMapRefsetResult = new SimpleMapRefsetResult();
		PropertyReader.loadSystemProperties();
		final String inputQuery = REFERENCEDCOMPONENT + CONCEPTID;
		QueryParser queryParser = new QueryParser(inputQuery, getAnalyzer());
		StringBuilder queryCriteria = new StringBuilder(inputQuery + ":" + refsetComponentId + AND + ACTIVE + ":1");
		SimpleMapRefsetResult.setMapStatus("NO_MAP_FOUND");
		if (inputQuery != null) {
			Query query = null;
			ScoreDoc[] results = null;
			query = queryParser.parse(queryCriteria.toString().trim());
			results = this.search(query, 100, SIMPLE_MAP_INDEX);
			SimpleMapRefsetGroup simpleMapRefsetGroup = new SimpleMapRefsetGroup();
			List<String> mapTargetCodes = (new ArrayList<String>());
			Document document = null;
			IndexSearcher indexSearcher = this.getIndexSearcher(getIndexReader(SIMPLE_MAP_INDEX));

			for (int i = 0; i < results.length; i++) {
				document = indexSearcher.doc(results[i].doc);
				SimpleMapRefsetResult.setConceptId(Long.parseLong(document.get(REFERENCEDCOMPONENT + CONCEPTID)));
				SimpleMapRefsetResult.setMapStatus("MAP_FOUND");
				mapTargetCodes.add("mapTarget : " + document.get(SIMPLE_MAP_TARGET));
				simpleMapRefsetGroup.setSimpleMappedTargetCodes(mapTargetCodes);
				SimpleMapRefsetResult.setSimpleMapRefsetGroup(simpleMapRefsetGroup);
			}
		}
		return SimpleMapRefsetResult;

	}

	/**
	 * Searches for the immediate children descriptions.
	 *
	 * @param results  array of {@link ScoreDoc} elements.
	 * @param refsetId Identifier of Refset
	 * @return relationshipTC set of {@link RelationshipTC}.
	 * @throws IOException              if any error occurs while processing.
	 * @throws java.text.ParseException
	 **/
	private Set<RelationshipTC> getRelationshipTC(ScoreDoc[] results, String refsetId)
			throws IOException, java.text.ParseException {
		Set<RelationshipTC> relationshipTCList = new HashSet<>();
		ComponentFactory componentFactory = ComponentFactory.getInstance();
		IndexSearcher indexSearcher = null;
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat(E_MMM_DD_HH_MM_SS_Z_YYYY, Locale.ENGLISH);
		Document document = null;
		indexSearcher = this.getIndexSearcher(getIndexReader(refsetId));
		for (int i = 0; i < results.length; i++) {
			document = indexSearcher.doc(results[i].doc);
			RelationshipTC relTc = new RelationshipTC();
			Concept child = (Concept) componentFactory.getComponent(EnumComponent.CONCEPT, null);
			child.setId(document.get(ID));
			child.setFullySpecifiedName(document.get(CONCEPT_FSN));
            child.setPreferredName(document.get(PREFERRED_NAME));
			child.setDefinitionStatusId(EnumDefStatus.getEnum(document.get(DEFINITIONSTATUS)));
			child.setActiveStatus(Byte.parseByte(document.get(ACTIVE)));
			child.setModuleId(document.get(DESCRIPTION_MODID));
			child.setTime(simpleDateFormat.parse((document.get(EFFECTIVETIME))));
			relTc.setChildConcept(child);
			relTc.setCountOfChildrens(Integer.parseInt(document.get(COUNTOFCHILDREN)));
			relTc.setIsImmediateChildren(Short.parseShort(document.get(ISIMMEDIATECHILDREN)));
			relationshipTCList.add(relTc);
		}
		return relationshipTCList;
	}

	/**
	 * Searches for the immediate children descriptions.
	 *
	 * @param conceptId Concept Id whose immediate children to search
	 * @return Set of {@link RelationshipTC} matching the given Concept ID.<br>
	 * @throws IOException              if any error occurs while processing.
	 * @throws java.text.ParseException
	 */
	@Override
	public Set<RelationshipTC> getIsARelationshipsWithCount(String conceptId) {
		Logger logger = LogManager.getLogger(LuceneSearchAgent.class);

		Set<RelationshipTC> emptySet = Collections.emptySet();
		String inputQueryS = "";
		inputQueryS = conceptId.trim();
		if (inputQueryS.length() < 6) {
			return emptySet;
		}
		ScoreDoc[] resultst = null;
		QueryParser queryParsers = null;
		queryParsers = new QueryParser(PARENTID, getAnalyzer());
		StringBuilder queryCriteria = new StringBuilder(
				PARENTID + ":" + inputQueryS + AND + ISIMMEDIATECHILDREN + ":1");
		queryParsers.setDefaultOperator(QueryParser.Operator.AND);
		queryParsers.setAllowLeadingWildcard(true);

		try {
			if (inputQueryS != null) { // 3DEC2015
				Query query = null;
				query = queryParsers.parse(queryCriteria.toString().trim());
				resultst = this.search(query, 200000, TCINDEX);

				try {
					return getRelationshipTC(resultst, TCINDEX);
				} catch (java.text.ParseException e) {
					// Log the Exception
					logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
				}
			}
		} catch (ParseException|IOException  e) {
			// Log the Exception
			logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
		}
		return Collections.emptySet();

	}

	/**
	 * This method is use to index distinct refsetid with respective name
	 *
	 * @param refsetIdList contains refsetid , refset term
	 * @param refsetId
	 */
	private void addDistinctRefsetForIndexing(HashMap<String, String> refsetIdList, String refsetId) {
		Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
		Connection connection = null;
		Statement statement = null;

		try {
			Document document = null;
			this.getIndexWriter(refsetId);
			connection = ConnectionManager.getConnection();
			statement = connection.createStatement();
			if (refsetIdList != null) {
				long refsetcount = 0;
				for (String refset : refsetIdList.keySet()) {
					refsetcount = DBUtil.getTotalRefsetCount(refset, statement);
					document = new Document();
					document.add(new StringField(DBSchemaMapping.REFSET_ID, refset, Field.Store.YES));
					document.add(new StringField(REFSETTERM, refsetIdList.get(refset), Field.Store.YES));
					document.add(new StringField(DISTINCT, "1", Field.Store.YES));
					document.add(new StringField(REFSETCOUNT, String.valueOf(refsetcount), Field.Store.YES));
					this.indexWriter.addDocument(document);
				}

			} else {
				long refsetcount = DBUtil.getTotalRefsetCount(refsetId, statement);
				document = new Document();
				document.add(new StringField(DBSchemaMapping.REFSET_ID, refsetId, Field.Store.YES));
				document.add(new StringField(REFSETTERM, "", Field.Store.YES));
				document.add(new StringField(DISTINCT, "1", Field.Store.YES));
				document.add(new StringField(REFSETCOUNT, String.valueOf(refsetcount), Field.Store.YES));
				this.indexWriter.addDocument(document);
			}

			this.getIndexWriter(refsetId).commit();
			this.getIndexWriter(refsetId).close();

		} catch (IOException | SQLException e) {
			logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
		} finally {
			try {
				this.releaseLuceneResources();
				if (statement != null)
					DBUtil.closeStatement(statement);
				if (connection != null)
					connection.close();
			} catch (SQLException e) {
				logger.error(LoggerUtility.Exception, STATEMENT_IS_NOT_CLOSED);
				logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
			}

		}
	}

	/**
	 * This method is used to delete old extension documents.
     * Helpful when index is recreated for an extension.
	 *
	 * @param moduleId
	 * 					Module Identifier .
	 */
	private void deleteOldExtensionDocument( String moduleId ) {
		Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
        try {
            this.getIndexWriter(REFINDEX);
            BooleanQuery.Builder builder = new BooleanQuery.Builder();
            //Queries
            Query distinctQuery = new TermQuery(new Term( DISTINCT, "2"));
            Query idQuery = new TermQuery(new Term( DBSchemaMapping.DESCRIPTION_CONCEPT_ID, moduleId));
            //Add Queries (AND)
            builder.add(distinctQuery, BooleanClause.Occur.MUST);
            builder.add(idQuery, BooleanClause.Occur.MUST);
            //Delete if already exist
            this.indexWriter.deleteDocuments( builder.build() );
		} catch (IOException e) {
			logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
		} finally {
            try {
                this.getIndexWriter(REFINDEX).commit();
                this.getIndexWriter(REFINDEX).close();
                this.releaseLuceneResources();
            } catch (IOException e) {
                logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
            }
		}
	}

	/**
	 * This method is use to index distinct refsetid with respective name
	 *
	 * @param refsetIdList contains refsetid , refset term
	 * @param refsetId
	 */
	public void addExtensionDetailsForIndexing(Set<String> moduleIdSet) {
		Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
		Connection connection = null;
		Statement statement = null;
        long extensionCount = 0;
        HashMap<String,String>refsetIdListCheck=new HashMap<>();
		try {
			Document document = null;
			connection = ConnectionManager.getConnection();
			statement = connection.createStatement();
            if (moduleIdSet != null && !moduleIdSet.isEmpty()) {
                for (String moduleId : moduleIdSet) {
                    if (!INDIA_MODULE_ID.equalsIgnoreCase(moduleId))
                        refsetIdListCheck = DBUtil.getDistinctRefsetId(moduleId, null, statement);
                    if(!INDIA_MODULE_ID.equalsIgnoreCase(moduleId) && refsetIdListCheck.isEmpty()) {
                        //Delete previously created document
                        deleteOldExtensionDocument( moduleId );
                        //Getting indexWriter
                        this.getIndexWriter(REFINDEX);
                        extensionCount = DBUtil.getTotalExtensionCount(moduleId,statement);
                        //Create new document
                        document = new Document();
                        document.add(new StringField(DBSchemaMapping.DESCRIPTION_CONCEPT_ID, moduleId, Field.Store.YES));
                        document.add(new StringField("ExtensionFSN", DBUtil.getConceptFSN(moduleId,statement), Field.Store.YES));
                        document.add(new StringField(DISTINCT, "2", Field.Store.YES));
                        document.add(new StringField("ExtensionCount", String.valueOf(extensionCount), Field.Store.YES));
                        this.indexWriter.addDocument(document);
                        //Saving index
                        this.getIndexWriter(REFINDEX).commit();
                        this.getIndexWriter(REFINDEX).close();
                    }
                }
            }
		} catch (IOException | SQLException e) {
			logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
		} finally {
			try {
				this.releaseLuceneResources();
				if (statement != null)
					DBUtil.closeStatement(statement);
				if (connection != null)
					connection.close();
			} catch (SQLException e) {
				logger.error(LoggerUtility.Exception, STATEMENT_IS_NOT_CLOSED);
				logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
			}

		}
	}

	/**
	 * Returns a {@link Set} of all Refset.
	 *
	 * @return Set of {@link Refset}s.
	 */
	@Override
	public Set<Refset> getAllRefset() throws ParseException, IOException, java.text.ParseException {
		PropertyReader.loadSystemProperties();
		ComponentFactory componentFactory = ComponentFactory.getInstance();
		final String inputQuery = "1";
		QueryParser queryParser = new QueryParser(DISTINCT, getAnalyzer());
		Query query = null;
		ScoreDoc[] results = null;
		Document document = null;
		IndexSearcher indexSearcher = null;
		Set<Refset> list = new HashSet<>();
		query = queryParser.parse(inputQuery);
		results = this.search(query, INDEX_OFFSET, REFINDEX);

		indexSearcher = this.getIndexSearcher(getIndexReader(REFINDEX));
		for (int i = 0; i < results.length; i++) {
			document = indexSearcher.doc(results[i].doc);
			Refset ref = new Refset();
			if (!document.get("RefsetCount").equalsIgnoreCase("0")) {
				Concept concept = (Concept) componentFactory.getComponent(EnumComponent.CONCEPT, null);
				concept.setId(document.get(DBSchemaMapping.REFSET_ID));
				concept.setFullySpecifiedName(document.get(REFSETTERM));
				concept.setActiveStatus((byte) 1);
				ref.setRefsetCount(document.get(REFSETCOUNT));
				ref.setRefsetConcept(concept);
				list.add(ref);
			}
		}

		return list;
	}

	/**
	 * Returns a {@link Set} of all Extension.
	 *
	 * @return Set of {@link Refset}s.
	 */
	@Override
	public Set<Refset> getAllExtension() throws ParseException, IOException, java.text.ParseException {
		PropertyReader.loadSystemProperties();
		ComponentFactory componentFactory = ComponentFactory.getInstance();
		final String inputQuery = "2";
		QueryParser queryParser = new QueryParser(DISTINCT, getAnalyzer());
		Query query = null;
		ScoreDoc[] results = null;
		Document document = null;
		IndexSearcher indexSearcher = null;
		Set<Refset> list = new HashSet<>();
		query = queryParser.parse(inputQuery);
		results = this.search(query, INDEX_OFFSET, REFINDEX);

		indexSearcher = this.getIndexSearcher(getIndexReader(REFINDEX));
		for (int i = 0; i < results.length; i++) {
			document = indexSearcher.doc(results[i].doc);
			Refset ref = new Refset();
			if (!document.get("ExtensionCount").equalsIgnoreCase("0")) {
				Concept concept = (Concept) componentFactory.getComponent(EnumComponent.CONCEPT, null);
				concept.setId(document.get(DBSchemaMapping.DESCRIPTION_CONCEPT_ID));
				concept.setFullySpecifiedName(document.get("ExtensionFSN"));
				concept.setActiveStatus((byte) 1);
				ref.setRefsetCount(document.get("ExtensionCount"));
				ref.setRefsetConcept(concept);
				list.add(ref);
			}
		}

		return list;
	}

	/**
	 * Returns a {@link Set} of all Descendants {@link Concept}s for given concept
	 * identifier.
	 *
	 * @param id SNOMED CT concept identifier.
	 * @return Set of {@link Concept}s.
	 */
	@Override
	public Set<Concept> getRefsetMember(String refsetId) throws ParseException, IOException, java.text.ParseException {
		if (refsetId == null)
			return Collections.emptySet();
		PropertyReader.loadSystemProperties();
		final String inputQuery = refsetId;
		QueryParser queryParser = new QueryParser(DBSchemaMapping.REFSET_ID, getAnalyzer());

		Query query = null;
		ScoreDoc[] results = null;
		query = queryParser.parse(inputQuery);
		results = this.search(query, INDEX_OFFSET, REFINDEX);
		return getRefSetConcept(results, REFINDEX, refsetId);
	}

	/**
	 * Returns a {@link Set} of all Descendants {@link Concept}s for given concept
	 * identifier. Used for Extension Member records.
	 * @param id SNOMED CT concept identifier.
     * @param pageNumber records to load for a specific page (pagination).
	 * @return Set of {@link Concept}s.
	 */
	@Override
	public Set<Concept> getExtensionMember(String moduleId, int pageNumber) throws ParseException, IOException, java.text.ParseException {
		if (moduleId == null)
			return Collections.emptySet();
		PropertyReader.loadSystemProperties();
		final String inputQuery = moduleId;
		QueryParser queryParser = new QueryParser(DESCRIPTION_MODID, getAnalyzer());

		Query query = null;
		ScoreDoc[] results = null;
		query = queryParser.parse(inputQuery);
		results = this.search(query, 1000000, SNOINDEX);
		return getExtensionConcept(results, SNOINDEX, moduleId, pageNumber);
	}

	/**
	 * Searches for the concepts.
	 *
	 * @param results  array of {@link ScoreDoc} elements.
	 * @param refsetId Index Name
	 * @param refConceptId Identifier of Refset
	 * @return concepts set of {@link Concept}s.
	 * @throws IOException              if any error occurs while processing.
	 * @throws java.text.ParseException
	 */
	private Set<Concept> getRefSetConcept(ScoreDoc[] results, String refsetId, String refConceptId)
			throws IOException {

		Set<Concept> topDescriptions = new HashSet<>();
		IndexSearcher indexSearcher = null;
		ComponentFactory componentFactory = ComponentFactory.getInstance();
		indexSearcher = this.getIndexSearcher(getIndexReader(refsetId));
		processScoreDoc(results, refConceptId, topDescriptions, indexSearcher, componentFactory);
		return topDescriptions;
	}

	/**
	 * Searches for the concepts.
     * For Extension Member records
	 *
	 * @param results  array of {@link ScoreDoc} elements.
	 * @param refsetId Index Name
	 * @param refConceptId Identifier of Refset
     * @param pageNumber records to load for a specific page (pagination).
	 * @return concepts set of {@link Concept}s.
	 * @throws IOException              if any error occurs while processing.
	 * @throws java.text.ParseException
	 */
	private Set<Concept> getExtensionConcept(ScoreDoc[] results, String refsetId, String refConceptId, int pageNumber)
			throws IOException {
		Set<Concept> topDescriptions = new HashSet<>();
		IndexSearcher indexSearcher = null;
		ComponentFactory componentFactory = ComponentFactory.getInstance();
		indexSearcher = this.getIndexSearcher(getIndexReader(refsetId));
		processScoreDocForExtension(results, refConceptId, topDescriptions, indexSearcher, componentFactory, pageNumber);
		return topDescriptions;
	}

	private void processScoreDoc(ScoreDoc[] results, String refConceptId, Set<Concept> topDescriptions,
		IndexSearcher indexSearcher, ComponentFactory componentFactory) throws IOException {
		Document document;
		for (int i = 0; i < results.length; i++) {
			document = indexSearcher.doc(results[i].doc);
			String temp = document.get("Distinct");
			if (temp == null) {
				if (EnumLangRefset.isValidEnum(refConceptId)) {
					addTopDescription(refConceptId, topDescriptions, componentFactory, document);
				} else {
					addTopDescription(topDescriptions, componentFactory, document);
				}

			}
		}
	}

    /**
     * Calculates startPoint, totalNumberOfRecordsInSet and nearestValue.
     * Helper function for processScoreDocForExtension.
     *
     * @param pageMap   contains page number as key and starting index as value for an extension
     * @param pageNumber   current page number
     * @param totalPages   total pages for an extension
     * @param extraRecords   no. of records in last page
     * @param ascOrDescSection   boolean value indicating location of current page (first or second half)
     * @return returnArray
     *                  contains startPoint at index 0,
     *                  nearestValue at index 1,
     *                  totalNumberOfRecordsInSet at index 2
     */
    private int[] getRequiredValuesForProcessing( HashMap<Integer, Integer> pageMap, int pageNumber,
           int totalPages, int extraRecords, boolean ascOrDescSection ) {
        int[] returnArray = new int[3];
        int startPoint = -1;
        int totalNumberOfRecordsInSet = -1;
        //-1 signifies page found
        int nearestValue = -1;
        //Page found
        if(pageMap.containsKey(pageNumber)) {
            startPoint = pageMap.get(pageNumber);
            if( pageNumber == totalPages )
                totalNumberOfRecordsInSet = extraRecords;
            else
                totalNumberOfRecordsInSet = RESPONSE_SIZE_FOR_EXTENSION;
        }
        //Not found
        else {
            //Find nearest in...
            nearestValue = pageNumber;
            //Ascending Section
            if( ascOrDescSection ) {
                do{
                    nearestValue--;
                    if(pageMap.containsKey(nearestValue)) {
                        startPoint = pageMap.get(nearestValue);
                        break;
                    }
                }while( nearestValue > 0 );
            }
            //Descending Section
            else {
                do{
                    nearestValue++;
                    if(pageMap.containsKey(nearestValue)) {
                        startPoint = pageMap.get(nearestValue);
                        break;
                    }
                }while( nearestValue < totalPages );
            }
            //If nearest found or starting from first
            if( nearestValue != totalPages) {
                //For Ascending Section
                if( ascOrDescSection ) {
                    totalNumberOfRecordsInSet = (pageNumber - nearestValue + 1 ) * RESPONSE_SIZE_FOR_EXTENSION;
                }
                //For Descending Section
                else {
                    totalNumberOfRecordsInSet = (nearestValue - pageNumber + 1 ) * RESPONSE_SIZE_FOR_EXTENSION;
                }
            }
            //nearest not found
            else {
                totalNumberOfRecordsInSet = extraRecords + (totalPages - pageNumber) * RESPONSE_SIZE_FOR_EXTENSION;
            }
        }
        //Setting values
        returnArray[0] = startPoint;
        returnArray[1] = nearestValue;
        returnArray[2] = totalNumberOfRecordsInSet;
        return returnArray;
    }

    /**
     * Returns count of members of an Extension from the Index.
     *
     * @param refConceptId
     *                  Module Identifier .
     * @return extensionCount
     *                  the total number of rows stored in index.
     */
    public long getExtensionCountFromIndex(String refConceptId) {
        Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
        long extensionCount=0;
        try {
            Set<Refset> refsetList = getAllExtension();
            for (Refset r : refsetList) {
                String refsetId = r.getRefsetConcept().getId();
                if (refsetId.equalsIgnoreCase(refConceptId)) {
                    extensionCount = Long.parseLong(r.getRefsetCount());
                    break;
                }
            }
        } catch (ParseException e) {
            // Log the Exception
            logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
        } catch (java.text.ParseException e) {
            // Log the Exception
            logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
        } catch (IOException e) {
            // Log the Exception
            logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
        } catch (Exception e) {
            // Log the Exception
            logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
        }
        return extensionCount;
    }

    //For Extension Member records
    private void processScoreDocForExtension(ScoreDoc[] results, String refConceptId, Set<Concept> topDescriptions,
        IndexSearcher indexSearcher, ComponentFactory componentFactory, int pageNumber) throws IOException {
        Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
        //Page Check 1
        if( pageNumber <= 0 ) {
            logger.error(LoggerUtility.ConsoleWithException, "Page {} is less than or equal to 0", pageNumber);
            return;
        }
        Document document;
        HashMap<Integer,Integer> pageMap = null;
        //Step 1:
        //Compute page number related variables
        long extensionCount = 0;
        extensionCount = getExtensionCountFromIndex( refConceptId );
        if( extensionCount == 0 )
            return;
        //Calculate total no. of pages
        int totalPages = (int) extensionCount / RESPONSE_SIZE_FOR_EXTENSION;
        int extraRecords = (int) extensionCount % RESPONSE_SIZE_FOR_EXTENSION;
        if( extraRecords > 0 )
            totalPages += 1;
        //Page Check 2
        if( pageNumber > totalPages ) {
            logger.error(LoggerUtility.ConsoleWithException, "Page {} is greater than the total pages in extension", pageNumber);
            return;
        }
        //Find out if page lies in
        //ascending or descending section.
        //true -> Ascending
        //false -> Descending
        boolean ascOrDescSection;
        if( pageNumber <= (totalPages / 2 ))
            ascOrDescSection = true;
        else
            ascOrDescSection = false;
        //Step 2:
        //Map of Modules (id)
        //if conceptId doesn't have a value
        if(!extensionIdMap.containsKey(refConceptId)) {
            pageMap = new HashMap<>();
            //first page starts from 0
            pageMap.put(1,0);
            //For last page
            //Since array starts from 0 to n - 1
            pageMap.put(totalPages, results.length - 1);
        }
        //if conceptId has a value
        else {
            pageMap = extensionIdMap.get(refConceptId);
        }
        //Step 3:
        //Calculate useful variables
        //Find if pageFound
        boolean pageFound = false;
        if(pageMap.containsKey(pageNumber)) {
            pageFound = true;
        }
        //Collect values from helper function
        int[] returnedValues = getRequiredValuesForProcessing(pageMap, pageNumber, totalPages, extraRecords, ascOrDescSection);
        int startPoint = returnedValues[0];
        int nearestValue = returnedValues[1];
        int totalNumberOfRecordsInSet = returnedValues[2];
        //Step 4:
        //Generating response
        //filling map with start points for different page numbers
        //(amongst other things)
        int i = startPoint;
        //Helper for child pages (nearestValue)
        //So that duplicate values don't set condition true
        //resulting in uneven no. of child pages
        boolean hasSizeChanged = true;
        int setSizeHolder = -1;
        //For descending section
        //different way to traverse based on starting point
        boolean startFromLast = false;
        if( nearestValue == totalPages )
            startFromLast = true;
        //Starting traversal and operations
        for (i = startPoint ; (ascOrDescSection) ?
                (i < results.length && topDescriptions.size() < totalNumberOfRecordsInSet) :
                (i >= 0 && topDescriptions.size() < totalNumberOfRecordsInSet) ;
                i = (ascOrDescSection) ? (++i) : (--i)) {
            if( topDescriptions.size() != setSizeHolder )
                hasSizeChanged = true;
            document = indexSearcher.doc(results[i].doc);
            String temp = document.get("Distinct");
            //For Ascending Section
            boolean conditionForActualResponse = (totalNumberOfRecordsInSet - topDescriptions.size()) == RESPONSE_SIZE_FOR_EXTENSION ;
            boolean conditionForChildPages =  (topDescriptions.size() >= RESPONSE_SIZE_FOR_EXTENSION)
                && ((topDescriptions.size() % RESPONSE_SIZE_FOR_EXTENSION) == 0) && hasSizeChanged;
            //For Descending Section
            boolean conditionForLastPage = (extraRecords != 0) && (nearestValue == totalPages) && (topDescriptions.size() == extraRecords);
            boolean conditionForChildPagesDesc = (topDescriptions.size() >= RESPONSE_SIZE_FOR_EXTENSION)
                    && (((topDescriptions.size() - extraRecords) % RESPONSE_SIZE_FOR_EXTENSION) == 0) && hasSizeChanged;
            //If page not found do...
            if( !pageFound ) {
                //Reset before final values
                if(conditionForActualResponse) {
                    pageMap.put(pageNumber, i);
                    totalNumberOfRecordsInSet = RESPONSE_SIZE_FOR_EXTENSION;
                    topDescriptions.clear();
                    pageFound = true;
                }
                //Indexing children
                else if( (ascOrDescSection) ? conditionForChildPages :
                       (startFromLast) ? (conditionForLastPage || conditionForChildPagesDesc) : conditionForChildPages) {
                    hasSizeChanged = false;
                    setSizeHolder = topDescriptions.size();
                    //Save i
                    if( ascOrDescSection )
                        pageMap.put(++nearestValue, i);
                    else
                        pageMap.put(--nearestValue, i);
                }
            }
            if (temp == null) {
                addTopDescriptionForExtension(topDescriptions, componentFactory, document);
            }
        }
        //Mapping page number to i
        if( (ascOrDescSection) ?
                (pageNumber != (totalPages / 2)) : ((pageNumber - 1) != (totalPages / 2 )) ) {
            if( ascOrDescSection )
                pageMap.put(pageNumber + 1, i);
            else
                pageMap.put(pageNumber - 1, i);
        }
        //Storing pageMap for an extension
        extensionIdMap.put(refConceptId, pageMap);
    }

	private void addTopDescription(Set<Concept> topDescriptions, ComponentFactory componentFactory, Document document) {
		if (document.get(DBSchemaMapping.REFSETLANG_REFSET_ID) != null
				&& ((document.get(DBSchemaMapping.REFSETLANG_REFSET_ID))
				.equalsIgnoreCase(EnumLangRefset.US.getId())
				&& (document.get(DBSchemaMapping.REFSET_ACTIVE)).equalsIgnoreCase("1")
				&& (document.get(TYPEID)).equalsIgnoreCase(EnumDescType.FULLY_SPECIFIED_NAME.getValue())
				&& (document.get(ACTIVE)).equalsIgnoreCase("1"))) {
				Concept concept = (Concept) componentFactory.getComponent(EnumComponent.CONCEPT, null);
				concept.setId(document.get(CONCEPTID));
				concept.setActiveStatus(Byte.parseByte(document.get(CONCEPT_STATE)));
				concept.setModuleId(document.get(DBSchemaMapping.REFSET_MODULE_ID));
				concept.setFullySpecifiedName(document.get(CONCEPT_FSN));
				topDescriptions.add(concept);

		} else {
			if ((document.get(DBSchemaMapping.REFSET_ACTIVE)).equalsIgnoreCase("1")) {
				Concept concept = (Concept) componentFactory.getComponent(EnumComponent.CONCEPT, null);
				concept.setId(document.get(CONCEPTID));
				concept.setActiveStatus(Byte.parseByte(document.get(CONCEPT_STATE)));
				concept.setModuleId(document.get(DBSchemaMapping.REFSET_MODULE_ID));
				concept.setFullySpecifiedName(document.get(TERM));
				topDescriptions.add(concept);
			}
		}
	}

    private void addTopDescription(String refConceptId, Set<Concept> topDescriptions, ComponentFactory componentFactory,
            Document document) {
        Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(E_MMM_DD_HH_MM_SS_Z_YYYY, Locale.ENGLISH);
        if ((document.get(DBSchemaMapping.DESCRIPTION_LANGUAGE_CODE)).equalsIgnoreCase(
                EnumLangRefset.getEnum(refConceptId).getId()) && refConceptId.contains("1000189")
                && document.get(TYPEID).equalsIgnoreCase(EnumDescType.SYNONYM.getValue())
                && (document.get(ACTIVE)).equalsIgnoreCase("1")) {
            Concept concept = (Concept) componentFactory.getComponent(EnumComponent.CONCEPT, null);
            concept.setId(document.get(CONCEPTID));
            concept.setActiveStatus(Byte.parseByte(document.get(CONCEPT_STATE)));
            //concept.setActiveStatus(Byte.parseByte(document.get(ACTIVE)));
            concept.setModuleId(document.get(DBSchemaMapping.REFSET_MODULE_ID));
            concept.setFullySpecifiedName(document.get(CONCEPT_FSN));
            //Added more properties for ECL
            concept.setPreferredName(document.get(TERM));
            concept.setDefinitionStatusId(EnumDefStatus.getEnum(document.get(DEFINITIONSTATUS)));
            concept.setModuleId(document.get(DESCRIPTION_MODID));
            try {
                concept.setTime(simpleDateFormat.parse(document.get(DBSchemaMapping.DESCRIPTION_EFFECTIVE_TIME)));
            } catch (java.text.ParseException e) {
                concept.setTime(null);
                logger.error(LoggerUtility.Exception, LoggerUtility.createErrorMessage(e));
            }
            topDescriptions.add(concept);

        }
    }

    //For Extension Member records
	private void addTopDescriptionForExtension(Set<Concept> topDescriptions, ComponentFactory componentFactory, Document document) {
        if ((document.get(ACTIVE)).equalsIgnoreCase("1")
				&& (document.get(DBSchemaMapping.REFSETLANG_REFSET_ID)).equalsIgnoreCase(EnumLangRefset.US.getId())
				&& (document.get(TYPEID)).equalsIgnoreCase(EnumDescType.FULLY_SPECIFIED_NAME.getValue()) ) {
            Concept concept = (Concept) componentFactory.getComponent(EnumComponent.CONCEPT, null);
            concept.setId(document.get(CONCEPTID));
            concept.setActiveStatus(Byte.parseByte(document.get(CONCEPT_STATE)));
            concept.setModuleId(document.get(DESCRIPTION_MODID));
            concept.setFullySpecifiedName(document.get(CONCEPT_FSN));
            topDescriptions.add(concept);
        }
	}

	/**
	 * For Deleting the old created index of refset.
	 *
	 * @param refsetId    Identifier of Refset
	 * @param indexWriter
	 */
	public void deleteRefsetDocument(final String refsetId) throws IOException {
		try {
			Query query1 = new WildcardQuery(new Term(DBSchemaMapping.REFSET_ID, refsetId));
			this.indexWriter = getIndexWriter(refsetId);
			this.indexWriter.deleteDocuments(query1);
		} finally {
			this.getIndexWriter(refsetId).commit();
			this.getIndexWriter(refsetId).close();
			this.releaseLuceneResources();
		}
	}

	/**
	 * Adds documents to lucene index.
	 *
	 * @param List of {@link CompositeDescription}, {@link Refset}}
	 * @return {@link IndexWriter}.
	 * @throws Exception
	 */
	private IndexWriter addRefsetLuceneDocument(List<CompositeDescription> list) throws Exception {
		Logger logger = LogManager.getLogger(LuceneSearchAgent.class);
		if (list != null) {
			Document document = null;
			if (!list.isEmpty()) {
				Field id = new StringField(ID, "", Field.Store.YES);
				Field active = new StringField(ACTIVE, "", Field.Store.YES);
				Field conceptId = new StringField(CONCEPTID, "", Field.Store.YES);
				Field typeId = new StringField(TYPEID, "", Field.Store.YES);
				Field term = new TextField(TERM, "", Field.Store.YES);
				Field hierarchy = new StringField(HIERARCHY, "", Field.Store.YES);
				Field preferredTerm = new StringField(IS_PREFERRED_TERM, "", Field.Store.YES);
				Field conceptState = new StringField(CONCEPT_STATE, "", Field.Store.YES);
				Field conceptFSN = new StringField(CONCEPT_FSN, "", Field.Store.YES);
				Field definitionStatus = new StringField(DEFINITIONSTATUS, "", Field.Store.YES);
				Field moduleId = new StringField(DESCRIPTION_MODID, "", Field.Store.YES);
				Field caseSignificanceId = new StringField(DBSchemaMapping.DESCRIPTION_CASE_SIGNIFICANCE_ID, "",
						Field.Store.YES);
				Field languageCode = new StringField(DBSchemaMapping.DESCRIPTION_LANGUAGE_CODE, "", Field.Store.YES);
				Field descriptionEffectiveTime = new StringField(DBSchemaMapping.DESCRIPTION_EFFECTIVE_TIME, "",
						Field.Store.YES);
				Field refsetLangReferenceComponentId = new StringField(
						DBSchemaMapping.REFSETLANG_REFERENCED_COMPONENT_ID, "", Field.Store.YES);
				Field refsetLangReferenceId = new StringField(DBSchemaMapping.REFSETLANG_REFSET_ID, "",
						Field.Store.YES);
				Field refsetLangActive = new StringField(DBSchemaMapping.REFSETLANG_ACTIVE, "", Field.Store.YES);
				Field refsetLangAcceptability = new StringField(DBSchemaMapping.REFSETLANG_ACCEPTABILITY_ID, "",
						Field.Store.YES);
				Field refsetReferenceId = new StringField(DBSchemaMapping.REFSET_ID, "", Field.Store.YES);
				Field refsetModuleId = new StringField(DBSchemaMapping.REFSET_MODULE_ID, "", Field.Store.YES);
				Field refsetTargetId = new StringField(DBSchemaMapping.cREFSET_TARGETID, "", Field.Store.YES);
				Field refsetActive = new StringField(DBSchemaMapping.REFSET_ACTIVE, "", Field.Store.YES);

				for (CompositeDescription compositeDescription : list) {
					/* create document to index */

					/*
					 * Create fields to be indexed for document As term need be indexed and to be
					 * shown in results , so use it as a text field. Add fields to document
					 */
					try {
						document = new Document();
						id.setStringValue(compositeDescription.getId());
						document.add(id);
						active.setStringValue(compositeDescription.getActiveStatus() + "");
						document.add(active);
						conceptId.setStringValue(compositeDescription.getConceptId());
						document.add(conceptId);
						typeId.setStringValue(compositeDescription.getType());
						document.add(typeId);
						term.setStringValue(compositeDescription.getTerm());
						document.add(term);
						hierarchy.setStringValue(compositeDescription.getHierarchy());
						document.add(hierarchy);
						preferredTerm.setStringValue(compositeDescription.getIsPreferredTerm());
						document.add(preferredTerm);
						conceptState.setStringValue(compositeDescription.getConceptState());
						document.add(conceptState);
						conceptFSN.setStringValue(compositeDescription.getConceptFsn());
						document.add(conceptFSN);
						definitionStatus.setStringValue(compositeDescription.getDefinitionStatus());
						document.add(definitionStatus);
						moduleId.setStringValue(compositeDescription.getModuleId());
						document.add(moduleId);
						caseSignificanceId.setStringValue(compositeDescription.getCaseSId());
						document.add(caseSignificanceId);
						languageCode.setStringValue(compositeDescription.getLangCode());
						document.add(languageCode);
						descriptionEffectiveTime.setStringValue(compositeDescription.getTime() + "");
						document.add(descriptionEffectiveTime);

						if (compositeDescription.getRefSetLangMember() != null) {
							refsetLangReferenceComponentId.setStringValue(
									compositeDescription.getRefSetLangMember().getReferencedComponentId());
							document.add(refsetLangReferenceComponentId);
							refsetLangReferenceId
									.setStringValue(compositeDescription.getRefSetLangMember().getRefSetId());
							document.add(refsetLangReferenceId);
							refsetLangActive
									.setStringValue(compositeDescription.getRefSetLangMember().getActiveStatus() + "");
							document.add(refsetLangActive);
							refsetLangAcceptability
									.setStringValue(compositeDescription.getRefSetLangMember().getAcceptabilityId());
							document.add(refsetLangAcceptability);
						}

						refsetReferenceId.setStringValue(compositeDescription.getRefset().getRefSetId());
						document.add(refsetReferenceId);
						refsetModuleId.setStringValue(compositeDescription.getRefset().getModuleId());
						document.add(refsetModuleId);
						refsetActive
								.setStringValue(Integer.toString(compositeDescription.getRefset().getResetActive()));
						document.add(refsetActive);
						if (compositeDescription.getRefset().getTargetId() != null) {
							refsetTargetId.setStringValue(compositeDescription.getRefset().getTargetId());
							document.add(refsetTargetId);
						}
						this.indexWriter.addDocument(document);
						// adding semantic tags into Set for storing index
						if (!compositeDescription.getHierarchy().isEmpty())
							semanticTagsForIndexing.add(compositeDescription.getHierarchy());
					} catch (Exception e) {
						if (compositeDescription.getConceptId() != null && compositeDescription.getId() != null) {
							String msg = "";
							if (e.getMessage() != null)
								msg = e.getMessage() + ", ";
							msg = msg + "conceptId: " + compositeDescription.getConceptId() + " , descriptionId: "
									+ compositeDescription.getId() + " , refset id : "
									+ compositeDescription.getRefset().getRefSetId();
							logger.error(LoggerUtility.ConsoleWithException, "{}", msg);
							throw new IndexException(msg);
						} else
							throw new Exception(e);
					}
				}
			}
		}
		return this.indexWriter;
	}

    //ECL Methods

    //SEclAttribute

    /**
     * Searches for all {@link Concept}s
     * having the (attribute-value) relationship stated in query.
     *
     * @param builder {@link BooleanQuery} query object (based on the type of attribute - value relationship).
     * @return Set of {@link Concept}s.
     * @throws IOException
     * @throws java.text.ParseException
     * @throws ParseException
     */
    public Set<Concept> getConceptsFromQuery(BooleanQuery.Builder builder) throws IOException, java.text.ParseException  {
        ScoreDoc[] results = null;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(E_MMM_DD_HH_MM_SS_Z_YYYY, Locale.ENGLISH);
        results = this.search(builder.build(), 9000000, RELATION_INDEX);
        Set<Concept> finalSctIds = new HashSet<>();
        ComponentFactory componentFactory = ComponentFactory.getInstance();
        Document document = null;
        IndexSearcher indexSearcher = this.getIndexSearcher(getIndexReader(RELATION_INDEX));
        for (int i = 0; i < results.length; i++) {
            document = indexSearcher.doc(results[i].doc);
            Concept concept = (Concept) componentFactory.getComponent(EnumComponent.CONCEPT, null);
            concept.setId(document.get(SOURCE + CONCEPTID));
            concept.setFullySpecifiedName(document.get(SOURCE + CONCEPT_FSN));
            concept.setPreferredName(document.get(SOURCE + PREFERRED_NAME));
            concept.setTime(simpleDateFormat.parse(document.get(SOURCE + EFFECTIVETIME)));
            concept.setActiveStatus(Byte.parseByte(document.get(SOURCE + ACTIVE)));
            concept.setDefinitionStatusId(EnumDefStatus.getEnum(document.get(SOURCE + DEFINITIONSTATUS)));
            concept.setModuleId(document.get(SOURCE + MODULEID));
            finalSctIds.add(concept);
        }
        return finalSctIds;
    }

    //SSubExpressionConstraint

    /**
     * Searches given conceptId in the SNOMED CT repository
     * and returns {@link Concept} suited for ECL reponse.
     *
     * @param conceptId {@link String} search on the basis of given conceptId.
     * @return Object of {@link Concept} respected to given conceptId
     * @throws IOException
     * @throws java.text.ParseException
     * @throws ParseException
     */
    public Concept getConceptDetails(final String conceptId) throws IOException, java.text.ParseException, ParseException {
        if (conceptId == null)
            return null;
        Concept concept = (Concept) ComponentFactory.getInstance().getComponent(EnumComponent.CONCEPT, null);
        QueryParser queryParser = new QueryParser(CONCEPTID, getAnalyzer());
        StringBuilder queryCriteria = new StringBuilder(" " + CONCEPTID + ":" + conceptId);
        Query query = null;
        ScoreDoc[] results = null;
        query = queryParser.parse(queryCriteria.toString().trim());
        results = this.search(query, 100, null);
        Set<CompositeDescription> compositeDescription = getTopDescriptions(results, conceptId, null, true);
        for( CompositeDescription compositeDescriptionConcept : compositeDescription ) {
            if( compositeDescriptionConcept.getIsPreferredTerm().equals("1")
                && compositeDescriptionConcept.getLangCode().equalsIgnoreCase("en")
                && compositeDescriptionConcept.getType().equals(EnumDescType.SYNONYM.getValue()) ) {
                concept.setId(compositeDescriptionConcept.getConceptId());
                concept.setFullySpecifiedName(compositeDescriptionConcept.getConceptFsn());
                concept.setPreferredName(compositeDescriptionConcept.getTerm());
                concept.setTime(compositeDescriptionConcept.getTime());
                concept.setActiveStatus(compositeDescriptionConcept.getActiveStatus());
                concept.setDefinitionStatusId(EnumDefStatus.getEnum(compositeDescriptionConcept.getDefinitionStatus()));
                concept.setModuleId(compositeDescriptionConcept.getModuleId());
                break;
            }
        }
        return concept;
    }

    /**
     * Searches for all {@link Concept}s in SNOINDEX (wildcard query).
     *
     * @param builder {@link BooleanQuery} query object (all active concepts).
     * @return Set of {@link Concept}s.
     * @throws IOException
     * @throws java.text.ParseException
     * @throws ParseException
     */
    public Set<Concept> getAllConceptsForWildcard(BooleanQuery.Builder builder) throws IOException, java.text.ParseException, ParseException {
        Set<Concept> finalSctIds = new HashSet<>();
        ComponentFactory componentFactory = ComponentFactory.getInstance();
        ScoreDoc[] results = null;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(E_MMM_DD_HH_MM_SS_Z_YYYY, Locale.ENGLISH);
        results = this.search(builder.build(), 9000000, SNOINDEX);
        Document document = null;
        IndexSearcher indexSearcher = this.getIndexSearcher(getIndexReader(SNOINDEX));
        for (int i = 0; i < results.length; i++) {
            document = indexSearcher.doc(results[i].doc);
            Concept concept = (Concept) componentFactory.getComponent(EnumComponent.CONCEPT, null);
            if( document.get(IS_PREFERRED_TERM).equals("1")
                && (document.get(DBSchemaMapping.DESCRIPTION_LANGUAGE_CODE)).equalsIgnoreCase("en")
                && document.get(TYPEID).equals(EnumDescType.SYNONYM.getValue()) ) {
                concept.setId(document.get(CONCEPTID));
                concept.setFullySpecifiedName(document.get(CONCEPT_FSN));
                concept.setPreferredName(document.get(TERM));
                concept.setTime(simpleDateFormat.parse(document.get(DBSchemaMapping.DESCRIPTION_EFFECTIVE_TIME)));
                concept.setActiveStatus(Byte.parseByte(document.get(ACTIVE)));
                concept.setDefinitionStatusId(EnumDefStatus.getEnum(document.get(DEFINITIONSTATUS)));
                concept.setModuleId(document.get(DESCRIPTION_MODID));
                finalSctIds.add(concept);
            }
        }
        return finalSctIds;
    }

}
