View Javadoc
1   /*
2    * Copyright (c) 2002-2018, Mairie de Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.directory.modules.multiview.service.search;
35  
36  import java.io.IOException;
37  import java.util.ArrayList;
38  import java.util.Iterator;
39  import java.util.List;
40  
41  import org.apache.commons.collections.CollectionUtils;
42  import org.apache.commons.lang.StringUtils;
43  import org.apache.commons.lang3.math.NumberUtils;
44  import org.apache.lucene.analysis.Analyzer;
45  import org.apache.lucene.document.Document;
46  import org.apache.lucene.queryparser.classic.ParseException;
47  import org.apache.lucene.queryparser.classic.QueryParser;
48  import org.apache.lucene.search.IndexSearcher;
49  import org.apache.lucene.search.Query;
50  import org.apache.lucene.search.ScoreDoc;
51  import org.apache.lucene.search.TopDocs;
52  
53  import fr.paris.lutece.plugins.directory.modules.multiview.business.record.DirectoryRecordItem;
54  import fr.paris.lutece.plugins.directory.modules.multiview.business.record.panel.IRecordPanel;
55  import fr.paris.lutece.plugins.directory.service.directorysearch.DirectorySearchFactory;
56  import fr.paris.lutece.plugins.directory.service.directorysearch.DirectorySearchItem;
57  import fr.paris.lutece.portal.service.search.LuceneSearchEngine;
58  import fr.paris.lutece.portal.service.util.AppException;
59  import fr.paris.lutece.portal.service.util.AppLogService;
60  
61  /**
62   * Implementation of the module-directory-multiview search service
63   */
64  public class DirectoryMultiviewSearchService implements IDirectoryMultiviewSearchService
65  {
66      // Variables
67      private Analyzer _analyzer;
68      private IndexSearcher _indexSearcher;
69      private DirectorySearchFactory _directorySearchFactory;
70  
71      /**
72       * Constructor
73       */
74      public DirectoryMultiviewSearchService( )
75      {
76          _directorySearchFactory = DirectorySearchFactory.getInstance( );
77          _analyzer = _directorySearchFactory.getAnalyzer( );
78      }
79  
80      /**
81       * Constructor
82       * 
83       * @param indexSearcher
84       *            The IndexSearcher to use for made the search
85       * @param analyzer
86       *            The Analyzer to use for parsing the query of the search
87       * @throws AppException
88       *             - if one of the given parameters are missing
89       */
90      public DirectoryMultiviewSearchService( IndexSearcher indexSearcher, Analyzer analyzer ) throws AppException
91      {
92          if ( indexSearcher == null || analyzer == null )
93          {
94              throw new AppException( "The parameters of the search service musn't be null !" );
95          }
96          else
97          {
98              _indexSearcher = indexSearcher;
99              _analyzer = analyzer;
100         }
101     }
102 
103     /**
104      * {@inheritDoc}
105      */
106     @Override
107     public void filterBySearchedText( IRecordPanel recordPanel, String strSearchText )
108     {
109         if ( recordPanel != null && StringUtils.isNotBlank( strSearchText ) )
110         {
111             List<DirectoryRecordItem> listDirectoryRecordItem = recordPanel.getDirectoryRecordItemList( );
112 
113             if ( !CollectionUtils.isEmpty( listDirectoryRecordItem ) )
114             {
115                 try
116                 {
117                     List<Integer> listIdRecord = searchIdRecordList( strSearchText );
118 
119                     if ( listIdRecord.isEmpty( ) )
120                     {
121                         recordPanel.setDirectoryRecordItemList( new ArrayList<>( ) );
122                     }
123                     else
124                     {
125                         removeDirectoryItemOutsideSearchResult( recordPanel, listIdRecord );
126                     }
127                 }
128                 catch( IOException | ParseException exception )
129                 {
130                     AppLogService.error( "An error occurred during the search on the index: " + exception.getMessage( ) );
131                 }
132             }
133         }
134     }
135 
136     /**
137      * Search the list of the identifier of the Record from the result of the search of the given text
138      * 
139      * @param strSearchText
140      *            The text to search
141      * @return the list of the identifier of the Record from the result of the search of the given text
142      * @throws ParseException
143      *             if the parsing fails
144      * @throws IOException
145      *             if there is a low-level IO error
146      */
147     private List<Integer> searchIdRecordList( String strSearchText ) throws ParseException, IOException
148     {
149         IndexSearcher indexSearcher = _indexSearcher;
150         if ( _indexSearcher == null && _directorySearchFactory != null )
151         {
152             indexSearcher = _directorySearchFactory.getIndexSearcher( );
153         }
154 
155         if ( indexSearcher == null )
156         {
157             throw new IOException( "The IndexSearcher is undefined !" );
158         }
159 
160         List<Integer> listIdRecordResult = new ArrayList<>( );
161 
162         Query querySearch = prepareQuery( strSearchText );
163 
164         TopDocs topDocs = indexSearcher.search( querySearch, LuceneSearchEngine.MAX_RESPONSES );
165         if ( topDocs != null && topDocs.scoreDocs != null )
166         {
167             for ( ScoreDoc scoreDoc : topDocs.scoreDocs )
168             {
169                 int nIdRecord = retrieveIdRecordFromDoc( indexSearcher, scoreDoc );
170                 if ( nIdRecord != NumberUtils.INTEGER_MINUS_ONE )
171                 {
172                     listIdRecordResult.add( nIdRecord );
173                 }
174             }
175         }
176 
177         return listIdRecordResult;
178     }
179 
180     /**
181      * Prepare the query to execute with the given text to make the search
182      * 
183      * @param strSearchText
184      *            The text to search
185      * @return the query to execute with the given text to make the search
186      * @throws ParseException
187      *             if the parsing fails
188      */
189     private Query prepareQuery( String strSearchText ) throws ParseException
190     {
191         QueryParser queryParser = new QueryParser( DirectorySearchItem.FIELD_CONTENTS, _analyzer );
192         Query queryParsed = queryParser.parse( strSearchText );
193 
194         return queryParsed;
195     }
196 
197     /**
198      * Retrieve the id of the Record with the given searcher for the specified ScoreDoc
199      * 
200      * @param indexSearcher
201      *            The searcher used to retrieve the Document
202      * @param scoreDoc
203      *            The scoreDoc to retrieve the id Record from
204      * @return the id of the Record of the given ScoreDoc or -1 if not found or if a problem occurred
205      * @throws IOException
206      *             if there is a low-level IO error
207      */
208     private int retrieveIdRecordFromDoc( IndexSearcher indexSearcher, ScoreDoc scoreDoc ) throws IOException
209     {
210         int nIdRecord = NumberUtils.INTEGER_MINUS_ONE;
211 
212         if ( scoreDoc != null )
213         {
214             int nIdDoc = scoreDoc.doc;
215             Document document = indexSearcher.doc( nIdDoc );
216             if ( document != null )
217             {
218                 String strIdRecord = document.get( DirectorySearchItem.FIELD_ID_DIRECTORY_RECORD );
219                 nIdRecord = NumberUtils.toInt( strIdRecord, NumberUtils.INTEGER_MINUS_ONE );
220             }
221         }
222 
223         return nIdRecord;
224     }
225 
226     /**
227      * Remove the DirectoryRecordItem which are not associated to a Record which are absent from the list of search result
228      * 
229      * @param recordPanel
230      *            The RecordPael to remove the DirectoryRecordItem which are not present in the search
231      * @param listIdRecord
232      *            The list of all id of the Record which are the result of the search
233      */
234     private void removeDirectoryItemOutsideSearchResult( IRecordPanel recordPanel, List<Integer> listIdRecord )
235     {
236         List<DirectoryRecordItem> listDirectoryRecordItem = recordPanel.getDirectoryRecordItemList( );
237         Iterator<DirectoryRecordItem> iteratorDirectoryRecordItem = listDirectoryRecordItem.iterator( );
238 
239         while ( iteratorDirectoryRecordItem.hasNext( ) )
240         {
241             DirectoryRecordItem directoryRecordItem = iteratorDirectoryRecordItem.next( );
242             int nIdRecord = directoryRecordItem.getIdRecord( );
243 
244             if ( !listIdRecord.contains( nIdRecord ) )
245             {
246                 iteratorDirectoryRecordItem.remove( );
247             }
248         }
249     }
250 }