fr.paris.lutece.plugins.directory.service.directorysearch.DirectorySearchService.java Source code

Java tutorial

Introduction

Here is the source code for fr.paris.lutece.plugins.directory.service.directorysearch.DirectorySearchService.java

Source

/*
 * Copyright (c) 2002-2014, Mairie de Paris
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice
 *     and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright notice
 *     and the following disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *
 *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * License 1.0
 */
package fr.paris.lutece.plugins.directory.service.directorysearch;

import fr.paris.lutece.plugins.directory.business.Directory;
import fr.paris.lutece.plugins.directory.business.DirectoryHome;
import fr.paris.lutece.plugins.directory.business.EntryTypeArray;
import fr.paris.lutece.plugins.directory.business.IndexerAction;
import fr.paris.lutece.plugins.directory.business.IndexerActionFilter;
import fr.paris.lutece.plugins.directory.business.IndexerActionHome;
import fr.paris.lutece.plugins.directory.business.Record;
import fr.paris.lutece.plugins.directory.business.RecordField;
import fr.paris.lutece.plugins.directory.business.RecordFieldFilter;
import fr.paris.lutece.plugins.directory.business.RecordFieldHome;
import fr.paris.lutece.plugins.directory.service.record.IRecordService;
import fr.paris.lutece.plugins.directory.service.record.RecordService;
import fr.paris.lutece.plugins.directory.utils.DirectoryUtils;
import fr.paris.lutece.portal.service.plugin.Plugin;
import fr.paris.lutece.portal.service.plugin.PluginService;
import fr.paris.lutece.portal.service.spring.SpringContextService;
import fr.paris.lutece.portal.service.util.AppException;
import fr.paris.lutece.portal.service.util.AppLogService;
import fr.paris.lutece.portal.service.util.AppPathService;
import fr.paris.lutece.portal.service.util.AppPropertiesService;

import org.apache.commons.collections.CollectionUtils;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.store.NIOFSDirectory;
import org.apache.lucene.util.Version;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.IOException;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletRequest;

/**
 * DirectorySearchService
 */
public class DirectorySearchService {
    private static final String BEAN_SEARCH_ENGINE = "directorySearchEngine";
    private static final String PATH_INDEX = "directory.internalIndexer.lucene.indexPath";
    private static final String PROPERTY_WRITER_MERGE_FACTOR = "directory.internalIndexer.lucene.writer.mergeFactor";
    private static final String PROPERTY_WRITER_MAX_FIELD_LENGTH = "directory.internalIndexer.lucene.writer.maxFieldLength";
    private static final String PROPERTY_ANALYSER_CLASS_NAME = "directory.internalIndexer.lucene.analyser.className";
    private static final String PARAMETER_ID_DIRECTORY = "id_directory";
    private static final String PARAMETER_ID_ENTRY = "id_entry";
    private static final String JSON_QUERY_RESULT = "query";

    //    private static final int DEFAULT_WRITER_MERGE_FACTOR = 20;
    //    private static final int DEFAULT_WRITER_MAX_FIELD_LENGTH = 1000000;
    private static org.apache.lucene.store.Directory _luceneDirectory;

    //    private static int _nWriterMergeFactor;
    //    private static int _nWriterMaxFieldLength;
    private static Analyzer _analyzer;
    private static IndexSearcher _searcher;
    private static IDirectorySearchIndexer _indexer;
    private static final int CONSTANT_TIME_CORRECTION = 3600000 * 12;

    // Constants corresponding to the variables defined in the lutece.properties file
    private static DirectorySearchService _singleton;

