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.file.CmsPropertyDefinition; 032import org.opencms.i18n.CmsEncoder; 033import org.opencms.json.JSONException; 034import org.opencms.jsp.search.config.CmsSearchConfigurationFacetField; 035import org.opencms.jsp.search.config.CmsSearchConfigurationFacetRange; 036import org.opencms.jsp.search.config.CmsSearchConfigurationSortOption; 037import org.opencms.jsp.search.config.I_CmsSearchConfigurationFacet.SortOrder; 038import org.opencms.jsp.search.config.I_CmsSearchConfigurationFacetField; 039import org.opencms.jsp.search.config.I_CmsSearchConfigurationFacetRange; 040import org.opencms.jsp.search.config.I_CmsSearchConfigurationPagination; 041import org.opencms.jsp.search.config.I_CmsSearchConfigurationSortOption; 042import org.opencms.jsp.search.config.parser.simplesearch.CmsCategoryFolderRestrictionBean; 043import org.opencms.jsp.search.config.parser.simplesearch.CmsConfigurationBean; 044import org.opencms.jsp.search.config.parser.simplesearch.CmsConfigurationBean.CombinationMode; 045import org.opencms.jsp.search.config.parser.simplesearch.CmsGeoFilterBean; 046import org.opencms.jsp.search.config.parser.simplesearch.daterestrictions.I_CmsDateRestriction; 047import org.opencms.jsp.search.config.parser.simplesearch.preconfiguredrestrictions.CmsRestrictionRule; 048import org.opencms.jsp.search.config.parser.simplesearch.preconfiguredrestrictions.CmsRestrictionsBean; 049import org.opencms.jsp.search.config.parser.simplesearch.preconfiguredrestrictions.CmsRestrictionsBean.FieldValues; 050import org.opencms.jsp.search.config.parser.simplesearch.preconfiguredrestrictions.CmsRestrictionsBean.FieldValues.FieldType; 051import org.opencms.main.CmsException; 052import org.opencms.relations.CmsCategoryService; 053import org.opencms.search.fields.CmsSearchField; 054import org.opencms.search.solr.CmsSolrQuery; 055import org.opencms.search.solr.CmsSolrQueryUtil; 056import org.opencms.util.CmsStringUtil; 057import org.opencms.util.CmsUUID; 058 059import java.util.Collection; 060import java.util.Collections; 061import java.util.HashMap; 062import java.util.HashSet; 063import java.util.LinkedHashMap; 064import java.util.LinkedList; 065import java.util.List; 066import java.util.Locale; 067import java.util.Map; 068import java.util.Objects; 069import java.util.stream.Collectors; 070 071import org.apache.solr.common.params.CommonParams; 072 073import com.google.common.collect.Lists; 074 075/** 076 * Search configuration parser using a list configuration file as the base configuration with additional JSON.<p> 077 */ 078public class CmsSimpleSearchConfigurationParser extends CmsJSONSearchConfigurationParser { 079 080 /** Sort options that are available by default. */ 081 public static enum SortOption { 082 083 /** Sort by date ascending. */ 084 DATE_ASC, 085 /** Sort by date descending. */ 086 DATE_DESC, 087 /** Sort by title ascending. */ 088 TITLE_ASC, 089 /** Sort by title descending. */ 090 TITLE_DESC, 091 /** Sort by order ascending. */ 092 ORDER_ASC, 093 /** Sort by order descending. */ 094 ORDER_DESC; 095 096 /** 097 * Generates the suitable {@link I_CmsSearchConfigurationSortOption} for the option. 098 * @param l the locale for which the option should be created 099 * @return the created {@link I_CmsSearchConfigurationSortOption} 100 */ 101 public I_CmsSearchConfigurationSortOption getOption(Locale l) { 102 103 switch (this) { 104 case DATE_ASC: 105 return new CmsSearchConfigurationSortOption("date.asc", "date_asc", getSortDateField(l) + " asc"); 106 case DATE_DESC: 107 return new CmsSearchConfigurationSortOption( 108 "date.desc", 109 "date_desc", 110 getSortDateField(l) + " desc"); 111 case TITLE_ASC: 112 return new CmsSearchConfigurationSortOption( 113 "title.asc", 114 "title_asc", 115 getSortTitleField(l) + " asc"); 116 case TITLE_DESC: 117 return new CmsSearchConfigurationSortOption( 118 "title.desc", 119 "title_desc", 120 getSortTitleField(l) + " desc"); 121 case ORDER_ASC: 122 return new CmsSearchConfigurationSortOption( 123 "order.asc", 124 "order_asc", 125 getSortOrderField(l) + " asc"); 126 case ORDER_DESC: 127 return new CmsSearchConfigurationSortOption( 128 "order.desc", 129 "order_desc", 130 getSortOrderField(l) + " desc"); 131 default: 132 throw new IllegalArgumentException(); 133 } 134 } 135 136 /** 137 * Returns the locale specific date field to use for sorting. 138 * @param l the locale to use, can be <code>null</code> 139 * @return the locale specific date field to use for sorting. 140 */ 141 protected String getSortDateField(Locale l) { 142 143 return CmsSearchField.FIELD_INSTANCEDATE 144 + (null != l ? "_" + l.toString() : "") 145 + CmsSearchField.FIELD_POSTFIX_DATE; 146 } 147 148 /** 149 * Returns the locale specific order field to use for sorting. 150 * @param l the locale to use, can be <code>null</code> 151 * @return the locale specific order field to use for sorting. 152 */ 153 protected String getSortOrderField(Locale l) { 154 155 return CmsSearchField.FIELD_DISPORDER 156 + (null != l ? "_" + l.toString() : "") 157 + CmsSearchField.FIELD_POSTFIX_INT; 158 } 159 160 /** 161 * Returns the locale specific title field to use for sorting. 162 * @param l the locale to use, can be <code>null</code> 163 * @return the locale specific title field to use for sorting. 164 */ 165 protected String getSortTitleField(Locale l) { 166 167 return CmsSearchField.FIELD_DISPTITLE 168 + (null != l ? "_" + l.toString() : "") 169 + CmsSearchField.FIELD_POSTFIX_SORT; 170 } 171 } 172 173 /** SOLR field name. */ 174 public static final String FIELD_CATEGORIES = "category_exact"; 175 176 /** SOLR field name. */ 177 public static final String FIELD_DATE = "instancedate_%s_dt"; 178 179 /** SOLR field name. */ 180 public static final String FIELD_DATE_RANGE = "instancedaterange_%s_dr"; 181 182 /** SOLR field name. */ 183 public static final String FIELD_DATE_FACET_NAME = "instancedate"; 184 185 /** SOLR field name. */ 186 public static final String FIELD_PARENT_FOLDERS = "parent-folders"; 187 188 /** Pagination which may override the default pagination. */ 189 private I_CmsSearchConfigurationPagination m_pagination; 190 191 /** The current cms context. */ 192 private CmsObject m_cms; 193 194 /** The list configuration bean. */ 195 private CmsConfigurationBean m_config; 196 197 /** The (mutable) search locale. */ 198 private Locale m_searchLocale; 199 200 /** The (mutable) sort order. */ 201 private CmsSimpleSearchConfigurationParser.SortOption m_sortOrder; 202 203 /** Flag which, if true, causes the search to ignore the blacklist. */ 204 private boolean m_ignoreBlacklist; 205 206 /** 207 * Constructor.<p> 208 * 209 * @param cms the cms context 210 * @param config the list configuration 211 * @param additionalParamJSON the additional JSON configuration 212 * 213 * @throws JSONException in case parsing the JSON fails 214 */ 215 public CmsSimpleSearchConfigurationParser(CmsObject cms, CmsConfigurationBean config, String additionalParamJSON) 216 throws JSONException { 217 218 super(CmsStringUtil.isEmptyOrWhitespaceOnly(additionalParamJSON) ? "{}" : additionalParamJSON); 219 m_cms = cms; 220 m_config = config; 221 } 222 223 /** 224 * Creates an instance for an empty JSON configuration.<p> 225 * 226 * The point of this is that we know that passing an empty configuration makes it impossible 227 * for a JSONException to thrown. 228 * 229 * @param cms the current CMS context 230 * @param config the search configuration 231 * 232 * @return the search config parser 233 */ 234 public static CmsSimpleSearchConfigurationParser createInstanceWithNoJsonConfig( 235 CmsObject cms, 236 CmsConfigurationBean config) { 237 238 try { 239 return new CmsSimpleSearchConfigurationParser(cms, config, null); 240 241 } catch (JSONException e) { 242 return null; 243 } 244 } 245 246 /** The default field facets. 247 * 248 * @param categoryConjunction flag, indicating if category selections in the facet should be "AND" combined. 249 * @return the default field facets. 250 */ 251 public static Map<String, I_CmsSearchConfigurationFacetField> getDefaultFieldFacets(boolean categoryConjunction) { 252 253 Map<String, I_CmsSearchConfigurationFacetField> fieldFacets = new LinkedHashMap<>(); 254 fieldFacets.put( 255 FIELD_CATEGORIES, 256 new CmsSearchConfigurationFacetField( 257 FIELD_CATEGORIES, 258 null, 259 Integer.valueOf(1), 260 Integer.valueOf(200), 261 null, 262 "Category", 263 SortOrder.index, 264 null, 265 Boolean.valueOf(categoryConjunction), 266 null, 267 Boolean.TRUE, 268 null)); 269 fieldFacets.put( 270 FIELD_PARENT_FOLDERS, 271 new CmsSearchConfigurationFacetField( 272 FIELD_PARENT_FOLDERS, 273 null, 274 Integer.valueOf(1), 275 Integer.valueOf(200), 276 null, 277 "Folders", 278 SortOrder.index, 279 null, 280 Boolean.FALSE, 281 null, 282 Boolean.TRUE, 283 null)); 284 return Collections.unmodifiableMap(fieldFacets); 285 286 } 287 288 /** 289 * Returns the initial SOLR query.<p> 290 * 291 * @return the SOLR query 292 */ 293 public CmsSolrQuery getInitialQuery() { 294 295 Map<String, String[]> queryParams = new HashMap<>(); 296 if (!m_cms.getRequestContext().getCurrentProject().isOnlineProject() && m_config.isShowExpired()) { 297 queryParams.put("fq", new String[] {"released:[* TO *]", "expired:[* TO *]"}); 298 } 299 return new CmsSolrQuery(null, queryParams); 300 } 301 302 /** 303 * Gets the search locale.<p> 304 * 305 * @return the search locale 306 */ 307 public Locale getSearchLocale() { 308 309 if (m_searchLocale != null) { 310 return m_searchLocale; 311 } 312 return m_cms.getRequestContext().getLocale(); 313 } 314 315 /** 316 * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseFieldFacets() 317 */ 318 @Override 319 public Map<String, I_CmsSearchConfigurationFacetField> parseFieldFacets() { 320 321 if (m_configObject.has(JSON_KEY_FIELD_FACETS)) { 322 return super.parseFieldFacets(); 323 } 324 return getDefaultFieldFacets(true); 325 } 326 327 /** 328 * @see org.opencms.jsp.search.config.parser.CmsJSONSearchConfigurationParser#parsePagination() 329 */ 330 @Override 331 public I_CmsSearchConfigurationPagination parsePagination() { 332 333 if (m_pagination != null) { 334 return m_pagination; 335 } 336 return super.parsePagination(); 337 } 338 339 /** 340 * @see org.opencms.jsp.search.config.parser.I_CmsSearchConfigurationParser#parseRangeFacets() 341 */ 342 @Override 343 public Map<String, I_CmsSearchConfigurationFacetRange> parseRangeFacets() { 344 345 if (m_configObject.has(JSON_KEY_RANGE_FACETS)) { 346 return super.parseRangeFacets(); 347 } 348 Map<String, I_CmsSearchConfigurationFacetRange> rangeFacets = new LinkedHashMap<>(); 349 String indexField = FIELD_DATE; 350 I_CmsSearchConfigurationFacetRange.Method method = I_CmsSearchConfigurationFacetRange.Method.dv; 351 if (Boolean.parseBoolean(m_config.getParameterValue(CmsConfigurationBean.PARAM_FILTER_MULTI_DAY))) { 352 indexField = FIELD_DATE_RANGE; 353 method = null; 354 } 355 I_CmsSearchConfigurationFacetRange rangeFacet = new CmsSearchConfigurationFacetRange( 356 String.format(indexField, getSearchLocale().toString()), 357 "NOW/YEAR-20YEARS", 358 "NOW/MONTH+5YEARS", 359 "+1MONTHS", 360 null, 361 Boolean.FALSE, 362 method, 363 FIELD_DATE_FACET_NAME, 364 Integer.valueOf(1), 365 "Date", 366 Boolean.FALSE, 367 null, 368 Boolean.TRUE, 369 null); 370 371 rangeFacets.put(rangeFacet.getName(), rangeFacet); 372 return rangeFacets; 373 } 374 375 /** 376 * Sets the 'ignore blacklist' flag.<p> 377 * 378 * If set, the search will ignore the blacklist from the list configuration.<p> 379 * 380 * @param ignoreBlacklist true if the blacklist should be ignored 381 */ 382 public void setIgnoreBlacklist(boolean ignoreBlacklist) { 383 384 m_ignoreBlacklist = ignoreBlacklist; 385 } 386 387 /** 388 * Sets the pagination.<p> 389 * 390 * If this is set, parsePagination will always return the set value instead of using the default way to compute the pagination 391 * 392 * @param pagination the pagination 393 */ 394 public void setPagination(I_CmsSearchConfigurationPagination pagination) { 395 396 m_pagination = pagination; 397 } 398 399 /** 400 * Sets the search locale.<p> 401 * 402 * @param locale the search locale 403 */ 404 public void setSearchLocale(Locale locale) { 405 406 m_searchLocale = locale; 407 } 408 409 /** 410 * Sets the sort option.<p> 411 * 412 * @param sortOption the sort option 413 */ 414 public void setSortOption(String sortOption) { 415 416 if (null != sortOption) { 417 try { 418 m_sortOrder = CmsSimpleSearchConfigurationParser.SortOption.valueOf(sortOption); 419 } catch (IllegalArgumentException e) { 420 m_sortOrder = null; 421 LOG.warn( 422 "Setting illegal default sort option " + sortOption + " failed. Using Solr's default sort option."); 423 } 424 } else { 425 m_sortOrder = null; 426 } 427 } 428 429 /** 430 * @see org.opencms.jsp.search.config.parser.CmsJSONSearchConfigurationParser#getEscapeQueryChars() 431 */ 432 @Override 433 protected Boolean getEscapeQueryChars() { 434 435 if (m_configObject.has(JSON_KEY_ESCAPE_QUERY_CHARACTERS)) { 436 return super.getEscapeQueryChars(); 437 } 438 return Boolean.TRUE; 439 } 440 441 /** 442 * @see org.opencms.jsp.search.config.parser.CmsJSONSearchConfigurationParser#getExtraSolrParams() 443 */ 444 @Override 445 protected String getExtraSolrParams() { 446 447 String params = super.getExtraSolrParams(); 448 if (CmsStringUtil.isEmptyOrWhitespaceOnly(params)) { 449 params = getCategoryFolderFilter() 450 + getResourceTypeFilter() 451 + getPreconfiguredFilterQuery() 452 + getFilterQuery() 453 + getBlacklistFilter() 454 + getGeoFilterQuery(); 455 } 456 return params; 457 } 458 459 /** 460 * @see org.opencms.jsp.search.config.parser.CmsJSONSearchConfigurationParser#getIgnoreExpirationDate() 461 */ 462 @Override 463 protected Boolean getIgnoreExpirationDate() { 464 465 return getIgnoreReleaseAndExpiration(); 466 467 } 468 469 /** 470 * @see org.opencms.jsp.search.config.parser.CmsJSONSearchConfigurationParser#getIgnoreReleaseDate() 471 */ 472 @Override 473 protected Boolean getIgnoreReleaseDate() { 474 475 return getIgnoreReleaseAndExpiration(); 476 } 477 478 /** 479 * @see org.opencms.jsp.search.config.parser.CmsJSONSearchConfigurationParser#getMaxReturnedResults(java.lang.String) 480 */ 481 @Override 482 protected int getMaxReturnedResults(String indexName) { 483 484 return null != m_config.getMaximallyReturnedResults() 485 ? m_config.getMaximallyReturnedResults().intValue() 486 : super.getMaxReturnedResults(indexName); 487 } 488 489 /** 490 * @see org.opencms.jsp.search.config.parser.CmsJSONSearchConfigurationParser#getQueryModifier() 491 */ 492 @Override 493 protected String getQueryModifier() { 494 495 String modifier = super.getQueryModifier(); 496 if (CmsStringUtil.isEmptyOrWhitespaceOnly(modifier)) { 497 modifier = "{!type=edismax qf=\"" 498 + CmsSearchField.FIELD_CONTENT 499 + "_" 500 + getSearchLocale().toString() 501 + " " 502 + CmsPropertyDefinition.PROPERTY_TITLE 503 + CmsSearchField.FIELD_DYNAMIC_PROPERTIES 504 + " " 505 + CmsPropertyDefinition.PROPERTY_DESCRIPTION 506 + CmsSearchField.FIELD_DYNAMIC_PROPERTIES_DIRECT 507 + " " 508 + CmsPropertyDefinition.PROPERTY_DESCRIPTION_HTML 509 + CmsSearchField.FIELD_DYNAMIC_PROPERTIES_DIRECT 510 + " " 511 + CmsSearchField.FIELD_DESCRIPTION 512 + "_" 513 + getSearchLocale().toString() 514 + " " 515 + CmsSearchField.FIELD_KEYWORDS 516 + "_" 517 + getSearchLocale().toString() 518 + "\"}%(query)"; 519 } 520 return modifier; 521 } 522 523 /** 524 * @see org.opencms.jsp.search.config.parser.CmsJSONSearchConfigurationParser#getSearchForEmptyQuery() 525 */ 526 @Override 527 protected Boolean getSearchForEmptyQuery() { 528 529 if (m_configObject.has(JSON_KEY_SEARCH_FOR_EMPTY_QUERY)) { 530 return super.getSearchForEmptyQuery(); 531 } 532 return Boolean.TRUE; 533 } 534 535 /** 536 * @see org.opencms.jsp.search.config.parser.CmsJSONSearchConfigurationParser#getSortOptions() 537 */ 538 @Override 539 protected List<I_CmsSearchConfigurationSortOption> getSortOptions() { 540 541 if (m_configObject.has(JSON_KEY_SORTOPTIONS)) { 542 return super.getSortOptions(); 543 } 544 List<I_CmsSearchConfigurationSortOption> options = new LinkedList<>(); 545 546 CmsSimpleSearchConfigurationParser.SortOption currentOption = CmsSimpleSearchConfigurationParser.SortOption.valueOf( 547 m_config.getSortOrder()); 548 if (m_sortOrder != null) { 549 currentOption = m_sortOrder; 550 } 551 Locale locale = getSearchLocale(); 552 options.add(currentOption.getOption(locale)); 553 CmsSimpleSearchConfigurationParser.SortOption[] sortOptions = CmsSimpleSearchConfigurationParser.SortOption.values(); 554 for (int i = 0; i < sortOptions.length; i++) { 555 CmsSimpleSearchConfigurationParser.SortOption option = sortOptions[i]; 556 if (!Objects.equals(currentOption, option)) { 557 options.add(option.getOption(locale)); 558 } 559 } 560 return options; 561 } 562 563 /** 564 * Generates the query part for the preconfigured restrictions for the type. 565 * @param type the type to generate the restriction for. 566 * @param restrictionsForType the preconfigured restrictions for the type. 567 * @return the part of the Solr query for the restriction. 568 */ 569 String generatePreconfiguredRestriction( 570 String type, 571 Map<CmsRestrictionRule, Collection<FieldValues>> restrictionsForType) { 572 573 String result = ""; 574 if ((null != restrictionsForType) && (restrictionsForType.size() > 0)) { 575 Collection<String> ruleRestrictions = new HashSet<>(restrictionsForType.size()); 576 for (Map.Entry<CmsRestrictionRule, Collection<FieldValues>> ruleEntry : restrictionsForType.entrySet()) { 577 ruleRestrictions.add(generatePreconfiguredRestrictionForRule(ruleEntry.getKey(), ruleEntry.getValue())); 578 } 579 result = ruleRestrictions.size() > 1 580 ? ruleRestrictions.stream().reduce((r1, r2) -> (r1 + " " + CombinationMode.AND + " " + r2)).get() 581 : ruleRestrictions.iterator().next(); 582 if (null != type) { 583 result = "type:\"" + type + "\" AND (" + result + ")"; 584 } 585 } else if (null != type) { 586 result = "type:\"" + type + "\""; 587 } 588 return result.isEmpty() ? result : "(" + result + ")"; 589 } 590 591 /** 592 * Generates the query part for the preconfigured restriction for a single rule. 593 * @param rule the rule to generate the restriction for. 594 * @param values the values provided for the rule. 595 * @return the part of the Solr query for the restriction. 596 */ 597 String generatePreconfiguredRestrictionForRule(CmsRestrictionRule rule, Collection<FieldValues> values) { 598 599 Collection<String> resolvedFieldValues = values.stream().map(v -> resolveFieldValues(rule, v)).collect( 600 Collectors.toSet()); 601 602 String seperator = " " + rule.getCombinationModeBetweenFields().toString() + " "; 603 return rule.getFieldForLocale(getSearchLocale()) 604 + ":(" 605 + resolvedFieldValues.stream().reduce((v1, v2) -> v1 + seperator + v2).get() 606 + ")"; 607 608 } 609 610 /** 611 * Returns the blacklist filter.<p> 612 * 613 * @return the blacklist filter 614 */ 615 String getBlacklistFilter() { 616 617 if (m_ignoreBlacklist) { 618 return ""; 619 } 620 String result = ""; 621 List<CmsUUID> blacklist = m_config.getBlacklist(); 622 List<String> blacklistStrings = Lists.newArrayList(); 623 for (CmsUUID id : blacklist) { 624 blacklistStrings.add("\"" + id.toString() + "\""); 625 } 626 if (!blacklistStrings.isEmpty()) { 627 result = "&fq=" + CmsEncoder.encode("-id:(" + CmsStringUtil.listAsString(blacklistStrings, " OR ") + ")"); 628 } 629 return result; 630 } 631 632 /** 633 * Returns the category filter string.<p> 634 * 635 * @return the category filter 636 */ 637 String getCategoryFilterPart() { 638 639 String result = ""; 640 if (!m_config.getCategories().isEmpty()) { 641 List<String> categoryVals = Lists.newArrayList(); 642 for (String path : m_config.getCategories()) { 643 try { 644 path = CmsCategoryService.getInstance().getCategory( 645 m_cms, 646 m_cms.getRequestContext().addSiteRoot(path)).getPath(); 647 categoryVals.add("\"" + path + "\""); 648 } catch (CmsException e) { 649 LOG.warn(e.getLocalizedMessage(), e); 650 } 651 } 652 if (!categoryVals.isEmpty()) { 653 String operator = " " + m_config.getCategoryMode() + " "; 654 String valueExpression = CmsStringUtil.listAsString(categoryVals, operator); 655 result = "category_exact:(" + valueExpression + ")"; 656 657 } 658 } 659 return result; 660 } 661 662 /** 663 * Returns the category filter string.<p> 664 * 665 * @return the category filter 666 */ 667 String getCategoryFolderFilter() { 668 669 String result = ""; 670 String defaultPart = getFolderFilterPart(); 671 String categoryFilterPart = getCategoryFilterPart(); 672 if (!categoryFilterPart.isEmpty()) { 673 defaultPart = "((" + defaultPart + ") AND (" + categoryFilterPart + "))"; 674 } 675 for (CmsCategoryFolderRestrictionBean restriction : m_config.getCategoryFolderRestrictions()) { 676 String restrictionQuery = restriction.toString(); 677 if (!restrictionQuery.isEmpty()) { 678 restrictionQuery = "(" + restrictionQuery + " AND " + defaultPart + ")"; 679 if (!result.isEmpty()) { 680 result += " OR "; 681 } 682 result += restrictionQuery; 683 } 684 } 685 if (result.isEmpty()) { 686 result = defaultPart; 687 } 688 return "fq=" + CmsEncoder.encode(result); 689 } 690 691 /** 692 * The fields returned by default. Typically the output is done via display formatters and hence nearly no 693 * field is necessary. Returning all fields might cause performance problems. 694 * 695 * @return the default return fields. 696 */ 697 String getDefaultReturnFields() { 698 699 StringBuffer fields = new StringBuffer(""); 700 fields.append(CmsSearchField.FIELD_PATH); 701 fields.append(','); 702 fields.append(CmsSearchField.FIELD_INSTANCEDATE).append(CmsSearchField.FIELD_POSTFIX_DATE); 703 fields.append(','); 704 fields.append(CmsSearchField.FIELD_INSTANCEDATE_END).append(CmsSearchField.FIELD_POSTFIX_DATE); 705 fields.append(','); 706 fields.append(CmsSearchField.FIELD_INSTANCEDATE_CURRENT_TILL).append(CmsSearchField.FIELD_POSTFIX_DATE); 707 fields.append(','); 708 fields.append(CmsSearchField.FIELD_INSTANCEDATE).append('_').append(getSearchLocale().toString()).append( 709 CmsSearchField.FIELD_POSTFIX_DATE); 710 fields.append(','); 711 fields.append(CmsSearchField.FIELD_INSTANCEDATE_END).append('_').append(getSearchLocale().toString()).append( 712 CmsSearchField.FIELD_POSTFIX_DATE); 713 fields.append(','); 714 fields.append(CmsSearchField.FIELD_INSTANCEDATE_CURRENT_TILL).append('_').append( 715 getSearchLocale().toString()).append(CmsSearchField.FIELD_POSTFIX_DATE); 716 fields.append(','); 717 fields.append(CmsSearchField.FIELD_ID); 718 fields.append(','); 719 fields.append(CmsSearchField.FIELD_SOLR_ID); 720 fields.append(','); 721 fields.append(CmsSearchField.FIELD_DISPTITLE).append('_').append(getSearchLocale().toString()).append("_sort"); 722 fields.append(','); 723 fields.append(CmsSearchField.FIELD_LINK); 724 fields.append(','); 725 fields.append(CmsSearchField.FIELD_GEOCOORDS); 726 return fields.toString(); 727 } 728 729 /** 730 * Returns the filter query string.<p> 731 * 732 * @return the filter query 733 */ 734 String getFilterQuery() { 735 736 String result = m_config.getFilterQuery(); 737 if (result == null) { 738 result = ""; 739 } 740 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(result) && !result.startsWith("&")) { 741 result = "&" + result; 742 } 743 if (!result.contains(CommonParams.FL + "=")) { 744 result += "&" + CommonParams.FL + "=" + CmsEncoder.encode(getDefaultReturnFields()); 745 } 746 I_CmsDateRestriction dateRestriction = m_config.getDateRestriction(); 747 if (dateRestriction != null) { 748 result += "&fq=" 749 + CmsEncoder.encode( 750 CmsSearchField.FIELD_INSTANCEDATE_CURRENT_TILL 751 + "_" 752 + getSearchLocale().toString() 753 + "_dt:" 754 + dateRestriction.getRange()); 755 756 } 757 result += "&fq=con_locales:" + getSearchLocale().toString(); 758 return result; 759 } 760 761 /** 762 * Returns the folder filter string.<p> 763 * 764 * @return the folder filter 765 */ 766 String getFolderFilterPart() { 767 768 String result = ""; 769 List<String> parentFolderVals = Lists.newArrayList(); 770 if (!m_config.getFolders().isEmpty()) { 771 for (String value : m_config.getFolders()) { 772 parentFolderVals.add("\"" + value + "\""); 773 } 774 } 775 if (parentFolderVals.isEmpty()) { 776 result = "parent-folders:(\"/\")"; 777 } else { 778 result = "parent-folders:(" + CmsStringUtil.listAsString(parentFolderVals, " OR ") + ")"; 779 } 780 return result; 781 } 782 783 /** 784 * Returns the Geo filter query string.<p> 785 * 786 * @return the Geo filter query string 787 */ 788 String getGeoFilterQuery() { 789 790 String result = ""; 791 CmsGeoFilterBean geoFilterBean = m_config.getGeoFilter(); 792 if (geoFilterBean != null) { 793 String fq = CmsSolrQueryUtil.composeGeoFilterQuery( 794 CmsSearchField.FIELD_GEOCOORDS, 795 geoFilterBean.getCoordinates(), 796 geoFilterBean.getRadius(), 797 "km"); 798 result = "&fq=" + fq; 799 } 800 return result; 801 } 802 803 /** 804 * Returns the filter query string.<p> 805 * 806 * @return the filter query 807 */ 808 String getPreconfiguredFilterQuery() { 809 810 String result = ""; 811 if (m_config.hasPreconfiguredRestrictions()) { 812 CmsRestrictionsBean restrictions = m_config.getPreconfiguredRestrictions(); 813 String restriction = generatePreconfiguredRestriction(null, restrictions.getRestrictionsForType(null)); 814 if (!restriction.isEmpty()) { 815 result = "&fq=" + CmsEncoder.encode(restriction); 816 } 817 Collection<String> typedRestrictions = new HashSet<>(); 818 for (String type : m_config.getTypes()) { 819 restriction = generatePreconfiguredRestriction(type, restrictions.getRestrictionsForType(type)); 820 if (!restriction.isEmpty()) { 821 typedRestrictions.add(restriction); 822 } 823 } 824 if (!typedRestrictions.isEmpty()) { 825 result += "&fq=" 826 + CmsEncoder.encode( 827 "(" + typedRestrictions.stream().reduce((r1, r2) -> (r1 + " OR " + r2)).get() + ")"); 828 } 829 } 830 return result; 831 } 832 833 /** 834 * Returns the resource type filter string.<p> 835 * 836 * @return the folder filter 837 */ 838 String getResourceTypeFilter() { 839 840 String result = ""; 841 // When we have pre-configured restrictions, we need to combine the type filter with these restrictions. 842 if (!m_config.hasPreconfiguredRestrictions()) { 843 List<String> typeVals = Lists.newArrayList(); 844 for (String type : m_config.getTypes()) { 845 typeVals.add("\"" + type + "\""); 846 } 847 if (!typeVals.isEmpty()) { 848 result = "&fq=" + CmsEncoder.encode("type:(" + CmsStringUtil.listAsString(typeVals, " OR ") + ")"); 849 } 850 } 851 return result; 852 } 853 854 /** 855 * Generates the search string part for one input field value. 856 * @param rule the preconfigured rule. 857 * @param fieldValues the values in the field. 858 * @return the search term part for the value in the field. 859 */ 860 String resolveFieldValues(CmsRestrictionRule rule, FieldValues fieldValues) { 861 862 Collection<String> values = fieldValues.getValues(); 863 Collection<String> finalValues; 864 if (FieldType.PLAIN.equals(fieldValues.getFieldType())) { 865 // We are sure that there is exactly one value in that case. 866 return "(" + values.iterator().next() + ")"; 867 } 868 switch (rule.getMatchType()) { 869 case DEFAULT: 870 finalValues = values; 871 break; 872 case EXACT: 873 finalValues = values.stream().map(v -> ("\"" + v + "\"")).collect(Collectors.toSet()); 874 break; 875 case INFIX: 876 finalValues = values.stream().map( 877 v -> ("(" + v + " OR *" + v + " OR *" + v + "* OR " + v + "*)")).collect(Collectors.toSet()); 878 break; 879 case POSTFIX: 880 finalValues = values.stream().map(v -> ("(" + v + " OR *" + v + ")")).collect(Collectors.toSet()); 881 break; 882 case PREFIX: 883 finalValues = values.stream().map(v -> ("(" + v + " OR " + v + "*)")).collect(Collectors.toSet()); 884 break; 885 default: 886 throw new IllegalArgumentException("Unknown match type '" + rule.getMatchType() + "'."); 887 } 888 if (finalValues.size() > 1) { 889 String seperator = " " + rule.getCombinationModeInField().toString() + " "; 890 return "(" + finalValues.stream().reduce((v1, v2) -> v1 + seperator + v2).get() + ")"; 891 } 892 return finalValues.iterator().next(); 893 } 894 895 /** 896 * Returns a flag, indicating if the release and expiration date should be ignored. 897 * @return a flag, indicating if the release and expiration date should be ignored. 898 */ 899 private Boolean getIgnoreReleaseAndExpiration() { 900 901 return Boolean.valueOf(m_config.isShowExpired()); 902 } 903}