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

Java tutorial

Introduction

Here is the source code for fr.paris.lutece.plugins.directory.service.directorysearch.DirectoryIndexer.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.DirectoryFilter;
import fr.paris.lutece.plugins.directory.business.DirectoryHome;
import fr.paris.lutece.plugins.directory.business.IndexerAction;
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.DirectoryPlugin;
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.message.SiteMessageException;
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.AppPropertiesService;

import org.apache.lucene.document.DateTools;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.FieldType;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;

import java.io.IOException;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

/**
 * Directory Indexer
 *
 */
public class DirectoryIndexer implements IDirectorySearchIndexer {
    private static final int LIST_RECORD_STEP = 50;
    private static final String ENABLE_VALUE_TRUE = "1";
    private static final String PROPERTY_INDEXER_DESCRIPTION = "directory.internalIndexer.description";
    private static final String PROPERTY_INDEXER_NAME = "directory.internalIndexer.name";
    private static final String PROPERTY_INDEXER_VERSION = "directory.internalIndexer.version";
    private static final String PROPERTY_INDEXER_ENABLE = "directory.internalIndexer.enable";
    private static final List<Integer> _lListIdRecordToDelete = new ArrayList<Integer>();

    /**
     * {@inheritDoc}
     */
    @Override
    public String getDescription() {
        return AppPropertiesService.getProperty(PROPERTY_INDEXER_DESCRIPTION);
    }

    /**
     * Index given list of record
     * @param indexWriter the indexWriter
     * @param lListId list of id directory / list of id record
     * @param plugin the plugin
     * @return new empty list of id directory / list of id record
     * @throws CorruptIndexException
     * @throws IOException
     * @throws InterruptedException
     */
    private HashMap<Integer, List<Integer>> indexListRecord(IndexWriter indexWriter,
            HashMap<Integer, List<Integer>> lListId, Plugin plugin)
            throws CorruptIndexException, IOException, InterruptedException {
        Iterator<Integer> it = lListId.keySet().iterator();
        IRecordService recordService = SpringContextService.getBean(RecordService.BEAN_SERVICE);

        while (it.hasNext()) {
            Integer nDirectoryId = it.next();
            Directory directory = DirectoryHome.findByPrimaryKey(nDirectoryId, plugin);
            List<Integer> lListRecordId = lListId.get(nDirectoryId);

            int nListRecordSize = lListRecordId.size();

            if (nListRecordSize > LIST_RECORD_STEP) {
                int nIndex = 0;
                int nMax = nListRecordSize - LIST_RECORD_STEP;

                for (int i = 0; i < nMax; i += LIST_RECORD_STEP) {
                    List<RecordField> lListrecordField = RecordFieldHome.getRecordFieldListByRecordIdList(
                            lListRecordId.subList(i, i + LIST_RECORD_STEP), plugin);

                    for (RecordField recordField : lListrecordField) {
                        indexWriter.addDocument(getDocument(recordField, recordField.getRecord(), directory));
                    }

                    for (Record record : recordService
                            .loadListByListId(lListRecordId.subList(i, i + LIST_RECORD_STEP), plugin)) {
                        indexWriter.addDocument(getDocument(record, directory));
                    }

                    nIndex = i;
                }

                nIndex += LIST_RECORD_STEP;

                List<RecordField> lListrecordField = RecordFieldHome
                        .getRecordFieldListByRecordIdList(lListRecordId.subList(nIndex, nListRecordSize), plugin);

                for (RecordField recordField : lListrecordField) {
                    indexWriter.addDocument(getDocument(recordField, recordField.getRecord(), directory));
                }

                for (Record record : recordService.loadListByListId(lListRecordId.subList(nIndex, nListRecordSize),
                        plugin)) {
                    indexWriter.addDocument(getDocument(record, directory));
                }
            } else {
                List<RecordField> lListrecordField = RecordFieldHome.getRecordFieldListByRecordIdList(lListRecordId,
                        plugin);

                for (RecordField recordField : lListrecordField) {
                    indexWriter.addDocument(getDocument(recordField, recordField.getRecord(), directory));
                }

                for (Record record : recordService.loadListByListId(lListRecordId, plugin)) {
                    indexWriter.addDocument(getDocument(record, directory));
                }
            }
        }

        return new HashMap<Integer, List<Integer>>();
    }