    /** Creates a new instance of DirectorySearchService */
    public DirectorySearchService() {
        // Read configuration properties
        String strIndex = AppPathService.getPath(PATH_INDEX);

        if ((strIndex == null) || (strIndex.equals(""))) {
            throw new AppException("Lucene index path not found in directory.properties", null);
        }

        try {
            _luceneDirectory = NIOFSDirectory.open(new File(strIndex));
        } catch (IOException e1) {
            throw new AppException("Lucene index path not found in directory.properties", null);
        }

        //        _nWriterMergeFactor = AppPropertiesService.getPropertyInt( PROPERTY_WRITER_MERGE_FACTOR,
        //                DEFAULT_WRITER_MERGE_FACTOR );
        //        _nWriterMaxFieldLength = AppPropertiesService.getPropertyInt( PROPERTY_WRITER_MAX_FIELD_LENGTH,
        //                DEFAULT_WRITER_MAX_FIELD_LENGTH );
        String strAnalyserClassName = AppPropertiesService.getProperty(PROPERTY_ANALYSER_CLASS_NAME);

        if ((strAnalyserClassName == null) || (strAnalyserClassName.equals(""))) {
            throw new AppException("Analyser class name not found in directory.properties", null);
        }

        _indexer = SpringContextService.getBean("directoryIndexer");

        try {
            _analyzer = (Analyzer) Class.forName(strAnalyserClassName).newInstance();
        } catch (Exception e) {
            throw new AppException("Failed to load Lucene Analyzer class", e);
        }
    }

    /**
     *
     * @return singleton
     */
    public static DirectorySearchService getInstance() {
        if (_singleton == null) {
            _singleton = new DirectorySearchService();
        }

        return _singleton;
    }

    /**
     * Return a list of record key return by the search
     * @param directory the directory
     * @param mapSearch a map which contains for each entry the list of
     *            recorField(value of field search) associate
     * @param dateCreation the creation date
     * @param dateCreationBegin the date begin to search for the creation date
     * @param dateCreationEnd the date end to search for the creation date
     * @param filter the filter
     * @param plugin the plugin
     * @return a list of record key return by the search
     */
    public List<Integer> getSearchResults(Directory directory, HashMap<String, List<RecordField>> mapSearch,
            Date dateCreation, Date dateCreationBegin, Date dateCreationEnd, RecordFieldFilter filter,
            Plugin plugin) {
        return getSearchResults(directory, mapSearch, dateCreationEnd, dateCreationBegin, dateCreationEnd, null,
                null, null, filter, plugin);
    }

