1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.solr.search;
20
21 import static org.mycore.solr.MCRSolrConstants.SOLR_CONFIG_PREFIX;
22
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Map.Entry;
31 import java.util.Set;
32 import java.util.StringTokenizer;
33
34 import org.apache.logging.log4j.LogManager;
35 import org.apache.logging.log4j.Logger;
36 import org.mycore.common.config.MCRConfiguration2;
37 import org.mycore.common.config.MCRConfigurationException;
38 import org.mycore.frontend.servlets.MCRServlet;
39 import org.mycore.frontend.servlets.MCRServletJob;
40 import org.mycore.solr.proxy.MCRSolrProxyServlet;
41
42 import jakarta.servlet.RequestDispatcher;
43 import jakarta.servlet.ServletException;
44 import jakarta.servlet.http.HttpServletRequest;
45 import jakarta.servlet.http.HttpServletResponse;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 public class MCRSolrSearchServlet extends MCRServlet {
70
71 private static final long serialVersionUID = 1L;
72
73 private enum QueryType {
74 phrase, term
75 }
76
77 private enum SolrParameterGroup {
78 QueryParameter, SolrParameter, SortParameter, TypeParameter
79 }
80
81 private static final Logger LOGGER = LogManager.getLogger(MCRSolrSearchServlet.class);
82
83 private static final String JOIN_PATTERN = "{!join from=returnId to=id}";
84
85 private static final String PHRASE_QUERY_PARAM = "solr.phrase";
86
87
88 static final List<String> RESERVED_PARAMETER_KEYS;
89
90 static {
91 String[] parameter = { "q", "qt", "sort", "start", "rows", "pageDoc", "pageScore", "fq", "cache",
92 "fl", "glob",
93 "debug", "explainOther", "defType", "timeAllowed", "omitHeader", "sortOrder", "sortBy", "wt", "qf", "q.alt",
94 "mm", "pf",
95 "ps", "qs", "tie", "bq", "bf", "lang", "facet", "facet.field", "facet.sort", "facet.method",
96 PHRASE_QUERY_PARAM, };
97
98 RESERVED_PARAMETER_KEYS = Collections.unmodifiableList(Arrays.asList(parameter));
99 }
100
101
102
103
104
105
106
107
108
109
110
111
112
113 private void addFieldToQuery(StringBuilder query, String[] fieldValues, String fieldName, QueryType queryType)
114 throws ServletException {
115 for (String fieldValue : fieldValues) {
116 if (fieldValue.length() == 0) {
117 continue;
118 }
119 switch (queryType) {
120 case term:
121 query.append(MCRConditionTransformer.getTermQuery(fieldName, fieldValue));
122 break;
123 case phrase:
124 query.append(MCRConditionTransformer.getPhraseQuery(fieldName, fieldValue));
125 break;
126 default:
127 throw new ServletException("Query type is unsupported: " + queryType);
128 }
129 query.append(' ');
130 }
131 }
132
133
134
135
136
137
138
139
140
141
142 protected Map<String, String[]> buildSelectParameterMap(Map<String, String[]> queryParameters,
143 Map<String, String[]> typeParameters,
144 Map<String, String[]> sortParameters, Set<String> phraseQuery) throws ServletException {
145 HashMap<String, String[]> queryParameterMap = new HashMap<>();
146
147 HashMap<String, String> fieldTypeMap = createFieldTypeMap(typeParameters);
148
149 HashMap<String, StringBuilder> filterQueryMap = new HashMap<>();
150 StringBuilder query = new StringBuilder();
151 for (Entry<String, String[]> queryParameter : queryParameters.entrySet()) {
152 String fieldName = queryParameter.getKey();
153 String[] fieldValues = queryParameter.getValue();
154 QueryType queryType = phraseQuery.contains(fieldName) ? QueryType.phrase : QueryType.term;
155
156 if (!fieldTypeMap.containsKey(fieldName)) {
157 addFieldToQuery(query, fieldValues, fieldName, queryType);
158
159 } else {
160 String fieldType = fieldTypeMap.get(fieldName);
161 StringBuilder filterQueryBuilder = getFilterQueryBuilder(filterQueryMap, fieldType);
162 addFieldToQuery(filterQueryBuilder, fieldValues, fieldName, queryType);
163 }
164 }
165
166
167 queryParameterMap.put("q", new String[] { query.toString().trim() });
168
169 for (StringBuilder filterQueryBuilder : filterQueryMap.values()) {
170
171 if (filterQueryBuilder.length() > JOIN_PATTERN.length()) {
172 queryParameterMap.put("fq", new String[] { filterQueryBuilder.toString() });
173 }
174 }
175
176 queryParameterMap.put("sort", new String[] { buildSolrSortParameter(sortParameters) });
177
178 return queryParameterMap;
179 }
180
181
182
183
184
185
186 private String buildSolrSortParameter(Map<String, String[]> sortParameters) {
187 Set<Entry<String, String[]>> sortParameterEntrys = sortParameters.entrySet();
188 Map<Integer, String> positionOrderMap = new HashMap<>();
189 Map<Integer, String> positionFieldMap = new HashMap<>();
190
191 for (Entry<String, String[]> sortParameterEntry : sortParameterEntrys) {
192 StringTokenizer st = new StringTokenizer(sortParameterEntry.getKey(), ".");
193 st.nextToken();
194 Integer position = Integer.parseInt(st.nextToken());
195 String type = st.nextToken();
196 String[] valueArray = sortParameterEntry.getValue();
197 if (valueArray.length > 0) {
198 String value = valueArray[0];
199 if ("order".equals(type)) {
200 positionOrderMap.put(position, value);
201 } else if ("field".equals(type)) {
202 positionFieldMap.put(position, value);
203 }
204 }
205 }
206
207 ArrayList<Integer> sortedPositions = new ArrayList<>();
208
209 sortedPositions.addAll(positionFieldMap.keySet());
210 Collections.sort(sortedPositions);
211
212 StringBuilder sortBuilder = new StringBuilder();
213 for (Integer position : sortedPositions) {
214 sortBuilder.append(",");
215 sortBuilder.append(positionFieldMap.get(position));
216 String order = positionOrderMap.get(position);
217 sortBuilder.append(" ");
218 if (order == null) {
219 order = "asc";
220 LOGGER.warn("No sort order found for field with number ''{}'' use default value : ''{}''", position,
221 order);
222 }
223 sortBuilder.append(order);
224 }
225 if (sortBuilder.length() != 0) {
226 sortBuilder.deleteCharAt(0);
227 }
228
229 return sortBuilder.toString();
230 }
231
232
233
234
235
236
237
238
239 private HashMap<String, String> createFieldTypeMap(Map<String, String[]> typeParameters) {
240 HashMap<String, String> fieldTypeMap = new HashMap<>();
241
242 for (Entry<String, String[]> currentType : typeParameters.entrySet()) {
243 for (String typeMember : currentType.getValue()) {
244 fieldTypeMap.put(typeMember, currentType.getKey());
245 }
246 }
247 return fieldTypeMap;
248 }
249
250 @Override
251 protected void doGetPost(MCRServletJob job) throws Exception {
252 Map<String, String[]> solrParameters = new HashMap<>();
253 Map<String, String[]> queryParameters = new HashMap<>();
254 Map<String, String[]> typeParameters = new HashMap<>();
255 Map<String, String[]> sortParameters = new HashMap<>();
256 Set<String> phraseQuery = new HashSet<>();
257 String[] phraseFields = job.getRequest().getParameterValues(PHRASE_QUERY_PARAM);
258 if (phraseFields != null) {
259 phraseQuery.addAll(Arrays.asList(phraseFields));
260 }
261
262 HttpServletRequest request = job.getRequest();
263 HttpServletResponse response = job.getResponse();
264
265 extractParameterList(request.getParameterMap(), queryParameters, solrParameters, typeParameters,
266 sortParameters);
267 Map<String, String[]> buildedSolrParameters = buildSelectParameterMap(queryParameters, typeParameters,
268 sortParameters, phraseQuery);
269 buildedSolrParameters.putAll(solrParameters);
270
271 request.setAttribute(MCRSolrProxyServlet.MAP_KEY, buildedSolrParameters);
272 LOGGER.info("Forward SOLR Parameters: {}", buildedSolrParameters);
273 RequestDispatcher requestDispatcher = getServletContext().getRequestDispatcher("/servlets/SolrSelectProxy");
274 requestDispatcher.forward(request, response);
275 }
276
277 @Override
278 public void init() throws ServletException {
279 super.init();
280 }
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296 protected void extractParameterList(Map<String, String[]> requestParameter, Map<String, String[]> queryParameter,
297 Map<String, String[]> solrParameter, Map<String, String[]> typeParameter, Map<String, String[]> sortParameter) {
298 for (Entry<String, String[]> currentEntry : requestParameter.entrySet()) {
299 String parameterName = currentEntry.getKey();
300 if (PHRASE_QUERY_PARAM.equals(parameterName)) {
301 continue;
302 }
303 SolrParameterGroup parameterGroup = getParameterType(parameterName);
304
305 switch (parameterGroup) {
306 case SolrParameter:
307 solrParameter.put(parameterName, currentEntry.getValue());
308 break;
309 case TypeParameter:
310 typeParameter.put(parameterName, currentEntry.getValue());
311 break;
312 case QueryParameter:
313 String[] strings = currentEntry.getValue();
314 for (String v : strings) {
315 if (v != null && v.length() > 0) {
316 queryParameter.put(parameterName, currentEntry.getValue());
317 }
318 }
319 break;
320 case SortParameter:
321 sortParameter.put(parameterName, currentEntry.getValue());
322 break;
323 default:
324 LOGGER.warn("Unknown parameter group. That should not happen.");
325 continue;
326 }
327 }
328 }
329
330
331
332
333
334
335
336 private StringBuilder getFilterQueryBuilder(HashMap<String, StringBuilder> filterQueryMap, String fieldType) {
337 if (!filterQueryMap.containsKey(fieldType)) {
338 filterQueryMap.put(fieldType, new StringBuilder(JOIN_PATTERN));
339 }
340 return filterQueryMap.get(fieldType);
341 }
342
343
344
345
346
347
348
349
350 private SolrParameterGroup getParameterType(String parameterName) {
351 if (isTypeParameter(parameterName)) {
352 LOGGER.debug("Parameter {} is a {}", parameterName, SolrParameterGroup.TypeParameter.toString());
353 return SolrParameterGroup.TypeParameter;
354 } else if (isSolrParameter(parameterName)) {
355 LOGGER.debug("Parameter {} is a {}", parameterName, SolrParameterGroup.SolrParameter.toString());
356 return SolrParameterGroup.SolrParameter;
357 } else if (isSortParameter(parameterName)) {
358 LOGGER.debug("Parameter {} is a {}", parameterName, SolrParameterGroup.SolrParameter.toString());
359 return SolrParameterGroup.SortParameter;
360 } else {
361 LOGGER.debug("Parameter {} is a {}", parameterName, SolrParameterGroup.QueryParameter.toString());
362 return SolrParameterGroup.QueryParameter;
363 }
364 }
365
366
367
368
369
370
371
372
373 private boolean isSolrParameter(String parameterName) {
374 boolean reservedCustomKey;
375 try {
376 reservedCustomKey = MCRConfiguration2
377 .getOrThrow(SOLR_CONFIG_PREFIX + "ReservedParameterKeys", MCRConfiguration2::splitValue)
378 .filter(parameterName::equals)
379 .findAny()
380 .isPresent();
381 } catch (MCRConfigurationException e) {
382 reservedCustomKey = false;
383 }
384 return parameterName.startsWith("XSL.") || RESERVED_PARAMETER_KEYS.contains(parameterName) || reservedCustomKey;
385 }
386
387
388
389
390
391
392
393
394 private boolean isSortParameter(String parameterName) {
395 return parameterName.startsWith("sort.");
396 }
397
398
399
400
401
402
403
404
405 private boolean isTypeParameter(String parameterName) {
406 return parameterName.startsWith("solr.type.");
407 }
408 }