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.appointment.modules.management.service.search;
35
36 import java.io.IOException;
37 import java.sql.Timestamp;
38 import java.time.LocalDate;
39 import java.time.LocalTime;
40 import java.util.List;
41
42 import javax.inject.Inject;
43
44 import org.apache.commons.lang3.StringUtils;
45 import org.apache.lucene.document.Document;
46 import org.apache.lucene.document.IntPoint;
47 import org.apache.lucene.document.LongPoint;
48 import org.apache.lucene.index.DirectoryReader;
49 import org.apache.lucene.index.IndexReader;
50 import org.apache.lucene.index.Term;
51 import org.apache.lucene.search.BooleanClause;
52 import org.apache.lucene.search.BooleanQuery;
53 import org.apache.lucene.search.IndexSearcher;
54 import org.apache.lucene.search.Query;
55 import org.apache.lucene.search.ScoreDoc;
56 import org.apache.lucene.search.Sort;
57 import org.apache.lucene.search.SortField;
58 import org.apache.lucene.search.SortedNumericSortField;
59 import org.apache.lucene.search.TermQuery;
60 import org.apache.lucene.search.TopDocs;
61 import org.apache.lucene.search.WildcardQuery;
62 import org.apache.lucene.store.Directory;
63
64 import fr.paris.lutece.plugins.appointment.modules.management.business.search.AppointmentSearchItem;
65 import fr.paris.lutece.plugins.appointment.modules.management.business.search.MultiviewFilter;
66 import fr.paris.lutece.plugins.appointment.modules.management.service.indexer.LuceneAppointmentIndexFactory;
67 import fr.paris.lutece.portal.service.search.LuceneSearchEngine;
68 import fr.paris.lutece.portal.service.util.AppLogService;
69
70 public class AppointmentSearchEngine implements IAppointmentSearchEngine
71 {
72
73 @Inject
74 private LuceneAppointmentIndexFactory _indexFactory;
75
76 @Override
77 public int getSearchResult( List<AppointmentSearchItem> result, MultiviewFilter filter, int nStartIndex, int nPageSize, AppointmentSortConfig sortConfig )
78 {
79 int nbResults = 0;
80 Query query = createQuery( filter );
81 Sort sort = buildLuceneSort( sortConfig );
82
83 try ( Directory directory = _indexFactory.getDirectory( ) ; IndexReader ir = DirectoryReader.open( directory ) ; )
84 {
85 IndexSearcher searcher = new IndexSearcher( ir );
86 TopDocs topDocs = null;
87
88 if ( sort != null )
89 {
90 topDocs = searcher.search( query, LuceneSearchEngine.MAX_RESPONSES, sort );
91 }
92 else
93 {
94 topDocs = searcher.search( query, LuceneSearchEngine.MAX_RESPONSES );
95 }
96
97 ScoreDoc [ ] hits = topDocs.scoreDocs;
98 nbResults = hits.length;
99 int nMaxIndex = hits.length;
100 if ( nPageSize > 0 )
101 {
102 nMaxIndex = Math.min( nStartIndex + nPageSize, hits.length );
103 }
104
105 for ( int i = nStartIndex; i < nMaxIndex; i++ )
106 {
107 Document document = searcher.doc( hits [i].doc );
108 result.add( new AppointmentSearchItem( document ) );
109 }
110 }
111 catch( IOException e )
112 {
113 AppLogService.error( e.getMessage( ), e );
114 }
115 return nbResults;
116 }
117
118 private Query createQuery( MultiviewFilter filter )
119 {
120 BooleanQuery.Builder builder = new BooleanQuery.Builder( );
121 if ( filter.getIdCategory( ) > 0 )
122 {
123 Query query = IntPoint.newExactQuery( AppointmentSearchItem.FIELD_ID_CATEGORY, filter.getIdCategory( ) );
124 builder.add( query, BooleanClause.Occur.MUST );
125 }
126 if ( filter.getIdForm( ) > 0 )
127 {
128 Query query = IntPoint.newExactQuery( AppointmentSearchItem.FIELD_ID_FORM, filter.getIdForm( ) );
129 builder.add( query, BooleanClause.Occur.MUST );
130 }
131 else
132 {
133 Query query = IntPoint.newSetQuery( AppointmentSearchItem.FIELD_ID_FORM, filter.getIdFormList( ) );
134 builder.add( query, BooleanClause.Occur.MUST );
135 }
136 if ( StringUtils.isNotEmpty( filter.getFirstName( ) ) )
137 {
138 Query query = new WildcardQuery( new Term( AppointmentSearchItem.FIELD_FIRST_NAME_SEARCH,
139 WildcardQuery.WILDCARD_STRING + filter.getFirstName( ).toLowerCase( ) + WildcardQuery.WILDCARD_STRING ) );
140 builder.add( query, BooleanClause.Occur.MUST );
141 }
142 if ( StringUtils.isNotEmpty( filter.getLastName( ) ) )
143 {
144 Query query = new WildcardQuery( new Term( AppointmentSearchItem.FIELD_LAST_NAME_SEARCH,
145 WildcardQuery.WILDCARD_STRING + filter.getLastName( ).toLowerCase( ) + WildcardQuery.WILDCARD_STRING ) );
146 builder.add( query, BooleanClause.Occur.MUST );
147 }
148 if ( StringUtils.isNotEmpty( filter.getEmail( ) ) )
149 {
150 Query query = new WildcardQuery( new Term( AppointmentSearchItem.FIELD_MAIL_SEARCH,
151 WildcardQuery.WILDCARD_STRING + filter.getEmail( ).toLowerCase( ) + WildcardQuery.WILDCARD_STRING ) );
152 builder.add( query, BooleanClause.Occur.MUST );
153 }
154 if ( StringUtils.isNotEmpty( filter.getPhoneNumber( ) ) )
155 {
156 Query query = new WildcardQuery( new Term( AppointmentSearchItem.FIELD_PHONE_NUMBER,
157 WildcardQuery.WILDCARD_STRING + filter.getPhoneNumber( ).toLowerCase( ) + WildcardQuery.WILDCARD_STRING ) );
158 builder.add( query, BooleanClause.Occur.MUST );
159 }
160 builder.add( createDateRangeQuery( filter ), BooleanClause.Occur.MUST );
161 if ( filter.getStatus( ) != -1 )
162 {
163 Query query = new TermQuery( new Term( AppointmentSearchItem.FIELD_CANCELLED, String.valueOf( filter.getStatus( ) == 1 ) ) );
164 builder.add( query, BooleanClause.Occur.MUST );
165 }
166
167 return builder.build( );
168 }
169
170 private Query createDateRangeQuery( MultiviewFilter filter )
171 {
172 Query query = null;
173 Timestamp startingTimestamp = null;
174 if ( filter.getStartingDateOfSearch( ) != null )
175 {
176 LocalDate startingDate = filter.getStartingDateOfSearch( ).toLocalDate( );
177 if ( StringUtils.isNotEmpty( filter.getStartingTimeOfSearch( ) ) )
178 {
179 startingTimestamp = Timestamp.valueOf( startingDate.atTime( LocalTime.parse( filter.getStartingTimeOfSearch( ) ) ) );
180 }
181 else
182 {
183 startingTimestamp = Timestamp.valueOf( startingDate.atStartOfDay( ) );
184 }
185 }
186 Timestamp endingTimestamp = null;
187 if ( filter.getEndingDateOfSearch( ) != null )
188 {
189 LocalDate startingDate = filter.getEndingDateOfSearch( ).toLocalDate( );
190 if ( StringUtils.isNotEmpty( filter.getEndingTimeOfSearch( ) ) )
191 {
192 endingTimestamp = Timestamp.valueOf( startingDate.atTime( LocalTime.parse( filter.getEndingTimeOfSearch( ) ) ) );
193 }
194 else
195 {
196 endingTimestamp = Timestamp.valueOf( startingDate.atTime( LocalTime.MAX ) );
197 }
198 }
199 if ( startingTimestamp != null && endingTimestamp != null )
200 {
201 query = LongPoint.newRangeQuery( AppointmentSearchItem.FIELD_START_DATE, startingTimestamp.getTime( ), endingTimestamp.getTime( ) );
202 }
203 else
204 if ( startingTimestamp != null )
205 {
206 query = LongPoint.newRangeQuery( AppointmentSearchItem.FIELD_START_DATE, startingTimestamp.getTime( ), Long.MAX_VALUE );
207 }
208 else
209 if ( endingTimestamp != null )
210 {
211 query = LongPoint.newRangeQuery( AppointmentSearchItem.FIELD_START_DATE, Long.MIN_VALUE, endingTimestamp.getTime( ) );
212 }
213 else
214 {
215 query = LongPoint.newRangeQuery( AppointmentSearchItem.FIELD_START_DATE, Long.MIN_VALUE, Long.MAX_VALUE );
216 }
217 return query;
218 }
219
220
221
222
223
224
225
226
227 private Sort buildLuceneSort( AppointmentSortConfig sortConfig )
228 {
229 if ( sortConfig != null )
230 {
231 String strAttributeName = sortConfig.getSortAttributeName( );
232 if ( strAttributeName != null )
233 {
234 if ( strAttributeName.endsWith( AppointmentSearchItem.FIELD_DATE_SUFFIX ) )
235 {
236 return new Sort( new SortedNumericSortField( sortConfig.getSortAttributeName( ), SortField.Type.LONG, sortConfig.isDescSort( ) ) );
237 }
238 if ( strAttributeName.endsWith( AppointmentSearchItem.FIELD_INT_SUFFIX ) )
239 {
240 return new Sort( new SortedNumericSortField( sortConfig.getSortAttributeName( ), SortField.Type.LONG, sortConfig.isDescSort( ) ) );
241
242 }
243 return new Sort( new SortField( sortConfig.getSortAttributeName( ), SortField.Type.STRING, sortConfig.isDescSort( ) ) );
244 }
245 }
246
247 return null;
248 }
249 }