001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: https://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: https://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.jsp.search.config.parser; 029 030import org.opencms.file.CmsObject; 031import org.opencms.json.JSONArray; 032import org.opencms.json.JSONException; 033import org.opencms.json.JSONObject; 034import org.opencms.jsp.search.config.CmsSearchConfigurationCommon; 035import org.opencms.jsp.search.config.CmsSearchConfigurationDidYouMean; 036import org.opencms.jsp.search.config.CmsSearchConfigurationFacetField; 037import org.opencms.jsp.search.config.CmsSearchConfigurationFacetQuery; 038import org.opencms.jsp.search.config.CmsSearchConfigurationFacetQuery.CmsFacetQueryItem; 039import org.opencms.jsp.search.config.CmsSearchConfigurationFacetRange; 040import org.opencms.jsp.search.config.CmsSearchConfigurationGeoFilter; 041import org.opencms.jsp.search.config.CmsSearchConfigurationHighlighting; 042import org.opencms.jsp.search.config.CmsSearchConfigurationPagination; 043import org.opencms.jsp.search.config.CmsSearchConfigurationSortOption; 044import org.opencms.jsp.search.config.CmsSearchConfigurationSorting; 045import org.opencms.jsp.search.config.I_CmsSearchConfiguration; 046import org.opencms.jsp.search.config.I_CmsSearchConfigurationCommon; 047import org.opencms.jsp.search.config.I_CmsSearchConfigurationDidYouMean; 048import org.opencms.jsp.search.config.I_CmsSearchConfigurationFacet; 049import org.opencms.jsp.search.config.I_CmsSearchConfigurationFacetField; 050import org.opencms.jsp.search.config.I_CmsSearchConfigurationFacetQuery; 051import org.opencms.jsp.search.config.I_CmsSearchConfigurationFacetQuery.I_CmsFacetQueryItem; 052import org.opencms.jsp.search.config.I_CmsSearchConfigurationFacetRange; 053import org.opencms.jsp.search.config.I_CmsSearchConfigurationGeoFilter; 054import org.opencms.jsp.search.config.I_CmsSearchConfigurationHighlighting; 055import org.opencms.jsp.search.config.I_CmsSearchConfigurationPagination; 056import org.opencms.jsp.search.config.I_CmsSearchConfigurationSortOption; 057import org.opencms.jsp.search.config.I_CmsSearchConfigurationSorting; 058import org.opencms.main.CmsLog; 059import org.opencms.main.OpenCms; 060import org.opencms.search.solr.CmsSolrIndex; 061 062import java.util.ArrayList; 063import java.util.Collections; 064import java.util.HashMap; 065import java.util.Iterator; 066import java.util.LinkedHashMap; 067import java.util.LinkedList; 068import java.util.List; 069import java.util.Map; 070import java.util.Objects; 071 072import org.apache.commons.logging.Log; 073 074/** Search configuration parser reading JSON. */ 075public class CmsJSONSearchConfigurationParser implements I_CmsSearchConfigurationParser { 076 077 /** Logger for the class. */ 078 protected static final Log LOG = CmsLog.getLog(CmsJSONSearchConfigurationParser.class); 079 080 /** The keys that can be used in the JSON object */ 081 /** JSON keys for common options. */ 082 /** A JSON key. */ 083 public static final String JSON_KEY_QUERYPARAM = "queryparam"; 084 /** A JSON key. */ 085 public static final String JSON_KEY_LAST_QUERYPARAM = "lastqueryparam"; 086 /** A JSON key. */ 087 public static final String JSON_KEY_ESCAPE_QUERY_CHARACTERS = "escapequerychars"; 088 /** A JSON key. */ 089 public static final String JSON_KEY_RELOADED_PARAM = "reloadedparam"; 090 /** A JSON key. */ 091 public static final String JSON_KEY_SEARCH_FOR_EMPTY_QUERY = "searchforemptyquery"; 092 /** A JSON key. */ 093 public static final String JSON_KEY_IGNORE_QUERY = "ignorequery"; 094 /** A JSON key. */ 095 public static final String JSON_KEY_IGNORE_RELEASE_DATE = "ignoreReleaseDate"; 096 /** A JSON key. */ 097 public static final String JSON_KEY_MAX_RETURNED_RESULTS = "maxReturnedResults"; 098 /** A JSON key. */ 099 public static final String JSON_KEY_IGNORE_EXPIRATION_DATE = "ignoreExpirationDate"; 100 /** A JSON key. */ 101 public static final String JSON_KEY_QUERY_MODIFIER = "querymodifier"; 102 /** A JSON key. */ 103 public static final String JSON_KEY_PAGEPARAM = "pageparam"; 104 /** A JSON key. */ 105 public static final String JSON_KEY_INDEX = "index"; 106 /** A JSON key. */ 107 public static final String JSON_KEY_CORE = "core"; 108 /** A JSON key. */ 109 public static final String JSON_KEY_EXTRASOLRPARAMS = "extrasolrparams"; 110 /** A JSON key. */ 111 public static final String JSON_KEY_ADDITIONAL_PARAMETERS = "additionalrequestparams"; 112 /** A JSON key. */ 113 public static final String JSON_KEY_ADDITIONAL_PARAMETERS_PARAM = "param"; 114 /** A JSON key. */ 115 public static final String JSON_KEY_ADDITIONAL_PARAMETERS_SOLRQUERY = "solrquery"; 116 /** A JSON key. */ 117 public static final String JSON_KEY_PAGESIZE = "pagesize"; 118 /** A JSON key. */ 119 public static final String JSON_KEY_PAGENAVLENGTH = "pagenavlength"; 120 /** JSON keys for facet configuration. */ 121 /** The JSON key for the sub-node with all field facet configurations. */ 122 public static final String JSON_KEY_FIELD_FACETS = "fieldfacets"; 123 /** The JSON key for the sub-node with all field facet configurations. */ 124 public static final String JSON_KEY_RANGE_FACETS = "rangefacets"; 125 /** The JSON key for the sub-node with the query facet configuration. */ 126 public static final String JSON_KEY_QUERY_FACET = "queryfacet"; 127 /** JSON keys for a single facet. */ 128 /** A JSON key. */ 129 public static final String JSON_KEY_FACET_LIMIT = "limit"; 130 /** A JSON key. */ 131 public static final String JSON_KEY_FACET_MINCOUNT = "mincount"; 132 /** A JSON key. */ 133 public static final String JSON_KEY_FACET_LABEL = "label"; 134 /** A JSON key. */ 135 public static final String JSON_KEY_FACET_FIELD = "field"; 136 /** A JSON key. */ 137 public static final String JSON_KEY_FACET_NAME = "name"; 138 /** A JSON key. */ 139 public static final String JSON_KEY_FACET_PREFIX = "prefix"; 140 /** A JSON key. */ 141 public static final String JSON_KEY_FACET_ORDER = "order"; 142 /** A JSON key. */ 143 public static final String JSON_KEY_FACET_FILTERQUERYMODIFIER = "filterquerymodifier"; 144 /** A JSON key. */ 145 public static final String JSON_KEY_FACET_ISANDFACET = "isAndFacet"; 146 /** A JSON key. */ 147 public static final String JSON_KEY_FACET_IGNOREALLFACETFILTERS = "ignoreAllFacetFilters"; 148 /** A JSON key. */ 149 public static final String JSON_KEY_FACET_EXCLUDETAGS = "excludeTags"; 150 /** A JSON key. */ 151 public static final String JSON_KEY_FACET_PRESELECTION = "preselection"; 152 /** A JSON key. */ 153 public static final String JSON_KEY_RANGE_FACET_RANGE = "range"; 154 /** A JSON key. */ 155 public static final String JSON_KEY_RANGE_FACET_START = "start"; 156 /** A JSON key. */ 157 public static final String JSON_KEY_RANGE_FACET_END = "end"; 158 /** A JSON key. */ 159 public static final String JSON_KEY_RANGE_FACET_GAP = "gap"; 160 /** A JSON key. */ 161 public static final String JSON_KEY_RANGE_FACET_OTHER = "other"; 162 /** A JSON key. */ 163 public static final String JSON_KEY_RANGE_FACET_HARDEND = "hardend"; 164 /** A JSON key. */ 165 public static final String JSON_KEY_RANGE_FACET_METHOD = "method"; 166 /** A JSON key. */ 167 public static final String JSON_KEY_QUERY_FACET_QUERY = "queryitems"; 168 /** A JSON key. */ 169 public static final String JSON_KEY_QUERY_FACET_QUERY_QUERY = "query"; 170 /** A JSON key. */ 171 public static final String JSON_KEY_QUERY_FACET_QUERY_LABEL = "label"; 172 173 /** JSON keys for sort options. */ 174 /** A JSON key. */ 175 public static final String JSON_KEY_SORTPARAM = "sortby"; 176 /** The JSON key for the default sort option, should hold the name paramvalue for the default option. */ 177 public static final String JSON_KEY_DEFAULT_SORT_OPTION = "defaultSortOption"; 178 /** The JSON key for the sub-node with all search option configurations. */ 179 public static final String JSON_KEY_SORTOPTIONS = "sortoptions"; 180 /** JSON keys for a single search option. */ 181 /** A JSON key. */ 182 public static final String JSON_KEY_SORTOPTION_LABEL = "label"; 183 /** A JSON key. */ 184 public static final String JSON_KEY_SORTOPTION_PARAMVALUE = "paramvalue"; 185 /** A JSON key. */ 186 public static final String JSON_KEY_SORTOPTION_SOLRVALUE = "solrvalue"; 187 /** JSON keys for the highlighting configuration. */ 188 /** The JSON key for the subnode of all highlighting configuration. */ 189 public static final String JSON_KEY_HIGHLIGHTER = "highlighter"; 190 /** A JSON key. */ 191 public static final String JSON_KEY_HIGHLIGHTER_FIELD = "field"; 192 /** A JSON key. */ 193 public static final String JSON_KEY_HIGHLIGHTER_SNIPPETS = "snippets"; 194 /** A JSON key. */ 195 public static final String JSON_KEY_HIGHLIGHTER_FRAGSIZE = "fragsize"; 196 /** A JSON key. */ 197 public static final String JSON_KEY_HIGHLIGHTER_ALTERNATE_FIELD = "alternateField"; 198 /** A JSON key. */ 199 public static final String JSON_KEY_HIGHLIGHTER_MAX_LENGTH_ALTERNATE_FIELD = "maxAlternateFieldLength"; 200 /** A JSON key. */ 201 public static final String JSON_KEY_HIGHLIGHTER_SIMPLE_PRE = "simple.pre"; 202 /** A JSON key. */ 203 public static final String JSON_KEY_HIGHLIGHTER_SIMPLE_POST = "simple.post"; 204 /** A JSON key. */ 205 public static final String JSON_KEY_HIGHLIGHTER_FORMATTER = "formatter"; 206 /** A JSON key. */ 207 public static final String JSON_KEY_HIGHLIGHTER_FRAGMENTER = "fragmenter"; 208 /** A JSON key. */ 209 public static final String JSON_KEY_HIGHLIGHTER_FASTVECTORHIGHLIGHTING = "useFastVectorHighlighting"; 210 211 /** JSON keys for "Did you mean ...?" */ 212 /** A JSON key. */ 213 public static final String JSON_KEY_DIDYOUMEAN = "didYouMean"; 214 /** The JSON key for the subnode of all "Did you mean?" configuration. */ 215 /** A JSON key. */ 216 public static final String JSON_KEY_DIDYOUMEAN_QUERYPARAM = "didYouMeanQueryParam"; 217 /** A JSON key. */ 218 public static final String JSON_KEY_DIDYOUMEAN_ESCAPE_QUERY = "escapeDidYouMeanQuery"; 219 /** A JSON key. */ 220 public static final String JSON_KEY_DIDYOUMEAN_COLLATE = "didYouMeanCollate"; 221 /** A JSON key. */ 222 public static final String JSON_KEY_DIDYOUMEAN_COUNT = "didYouMeanCount"; 223 224 /** JSON keys for the Geo filter. */ 225 public static final String JSON_KEY_GEO_FILTER = "geofilter"; 226 /** A JSON key. */ 227 public static final String JSON_KEY_GEO_FILTER_COORDINATES = "coordinates"; 228 /** A JSON key. */ 229 public static final String JSON_KEY_GEO_FILTER_COORDINATES_PARAM = "coordinatesparam"; 230 /** A JSON key. */ 231 public static final String JSON_KEY_GEO_FILTER_FIELD_NAME = "fieldName"; 232 /** A JSON key. */ 233 public static final String JSON_KEY_GEO_FILTER_RADIUS = "radius"; 234 /** A JSON key. */ 235 public static final String JSON_KEY_GEO_FILTER_RADIUS_PARAM = "radiusparam"; 236 /** A JSON key. */ 237 public static final String JSON_KEY_GEO_FILTER_UNITS = "units"; 238 /** A JSON key. */ 239 public static final String JSON_KEY_GEO_FILTER_UNITS_PARAM = "unitsparam"; 240 241 /** The default values. */ 242 /** A JSON key. */ 243 public static final String DEFAULT_QUERY_PARAM = "q"; 244 /** A JSON key. */ 245 public static final String DEFAULT_LAST_QUERY_PARAM = "lq"; 246 /** A JSON key. */ 247 public static final String DEFAULT_RELOADED_PARAM = "reloaded"; 248 249 /** The whole JSON file. */ 250 protected JSONObject m_configObject; 251 252 /** The optional base configuration that should be changed by the JSON configuration. */ 253 private I_CmsSearchConfiguration m_baseConfig; 254 255 /** Constructor taking the JSON as JSONObject. 256 * @param jsonObject The JSON that should be evaluated as JSONObject. 257 */ 258 public CmsJSONSearchConfigurationParser(JSONObject jsonObject) { 259 260 m_configObject = jsonObject; 261 262 } 263 264 /** Constructor taking the JSON as String. 265 * @param json The JSON that should be parsed as String. 266 * @throws JSONException Thrown if parsing fails. 267 */ 268 public CmsJSONSearchConfigurationParser(String json) 269 throws JSONException { 270 271 init(json, null); 272 } 273 274 /** Constructor taking the JSON as String. 275 * @param json The JSON that should be parsed as String. 276 * @param baseConfig A base configuration that is adjusted by the JSON configuration string. 277 * @throws JSONException Thrown if parsing fails. 278 */ 279 public CmsJSONSearchConfigurationParser(String json, I_CmsSearchConfiguration baseConfig) 280 throws JSONException { 281 282 init(json, baseConfig); 283 } 284 285 /** Helper for reading a mandatory String value list - throwing an Exception if parsing fails. 286 * @param json The JSON object where the list should be read from. 287 * @param key The key of the value to read. 288 * @return The value from the JSON. 289 * @throws JSONException thrown when parsing fails. 290 */ 291 protected static List<String> parseMandatoryStringValues(JSONObject json, String key) throws JSONException { 292 293 List<String> list = null; 294 JSONArray array = json.getJSONArray(key); 295 list = new ArrayList<>(array.length()); 296 for (int i = 0; i < array.length(); i++) { 297 try { 298 String entry = array.getString(i); 299 list.add(entry); 300 } catch (JSONException e) { 301 LOG.info(Messages.get().getBundle().key(Messages.LOG_OPTIONAL_STRING_ENTRY_UNPARSABLE_1, key), e); 302 } 303 } 304 return list; 305 } 306 307 /** Helper for reading an optional JSON value of type String, boolean or number. The result is always returned as String. 308 * @param json The JSON object where the list should be read from. 309 * @param key The key of the value to read. 310 * @return The value from the JSON. 311 */ 312 protected static String parseOptionalAtomicValue(JSONObject json, String key) { 313 314 String result = parseOptionalStringValue(json, key); 315 if (null == result) { 316 Boolean b = parseOptionalBooleanValue(json, key); 317 if (null != b) { 318 result = b.booleanValue() ? "true" : "false"; 319 } 320 } 321 if (null == result) { 322 Integer i = parseOptionalIntValue(json, key); 323 if (null != i) { 324 result = String.valueOf(i); 325 } 326 } 327 if (null == result) { 328 Double d = parseOptionalDoubleValue(json, key); 329 if (null != d) { 330 result = String.valueOf(d); 331 } 332 } 333 return result; 334 } 335 336 /** Helper for reading an optional Boolean value - returning <code>null</code> if parsing fails. 337 * @param json The JSON object where the value should be read from. 338 * @param key The key of the value to read. 339 * @return The value from the JSON, or <code>null</code> if the value does not exist, or is no Boolean. 340 */ 341 protected static Boolean parseOptionalBooleanValue(JSONObject json, String key) { 342 343 try { 344 return Boolean.valueOf(json.getBoolean(key)); 345 } catch (JSONException e) { 346 LOG.info(Messages.get().getBundle().key(Messages.LOG_OPTIONAL_BOOLEAN_MISSING_1, key), e); 347 return null; 348 } 349 } 350 351 /** Helper for reading an optional Double value - returning <code>null</code> if parsing fails. 352 * @param json The JSON object where the value should be read from. 353 * @param key The key of the value to read. 354 * @return The value from the JSON, or <code>null</code> if the value does not exist, or is no Double. 355 */ 356 protected static Double parseOptionalDoubleValue(JSONObject json, String key) { 357 358 try { 359 return Double.valueOf(json.getDouble(key)); 360 } catch (JSONException e) { 361 LOG.info(Messages.get().getBundle().key(Messages.LOG_OPTIONAL_DOUBLE_MISSING_1, key), e); 362 return null; 363 } 364 } 365 366 /** Helper for reading an optional Integer value - returning <code>null</code> if parsing fails. 367 * @param json The JSON object where the value should be read from. 368 * @param key The key of the value to read. 369 * @return The value from the JSON, or <code>null</code> if the value does not exist, or is no Integer. 370 */ 371 protected static Integer parseOptionalIntValue(JSONObject json, String key) { 372 373 try { 374 return Integer.valueOf(json.getInt(key)); 375 } catch (JSONException e) { 376 LOG.info(Messages.get().getBundle().key(Messages.LOG_OPTIONAL_INTEGER_MISSING_1, key), e); 377 return null; 378 } 379 } 380 381 /** Helper for reading an optional JSON object - returning <code>null</code> if parsing fails. 382 * @param json The JSON object where the value should be read from. 383 * @param key The key of the JSON object to read. 384 * @return The value from the JSON, or <code>null</code> if the value does not exist, or is no JSON object. 385 */ 386 protected static JSONObject parseOptionalJSONObject(JSONObject json, String key) { 387 388 try { 389 return json.getJSONObject(key); 390 } catch (JSONException e) { 391 LOG.info(Messages.get().getBundle().key(Messages.LOG_OPTIONAL_JSON_OBJECT_MISSING_1, key), e); 392 return null; 393 } 394 } 395 396 /** Helper for reading an optional String value - returning <code>null</code> if parsing fails. 397 * @param json The JSON object where the value should be read from. 398 * @param key The key of the value to read. 399 * @return The value from the JSON, or <code>null</code> if the value does not exist. 400 */ 401 protected static String parseOptionalStringValue(JSONObject json, String key) { 402 403 try { 404 return json.getString(key); 405 } catch (JSONException e) { 406 LOG.info(Messages.get().getBundle().key(Messages.LOG_OPTIONAL_STRING_MISSING_1, key), e); 407 return null; 408 } 409 } 410 411 /** Helper for reading an optional String value list - returning <code>null</code> if parsing fails for the whole list, otherwise just skipping unparsable entries. 412 * @param json The JSON object where the list should be read from. 413 * @param key The key of the value to read. 414 * @return The value from the JSON, or <code>null</code> if the value does not exist. 415 */ 416 protected static List<String> parseOptionalStringValues(JSONObject json, String key) { 417 418 List<String> list = null; 419 try { 420 list = parseMandatoryStringValues(json, key); 421 } catch (JSONException e) { 422 LOG.info(Messages.get().getBundle().key(Messages.LOG_OPTIONAL_STRING_LIST_MISSING_1, key), e); 423 return null; 424 } 425 return list; 426 } 427 428 /** 429 * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseCommon(CmsObject) 430 */ 431 @Override 432 public I_CmsSearchConfigurationCommon parseCommon(CmsObject cms) { 433 434 String indexName = getIndex(cms); 435 436 return new CmsSearchConfigurationCommon( 437 getQueryParam(), 438 getLastQueryParam(), 439 getEscapeQueryChars(), 440 getFirstCallParam(), 441 getSearchForEmptyQuery(), 442 getIgnoreQuery(), 443 getQueryModifier(), 444 indexName, 445 getCore(), 446 getExtraSolrParams(), 447 getAdditionalParameters(), 448 getIgnoreReleaseDate(), 449 getIgnoreExpirationDate(), 450 getMaxReturnedResults(indexName)); 451 } 452 453 /** 454 * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseDidYouMean() 455 */ 456 @Override 457 public I_CmsSearchConfigurationDidYouMean parseDidYouMean() { 458 459 try { 460 JSONObject didYouMean = m_configObject.getJSONObject(JSON_KEY_DIDYOUMEAN); 461 String param = parseOptionalStringValue(didYouMean, JSON_KEY_DIDYOUMEAN_QUERYPARAM); 462 // default to the normal query param 463 if (null == param) { 464 param = getQueryParam(); 465 } 466 Boolean escape = parseOptionalBooleanValue(didYouMean, JSON_KEY_DIDYOUMEAN_ESCAPE_QUERY); 467 if (null == escape) { 468 escape = getEscapeQueryChars(); 469 } 470 Boolean collate = parseOptionalBooleanValue(didYouMean, JSON_KEY_DIDYOUMEAN_COLLATE); 471 Integer count = parseOptionalIntValue(didYouMean, JSON_KEY_DIDYOUMEAN_COUNT); 472 return new CmsSearchConfigurationDidYouMean(param, escape, collate, count); 473 474 } catch (JSONException e) { 475 if (null == m_baseConfig) { 476 if (LOG.isInfoEnabled()) { 477 LOG.info(Messages.get().getBundle().key(Messages.LOG_NO_HIGHLIGHTING_CONFIG_0), e); 478 } 479 return null; 480 } 481 return m_baseConfig.getDidYouMeanConfig(); 482 } 483 484 } 485 486 /** 487 * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseFieldFacets() 488 */ 489 @Override 490 public Map<String, I_CmsSearchConfigurationFacetField> parseFieldFacets() { 491 492 Map<String, I_CmsSearchConfigurationFacetField> facetConfigs = new LinkedHashMap<>(); 493 try { 494 JSONArray fieldFacets = m_configObject.getJSONArray(JSON_KEY_FIELD_FACETS); 495 for (int i = 0; i < fieldFacets.length(); i++) { 496 497 I_CmsSearchConfigurationFacetField config = parseFieldFacet(fieldFacets.getJSONObject(i)); 498 if (config != null) { 499 facetConfigs.put(config.getName(), config); 500 } 501 } 502 } catch (JSONException e) { 503 if (null == m_baseConfig) { 504 if (LOG.isInfoEnabled()) { 505 LOG.info(Messages.get().getBundle().key(Messages.LOG_NO_FACET_CONFIG_0), e); 506 } 507 } else { 508 facetConfigs = m_baseConfig.getFieldFacetConfigs(); 509 } 510 } 511 return facetConfigs; 512 } 513 514 /** 515 * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseGeoFilter() 516 */ 517 @Override 518 public I_CmsSearchConfigurationGeoFilter parseGeoFilter() { 519 520 try { 521 JSONObject geoFilter = m_configObject.getJSONObject(JSON_KEY_GEO_FILTER); 522 String coordinates = parseOptionalStringValue(geoFilter, JSON_KEY_GEO_FILTER_COORDINATES); 523 String coordinatesParam = parseOptionalStringValue(geoFilter, JSON_KEY_GEO_FILTER_COORDINATES_PARAM); 524 String fieldName = parseOptionalStringValue(geoFilter, JSON_KEY_GEO_FILTER_FIELD_NAME); 525 String radius = parseOptionalStringValue(geoFilter, JSON_KEY_GEO_FILTER_RADIUS); 526 String radiusParam = parseOptionalStringValue(geoFilter, JSON_KEY_GEO_FILTER_RADIUS_PARAM); 527 String units = parseOptionalStringValue(geoFilter, JSON_KEY_GEO_FILTER_UNITS); 528 String unitsParam = parseOptionalStringValue(geoFilter, JSON_KEY_GEO_FILTER_UNITS_PARAM); 529 return new CmsSearchConfigurationGeoFilter( 530 coordinates, 531 coordinatesParam, 532 fieldName, 533 radius, 534 radiusParam, 535 units, 536 unitsParam); 537 } catch (JSONException e) { 538 if (null == m_baseConfig) { 539 if (LOG.isInfoEnabled()) { 540 LOG.info(Messages.get().getBundle().key(Messages.LOG_NO_GEOFILTER_CONFIG_0), e); 541 } 542 return null; 543 } 544 return m_baseConfig.getGeoFilterConfig(); 545 } 546 } 547 548 /** 549 * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseHighlighter() 550 */ 551 @Override 552 public I_CmsSearchConfigurationHighlighting parseHighlighter() { 553 554 try { 555 Map<String, String> hlParams = new LinkedHashMap<>(); 556 JSONObject highlighter = m_configObject.getJSONObject(JSON_KEY_HIGHLIGHTER); 557 String field = highlighter.getString(JSON_KEY_HIGHLIGHTER_FIELD); 558 hlParams.put("fl", field); 559 Integer snippets = parseOptionalIntValue(highlighter, JSON_KEY_HIGHLIGHTER_SNIPPETS); 560 if (null != snippets) { 561 hlParams.put("snippets", snippets.toString()); 562 } 563 Integer fragsize = parseOptionalIntValue(highlighter, JSON_KEY_HIGHLIGHTER_FRAGSIZE); 564 if (null != fragsize) { 565 hlParams.put("fragsize", fragsize.toString()); 566 } 567 String alternateField = parseOptionalStringValue(highlighter, JSON_KEY_HIGHLIGHTER_ALTERNATE_FIELD); 568 if (null != alternateField) { 569 hlParams.put("alternateField", alternateField); 570 } 571 Integer maxAlternateFieldLength = parseOptionalIntValue( 572 highlighter, 573 JSON_KEY_HIGHLIGHTER_MAX_LENGTH_ALTERNATE_FIELD); 574 if (null != maxAlternateFieldLength) { 575 hlParams.put("maxAlternateFieldLength", maxAlternateFieldLength.toString()); 576 } 577 String pre = parseOptionalStringValue(highlighter, JSON_KEY_HIGHLIGHTER_SIMPLE_PRE); 578 if (null != pre) { 579 hlParams.put("simple.pre", pre); 580 hlParams.put("tag.pre", pre); 581 } 582 String post = parseOptionalStringValue(highlighter, JSON_KEY_HIGHLIGHTER_SIMPLE_POST); 583 if (null != post) { 584 hlParams.put("simple.post", post); 585 hlParams.put("tag.post", post); 586 } 587 String formatter = parseOptionalStringValue(highlighter, JSON_KEY_HIGHLIGHTER_FORMATTER); 588 if (null != formatter) { 589 hlParams.put("formatter", formatter); 590 } 591 String fragmenter = parseOptionalStringValue(highlighter, JSON_KEY_HIGHLIGHTER_FRAGMENTER); 592 if (null != fragmenter) { 593 hlParams.put("fragmenter", fragmenter); 594 } 595 Boolean useFastVectorHighlighting = parseOptionalBooleanValue( 596 highlighter, 597 JSON_KEY_HIGHLIGHTER_FASTVECTORHIGHLIGHTING); 598 if (null != useFastVectorHighlighting) { 599 hlParams.put("method", "fastVector"); 600 } 601 JSONObject params = parseOptionalJSONObject(highlighter, "params"); 602 if (null != params) { 603 Iterator<String> it = params.keys(); 604 while (it.hasNext()) { 605 String key = it.next(); 606 String value = parseOptionalAtomicValue(params, key); 607 if (value != null) { 608 hlParams.put(key, value); 609 } else { 610 LOG.warn("Invalid highlight option " + key + "=" + value + " will be ignored."); 611 } 612 } 613 } 614 return new CmsSearchConfigurationHighlighting(hlParams); 615 } catch (JSONException e) { 616 if (null == m_baseConfig) { 617 if (LOG.isInfoEnabled()) { 618 LOG.info(Messages.get().getBundle().key(Messages.LOG_NO_HIGHLIGHTING_CONFIG_0), e); 619 } 620 return null; 621 } 622 return m_baseConfig.getHighlighterConfig(); 623 } 624 } 625 626 /** 627 * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parsePagination() 628 */ 629 @Override 630 public I_CmsSearchConfigurationPagination parsePagination() { 631 632 return CmsSearchConfigurationPagination.create(getPageParam(), getPageSizes(), getPageNavLength()); 633 } 634 635 /** 636 * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseQueryFacet() 637 */ 638 @Override 639 public I_CmsSearchConfigurationFacetQuery parseQueryFacet() { 640 641 try { 642 JSONObject queryFacetObject = m_configObject.getJSONObject(JSON_KEY_QUERY_FACET); 643 try { 644 List<I_CmsFacetQueryItem> queries = parseFacetQueryItems(queryFacetObject); 645 String label = parseOptionalStringValue(queryFacetObject, JSON_KEY_FACET_LABEL); 646 Boolean isAndFacet = parseOptionalBooleanValue(queryFacetObject, JSON_KEY_FACET_ISANDFACET); 647 List<String> preselection = parseOptionalStringValues(queryFacetObject, JSON_KEY_FACET_PRESELECTION); 648 Boolean ignoreAllFacetFilters = parseOptionalBooleanValue( 649 queryFacetObject, 650 JSON_KEY_FACET_IGNOREALLFACETFILTERS); 651 List<String> excludeTags = parseOptionalStringValues(queryFacetObject, JSON_KEY_FACET_EXCLUDETAGS); 652 return new CmsSearchConfigurationFacetQuery( 653 queries, 654 label, 655 isAndFacet, 656 preselection, 657 ignoreAllFacetFilters, 658 excludeTags); 659 } catch (JSONException e) { 660 LOG.error( 661 Messages.get().getBundle().key( 662 Messages.ERR_QUERY_FACET_MANDATORY_KEY_MISSING_1, 663 JSON_KEY_QUERY_FACET_QUERY), 664 e); 665 return null; 666 } 667 } catch (JSONException e) { 668 // nothing to do, configuration is optional 669 return null != m_baseConfig ? m_baseConfig.getQueryFacetConfig() : null; 670 } 671 } 672 673 /** 674 * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseRangeFacets() 675 */ 676 @Override 677 public Map<String, I_CmsSearchConfigurationFacetRange> parseRangeFacets() { 678 679 Map<String, I_CmsSearchConfigurationFacetRange> facetConfigs = new LinkedHashMap<>(); 680 try { 681 JSONArray rangeFacets = m_configObject.getJSONArray(JSON_KEY_RANGE_FACETS); 682 for (int i = 0; i < rangeFacets.length(); i++) { 683 684 I_CmsSearchConfigurationFacetRange config = parseRangeFacet(rangeFacets.getJSONObject(i)); 685 if (config != null) { 686 facetConfigs.put(config.getName(), config); 687 } 688 } 689 } catch (JSONException e) { 690 if (null == m_baseConfig) { 691 if (LOG.isInfoEnabled()) { 692 LOG.info(Messages.get().getBundle().key(Messages.LOG_NO_FACET_CONFIG_0), e); 693 } 694 } else { 695 facetConfigs = m_baseConfig.getRangeFacetConfigs(); 696 } 697 } 698 return facetConfigs; 699 700 } 701 702 /** 703 * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseSorting() 704 */ 705 @Override 706 public I_CmsSearchConfigurationSorting parseSorting() { 707 708 List<I_CmsSearchConfigurationSortOption> options = getSortOptions(); 709 String defaultOptionParamValue = parseOptionalStringValue(m_configObject, JSON_KEY_DEFAULT_SORT_OPTION); 710 I_CmsSearchConfigurationSortOption defaultSortOption = null; 711 if (null != defaultOptionParamValue) { 712 Iterator<I_CmsSearchConfigurationSortOption> optIterator = options.iterator(); 713 while ((null == defaultSortOption) && optIterator.hasNext()) { 714 I_CmsSearchConfigurationSortOption opt = optIterator.next(); 715 if (Objects.equals(opt.getParamValue(), defaultOptionParamValue)) { 716 defaultSortOption = opt; 717 } 718 } 719 } 720 if ((null == defaultSortOption) && !options.isEmpty()) { 721 defaultSortOption = options.get(0); 722 } 723 return CmsSearchConfigurationSorting.create(getSortParam(), options, defaultSortOption); 724 } 725 726 /** Returns a map with additional request parameters, mapping the parameter names to Solr query parts. 727 * @return A map with additional request parameters, mapping the parameter names to Solr query parts. 728 */ 729 protected Map<String, String> getAdditionalParameters() { 730 731 Map<String, String> result; 732 try { 733 JSONArray additionalParams = m_configObject.getJSONArray(JSON_KEY_ADDITIONAL_PARAMETERS); 734 result = new HashMap<>(additionalParams.length()); 735 for (int i = 0; i < additionalParams.length(); i++) { 736 try { 737 JSONObject currentParam = additionalParams.getJSONObject(i); 738 String param = currentParam.getString(JSON_KEY_ADDITIONAL_PARAMETERS_PARAM); 739 String solrQuery = parseOptionalStringValue(currentParam, JSON_KEY_ADDITIONAL_PARAMETERS_SOLRQUERY); 740 result.put(param, solrQuery); 741 } catch (JSONException e) { 742 LOG.error(Messages.get().getBundle().key(Messages.ERR_ADDITIONAL_PARAMETER_CONFIG_WRONG_0), e); 743 continue; 744 } 745 } 746 } catch (JSONException e) { 747 LOG.info(Messages.get().getBundle().key(Messages.LOG_ADDITIONAL_PARAMETER_CONFIG_NOT_PARSED_0), e); 748 return null != m_baseConfig ? m_baseConfig.getGeneralConfig().getAdditionalParameters() : new HashMap<>(); 749 } 750 return result; 751 } 752 753 /** Returns the configured Solr core, or <code>null</code> if no core is configured. 754 * @return The configured Solr core, or <code>null</code> if no core is configured. 755 */ 756 protected String getCore() { 757 758 try { 759 return m_configObject.getString(JSON_KEY_CORE); 760 } catch (JSONException e) { 761 if (null == m_baseConfig) { 762 if (LOG.isInfoEnabled()) { 763 LOG.info(Messages.get().getBundle().key(Messages.LOG_NO_CORE_SPECIFIED_0), e); 764 } 765 return null; 766 } 767 return m_baseConfig.getGeneralConfig().getSolrCore(); 768 } 769 } 770 771 /** 772 * Returns the flag, indicating if the characters in the query string that are commands to Solr should be escaped. 773 * @return the flag, indicating if the characters in the query string that are commands to Solr should be escaped. 774 */ 775 protected Boolean getEscapeQueryChars() { 776 777 Boolean isEscape = parseOptionalBooleanValue(m_configObject, JSON_KEY_ESCAPE_QUERY_CHARACTERS); 778 return (null == isEscape) && (m_baseConfig != null) 779 ? Boolean.valueOf(m_baseConfig.getGeneralConfig().getEscapeQueryChars()) 780 : isEscape; 781 } 782 783 /** Returns the configured extra parameters that should be given to Solr, or the empty string if no parameters are configured. 784 * @return The configured extra parameters that should be given to Solr, or the empty string if no parameters are configured. 785 */ 786 protected String getExtraSolrParams() { 787 788 try { 789 return m_configObject.getString(JSON_KEY_EXTRASOLRPARAMS); 790 } catch (JSONException e) { 791 if (null == m_baseConfig) { 792 if (LOG.isInfoEnabled()) { 793 LOG.info(Messages.get().getBundle().key(Messages.LOG_NO_EXTRA_PARAMETERS_0), e); 794 } 795 return ""; 796 } 797 return m_baseConfig.getGeneralConfig().getExtraSolrParams(); 798 } 799 } 800 801 /** Returns the configured request parameter for the last query, or the default parameter if no core is configured. 802 * @return The configured request parameter for the last query, or the default parameter if no core is configured. 803 */ 804 protected String getFirstCallParam() { 805 806 String param = parseOptionalStringValue(m_configObject, JSON_KEY_RELOADED_PARAM); 807 if (param == null) { 808 return null != m_baseConfig ? m_baseConfig.getGeneralConfig().getReloadedParam() : DEFAULT_RELOADED_PARAM; 809 } 810 return param; 811 } 812 813 /** Returns a flag indicating if also expired resources should be found. 814 * @return A flag indicating if also expired resources should be found. 815 */ 816 protected Boolean getIgnoreExpirationDate() { 817 818 Boolean isIgnoreExpirationDate = parseOptionalBooleanValue(m_configObject, JSON_KEY_IGNORE_EXPIRATION_DATE); 819 return (null == isIgnoreExpirationDate) && (m_baseConfig != null) 820 ? Boolean.valueOf(m_baseConfig.getGeneralConfig().getIgnoreExpirationDate()) 821 : isIgnoreExpirationDate; 822 } 823 824 /** Returns a flag indicating if the query given by the parameters should be ignored. 825 * @return A flag indicating if the query given by the parameters should be ignored. 826 */ 827 protected Boolean getIgnoreQuery() { 828 829 Boolean isIgnoreQuery = parseOptionalBooleanValue(m_configObject, JSON_KEY_IGNORE_QUERY); 830 return (null == isIgnoreQuery) && (m_baseConfig != null) 831 ? Boolean.valueOf(m_baseConfig.getGeneralConfig().getIgnoreQueryParam()) 832 : isIgnoreQuery; 833 } 834 835 /** Returns a flag indicating if also unreleased resources should be found. 836 * @return A flag indicating if also unreleased resources should be found. 837 */ 838 protected Boolean getIgnoreReleaseDate() { 839 840 Boolean isIgnoreReleaseDate = parseOptionalBooleanValue(m_configObject, JSON_KEY_IGNORE_RELEASE_DATE); 841 return (null == isIgnoreReleaseDate) && (m_baseConfig != null) 842 ? Boolean.valueOf(m_baseConfig.getGeneralConfig().getIgnoreReleaseDate()) 843 : isIgnoreReleaseDate; 844 } 845 846 /** Returns the configured Solr index, or <code>null</code> if no core is configured. 847 * @param cms the current context. 848 * @return The configured Solr index, or <code>null</code> if no core is configured. 849 */ 850 protected String getIndex(CmsObject cms) { 851 852 String indexName = null; 853 try { 854 indexName = m_configObject.getString(JSON_KEY_INDEX); 855 } catch (JSONException e) { 856 if (null == m_baseConfig) { 857 if (LOG.isInfoEnabled()) { 858 LOG.info(Messages.get().getBundle().key(Messages.LOG_NO_INDEX_SPECIFIED_0), e); 859 } 860 } else { 861 indexName = m_baseConfig.getGeneralConfig().getSolrIndex(); 862 } 863 } 864 return null != indexName 865 ? indexName 866 : (cms.getRequestContext().getCurrentProject().isOnlineProject() 867 ? CmsSolrIndex.DEFAULT_INDEX_NAME_ONLINE 868 : CmsSolrIndex.DEFAULT_INDEX_NAME_OFFLINE); 869 } 870 871 /** Returns the configured request parameter for the last query, or the default parameter if no core is configured. 872 * @return The configured request parameter for the last query, or the default parameter if no core is configured. 873 */ 874 protected String getLastQueryParam() { 875 876 String param = parseOptionalStringValue(m_configObject, JSON_KEY_LAST_QUERYPARAM); 877 if (param == null) { 878 return null != m_baseConfig 879 ? m_baseConfig.getGeneralConfig().getLastQueryParam() 880 : DEFAULT_LAST_QUERY_PARAM; 881 } 882 return param; 883 } 884 885 /** Returns the number of maximally returned results, or <code>null</code> if the indexes default should be used. 886 * @param indexName the name of the index to search in. 887 * @return The number of maximally returned results, or <code>null</code> if the indexes default should be used. 888 */ 889 protected int getMaxReturnedResults(String indexName) { 890 891 Integer maxReturnedResults = parseOptionalIntValue(m_configObject, JSON_KEY_MAX_RETURNED_RESULTS); 892 if (null != maxReturnedResults) { 893 return maxReturnedResults.intValue(); 894 } else if (m_baseConfig != null) { 895 return m_baseConfig.getGeneralConfig().getMaxReturnedResults(); 896 } else { 897 try { 898 CmsSolrIndex idx = OpenCms.getSearchManager().getIndexSolr(indexName); 899 if (null != idx) { 900 return idx.getMaxProcessedResults(); 901 } 902 } catch (Throwable t) { 903 // This is ok, it's allowed to have an external other index here. 904 LOG.debug( 905 "Parsing JSON search configuration for none-CmsSolrIndex " 906 + indexName 907 + ". Setting max processed results to unlimited."); 908 } 909 return CmsSolrIndex.MAX_RESULTS_UNLIMITED; 910 } 911 } 912 913 /** Returns the configured length of the "Google"-like page navigation, or the default parameter if no core is configured. 914 * @return The configured length of the "Google"-like page navigation, or the default parameter if no core is configured. 915 */ 916 protected Integer getPageNavLength() { 917 918 return parseOptionalIntValue(m_configObject, JSON_KEY_PAGENAVLENGTH); 919 } 920 921 /** Returns the configured request parameter for the current page, or the default parameter if no core is configured. 922 * @return The configured request parameter for the current page, or the default parameter if no core is configured. 923 */ 924 protected String getPageParam() { 925 926 return parseOptionalStringValue(m_configObject, JSON_KEY_PAGEPARAM); 927 } 928 929 /** Returns the configured page sizes, or the default page size if no core is configured. 930 * @return The configured page sizes, or the default page size if no core is configured. 931 */ 932 protected List<Integer> getPageSizes() { 933 934 if (m_configObject.has(JSON_KEY_PAGESIZE)) { 935 try { 936 return Collections.singletonList(Integer.valueOf(m_configObject.getInt(JSON_KEY_PAGESIZE))); 937 } catch (JSONException e) { 938 List<Integer> result = null; 939 String pageSizesString = null; 940 try { 941 pageSizesString = m_configObject.getString(JSON_KEY_PAGESIZE); 942 String[] pageSizesArray = pageSizesString.split("-"); 943 if (pageSizesArray.length > 0) { 944 result = new ArrayList<>(pageSizesArray.length); 945 for (int i = 0; i < pageSizesArray.length; i++) { 946 result.add(Integer.valueOf(pageSizesArray[i])); 947 } 948 } 949 return result; 950 } catch (NumberFormatException | JSONException e1) { 951 LOG.warn( 952 Messages.get().getBundle().key(Messages.LOG_PARSING_PAGE_SIZES_FAILED_1, pageSizesString), 953 e); 954 } 955 } 956 } 957 if (null == m_baseConfig) { 958 if (LOG.isInfoEnabled()) { 959 LOG.info(Messages.get().getBundle().key(Messages.LOG_NO_PAGESIZE_SPECIFIED_0)); 960 } 961 return null; 962 } 963 return m_baseConfig.getPaginationConfig().getPageSizes(); 964 } 965 966 /** Returns the optional query modifier. 967 * @return the optional query modifier. 968 */ 969 protected String getQueryModifier() { 970 971 String queryModifier = parseOptionalStringValue(m_configObject, JSON_KEY_QUERY_MODIFIER); 972 return (null == queryModifier) && (null != m_baseConfig) 973 ? m_baseConfig.getGeneralConfig().getQueryModifier() 974 : queryModifier; 975 } 976 977 /** Returns the configured request parameter for the query string, or the default parameter if no core is configured. 978 * @return The configured request parameter for the query string, or the default parameter if no core is configured. 979 */ 980 protected String getQueryParam() { 981 982 String param = parseOptionalStringValue(m_configObject, JSON_KEY_QUERYPARAM); 983 if (param == null) { 984 return null != m_baseConfig ? m_baseConfig.getGeneralConfig().getQueryParam() : DEFAULT_QUERY_PARAM; 985 } 986 return param; 987 } 988 989 /** Returns a flag, indicating if search should be performed using a wildcard if the empty query is given. 990 * @return A flag, indicating if search should be performed using a wildcard if the empty query is given. 991 */ 992 protected Boolean getSearchForEmptyQuery() { 993 994 Boolean isSearchForEmptyQuery = parseOptionalBooleanValue(m_configObject, JSON_KEY_SEARCH_FOR_EMPTY_QUERY); 995 return (isSearchForEmptyQuery == null) && (null != m_baseConfig) 996 ? Boolean.valueOf(m_baseConfig.getGeneralConfig().getSearchForEmptyQueryParam()) 997 : isSearchForEmptyQuery; 998 } 999 1000 /** Returns the list of the configured sort options, or the empty list if no sort options are configured. 1001 * @return The list of the configured sort options, or the empty list if no sort options are configured. 1002 */ 1003 protected List<I_CmsSearchConfigurationSortOption> getSortOptions() { 1004 1005 List<I_CmsSearchConfigurationSortOption> options = new LinkedList<>(); 1006 try { 1007 JSONArray sortOptions = m_configObject.getJSONArray(JSON_KEY_SORTOPTIONS); 1008 for (int i = 0; i < sortOptions.length(); i++) { 1009 I_CmsSearchConfigurationSortOption option = parseSortOption(sortOptions.getJSONObject(i)); 1010 if (option != null) { 1011 options.add(option); 1012 } 1013 } 1014 } catch (JSONException e) { 1015 if (null == m_baseConfig) { 1016 if (LOG.isInfoEnabled()) { 1017 LOG.info(Messages.get().getBundle().key(Messages.LOG_NO_SORT_CONFIG_0), e); 1018 } 1019 } else { 1020 options = m_baseConfig.getSortConfig().getSortOptions(); 1021 } 1022 } 1023 return options; 1024 } 1025 1026 /** Returns the configured request parameter for the sort option, or the default parameter if no core is configured. 1027 * @return The configured request parameter for the sort option, or the default parameter if no core is configured. 1028 */ 1029 protected String getSortParam() { 1030 1031 return parseOptionalStringValue(m_configObject, JSON_KEY_SORTPARAM); 1032 } 1033 1034 /** Initialization that parses the String to a JSON object. 1035 * @param configString The JSON as string. 1036 * @param baseConfig The optional basic search configuration to overwrite (partly) by the JSON configuration. 1037 * @throws JSONException thrown if parsing fails. 1038 */ 1039 protected void init(String configString, I_CmsSearchConfiguration baseConfig) throws JSONException { 1040 1041 m_configObject = new JSONObject(configString); 1042 m_baseConfig = baseConfig; 1043 } 1044 1045 /** Parses a single query item for the query facet. 1046 * @param item JSON object of the query item. 1047 * @return the parsed query item, or <code>null</code> if parsing failed. 1048 */ 1049 protected I_CmsFacetQueryItem parseFacetQueryItem(JSONObject item) { 1050 1051 String query; 1052 try { 1053 query = item.getString(JSON_KEY_QUERY_FACET_QUERY_QUERY); 1054 } catch (JSONException e) { 1055 // TODO: Log 1056 return null; 1057 } 1058 String label = parseOptionalStringValue(item, JSON_KEY_QUERY_FACET_QUERY_LABEL); 1059 return new CmsFacetQueryItem(query, label); 1060 } 1061 1062 /** Parses the list of query items for the query facet. 1063 * @param queryFacetObject JSON object representing the node with the query facet. 1064 * @return list of query options 1065 * @throws JSONException if the list cannot be parsed. 1066 */ 1067 protected List<I_CmsFacetQueryItem> parseFacetQueryItems(JSONObject queryFacetObject) throws JSONException { 1068 1069 JSONArray items = queryFacetObject.getJSONArray(JSON_KEY_QUERY_FACET_QUERY); 1070 List<I_CmsFacetQueryItem> result = new ArrayList<>(items.length()); 1071 for (int i = 0; i < items.length(); i++) { 1072 I_CmsFacetQueryItem item = parseFacetQueryItem(items.getJSONObject(i)); 1073 if (item != null) { 1074 result.add(item); 1075 } 1076 } 1077 return result; 1078 } 1079 1080 /** Parses the field facet configurations. 1081 * @param fieldFacetObject The JSON sub-node with the field facet configurations. 1082 * @return The field facet configurations. 1083 */ 1084 protected I_CmsSearchConfigurationFacetField parseFieldFacet(JSONObject fieldFacetObject) { 1085 1086 try { 1087 String field = fieldFacetObject.getString(JSON_KEY_FACET_FIELD); 1088 String name = parseOptionalStringValue(fieldFacetObject, JSON_KEY_FACET_NAME); 1089 String label = parseOptionalStringValue(fieldFacetObject, JSON_KEY_FACET_LABEL); 1090 Integer minCount = parseOptionalIntValue(fieldFacetObject, JSON_KEY_FACET_MINCOUNT); 1091 Integer limit = parseOptionalIntValue(fieldFacetObject, JSON_KEY_FACET_LIMIT); 1092 String prefix = parseOptionalStringValue(fieldFacetObject, JSON_KEY_FACET_PREFIX); 1093 String sorder = parseOptionalStringValue(fieldFacetObject, JSON_KEY_FACET_ORDER); 1094 I_CmsSearchConfigurationFacet.SortOrder order; 1095 try { 1096 order = I_CmsSearchConfigurationFacet.SortOrder.valueOf(sorder); 1097 } catch (Exception e) { 1098 order = null; 1099 } 1100 String filterQueryModifier = parseOptionalStringValue(fieldFacetObject, JSON_KEY_FACET_FILTERQUERYMODIFIER); 1101 Boolean isAndFacet = parseOptionalBooleanValue(fieldFacetObject, JSON_KEY_FACET_ISANDFACET); 1102 List<String> preselection = parseOptionalStringValues(fieldFacetObject, JSON_KEY_FACET_PRESELECTION); 1103 Boolean ignoreFilterAllFacetFilters = parseOptionalBooleanValue( 1104 fieldFacetObject, 1105 JSON_KEY_FACET_IGNOREALLFACETFILTERS); 1106 List<String> excludeTags = parseOptionalStringValues(fieldFacetObject, JSON_KEY_FACET_EXCLUDETAGS); 1107 return new CmsSearchConfigurationFacetField( 1108 field, 1109 name, 1110 minCount, 1111 limit, 1112 prefix, 1113 label, 1114 order, 1115 filterQueryModifier, 1116 isAndFacet, 1117 preselection, 1118 ignoreFilterAllFacetFilters, 1119 excludeTags); 1120 } catch (JSONException e) { 1121 LOG.error( 1122 Messages.get().getBundle().key(Messages.ERR_FIELD_FACET_MANDATORY_KEY_MISSING_1, JSON_KEY_FACET_FIELD), 1123 e); 1124 return null; 1125 } 1126 } 1127 1128 /** Parses the query facet configurations. 1129 * @param rangeFacetObject The JSON sub-node with the query facet configurations. 1130 * @return The query facet configurations. 1131 */ 1132 protected I_CmsSearchConfigurationFacetRange parseRangeFacet(JSONObject rangeFacetObject) { 1133 1134 try { 1135 String range = rangeFacetObject.getString(JSON_KEY_RANGE_FACET_RANGE); 1136 String name = parseOptionalStringValue(rangeFacetObject, JSON_KEY_FACET_NAME); 1137 String label = parseOptionalStringValue(rangeFacetObject, JSON_KEY_FACET_LABEL); 1138 Integer minCount = parseOptionalIntValue(rangeFacetObject, JSON_KEY_FACET_MINCOUNT); 1139 String start = rangeFacetObject.getString(JSON_KEY_RANGE_FACET_START); 1140 String end = rangeFacetObject.getString(JSON_KEY_RANGE_FACET_END); 1141 String gap = rangeFacetObject.getString(JSON_KEY_RANGE_FACET_GAP); 1142 List<String> sother = parseOptionalStringValues(rangeFacetObject, JSON_KEY_RANGE_FACET_OTHER); 1143 Boolean hardEnd = parseOptionalBooleanValue(rangeFacetObject, JSON_KEY_RANGE_FACET_HARDEND); 1144 I_CmsSearchConfigurationFacetRange.Method method = null; 1145 String methodStr = parseOptionalStringValue(rangeFacetObject, JSON_KEY_RANGE_FACET_METHOD); 1146 if (null != methodStr) { 1147 try { 1148 method = I_CmsSearchConfigurationFacetRange.Method.valueOf(methodStr); 1149 } catch (Exception e) { 1150 LOG.error(Messages.get().getBundle().key(Messages.ERR_INVALID_RANGE_METHOD_OPTION_1, methodStr), e); 1151 } 1152 } 1153 List<I_CmsSearchConfigurationFacetRange.Other> other = null; 1154 if (sother != null) { 1155 other = new ArrayList<>(sother.size()); 1156 for (String so : sother) { 1157 try { 1158 I_CmsSearchConfigurationFacetRange.Other o = I_CmsSearchConfigurationFacetRange.Other.valueOf( 1159 so); 1160 other.add(o); 1161 } catch (Exception e) { 1162 LOG.error(Messages.get().getBundle().key(Messages.ERR_INVALID_OTHER_OPTION_1, so), e); 1163 } 1164 } 1165 } 1166 Boolean isAndFacet = parseOptionalBooleanValue(rangeFacetObject, JSON_KEY_FACET_ISANDFACET); 1167 List<String> preselection = parseOptionalStringValues(rangeFacetObject, JSON_KEY_FACET_PRESELECTION); 1168 Boolean ignoreAllFacetFilters = parseOptionalBooleanValue( 1169 rangeFacetObject, 1170 JSON_KEY_FACET_IGNOREALLFACETFILTERS); 1171 List<String> excludeTags = parseOptionalStringValues(rangeFacetObject, JSON_KEY_FACET_EXCLUDETAGS); 1172 return new CmsSearchConfigurationFacetRange( 1173 range, 1174 start, 1175 end, 1176 gap, 1177 other, 1178 hardEnd, 1179 method, 1180 name, 1181 minCount, 1182 label, 1183 isAndFacet, 1184 preselection, 1185 ignoreAllFacetFilters, 1186 excludeTags); 1187 } catch (JSONException e) { 1188 LOG.error( 1189 Messages.get().getBundle().key( 1190 Messages.ERR_RANGE_FACET_MANDATORY_KEY_MISSING_1, 1191 JSON_KEY_RANGE_FACET_RANGE 1192 + ", " 1193 + JSON_KEY_RANGE_FACET_START 1194 + ", " 1195 + JSON_KEY_RANGE_FACET_END 1196 + ", " 1197 + JSON_KEY_RANGE_FACET_GAP), 1198 e); 1199 return null; 1200 } 1201 1202 } 1203 1204 /** Returns a single sort option configuration as configured via the methods parameter, or null if the parameter does not specify a sort option. 1205 * @param json The JSON sort option configuration. 1206 * @return The sort option configuration, or null if the JSON could not be read. 1207 */ 1208 protected I_CmsSearchConfigurationSortOption parseSortOption(JSONObject json) { 1209 1210 try { 1211 String solrValue = json.getString(JSON_KEY_SORTOPTION_SOLRVALUE); 1212 String paramValue = parseOptionalStringValue(json, JSON_KEY_SORTOPTION_PARAMVALUE); 1213 paramValue = (paramValue == null) ? solrValue : paramValue; 1214 String label = parseOptionalStringValue(json, JSON_KEY_SORTOPTION_LABEL); 1215 label = (label == null) ? paramValue : label; 1216 return new CmsSearchConfigurationSortOption(label, paramValue, solrValue); 1217 } catch (JSONException e) { 1218 LOG.error( 1219 Messages.get().getBundle().key(Messages.ERR_SORT_OPTION_NOT_PARSABLE_1, JSON_KEY_SORTOPTION_SOLRVALUE), 1220 e); 1221 return null; 1222 } 1223 } 1224}