LuceneFormSearchIndexer.java
/*
* Copyright (c) 2002-2025, City of 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.forms.service.search;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.inject.Inject;
import fr.paris.lutece.plugins.forms.exception.LockException;
import fr.paris.lutece.plugins.forms.service.lock.LockResult;
import fr.paris.lutece.plugins.forms.service.lock.LuceneLockManager;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.IntPoint;
import org.apache.lucene.document.LongPoint;
import org.apache.lucene.document.NumericDocValuesField;
import org.apache.lucene.document.SortedDocValuesField;
import org.apache.lucene.document.StoredField;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.search.Query;
import org.apache.lucene.util.BytesRef;
import org.springframework.beans.factory.annotation.Autowired;
import fr.paris.lutece.plugins.forms.business.Form;
import fr.paris.lutece.plugins.forms.business.FormHome;
import fr.paris.lutece.plugins.forms.business.FormQuestionResponse;
import fr.paris.lutece.plugins.forms.business.FormResponse;
import fr.paris.lutece.plugins.forms.business.FormResponseHome;
import fr.paris.lutece.plugins.forms.business.FormResponseStep;
import fr.paris.lutece.plugins.forms.business.form.search.FormResponseSearchItem;
import fr.paris.lutece.plugins.forms.business.form.search.IndexerAction;
import fr.paris.lutece.plugins.forms.business.form.search.IndexerActionHome;
import fr.paris.lutece.plugins.forms.service.FormsPlugin;
import fr.paris.lutece.plugins.forms.service.entrytype.EntryTypeCheckBox;
import fr.paris.lutece.plugins.forms.service.entrytype.EntryTypeDate;
import fr.paris.lutece.plugins.forms.service.entrytype.EntryTypeNumbering;
import fr.paris.lutece.plugins.forms.service.entrytype.EntryTypeRadioButton;
import fr.paris.lutece.plugins.forms.service.entrytype.EntryTypeSelect;
import fr.paris.lutece.plugins.forms.util.LuceneUtils;
import fr.paris.lutece.plugins.genericattributes.business.Entry;
import fr.paris.lutece.plugins.genericattributes.business.Response;
import fr.paris.lutece.plugins.genericattributes.service.entrytype.EntryTypeServiceManager;
import fr.paris.lutece.plugins.genericattributes.service.entrytype.IEntryTypeService;
import fr.paris.lutece.plugins.workflowcore.business.state.State;
import fr.paris.lutece.plugins.workflowcore.service.state.StateService;
import fr.paris.lutece.portal.service.plugin.Plugin;
import fr.paris.lutece.portal.service.plugin.PluginService;
import fr.paris.lutece.portal.service.search.SearchItem;
import fr.paris.lutece.portal.service.spring.SpringContextService;
import fr.paris.lutece.portal.service.util.AppLogService;
import fr.paris.lutece.portal.service.util.AppPropertiesService;
/**
* Forms global indexer
*/
public class LuceneFormSearchIndexer implements IFormSearchIndexer
{
public static final String BEAN_SERVICE = "forms.luceneFormsSearchIndexer";
private static final String LOCKNAME = "forms.lucene.lock";
private static final String FILTER_DATE_FORMAT = AppPropertiesService.getProperty( "forms.index.date.format", "dd/MM/yyyy" );
private static final int TAILLE_LOT = AppPropertiesService.getPropertyInt( "forms.index.writer.commit.size", 100 );
private static final boolean CLOSE_WRITER = AppPropertiesService.getPropertyBoolean( "forms.index.writer.multi.apps", true );
private static final long MS_TIMEOUT_LOCK = AppPropertiesService.getPropertyLong( "forms.index.writer.ms.timeout.lock", 3600000L );
@Inject
private LuceneFormSearchFactory _luceneFormSearchFactory;
private IndexWriter _indexWriter;
@Autowired( required = false )
private StateService _stateService;
private LuceneLockManager _lockManager;
/**
* Constructor
*
* @param lockManager
* The LockManager
*/
@Inject
public LuceneFormSearchIndexer( LuceneLockManager lockManager )
{
_lockManager = lockManager;
}
/**
* Add Indexer Action to perform on a form response
*
* @param nIdFormResponse
* the id of the formResponse
* @param nIdTask
* the key of the action to do
* @param plugin
* the plugin
*/
@Override
public void addIndexerAction( int nIdFormResponse, int nIdTask, Plugin plugin )
{
IndexerAction indexerAction = new IndexerAction( );
indexerAction.setIdFormResponse( nIdFormResponse );
indexerAction.setIdTask( nIdTask );
IndexerActionHome.create( indexerAction, plugin );
}
/**
* {@inheritDoc }
*/
public String fullIndexing( )
{
StringBuilder log = new StringBuilder();
Plugin plugin = PluginService.getPlugin( FormsPlugin.PLUGIN_NAME );
try
{
LockResult lockResult = _lockManager.acquireLock( LOCKNAME, MS_TIMEOUT_LOCK );
log.append("Full indexing launch");
try
{
IndexerActionHome.removeAll( plugin );
List<Integer> listFormResponsesId = FormResponseHome.selectAllFormResponsesId( );
initIndexing( true );
deleteIndex();
indexFormResponseList( listFormResponsesId, plugin );
_luceneFormSearchFactory.swapIndex();
}
catch (Exception e)
{
AppLogService.error(e.getMessage(), e);
Thread.currentThread().interrupt();
}
finally
{
closeIndexing();
_lockManager.releaseLock( lockResult );
}
}
catch (LockException e) {
log.append("Indexing already in progress, Full indexing Aborted");
}
return log.toString();
}
/**
* {@inheritDoc }
*/
public String incrementalIndexing() {
StringBuilder log = new StringBuilder();
try {
LockResult lockResult = _lockManager.acquireLock(LOCKNAME, MS_TIMEOUT_LOCK);
log.append("Incremental indexing launch");
try {
initIndexing( false );
processIndexing();
} catch (Exception e) {
AppLogService.error(e.getMessage(), e);
log.append("Incremental indexing with error");
} finally {
closeIndexing();
_lockManager.releaseLock(lockResult);
}
} catch (LockException e) {
log.append("Indexing already in progress, Incremental indexing Aborted");
}
return log.toString();
}
/**
* {@inheritDoc }
*/
@Override
public void indexDocument( int nIdFormResponse, int nIdTask, Plugin plugin )
{
addIndexerAction( nIdFormResponse, nIdTask, plugin );
}
/**
* Index all the idFormResponses
*
* @param listFormResponsesId list id of form responses
* @param plugin The plugin
*/
private void indexFormResponseList( List<Integer> listFormResponsesId, Plugin plugin )
{
List<FormResponse> listFormResponses = new ArrayList<>(TAILLE_LOT);
for (Integer nIdFormResponse : listFormResponsesId) {
FormResponse response = FormResponseHome.findByPrimaryKeyForIndex(nIdFormResponse);
if (response != null)
{
listFormResponses.add(response);
}
if (listFormResponses.size() == TAILLE_LOT)
{
indexFormResponseList(listFormResponses, null, false, plugin);
listFormResponses.clear();
}
}
indexFormResponseList(listFormResponses, null, false, plugin);
}
/**
* Remove IndexerAction that have no use
*
* @param listActionAdd list of inderAction Add
* @param listActionUpdate list of inderAction Update
* @param listActionDelete list of inderAction Delete
* @param plugin the plugin
*/
private void removeConcurrentAction( List<IndexerAction> listActionAdd, List<IndexerAction> listActionUpdate, List<IndexerAction> listActionDelete, Plugin plugin)
{
List<IndexerAction> listActionToRemove = new ArrayList<>();
Set<Integer> listIdsToAdd = listActionAdd.stream().map( IndexerAction::getIdFormResponse ).collect( Collectors.toSet() );
Set<Integer> listIdsToDelete = listActionDelete.stream().map( IndexerAction::getIdFormResponse ).collect( Collectors.toSet() );
List<Integer> duplicateIdForm = new ArrayList<>();
for ( IndexerAction action : listActionDelete )
{
// No need to remove document
if ( listIdsToAdd.contains( action.getIdFormResponse() ) || duplicateIdForm.contains( action.getIdFormResponse() ) )
{
listActionToRemove.add( action );
}
else
{
duplicateIdForm.add( action.getIdFormResponse() );
}
}
duplicateIdForm.clear();
for ( IndexerAction action : listActionUpdate )
{
// No need to update indexe, The document going to be created with last version
if ( listIdsToAdd.contains( action.getIdFormResponse() ) || duplicateIdForm.contains( action.getIdFormResponse() ) )
{
listActionToRemove.add( action );
}
else
{
duplicateIdForm.add(action.getIdFormResponse());
}
}
duplicateIdForm.clear();
for ( IndexerAction action : listActionAdd )
{
// No need to insert indexe
if ( listIdsToDelete.contains( action.getIdFormResponse() ) || duplicateIdForm.contains( action.getIdFormResponse() ) )
{
listActionToRemove.add( action );
}
else
{
duplicateIdForm.add(action.getIdFormResponse());
}
}
listActionAdd.removeAll( listActionToRemove.stream().filter( a -> a.getIdTask() == IndexerAction.TASK_CREATE ).collect( Collectors.toSet() ) );
listActionDelete.removeAll( listActionToRemove.stream().filter( a -> a.getIdTask() == IndexerAction.TASK_DELETE ).collect( Collectors.toSet() ) );
listActionUpdate.removeAll( listActionToRemove.stream().filter( a -> a.getIdTask() == IndexerAction.TASK_MODIFY ).collect( Collectors.toSet() ) );
removeListIndexerAction( listActionToRemove, plugin );
}
/**
* process the incremental indexing
*/
public synchronized void processIndexing( )
{
Plugin plugin = PluginService.getPlugin( FormsPlugin.PLUGIN_NAME );
List<IndexerAction> listActionAdd = new ArrayList<>();
List<IndexerAction> listActionUpdate = new ArrayList<>();
List<IndexerAction> listActionDelete = new ArrayList<>();
for ( IndexerAction action : getAllIndexerAction( plugin ) )
{
switch ( action.getIdTask() )
{
case IndexerAction.TASK_DELETE :
listActionDelete.add( action );
break;
case IndexerAction.TASK_CREATE :
listActionAdd.add( action );
break;
case IndexerAction.TASK_MODIFY :
listActionUpdate.add( action );
break;
default:
throw new IllegalStateException("Unexpected value: " + action.getIdTask());
}
}
removeConcurrentAction(listActionAdd, listActionUpdate, listActionDelete, plugin);
// Document to remove
deleteDocument( listActionDelete, plugin);
List<IndexerAction> listComputingAction = new ArrayList<>();
//Create document
List<FormResponse> listFormResponses = new ArrayList<>( TAILLE_LOT );
for ( IndexerAction actionAdd : listActionAdd )
{
FormResponse response = FormResponseHome.findByPrimaryKeyForIndex( actionAdd.getIdFormResponse() );
listComputingAction.add( actionAdd );
if ( response != null )
{
listFormResponses.add( response );
}
if ( listFormResponses.size( ) == TAILLE_LOT )
{
indexFormResponseList( listFormResponses, listComputingAction, false, plugin );
listFormResponses.clear( );
listComputingAction.clear();
}
}
indexFormResponseList( listFormResponses, listComputingAction, false, plugin );
listComputingAction.clear();
//Update document
listFormResponses.clear();
for ( IndexerAction actionUpdate : listActionUpdate )
{
FormResponse response = FormResponseHome.findByPrimaryKeyForIndex( actionUpdate.getIdFormResponse() );
listComputingAction.add( actionUpdate );
if ( response != null )
{
listFormResponses.add( response );
}
if ( listFormResponses.size( ) == TAILLE_LOT )
{
indexFormResponseList( listFormResponses, listComputingAction, true, plugin );
listFormResponses.clear( );
listComputingAction.clear();
}
}
indexFormResponseList( listFormResponses, listComputingAction, true, plugin );
listComputingAction.clear();
}
/**
* {@inheritDoc}
*/
public boolean isIndexerInitialized( )
{
try
{
return DirectoryReader.indexExists( _luceneFormSearchFactory.getDirectory( ) );
}
catch( Exception e )
{
AppLogService.error( "Indexing error ", e );
}
return false;
}
/**
* {@inheritDoc}
*/
private void indexFormResponseList( List<FormResponse> listFormResponse, List<IndexerAction> listComputingAction, boolean update, Plugin plugin )
{
Map<Integer, Form> mapForms = FormHome.getFormList( ).stream( ).collect( Collectors.toMap( Form::getId, form -> form ) );
List<Document> documentList = new ArrayList<>( );
for ( FormResponse formResponse : listFormResponse )
{
Document doc = null;
Form form = mapForms.get( formResponse.getFormId( ) );
State formResponseState = null;
if ( _stateService != null )
{
formResponseState = _stateService.findByResource( formResponse.getId( ), FormResponse.RESOURCE_TYPE, form.getIdWorkflow( ) );
}
else
{
formResponseState = new State( );
formResponseState.setId( -1 );
formResponseState.setName( StringUtils.EMPTY );
}
try
{
doc = getDocument( formResponse, form, formResponseState );
}
catch( Exception e )
{
AppLogService.error(e.getMessage(), e);
}
if ( doc != null )
{
documentList.add( doc );
}
}
if ( !documentList.isEmpty( ) )
{
if( update )
{
updateDocuments( documentList, listComputingAction, plugin );
}
else
{
addDocuments( documentList, listComputingAction, plugin );
}
}
}
/**
* update the list of document in the writer and remove the list of action in the database
*
* @param documentList the documentList
* @param listComputingAction the list of action
* @param plugin the plugin
*/
private void updateDocuments( List<Document> documentList, List<IndexerAction> listComputingAction, Plugin plugin)
{
provideExternalFields( documentList );
try
{
List<Query> luceneQueryList = new ArrayList<>( );
for (IndexerAction action : listComputingAction)
{
luceneQueryList.add(IntPoint.newExactQuery( FormResponseSearchItem.FIELD_ID_FORM_RESPONSE, action.getIdFormResponse() ));
}
_indexWriter.deleteDocuments( luceneQueryList.toArray(new Query[luceneQueryList.size()]) );
_indexWriter.addDocuments( documentList);
_indexWriter.commit();
removeListIndexerAction( listComputingAction , plugin );
}
catch( IOException e )
{
AppLogService.error( "Unable to index form response", e );
}
documentList.clear( );
}
/**
* add the list of document in the writer, and remove the list of action
* @param documentList the documentList
* @param listComputingAction the listComputingAction
* @param plugin the plugin
*/
private void addDocuments( List<Document> documentList, List<IndexerAction> listComputingAction, Plugin plugin )
{
provideExternalFields( documentList );
try
{
_indexWriter.addDocuments( documentList );
_indexWriter.commit();
if( listComputingAction != null)
{
removeListIndexerAction( listComputingAction , plugin );
}
}
catch( IOException e )
{
AppLogService.error( "Unable to index form response", e );
}
documentList.clear( );
}
/**
* Provide external fields to Document objects
*
* @param documentList
* list of Document objects
*
*/
private void provideExternalFields( List<Document> documentList )
{
for ( ILucenDocumentExternalFieldProvider provider : SpringContextService.getBeansOfType( ILucenDocumentExternalFieldProvider.class ) )
{
provider.provideFields( documentList );
}
}
/**
* Init the indexing action
*
* @param fullIndexing
* The boolean which tell if the indexing are for full or incremental
*/
private void initIndexing( boolean fullIndexing )
{
if (fullIndexing)
{
_indexWriter = _luceneFormSearchFactory.getIndexWriter( true, false );
}
else
{
_indexWriter = _luceneFormSearchFactory.getIndexWriter( false, true );
}
}
/**
* close the indexing action
*/
private void closeIndexing( )
{
if ( _indexWriter != null && _indexWriter.isOpen() && CLOSE_WRITER ) {
try {
_indexWriter.close();
} catch (IOException e) {
AppLogService.error("Unable to close index writer ", e);
}
}
}
/**
* {@inheritDoc }
*/
private void deleteIndex( )
{
try
{
_indexWriter.deleteAll( );
}
catch( IOException e )
{
AppLogService.error( "Unable to delete all docs in index ", e );
}
}
/**
* cut listActionDelete in TAILLE_LOT, and call deleteDocumentBlock
* @param listActionDelete
* @param plugin
*/
private void deleteDocument( List<IndexerAction> listActionDelete, Plugin plugin )
{
List<IndexerAction> listActionToCompute;
while ( !listActionDelete.isEmpty() ) {
if (listActionDelete.size() >= TAILLE_LOT) {
listActionToCompute = listActionDelete.subList(0, TAILLE_LOT);
} else {
listActionToCompute = listActionDelete.subList(0, listActionDelete.size() );
}
deleteDocumentBlock( listActionToCompute , plugin );
listActionToCompute.clear();
}
}
/**
* remove document in indexe, and remove the action after
* @param listActionDelete the listActionDelete
* @param plugin the plugin
*/
private void deleteDocumentBlock( List<IndexerAction> listActionDelete, Plugin plugin )
{
try
{
Query[] queryList = new Query [listActionDelete.size( )];
for ( int i = 0; i < listActionDelete.size( ) ; i++)
{
queryList[i] = IntPoint.newExactQuery( FormResponseSearchItem.FIELD_ID_FORM_RESPONSE, listActionDelete.get(i).getIdFormResponse() );
}
_indexWriter.deleteDocuments( queryList );
_indexWriter.commit();
removeListIndexerAction( listActionDelete , plugin );
}
catch( IOException e )
{
AppLogService.error( "Unable to delete document ", e );
}
}
/**
* Remove a list of indexer Action
*
* @param listAction
* the list of the action to remove
* @param plugin
* the plugin
*/
private void removeListIndexerAction( List<IndexerAction> listAction, Plugin plugin )
{
IndexerActionHome.remove( listAction.stream( ).map( IndexerAction::getIdAction ).distinct( ).collect( Collectors.toList( ) ), plugin );
}
/**
* return a list of IndexerAction
*
* @param plugin
* the plugin
* @return a list of IndexerAction
*/
private List<IndexerAction> getAllIndexerAction( Plugin plugin )
{
return IndexerActionHome.getList( plugin );
}
/**
* Builds a document which will be used by Lucene during the indexing of this record
*
* @param formResponse
* the formResponse object
* @param form
* the form
* @return a lucene document filled with the record data
*/
private Document getDocument( FormResponse formResponse, Form form, State formResponseState )
{
// make a new, empty document
Document doc = new Document( );
int nIdFormResponse = formResponse.getId( );
// --- document identifier
doc.add( new StringField( SearchItem.FIELD_UID, String.valueOf( nIdFormResponse ), Field.Store.YES ) );
// --- form response identifier
doc.add( new IntPoint( FormResponseSearchItem.FIELD_ID_FORM_RESPONSE, nIdFormResponse ) );
doc.add( new NumericDocValuesField( FormResponseSearchItem.FIELD_ID_FORM_RESPONSE, nIdFormResponse ) );
doc.add( new StoredField( FormResponseSearchItem.FIELD_ID_FORM_RESPONSE, nIdFormResponse ) );
// --- field contents
doc.add( new TextField( SearchItem.FIELD_CONTENTS, manageNullValue( getContentToIndex( formResponse ) ), Field.Store.NO ) );
// --- form title
String strFormTitle = manageNullValue( form.getTitle( ) );
doc.add( new StringField( FormResponseSearchItem.FIELD_FORM_TITLE, strFormTitle, Field.Store.YES ) );
doc.add( new SortedDocValuesField( FormResponseSearchItem.FIELD_FORM_TITLE, new BytesRef( strFormTitle ) ) );
// --- id form
doc.add( new IntPoint( FormResponseSearchItem.FIELD_ID_FORM, form.getId( ) ) );
doc.add( new NumericDocValuesField( FormResponseSearchItem.FIELD_ID_FORM, form.getId( ) ) );
doc.add( new StoredField( FormResponseSearchItem.FIELD_ID_FORM, form.getId( ) ) );
// --- form response date create
Long longCreationDate = formResponse.getCreation( ).getTime( );
doc.add( new LongPoint( FormResponseSearchItem.FIELD_DATE_CREATION, longCreationDate ) );
doc.add( new NumericDocValuesField( FormResponseSearchItem.FIELD_DATE_CREATION, longCreationDate ) );
doc.add( new StoredField( FormResponseSearchItem.FIELD_DATE_CREATION, longCreationDate ) );
// --- form response date closure
Long longUpdateDate = formResponse.getUpdate( ).getTime( );
doc.add( new LongPoint( FormResponseSearchItem.FIELD_DATE_UPDATE, longUpdateDate ) );
doc.add( new NumericDocValuesField( FormResponseSearchItem.FIELD_DATE_UPDATE, longUpdateDate ) );
doc.add( new StoredField( FormResponseSearchItem.FIELD_DATE_UPDATE, longUpdateDate ) );
if ( formResponseState != null )
{
// --- id form response workflow state
int nIdFormResponseWorkflowState = formResponseState.getId( );
doc.add( new IntPoint( FormResponseSearchItem.FIELD_ID_WORKFLOW_STATE, nIdFormResponseWorkflowState ) );
doc.add( new NumericDocValuesField( FormResponseSearchItem.FIELD_ID_WORKFLOW_STATE, nIdFormResponseWorkflowState ) );
doc.add( new StoredField( FormResponseSearchItem.FIELD_ID_WORKFLOW_STATE, nIdFormResponseWorkflowState ) );
// --- form response workflow state title
String strFormResponseWorkflowStateTitle = manageNullValue( formResponseState.getName( ) );
doc.add( new StringField( FormResponseSearchItem.FIELD_TITLE_WORKFLOW_STATE, strFormResponseWorkflowStateTitle, Field.Store.YES ) );
doc.add( new SortedDocValuesField( FormResponseSearchItem.FIELD_TITLE_WORKFLOW_STATE, new BytesRef( strFormResponseWorkflowStateTitle ) ) );
}
// --- form response entry code / fields
Set<String> setFieldNameBuilderUsed = new HashSet<>( );
for ( FormResponseStep formResponseStep : formResponse.getSteps( ) )
{
for ( FormQuestionResponse formQuestionResponse : formResponseStep.getQuestions( ) )
{
String strQuestionCode = formQuestionResponse.getQuestion( ).getCode( );
Entry entry = formQuestionResponse.getQuestion( ).getEntry( );
IEntryTypeService entryTypeService = EntryTypeServiceManager.getEntryTypeService( entry );
for ( Response response : formQuestionResponse.getEntryResponse( ) )
{
fr.paris.lutece.plugins.genericattributes.business.Field responseField = response.getField( );
if ( !StringUtils.isEmpty( response.getResponseValue( ) ) )
{
StringBuilder fieldNameBuilder = new StringBuilder(
LuceneUtils.createLuceneEntryKey( strQuestionCode, response.getIterationNumber( ) ) );
if ( responseField != null )
{
String getFieldName = getFieldName( responseField, response );
fieldNameBuilder.append( FormResponseSearchItem.FIELD_RESPONSE_FIELD_SEPARATOR );
fieldNameBuilder.append( getFieldName );
}
if ( !setFieldNameBuilderUsed.contains( fieldNameBuilder.toString( ) ) )
{
setFieldNameBuilderUsed.add( fieldNameBuilder.toString( ) );
if ( entryTypeService instanceof EntryTypeDate )
{
try
{
Long timestamp = Long.valueOf( response.getResponseValue( ) );
doc.add( new LongPoint( fieldNameBuilder.toString( ) + FormResponseSearchItem.FIELD_DATE_SUFFIX, timestamp ) );
doc.add( new NumericDocValuesField( fieldNameBuilder.toString( ) + FormResponseSearchItem.FIELD_DATE_SUFFIX, timestamp ) );
doc.add( new StoredField( fieldNameBuilder.toString( ) + FormResponseSearchItem.FIELD_DATE_SUFFIX, timestamp ) );
}
catch( Exception e )
{
AppLogService.error( "Unable to parse " + response.getResponseValue( ) + " with date formatter " + FILTER_DATE_FORMAT, e );
}
}
else
if ( entryTypeService instanceof EntryTypeNumbering )
{
try
{
Integer value = Integer.valueOf( response.getResponseValue( ) );
doc.add( new IntPoint( fieldNameBuilder.toString( ) + FormResponseSearchItem.FIELD_INT_SUFFIX, value ) );
doc.add( new NumericDocValuesField( fieldNameBuilder.toString( ) + FormResponseSearchItem.FIELD_INT_SUFFIX, value ) );
doc.add( new StoredField( fieldNameBuilder.toString( ) + FormResponseSearchItem.FIELD_INT_SUFFIX, value ) );
}
catch( NumberFormatException e )
{
AppLogService.error( "Unable to parse " + response.getResponseValue( ) + " to integer ", e );
}
}
else
if ( entryTypeService instanceof EntryTypeSelect || entryTypeService instanceof EntryTypeRadioButton || entryTypeService instanceof EntryTypeCheckBox )
{
doc.add( new StringField( fieldNameBuilder.toString( ) + FormResponseSearchItem.FIELD_SELECT_SUFFIX, response.getResponseValue( ), Field.Store.YES ) );
doc.add( new SortedDocValuesField( fieldNameBuilder.toString( ) + FormResponseSearchItem.FIELD_SELECT_SUFFIX, new BytesRef( response.getResponseValue( ) ) ) );
if ( responseField != null && StringUtils.isNotEmpty( responseField.getTitle( ) ) )
{
doc.add( new StringField( fieldNameBuilder.toString( ) + FormResponseSearchItem.FIELD_SELECT_TITLE , responseField.getTitle( ), Field.Store.YES ) );
}
}
else
{
doc.add( new StringField( fieldNameBuilder.toString( ), response.getResponseValue( ), Field.Store.YES ) );
doc.add( new SortedDocValuesField( fieldNameBuilder.toString( ), new BytesRef( response.getResponseValue( ) ) ) );
}
}
else
{
AppLogService.error( " FieldNameBuilder " + fieldNameBuilder.toString( ) + " already used for formResponse.getId( ) "
+ formResponse.getId( ) + " formQuestionResponse.getId( ) " + formQuestionResponse.getId( )
+ " response.getIdResponse( ) " + response.getIdResponse( ) + " formResponseStep" + formResponseStep.getId( ) );
}
}
}
}
}
return doc;
}
/**
* Concatenates the value of the specified field in this record
*
* @param formResponse the formResponse object
*
* @return the index
*/
private String getContentToIndex( FormResponse formResponse )
{
StringBuilder sb = new StringBuilder( );
for ( FormResponseStep formResponseStep : formResponse.getSteps( ) )
{
for ( FormQuestionResponse questionResponse : formResponseStep.getQuestions( ) )
{
// Only index the indexable entries
if ( questionResponse.getQuestion( ).isResponsesIndexed( ) )
{
Entry entry = questionResponse.getQuestion( ).getEntry( );
for ( Response response : questionResponse.getEntryResponse( ) )
{
String responseString = EntryTypeServiceManager.getEntryTypeService( entry ).getResponseValueForExport( entry, null, response, null );
if ( !StringUtils.isEmpty( responseString ) )
{
sb.append( responseString );
sb.append( " " );
}
}
}
}
}
return sb.toString( );
}
/**
* Get the field name
*
* @param responseField
* @param response
* @return the field name
*/
private String getFieldName( fr.paris.lutece.plugins.genericattributes.business.Field responseField, Response response )
{
if ( responseField.getIdField( ) > 0 )
{
return String.valueOf( responseField.getIdField( ) );
}
if ( !StringUtils.isEmpty( responseField.getCode( ) ) )
{
return responseField.getCode( );
}
if ( !StringUtils.isEmpty( responseField.getTitle( ) ) )
{
return responseField.getTitle( );
}
return String.valueOf( response.getIdResponse( ) );
}
/**
* Manage a given string null value
*
* @param strValue
* @return the string if not null, empty string otherwise
*/
private String manageNullValue( String strValue )
{
if ( strValue == null )
{
return StringUtils.EMPTY;
}
return strValue;
}
}