View Javadoc
1   /*
2    * Copyright (c) 2002-2022, City of 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.appointment.modules.solrsearchapp.service;
35  
36  import java.time.LocalDateTime;
37  import java.time.format.DateTimeParseException;
38  import java.util.AbstractMap.SimpleImmutableEntry;
39  import java.util.Arrays;
40  import java.util.Collections;
41  import java.util.List;
42  import java.util.Map;
43  
44  import javax.servlet.http.HttpServletRequest;
45  
46  import org.apache.commons.lang3.StringUtils;
47  import org.apache.commons.lang3.ArrayUtils;
48  import org.apache.solr.client.solrj.SolrQuery;
49  import org.apache.solr.client.solrj.util.ClientUtils;
50  
51  public class SolrQueryService
52  {
53      private static final String SOLR_FIELD_SITE = "site";
54      private static final String SOLR_FIELD_CATEGORY = "categorie";
55      public static final String SOLR_FIELD_FORM_UID_TITLE = "form_id_title_string";
56      private static final String SOLR_FIELD_FORM_UID = "uid_form_string";
57      private static final String SOLR_QUERY_ALL = "*:*";
58      private static final String SOLR_FILTERQUERY_ALLOWED_NOW = "{!frange l=0}sub(sub(ms(date),mul(3600000,min_hours_before_appointment_long)),ms())";
59      private static final String SOLR_FILTERQUERY_DAY_OPEN = "day_open_string:true";
60      private static final String SOLR_FILTERQUERY_ENABLED = "enabled_string:true";
61      private static final String SOLR_FILTERQUERY_ACTIVE = "appointment_active_string:true";
62      private static final String SOLR_FIELD_TYPE = "type";
63      public static final String SOLR_FIELD_DATE = "date";
64      private static final String SOLR_FIELD_MINUTE_OF_DAY = "minute_of_day_long";
65      private static final String SOLR_NB_CONSECUTIVES_SLOTS = "nb_consecutives_slots_long";
66      private static final String SOLR_ROLE = "role";
67      public static final String SOLR_FIELD_DAY_OF_WEEK = "day_of_week_long";
68      private static final String SOLR_TYPE_APPOINTMENT_SLOT = "appointment-slot";
69      public static final String VALUE_FQ_EMPTY = "__EMPTY__";
70  
71      public static final List<SimpleImmutableEntry<String, String>> EXACT_FACET_QUERIES = Collections
72              .unmodifiableList( Arrays.asList( new SimpleImmutableEntry<>( SOLR_FIELD_SITE, Utilities.PARAMETER_SITE ),
73                      new SimpleImmutableEntry<>( SOLR_FIELD_CATEGORY, Utilities.PARAMETER_CATEGORY ),
74                      new SimpleImmutableEntry<>( SOLR_FIELD_FORM_UID, Utilities.PARAMETER_FORM ) ) );
75  
76      public static final List<SimpleImmutableEntry<String, String>> FACET_FIELDS = Collections
77              .unmodifiableList( Arrays.asList( new SimpleImmutableEntry<>( SOLR_FIELD_SITE, Utilities.MARK_ITEM_SITES ),
78                      new SimpleImmutableEntry<>( SOLR_FIELD_CATEGORY, Utilities.MARK_ITEM_CATEGORIES ),
79                      new SimpleImmutableEntry<>( SOLR_FIELD_FORM_UID_TITLE, Utilities.MARK_ITEM_FORMS ) ) );
80  
81      private SolrQueryService( )
82      {
83          // private constructor
84      }
85  
86      public static SolrQuery getCommonFilteredQuery( HttpServletRequest request, Map<String, String> searchParameters,
87              Map<String, String [ ]> searchMultiParameters )
88      {
89          SolrQuery query = new SolrQuery( );
90          query.setQuery( SOLR_QUERY_ALL );
91          query.addFilterQuery( SOLR_FIELD_TYPE + ":" + SOLR_TYPE_APPOINTMENT_SLOT );
92          query.addFilterQuery( SOLR_FILTERQUERY_ALLOWED_NOW );
93          query.addFilterQuery( SOLR_FILTERQUERY_DAY_OPEN );
94          query.addFilterQuery( SOLR_FILTERQUERY_ENABLED );
95          query.addFilterQuery( SOLR_FILTERQUERY_ACTIVE );
96  
97          for ( SimpleImmutableEntry<String, String> entry : EXACT_FACET_QUERIES )
98          {
99              addFacetToQuery( query, request, searchParameters, entry );
100         }
101 
102         StringBuilder sbFqDaysOfWeek = new StringBuilder( );
103         String [ ] searchDays = Utilities.getSearchMultiParameter( Utilities.PARAMETER_DAYS_OF_WEEK, request, searchMultiParameters );
104         if ( ArrayUtils.isNotEmpty( searchDays ) )
105         {
106             sbFqDaysOfWeek.append( "{!tag=tag" + SOLR_FIELD_DAY_OF_WEEK + "}" + SOLR_FIELD_DAY_OF_WEEK + ":(" );
107             for ( int nDay = 0; nDay < searchDays.length; nDay++ )
108             {
109                 if ( nDay > 0 )
110                 {
111                     sbFqDaysOfWeek.append( " OR " );
112                 }
113                 sbFqDaysOfWeek.append( searchDays [nDay] );
114             }
115             sbFqDaysOfWeek.append( ")" );
116         }
117         query.addFilterQuery( sbFqDaysOfWeek.toString( ) );
118         query.addFacetField( "{!ex=tag" + SOLR_FIELD_DAY_OF_WEEK + "}" + SOLR_FIELD_DAY_OF_WEEK );
119 
120         String strFromDate = Utilities.getSearchParameterValue( Utilities.PARAMETER_FROM_DATE, request, searchParameters );
121         String strFromTime = Utilities.getSearchParameterValue( Utilities.PARAMETER_FROM_TIME, request, searchParameters );
122         String strToDate = Utilities.getSearchParameterValue( Utilities.PARAMETER_TO_DATE, request, searchParameters );
123         String strToTime = Utilities.getSearchParameterValue( Utilities.PARAMETER_TO_TIME, request, searchParameters );
124         LocalDateTime localDateTimeFrom = null;
125         try
126         {
127             localDateTimeFrom = LocalDateTime.parse( strFromDate + " " + strFromTime, Utilities.inputFormatter );
128         }
129         catch( DateTimeParseException e )
130         {
131             localDateTimeFrom = null;
132         }
133         String strSolrDateTimeFrom = "*";
134         if ( localDateTimeFrom != null )
135         {
136             strSolrDateTimeFrom = localDateTimeFrom.format( Utilities.outputFormatter );
137         }
138         LocalDateTime localDateTimeTo = null;
139         try
140         {
141             localDateTimeTo = LocalDateTime.parse( strToDate + " " + strToTime, Utilities.inputFormatter );
142         }
143         catch( DateTimeParseException e )
144         {
145             localDateTimeTo = null;
146         }
147         String strSolrDateTimeTo = "*";
148         if ( localDateTimeTo != null )
149         {
150             strSolrDateTimeTo = localDateTimeTo.format( Utilities.outputFormatter );
151         }
152         query.addFilterQuery( SOLR_FIELD_DATE + ":[" + strSolrDateTimeFrom + " TO " + strSolrDateTimeTo + "]" );
153         String strFromDayMinute = Utilities.getSearchParameterValue( Utilities.PARAMETER_FROM_DAY_MINUTE, request, searchParameters );
154         String strToDayMinute = Utilities.getSearchParameterValue( Utilities.PARAMETER_TO_DAY_MINUTE, request, searchParameters );
155         String strSolrDayMinuteFrom = "*";
156         if ( strFromDayMinute != null )
157         {
158             strSolrDayMinuteFrom = strFromDayMinute;
159         }
160         String strSolrDayMinuteTo = "*";
161         if ( strToDayMinute != null )
162         {
163             strSolrDayMinuteTo = strToDayMinute;
164         }
165         query.addFilterQuery( SOLR_FIELD_MINUTE_OF_DAY + ":[" + strSolrDayMinuteFrom + " TO " + strSolrDayMinuteTo + "]" );
166 
167         String strNbConsecutiveSlots = Utilities.getSearchParameterValue( Utilities.PARAMETER_NB_SLOTS, request, searchParameters );
168         int nbConsecutiveSlots = Integer.parseInt( strNbConsecutiveSlots );
169         query.addFilterQuery( SOLR_NB_CONSECUTIVES_SLOTS + ":[" + nbConsecutiveSlots + " TO *]" );
170 
171         String strRole = Utilities.getSearchParameterValue( Utilities.PARAMETER_ROLE, request, searchParameters );
172         if ( StringUtils.isNotEmpty( strRole ) && !"none".equals( strRole ) )
173         {
174             query.addFilterQuery( SOLR_ROLE + ":" + strRole );
175         }
176         return query;
177     }
178 
179     private static void addFacetToQuery( SolrQuery query, HttpServletRequest request, Map<String, String> searchParameters,
180             SimpleImmutableEntry<String, String> entry )
181     {
182         String strValue = Utilities.getSearchParameterValue( entry.getValue( ), request, searchParameters );
183         String strFacetField;
184         if ( SOLR_FIELD_FORM_UID.equals( entry.getKey( ) ) )
185         {
186             strFacetField = SOLR_FIELD_FORM_UID_TITLE;
187         }
188         else
189         {
190             strFacetField = entry.getKey( );
191         }
192         if ( StringUtils.isNotBlank( strValue ) )
193         {
194             String strFilterQuery;
195             if ( VALUE_FQ_EMPTY.equals( strValue ) )
196             {
197                 strFilterQuery = entry.getKey( ) + ":" + "\"\" OR (*:* NOT " + entry.getKey( ) + ":*)";
198             }
199             else
200             {
201                 strFilterQuery = entry.getKey( ) + ":" + ClientUtils.escapeQueryChars( strValue );
202             }
203             query.addFilterQuery( "{!tag=tag" + strFacetField + "}" + strFilterQuery );
204             strFacetField = "{!ex=tag" + strFacetField + "}" + strFacetField;
205         }
206         query.addFacetField( strFacetField );
207     }
208 }