1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package fr.paris.lutece.plugins.announce.service.announcesearch;
35
36 import java.text.ParseException;
37 import java.text.SimpleDateFormat;
38 import java.util.ArrayList;
39 import java.util.Collection;
40 import java.util.Date;
41 import java.util.List;
42 import java.util.Locale;
43
44 import org.apache.commons.lang3.StringUtils;
45 import org.apache.lucene.document.DateTools;
46 import org.apache.lucene.document.Document;
47 import org.apache.lucene.index.Term;
48 import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
49 import org.apache.lucene.search.BooleanClause;
50 import org.apache.lucene.search.IndexSearcher;
51 import org.apache.lucene.search.PhraseQuery;
52 import org.apache.lucene.search.Query;
53 import org.apache.lucene.search.ScoreDoc;
54 import org.apache.lucene.search.TermQuery;
55 import org.apache.lucene.search.TermRangeQuery;
56 import org.apache.lucene.search.TopDocs;
57 import org.apache.lucene.util.BytesRef;
58
59 import fr.paris.lutece.plugins.announce.business.Announce;
60 import fr.paris.lutece.plugins.announce.business.AnnounceHome;
61 import fr.paris.lutece.plugins.announce.business.AnnounceSearchFilter;
62 import fr.paris.lutece.plugins.announce.business.AnnounceSort;
63 import fr.paris.lutece.plugins.announce.service.AnnouncePlugin;
64 import fr.paris.lutece.portal.service.plugin.Plugin;
65 import fr.paris.lutece.portal.service.search.SearchItem;
66 import fr.paris.lutece.portal.service.search.SearchResult;
67 import fr.paris.lutece.portal.service.util.AppLogService;
68
69
70
71
72 public class AnnounceLuceneSearchEngine implements IAnnounceSearchEngine
73 {
74 private static final int NO_CATEGORY = 0;
75 private static final int NO_SECTOR = 0;
76 private final SimpleDateFormat _dayFormat = new SimpleDateFormat( "yyyyMMdd", Locale.US );
77
78
79
80
81 @Override
82 public int getSearchResults( AnnounceSearchFilter filter, Plugin plugin, List<SearchResult> listSearchResult, int nPage, int nItemsPerPage )
83 {
84 ArrayList<SearchItem> listResults = new ArrayList<>( );
85 IndexSearcher searcher;
86
87 int nNbResults = 0;
88
89 try
90 {
91 searcher = AnnounceSearchService.getInstance( ).getSearcher( );
92
93 Collection<String> queries = new ArrayList<>( );
94 Collection<String> sectors = new ArrayList<>( );
95 Collection<BooleanClause.Occur> flags = new ArrayList<>( );
96
97
98 if ( filter.getIdCategory( ) != NO_CATEGORY )
99 {
100 Query queryCategoryId = new TermQuery( new Term( AnnounceSearchItem.FIELD_CATEGORY_ID, String.valueOf( filter.getIdCategory( ) ) ) );
101 queries.add( queryCategoryId.toString( ) );
102 sectors.add( AnnounceSearchItem.FIELD_CATEGORY_ID );
103 flags.add( BooleanClause.Occur.MUST );
104 }
105
106
107 if ( filter.getIdSector( ) != NO_SECTOR )
108 {
109 Query querySectorId = new TermQuery( new Term( AnnounceSearchItem.FIELD_SECTOR_ID, String.valueOf( filter.getIdSector( ) ) ) );
110 queries.add( querySectorId.toString( ) );
111 sectors.add( AnnounceSearchItem.FIELD_SECTOR_ID );
112 flags.add( BooleanClause.Occur.MUST );
113 }
114
115
116 PhraseQuery.Builder queryTypeBuilder = new PhraseQuery.Builder( );
117
118
119 queryTypeBuilder.add( new Term( SearchItem.FIELD_TYPE, AnnouncePlugin.PLUGIN_NAME + "e" ) );
120 queries.add( queryTypeBuilder.build( ).toString( ) );
121 sectors.add( SearchItem.FIELD_TYPE );
122 flags.add( BooleanClause.Occur.MUST );
123
124
125 if ( StringUtils.isNotBlank( filter.getKeywords( ) ) )
126 {
127 PhraseQuery.Builder queryContentBuilder = new PhraseQuery.Builder( );
128 queryContentBuilder.add( new Term( SearchItem.FIELD_CONTENTS, filter.getKeywords( ) ) );
129 queries.add( queryContentBuilder.build( ).toString( ) );
130 sectors.add( SearchItem.FIELD_CONTENTS );
131 flags.add( BooleanClause.Occur.MUST );
132 }
133
134
135 if ( ( filter.getDateMin( ) != null ) || ( filter.getDateMax( ) != null ) )
136 {
137 Date dateMinToSearch = new Date( 0L );
138 Date dateMaxToSearch = new Date( );
139 if ( filter.getDateMin( ) != null )
140 {
141 dateMinToSearch = filter.getDateMin( );
142 }
143
144 if ( filter.getDateMax( ) != null )
145 {
146 dateMaxToSearch = filter.getDateMax( );
147 }
148
149
150 String strLowerTerm = _dayFormat.format( dateMinToSearch );
151 String strUpperTerm = _dayFormat.format( dateMaxToSearch );
152 BytesRef bRLowerTerm = new BytesRef( strLowerTerm );
153 BytesRef bRUpperTerm = new BytesRef( strUpperTerm );
154 Query queryRangeDate = new TermRangeQuery( SearchItem.FIELD_DATE, bRLowerTerm, bRUpperTerm, true, true );
155 queries.add( queryRangeDate.toString( ) );
156 sectors.add( SearchItem.FIELD_DATE );
157 flags.add( BooleanClause.Occur.MUST );
158 }
159
160
161 if ( ( filter.getPriceMin( ) > 0 ) || ( filter.getPriceMax( ) > 0 ) )
162 {
163 int nPriceMin = ( filter.getPriceMin( ) > 0 ) ? filter.getPriceMin( ) : 0;
164 int nPriceMax = ( filter.getPriceMax( ) > 0 ) ? filter.getPriceMax( ) : Integer.MAX_VALUE;
165 Query queryRangePrice = new TermRangeQuery( AnnounceSearchItem.FIELD_PRICE,
166 new BytesRef( AnnounceSearchService.formatPriceForIndexer( nPriceMin ) ),
167 new BytesRef( AnnounceSearchService.formatPriceForIndexer( nPriceMax ) ), true, true );
168 queries.add( queryRangePrice.toString( ) );
169 sectors.add( AnnounceSearchItem.FIELD_PRICE );
170 flags.add( BooleanClause.Occur.MUST );
171 }
172
173 Query queryMulti = MultiFieldQueryParser.parse( queries.toArray( new String [ queries.size( )] ), sectors.toArray( new String [ sectors.size( )] ),
174 flags.toArray( new BooleanClause.Occur [ flags.size( )] ), AnnounceSearchService.getInstance( ).getAnalyzer( ) );
175
176 TopDocs topDocs = searcher.search( queryMulti, 1000000 );
177 ScoreDoc [ ] hits = topDocs.scoreDocs;
178 nNbResults = hits.length;
179
180
181 int nFrom = ( nPage - 1 ) * nItemsPerPage;
182
183 if ( nFrom < 0 )
184 {
185 nFrom = 0;
186 }
187
188 int nTo = ( nPage * nItemsPerPage );
189
190 if ( ( nTo == 0 ) || ( nTo > nNbResults ) )
191 {
192 nTo = nNbResults;
193 }
194
195 for ( int i = nFrom; i < nTo; i++ )
196 {
197 int docId = hits [i].doc;
198 Document document = searcher.doc( docId );
199 SearchItem si = new SearchItem( document );
200 listResults.add( si );
201 }
202 }
203 catch( Exception e )
204 {
205 AppLogService.error( e.getMessage( ), e );
206 }
207 convertList( listResults, listSearchResult );
208
209 return nNbResults;
210 }
211
212
213
214
215 @Override
216 public int getSearchResultsBis( AnnounceSearchFilter filter, Plugin plugin, List<Announce> listAnnouncesResult, int nPage, int nItemsPerPage,
217 AnnounceSort anSort )
218 {
219 ArrayList<SearchItem> listResults = new ArrayList<>( );
220 List<Integer> listIdAnnounces = new ArrayList<>( );
221 IndexSearcher searcher;
222
223 Date dateMinToSearch;
224 Date dateMaxToSearch;
225 int nNbResults = 0;
226 try
227 {
228 searcher = AnnounceSearchService.getInstance( ).getSearcher( );
229
230 Collection<String> queries = new ArrayList<>( );
231 Collection<String> sectors = new ArrayList<>( );
232 Collection<BooleanClause.Occur> flags = new ArrayList<>( );
233
234
235 if ( filter.getIdCategory( ) != NO_CATEGORY )
236 {
237 Query queryCategoryId = new TermQuery( new Term( AnnounceSearchItem.FIELD_CATEGORY_ID, String.valueOf( filter.getIdCategory( ) ) ) );
238 queries.add( queryCategoryId.toString( ) );
239 sectors.add( AnnounceSearchItem.FIELD_CATEGORY_ID );
240 flags.add( BooleanClause.Occur.MUST );
241 }
242
243
244 if ( filter.getIdSector( ) != NO_SECTOR )
245 {
246 Query querySectorId = new TermQuery( new Term( AnnounceSearchItem.FIELD_SECTOR_ID, String.valueOf( filter.getIdSector( ) ) ) );
247 queries.add( querySectorId.toString( ) );
248 sectors.add( AnnounceSearchItem.FIELD_SECTOR_ID );
249 flags.add( BooleanClause.Occur.MUST );
250 }
251
252
253 PhraseQuery.Builder queryTypeBuilder = new PhraseQuery.Builder( );
254
255
256 queryTypeBuilder.add( new Term( SearchItem.FIELD_TYPE, AnnouncePlugin.PLUGIN_NAME + "e" ) );
257 queries.add( queryTypeBuilder.build( ).toString( ) );
258 sectors.add( SearchItem.FIELD_TYPE );
259 flags.add( BooleanClause.Occur.MUST );
260
261
262 if ( StringUtils.isNotBlank( filter.getKeywords( ) ) )
263 {
264 PhraseQuery.Builder queryContentBuilder = new PhraseQuery.Builder( );
265 queryContentBuilder.add( new Term( SearchItem.FIELD_CONTENTS, filter.getKeywords( ) ) );
266 queries.add( queryContentBuilder.build( ).toString( ) );
267 sectors.add( SearchItem.FIELD_CONTENTS );
268 flags.add( BooleanClause.Occur.MUST );
269 }
270
271
272 if ( ( filter.getDateMin( ) != null ) || ( filter.getDateMax( ) != null ) )
273 {
274 if ( filter.getDateMin( ) == null )
275 {
276 dateMinToSearch = new Date( 0L );
277 }
278 else
279 {
280 dateMinToSearch = filter.getDateMin( );
281 }
282
283 if ( filter.getDateMax( ) == null )
284 {
285 dateMaxToSearch = new Date( );
286 }
287 else
288 {
289 dateMaxToSearch = filter.getDateMax( );
290 }
291
292
293 String strLowerTerm = _dayFormat.format( dateMinToSearch );
294 String strUpperTerm = _dayFormat.format( dateMaxToSearch );
295 BytesRef bRLowerTerm = new BytesRef( strLowerTerm );
296 BytesRef bRUpperTerm = new BytesRef( strUpperTerm );
297 Query queryRangeDate = new TermRangeQuery( SearchItem.FIELD_DATE, bRLowerTerm, bRUpperTerm, true, true );
298 queries.add( queryRangeDate.toString( ) );
299 sectors.add( SearchItem.FIELD_DATE );
300 flags.add( BooleanClause.Occur.MUST );
301 }
302
303
304 if ( ( filter.getPriceMin( ) > 0 ) || ( filter.getPriceMax( ) > 0 ) )
305 {
306 int nPriceMin = ( filter.getPriceMin( ) > 0 ) ? filter.getPriceMin( ) : 0;
307 int nPriceMax = ( filter.getPriceMax( ) > 0 ) ? filter.getPriceMax( ) : Integer.MAX_VALUE;
308 Query queryRangePrice = new TermRangeQuery( AnnounceSearchItem.FIELD_PRICE,
309 new BytesRef( AnnounceSearchService.formatPriceForIndexer( nPriceMin ) ),
310 new BytesRef( AnnounceSearchService.formatPriceForIndexer( nPriceMax ) ), true, true );
311 queries.add( queryRangePrice.toString( ) );
312 sectors.add( AnnounceSearchItem.FIELD_PRICE );
313 flags.add( BooleanClause.Occur.MUST );
314 }
315
316 Query queryMulti = MultiFieldQueryParser.parse( queries.toArray( new String [ queries.size( )] ), sectors.toArray( new String [ sectors.size( )] ),
317 flags.toArray( new BooleanClause.Occur [ flags.size( )] ), AnnounceSearchService.getInstance( ).getAnalyzer( ) );
318
319 TopDocs topDocs = searcher.search( queryMulti, 1000000 );
320 ScoreDoc [ ] hits = topDocs.scoreDocs;
321 nNbResults = hits.length;
322
323
324 for ( int i = 0; i < nNbResults; i++ )
325 {
326 int docId = hits [i].doc;
327 Document document = searcher.doc( docId );
328 SearchItem si = new SearchItem( document );
329 listResults.add( si );
330 }
331 for ( SearchItem searchResult : listResults )
332 {
333 if ( searchResult.getId( ) != null )
334 {
335 listIdAnnounces.add( Integer.parseInt( searchResult.getId( ) ) );
336 }
337 }
338
339 List<Announce> listAnnounces = AnnounceHome.findByListId( listIdAnnounces, anSort );
340
341
342
343
344 int nFrom = ( nPage - 1 ) * nItemsPerPage;
345
346 if ( nFrom < 0 )
347 {
348 nFrom = 0;
349 }
350
351 int nTo = ( nPage * nItemsPerPage );
352
353 if ( ( nTo == 0 ) || ( nTo > nNbResults ) )
354 {
355 nTo = nNbResults;
356 }
357
358 for ( int i = nFrom; i < nTo; i++ )
359 {
360 listAnnouncesResult.add( listAnnounces.get( i ) );
361 }
362 }
363 catch( Exception e )
364 {
365 AppLogService.error( e.getMessage( ), e );
366 }
367
368 return nNbResults;
369 }
370
371
372
373
374
375
376
377
378
379 private void convertList( List<SearchItem> listSource, List<SearchResult> listSearchResult )
380 {
381 for ( SearchItem item : listSource )
382 {
383 SearchResult result = new SearchResult( );
384 result.setId( item.getId( ) );
385
386 try
387 {
388 result.setDate( DateTools.stringToDate( item.getDate( ) ) );
389 }
390 catch( ParseException e )
391 {
392 AppLogService.error( "Bad Date Format for indexed item \"" + item.getTitle( ) + "\" : " + e.getMessage( ) );
393 }
394
395 result.setUrl( item.getUrl( ) );
396 result.setTitle( item.getTitle( ) );
397 result.setSummary( item.getSummary( ) );
398 result.setType( item.getType( ) );
399 listSearchResult.add( result );
400 }
401 }
402 }