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