    /**
     * Return a list of record key return by the search
     * @param directory the directory
     * @param mapSearch a map which contains for each entry the list of
     *            recorField(value of field search) associate
     * @param dateCreation the creation date
     * @param dateCreationBegin the date begin to search for the creation date
     * @param dateCreationEnd the date end to search for the creation date
     * @param dateModification the modification date
     * @param dateModificationBegin the date begin to search for the
     *            modification date
     * @param dateModificationEnd the date end to search for the modification
     *            date
     * @param filter the filter
     * @param plugin the plugin
     * @return a list of record key return by the search
     */
    public List<Integer> getSearchResults(Directory directory, HashMap<String, List<RecordField>> mapSearch,
            Date dateCreation, Date dateCreationBegin, Date dateCreationEnd, Date dateModification,
            Date dateModificationBegin, Date dateModificationEnd, RecordFieldFilter filter, Plugin plugin) {
        List<Integer> listRecordResult = new ArrayList<Integer>();

        IRecordService recordService = SpringContextService.getBean(RecordService.BEAN_SERVICE);
        listRecordResult = recordService.getListRecordId(filter, plugin);

        if (mapSearch != null) {
            List<Integer> listRecordResultTmp = null;
            List<RecordField> recordFieldSearch;
            HashMap<String, Object> mapSearchItemEntry;
            boolean bSearchRecordEmpty;
            boolean bSearchEmpty;

            try {
                _searcher = new IndexSearcher(DirectoryReader.open(_luceneDirectory));

                IDirectorySearchEngine engine = SpringContextService.getBean(BEAN_SEARCH_ENGINE);

                listRecordResultTmp = new ArrayList<Integer>();
                bSearchEmpty = true;

                for (Object entryMapSearch : mapSearch.entrySet()) {
                    recordFieldSearch = ((Entry<String, List<RecordField>>) entryMapSearch).getValue();

                    int nIdEntry = DirectoryUtils
                            .convertStringToInt(((Entry<String, List<RecordField>>) entryMapSearch).getKey());
                    bSearchRecordEmpty = true;

                    if (recordFieldSearch != null) {
                        mapSearchItemEntry = new HashMap<String, Object>();

                        boolean bIsArray = false;
                        boolean bFirstRecord = true;

                        for (RecordField recordField : recordFieldSearch) {
                            if (recordField.getEntry() instanceof EntryTypeArray) {
                                // for array, we do a search on content for each case
                                bIsArray = true;
                                mapSearchItemEntry = new HashMap<String, Object>();
                                recordField.getEntry().addSearchCriteria(mapSearchItemEntry, recordField);

                                if (mapSearchItemEntry.size() > 0) {
                                    bSearchRecordEmpty = false;
                                    bSearchEmpty = false;
                                    mapSearchItemEntry.put(DirectorySearchItem.FIELD_ID_DIRECTORY,
                                            directory.getIdDirectory());
                                    mapSearchItemEntry.put(DirectorySearchItem.FIELD_ID_DIRECTORY_ENTRY, nIdEntry);

                                    List<Integer> ids = engine.getSearchResults(mapSearchItemEntry);

                                    if (CollectionUtils.isEmpty(ids)) {
                                        listRecordResultTmp = new ArrayList<Integer>();

                                        break;
                                    } else if (bFirstRecord) {
                                        listRecordResultTmp = ids;
                                        bFirstRecord = false;
                                    } else {
                                        listRecordResultTmp = (List<Integer>) CollectionUtils
                                                .intersection(listRecordResultTmp, ids);
                                    }
                                }
                            } else {
                                recordField.getEntry().addSearchCriteria(mapSearchItemEntry, recordField);
                            }
                        }

                        if (!bIsArray && (mapSearchItemEntry.size() > 0)) {
                            bSearchRecordEmpty = false;
                            bSearchEmpty = false;
                            mapSearchItemEntry.put(DirectorySearchItem.FIELD_ID_DIRECTORY,
                                    directory.getIdDirectory());
                            mapSearchItemEntry.put(DirectorySearchItem.FIELD_ID_DIRECTORY_ENTRY, nIdEntry);
                            listRecordResultTmp.addAll(engine.getSearchResults(mapSearchItemEntry));
                        }

                        if (!bSearchRecordEmpty && !directory.isSearchOperatorOr()) {
                            // keeping order is important for display
                            listRecordResult = DirectoryUtils.retainAllIdsKeepingFirstOrder(listRecordResult,
                                    listRecordResultTmp);
                            listRecordResultTmp = new ArrayList<Integer>();
                        }
                    }
                }

                if (directory.isSearchOperatorOr() && !bSearchEmpty) {
                    listRecordResult = DirectoryUtils.retainAllIdsKeepingFirstOrder(listRecordResult,
                            listRecordResultTmp);
                }

                //date creation of a record
                if (dateCreation != null) {
                    listRecordResultTmp = new ArrayList<Integer>();
                    mapSearchItemEntry = new HashMap<String, Object>();
                    mapSearchItemEntry.put(DirectorySearchItem.FIELD_ID_DIRECTORY, directory.getIdDirectory());
                    dateCreation.setTime(dateCreation.getTime() + CONSTANT_TIME_CORRECTION);

                    mapSearchItemEntry.put(DirectorySearchItem.FIELD_DATE_CREATION, dateCreation);
                    listRecordResultTmp.addAll(engine.getSearchResults(mapSearchItemEntry));

                    // keeping order is important for display
                    listRecordResult = DirectoryUtils.retainAllIdsKeepingFirstOrder(listRecordResult,
                            listRecordResultTmp);
                } else if ((dateCreationBegin != null) && (dateCreationEnd != null)) {
                    dateCreationBegin.setTime(dateCreationBegin.getTime() + CONSTANT_TIME_CORRECTION);
                    dateCreationEnd.setTime(dateCreationEnd.getTime() + CONSTANT_TIME_CORRECTION);

                    listRecordResultTmp = new ArrayList<Integer>();
                    mapSearchItemEntry = new HashMap<String, Object>();
                    mapSearchItemEntry.put(DirectorySearchItem.FIELD_ID_DIRECTORY, directory.getIdDirectory());
                    mapSearchItemEntry.put(DirectorySearchItem.FIELD_DATE_CREATION_BEGIN, dateCreationBegin);
                    mapSearchItemEntry.put(DirectorySearchItem.FIELD_DATE_CREATION_END, dateCreationEnd);
                    listRecordResultTmp.addAll(engine.getSearchResults(mapSearchItemEntry));

                    // keeping order is important for display
                    listRecordResult = DirectoryUtils.retainAllIdsKeepingFirstOrder(listRecordResult,
                            listRecordResultTmp);
                }

                //date modification of a record
                if (dateModification != null) {
                    listRecordResultTmp = new ArrayList<Integer>();
                    mapSearchItemEntry = new HashMap<String, Object>();
                    mapSearchItemEntry.put(DirectorySearchItem.FIELD_ID_DIRECTORY, directory.getIdDirectory());
                    dateModification.setTime(dateModification.getTime() + CONSTANT_TIME_CORRECTION);

                    mapSearchItemEntry.put(DirectorySearchItem.FIELD_DATE_MODIFICATION, dateModification);
                    listRecordResultTmp.addAll(engine.getSearchResults(mapSearchItemEntry));

                    // keeping order is important for display
                    listRecordResult = DirectoryUtils.retainAllIdsKeepingFirstOrder(listRecordResult,
                            listRecordResultTmp);
                } else if ((dateModificationBegin != null) && (dateModificationEnd != null)) {
                    dateModificationBegin.setTime(dateModificationBegin.getTime() + CONSTANT_TIME_CORRECTION);
                    dateModificationEnd.setTime(dateModificationEnd.getTime() + CONSTANT_TIME_CORRECTION);

                    listRecordResultTmp = new ArrayList<Integer>();
                    mapSearchItemEntry = new HashMap<String, Object>();
                    mapSearchItemEntry.put(DirectorySearchItem.FIELD_ID_DIRECTORY, directory.getIdDirectory());
                    mapSearchItemEntry.put(DirectorySearchItem.FIELD_DATE_MODIFICATION_BEGIN,
                            dateModificationBegin);
                    mapSearchItemEntry.put(DirectorySearchItem.FIELD_DATE_MODIFICATION_END, dateModificationEnd);
                    listRecordResultTmp.addAll(engine.getSearchResults(mapSearchItemEntry));

                    // keeping order is important for display
                    listRecordResult = DirectoryUtils.retainAllIdsKeepingFirstOrder(listRecordResult,
                            listRecordResultTmp);
                }
            } catch (Exception e) {
                AppLogService.error(e.getMessage(), e);
                // If an error occurred clean result list
                listRecordResult = new ArrayList<Integer>();
            }
        }

        return listRecordResult;
    }