    /**
     * Append key to list of id directory / list of id record
     * @param nIdDirectory the directory id
     * @param nIdAction the action id
     * @param hm current list of id directory / list of id record
     * @return list of id directory / list of id record
     */
    private HashMap<Integer, List<Integer>> appendKey(Integer nIdDirectory, Integer nIdAction,
            HashMap<Integer, List<Integer>> hm) {
        if (hm.containsKey(nIdDirectory)) {
            hm.get(nIdDirectory).add(nIdAction);
        } else {
            List<Integer> lListIdRecord = new ArrayList<Integer>();
            lListIdRecord.add(nIdAction);
            hm.put(nIdDirectory, lListIdRecord);
        }

        return hm;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void processIndexing(IndexWriter indexWriter, boolean bCreate, StringBuffer sbLogs)
            throws IOException, InterruptedException, SiteMessageException {
        Plugin plugin = PluginService.getPlugin(DirectoryPlugin.PLUGIN_NAME);
        RecordFieldFilter recordFieldFilter = new RecordFieldFilter();
        HashMap<Integer, List<Integer>> hm = new HashMap<Integer, List<Integer>>();
        IRecordService recordService = SpringContextService.getBean(RecordService.BEAN_SERVICE);

        if (!bCreate) {
            //incremental indexing
            //delete all record which must be delete
            for (IndexerAction action : DirectorySearchService.getInstance()
                    .getAllIndexerActionByTask(IndexerAction.TASK_DELETE, plugin)) {
                sbLogRecord(sbLogs, action.getIdRecord(), DirectoryUtils.CONSTANT_ID_NULL,
                        IndexerAction.TASK_DELETE);
                indexWriter.deleteDocuments(new Term(DirectorySearchItem.FIELD_ID_DIRECTORY_RECORD,
                        Integer.toString(action.getIdRecord())));
                DirectorySearchService.getInstance().removeIndexerAction(action.getIdAction(), plugin);
            }

            //Hack : see this.appendListRecordToDelete() comments
            for (Integer nIdRecord : _lListIdRecordToDelete) {
                indexWriter.deleteDocuments(
                        new Term(DirectorySearchItem.FIELD_ID_DIRECTORY_RECORD, Integer.toString(nIdRecord)));
            }

            _lListIdRecordToDelete.clear();

            //Update all record which must be update
            for (IndexerAction action : DirectorySearchService.getInstance()
                    .getAllIndexerActionByTask(IndexerAction.TASK_MODIFY, plugin)) {
                Integer nDirectoryId = recordService.getDirectoryIdByRecordId(Integer.valueOf(action.getIdRecord()),
                        plugin);

                if (nDirectoryId != null) {
                    sbLogRecord(sbLogs, action.getIdRecord(), nDirectoryId.intValue(), IndexerAction.TASK_MODIFY);

                    indexWriter.deleteDocuments(new Term(DirectorySearchItem.FIELD_ID_DIRECTORY_RECORD,
                            Integer.toString(action.getIdRecord())));

                    this.appendKey(nDirectoryId, action.getIdRecord(), hm);
                }

                DirectorySearchService.getInstance().removeIndexerAction(action.getIdAction(), plugin);
            }

            hm = this.indexListRecord(indexWriter, hm, plugin);

            //add all record which must be add
            for (IndexerAction action : DirectorySearchService.getInstance()
                    .getAllIndexerActionByTask(IndexerAction.TASK_CREATE, plugin)) {
                Integer nDirectoryId = recordService.getDirectoryIdByRecordId(Integer.valueOf(action.getIdRecord()),
                        plugin);

                if (nDirectoryId != null) {
                    sbLogRecord(sbLogs, action.getIdRecord(), nDirectoryId, IndexerAction.TASK_CREATE);

                    this.appendKey(nDirectoryId, action.getIdRecord(), hm);
                }

                DirectorySearchService.getInstance().removeIndexerAction(action.getIdAction(), plugin);
            }

            hm = this.indexListRecord(indexWriter, hm, plugin);
        } else {
            // Index only the directories that have the attribute is_indexed as true
            DirectoryFilter filter = new DirectoryFilter();
            filter.setIsIndexed(DirectoryFilter.FILTER_TRUE);

            for (Directory directory : DirectoryHome.getDirectoryList(filter, plugin)) {
                sbLogs.append("Indexing Directory");
                sbLogs.append("\r\n");
                recordFieldFilter.setIdDirectory(directory.getIdDirectory());

                for (Record record : recordService.getListRecord(recordFieldFilter, plugin)) {
                    sbLogRecord(sbLogs, record.getIdRecord(), record.getDirectory().getIdDirectory(),
                            IndexerAction.TASK_CREATE);

                    this.appendKey(directory.getIdDirectory(), record.getIdRecord(), hm);
                }
            }

            hm = this.indexListRecord(indexWriter, hm, plugin);
        }
    }

    /**
     * Builds a document which will be used by Lucene during the indexing of the
     * directory
     *
     * @param recordField the recordField to index
     * @param record the record associate to the recordField
     * @param directory the directory associate to the recordField
     * @return A Lucene {@link Document} containing QuestionAnswer Data
     * @throws IOException The IO Exception
     * @throws InterruptedException The InterruptedException
     */
    private org.apache.lucene.document.Document getDocument(RecordField recordField, Record record,
            Directory directory) throws IOException, InterruptedException {
        // make a new, empty document
        org.apache.lucene.document.Document doc = new org.apache.lucene.document.Document();

        FieldType ft = new FieldType(StringField.TYPE_STORED);
        ft.setOmitNorms(false);

        HashMap<String, Object> mapSearchItemField;
        doc.add(new Field(DirectorySearchItem.FIELD_ID_DIRECTORY, Integer.toString(directory.getIdDirectory()),
                ft));

        doc.add(new Field(DirectorySearchItem.FIELD_ID_DIRECTORY_RECORD, Integer.toString(record.getIdRecord()),
                ft));

        doc.add(new Field(DirectorySearchItem.FIELD_ID_DIRECTORY_ENTRY,
                Integer.toString(recordField.getEntry().getIdEntry()), ft));

        mapSearchItemField = new HashMap<String, Object>();
        recordField.getEntry().addSearchCriteria(mapSearchItemField, recordField);

        if (mapSearchItemField.containsKey(DirectorySearchItem.FIELD_ID_DIRECTORY_FIELD)) {
            for (Integer idField : (List<Integer>) mapSearchItemField
                    .get(DirectorySearchItem.FIELD_ID_DIRECTORY_FIELD)) {
                doc.add(new Field(DirectorySearchItem.FIELD_ID_DIRECTORY_FIELD, Integer.toString(idField), ft));
            }
        }

        if (mapSearchItemField.containsKey(DirectorySearchItem.FIELD_DATE)) {
            String strDate = DateTools.dateToString((Date) mapSearchItemField.get(DirectorySearchItem.FIELD_DATE),
                    DateTools.Resolution.DAY);
            doc.add(new Field(DirectorySearchItem.FIELD_DATE, strDate, ft));
        }

        if (mapSearchItemField.containsKey(DirectorySearchItem.FIELD_CONTENTS)) {
            doc.add(new Field(DirectorySearchItem.FIELD_CONTENTS,
                    (String) mapSearchItemField.get(DirectorySearchItem.FIELD_CONTENTS),
                    TextField.TYPE_NOT_STORED));
        }

        // return the document
        return doc;
    }

    /**
     * Builds a document which will be used by Lucene during the indexing of the
     * directory
     *
     * @param record the record to index
     * @param directory the directory associate to the recordField
     * @return A Lucene Document containing QuestionAnswer Data
     * @throws IOException The IO Exception
     * @throws InterruptedException The InterruptedException
     */
    private org.apache.lucene.document.Document getDocument(Record record, Directory directory)
            throws IOException, InterruptedException {
        // make a new, empty document
        org.apache.lucene.document.Document doc = new org.apache.lucene.document.Document();

        FieldType ft = new FieldType(StringField.TYPE_STORED);
        ft.setOmitNorms(false);

        if ((directory != null) && (record != null)) {
            doc.add(new Field(DirectorySearchItem.FIELD_ID_DIRECTORY, Integer.toString(directory.getIdDirectory()),
                    ft));

            doc.add(new Field(DirectorySearchItem.FIELD_ID_DIRECTORY_RECORD, Integer.toString(record.getIdRecord()),
                    ft));

            if (record.getWorkgroup() != null) {
                doc.add(new Field(DirectorySearchItem.FIELD_WORKGROUP_KEY, record.getWorkgroup(), ft));
            }

            if (record.getRoleKey() != null) {
                doc.add(new Field(DirectorySearchItem.FIELD_ROLE_KEY, record.getRoleKey(), ft));
            }

            String strDate = DateTools.dateToString(record.getDateCreation(), DateTools.Resolution.DAY);
            doc.add(new Field(DirectorySearchItem.FIELD_DATE_CREATION, strDate, ft));

            String strDateModification = DateTools.dateToString(record.getDateModification(),
                    DateTools.Resolution.DAY);
            doc.add(new Field(DirectorySearchItem.FIELD_DATE_MODIFICATION, strDateModification, ft));
        }

        // return the document
        return doc;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getName() {
        return AppPropertiesService.getProperty(PROPERTY_INDEXER_NAME);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getVersion() {
        return AppPropertiesService.getProperty(PROPERTY_INDEXER_VERSION);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isEnable() {
        boolean bReturn = false;
        String strEnable = AppPropertiesService.getProperty(PROPERTY_INDEXER_ENABLE);

        if ((strEnable != null)
                && (strEnable.equalsIgnoreCase(Boolean.TRUE.toString()) || strEnable.equals(ENABLE_VALUE_TRUE))) {
            bReturn = true;
        }

        return bReturn;
    }

    /**
     * indexing action performed on the recording
     * @param sbLogs the buffer log
     * @param nIdRecord the id of the record
     * @param nIdDirectory the id of the directory
     * @param nAction the indexer action key performed
     */
    private void sbLogRecord(StringBuffer sbLogs, int nIdRecord, int nIdDirectory, int nAction) {
        sbLogs.append("Indexing Directory record:");

        switch (nAction) {
        case IndexerAction.TASK_CREATE:
            sbLogs.append("Insert ");

            break;

        case IndexerAction.TASK_MODIFY:
            sbLogs.append("Modify ");

            break;

        case IndexerAction.TASK_DELETE:
            sbLogs.append("Delete ");

            break;

        default:
            break;
        }

        sbLogs.append("record :");
        sbLogs.append("id_record=");
        sbLogs.append(nIdRecord);

        if (nIdDirectory != DirectoryUtils.CONSTANT_ID_NULL) {
            sbLogs.append("&");
            sbLogs.append("id_directory=");
            sbLogs.append(nIdDirectory);
        }

        sbLogs.append("\r\n");
    }

    /**
     * Append list record id to delete
     * Hack (ugly) to bypass problem of primary key violation on table
     * "directory_indexer_action"
     * when inserting many records
     * @param lListIdRecord List record to delete
     */
    public static void appendListRecordToDelete(List<Integer> lListIdRecord) {
        _lListIdRecordToDelete.addAll(lListIdRecord);
    }
}