    public String getAutocompleteResult(HttpServletRequest request) throws Exception {
        String strIdDirectory = request.getParameter(PARAMETER_ID_DIRECTORY);
        String strIdEntry = request.getParameter(PARAMETER_ID_ENTRY);
        Plugin plugin = PluginService.getPlugin("directory");
        StringBuffer result = new StringBuffer();

        _searcher = new IndexSearcher(DirectoryReader.open(_luceneDirectory));

        IDirectorySearchEngine engine = SpringContextService.getBean(BEAN_SEARCH_ENGINE);

        if (((strIdDirectory != null) && !strIdDirectory.equals(DirectoryUtils.EMPTY_STRING))
                && ((strIdEntry != null) && !strIdEntry.equals(DirectoryUtils.EMPTY_STRING))) {
            HashMap<String, Object> mapSearchItemEntry = new HashMap<String, Object>();
            mapSearchItemEntry.put(DirectorySearchItem.FIELD_ID_DIRECTORY, Integer.parseInt(strIdDirectory));
            mapSearchItemEntry.put(DirectorySearchItem.FIELD_ID_DIRECTORY_ENTRY, Integer.parseInt(strIdEntry));

            List<Integer> listIntRecordField = engine.getSearchResults(mapSearchItemEntry);
            List<RecordField> listRecordField = new ArrayList<RecordField>();

            for (Integer idDirectoryRecord : listIntRecordField) {
                RecordFieldFilter recordFilter = new RecordFieldFilter();
                recordFilter.setIdDirectory(Integer.parseInt(strIdDirectory));
                recordFilter.setIdEntry(Integer.parseInt(strIdEntry));
                recordFilter.setIdRecord(idDirectoryRecord);
                listRecordField.addAll(RecordFieldHome.getRecordFieldList(recordFilter, plugin));
            }

            HashMap<String, String> mapResult = new HashMap<String, String>();

            for (RecordField recordField : listRecordField) {
                //result.append( recordField.getValue(  ) + "%" );
                mapResult.put(recordField.getValue(), recordField.getValue());
            }

            for (String key : mapResult.keySet()) {
                result.append(key + "%");
            }
        }

        JSONObject jo = new JSONObject();

        try {
            jo.put(JSON_QUERY_RESULT, result.toString());
        } catch (JSONException e) {
            AppLogService.error(e.getMessage(), e);
        }

        return jo.toString();
    }

    /**
     * Process indexing
     * @param bCreate true for start full indexing
     *            false for begin incremental indexing
     * @return the log
     */
    public String processIndexing(boolean bCreate) {
        StringBuffer sbLogs = new StringBuffer();
        IndexWriter writer = null;
        boolean bCreateIndex = bCreate;

        try {
            sbLogs.append("\r\nIndexing all contents ...\r\n");

            if (!DirectoryReader.indexExists(_luceneDirectory)) { //init index
                bCreateIndex = true;
            }

            if (!bCreateIndex && IndexWriter.isLocked(_luceneDirectory)) {
                IndexWriter.unlock(_luceneDirectory);
            }

            IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_46, _analyzer);

            if (bCreateIndex) {
                conf.setOpenMode(OpenMode.CREATE);
            } else {
                conf.setOpenMode(OpenMode.APPEND);
            }

            writer = new IndexWriter(_luceneDirectory, conf);

            Date start = new Date();

            sbLogs.append("\r\n<strong>Indexer : ");
            sbLogs.append(_indexer.getName());
            sbLogs.append(" - ");
            sbLogs.append(_indexer.getDescription());
            sbLogs.append("</strong>\r\n");
            _indexer.processIndexing(writer, bCreateIndex, sbLogs);

            Date end = new Date();

            sbLogs.append("Duration of the treatment : ");
            sbLogs.append(end.getTime() - start.getTime());
            sbLogs.append(" milliseconds\r\n");
        } catch (Exception e) {
            sbLogs.append(" caught a ");
            sbLogs.append(e.getClass());
            sbLogs.append("\n with message: ");
            sbLogs.append(e.getMessage());
            sbLogs.append("\r\n");
            AppLogService.error("Indexing error : " + e.getMessage(), e);
        } finally {
            try {
                if (writer != null) {
                    writer.close();
                }
            } catch (IOException e) {
                AppLogService.error(e.getMessage(), e);
            }
        }

        return sbLogs.toString();
    }

    /**
     * Add Indexer Action to perform on a record
     * @param nIdRecord the id of the record
     * @param nIdTask the key of the action to do
     * @param plugin the plugin
     */
    public void addIndexerAction(int nIdRecord, int nIdTask, Plugin plugin) {
        IRecordService recordService = SpringContextService.getBean(RecordService.BEAN_SERVICE);
        Record record = recordService.findByPrimaryKey(nIdRecord, plugin);

        if ((record != null) && (record.getDirectory() != null)) {
            int nDirectoryId = record.getDirectory().getIdDirectory();
            Directory directory = DirectoryHome.findByPrimaryKey(nDirectoryId, plugin);

            if ((directory != null) && directory.isIndexed()) {
                IndexerAction indexerAction = new IndexerAction();
                indexerAction.setIdRecord(nIdRecord);
                indexerAction.setIdTask(nIdTask);
                IndexerActionHome.create(indexerAction, plugin);
            }
        }
    }

    /**
     * Remove a Indexer Action
     * @param nIdAction the key of the action to remove
     * @param plugin the plugin
     */
    public void removeIndexerAction(int nIdAction, Plugin plugin) {
        IndexerActionHome.remove(nIdAction, plugin);
    }

    /**
     * return a list of IndexerAction by task key
     * @param nIdTask the task kety
     * @param plugin the plugin
     * @return a list of IndexerAction
     */
    public List<IndexerAction> getAllIndexerActionByTask(int nIdTask, Plugin plugin) {
        IndexerActionFilter filter = new IndexerActionFilter();
        filter.setIdTask(nIdTask);

        return IndexerActionHome.getList(filter, plugin);
    }

    /**
     * return searcher
     * @return searcher
     */
    public IndexSearcher getSearcher() {
        return _searcher;
    }

    /**
     * set searcher
     * @param searcher searcher
     */
    public void setSearcher(IndexSearcher searcher) {
        _searcher = searcher;
    }
}