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 if (Boolean.parseBoolean(m_config.getParameterValue(CmsConfigurationBean.PARAM_FILTER_MULTI_DAY))) { 351 indexField = FIELD_DATE_RANGE; 352 } 353 I_CmsSearchConfigurationFacetRange rangeFacet = new CmsSearchConfigurationFacetRange( 354 String.format(indexField, getSearchLocale().toString()), 355 "NOW/YEAR-20YEARS", 356 "NOW/MONTH+5YEARS", 357 "+1MONTHS", 358 null, 359 Boolean.FALSE, 360 FIELD_DATE_FACET_NAME, 361 Integer.valueOf(1), 362 "Date", 363 Boolean.FALSE, 364 null, 365 Boolean.TRUE, 366 null); 367 368 rangeFacets.put(rangeFacet.getName(), rangeFacet); 369 return rangeFacets; 370 } 371 } 372 373 /** 374 * Sets the 'ignore blacklist' flag.<p> 375 * 376 * If set, the search will ignore the blacklist from the list configuration.<p> 377 * 378 * @param ignoreBlacklist true if the blacklist should be ignored 379 */ 380 public void setIgnoreBlacklist(boolean ignoreBlacklist) { 381 382 m_ignoreBlacklist = ignoreBlacklist; 383 } 384 385 /** 386 * Sets the pagination.<p> 387 * 388 * If this is set, parsePagination will always return the set value instead of using the default way to compute the pagination 389 * 390 * @param pagination the pagination 391 */ 392 public void setPagination(I_CmsSearchConfigurationPagination pagination) { 393 394 m_pagination = pagination; 395 } 396 397 /** 398 * Sets the search locale.<p> 399 * 400 * @param locale the search locale 401 */ 402 public void setSearchLocale(Locale locale) { 403 404 m_searchLocale = locale; 405 } 406 407 /** 408 * Sets the sort option.<p> 409 * 410 * @param sortOption the sort option 411 */ 412 public void setSortOption(String sortOption) { 413 414 if (null != sortOption) { 415 try { 416 m_sortOrder = CmsSimpleSearchConfigurationParser.SortOption.valueOf(sortOption); 417 } catch (IllegalArgumentException e) { 418 m_sortOrder = null; 419 LOG.warn( 420 "Setting illegal default sort option " + sortOption + " failed. Using Solr's default sort option."); 421 } 422 } else { 423 m_sortOrder = null; 424 } 425 } 426 427 /** 428 * @see org.opencms.jsp.search.config.parser.CmsJSONSearchConfigurationParser#getEscapeQueryChars() 429 */ 430 @Override 431 protected Boolean getEscapeQueryChars() { 432 433 if (m_configObject.has(JSON_KEY_ESCAPE_QUERY_CHARACTERS)) { 434 return super.getEscapeQueryChars(); 435 } else { 436 return Boolean.TRUE; 437 } 438 } 439 440 /** 441 * @see org.opencms.jsp.search.config.parser.CmsJSONSearchConfigurationParser#getExtraSolrParams() 442 */ 443 @Override 444 protected String getExtraSolrParams() { 445 446 String params = super.getExtraSolrParams(); 447 if (CmsStringUtil.isEmptyOrWhitespaceOnly(params)) { 448 params = getCategoryFolderFilter() 449 + getResourceTypeFilter() 450 + getPreconfiguredFilterQuery() 451 + getFilterQuery() 452 + getBlacklistFilter() 453 + getGeoFilterQuery(); 454 } 455 return params; 456 } 457 458 /** 459 * @see org.opencms.jsp.search.config.parser.CmsJSONSearchConfigurationParser#getIgnoreExpirationDate() 460 */ 461 @Override 462 protected Boolean getIgnoreExpirationDate() { 463 464 return getIgnoreReleaseAndExpiration(); 465 466 } 467 468 /** 469 * @see org.opencms.jsp.search.config.parser.CmsJSONSearchConfigurationParser#getIgnoreReleaseDate() 470 */ 471 @Override 472 protected Boolean getIgnoreReleaseDate() { 473 474 return getIgnoreReleaseAndExpiration(); 475 } 476 477 /** 478 * @see org.opencms.jsp.search.config.parser.CmsJSONSearchConfigurationParser#getMaxReturnedResults(java.lang.String) 479 */ 480 @Override 481 protected int getMaxReturnedResults(String indexName) { 482 483 return null != m_config.getMaximallyReturnedResults() 484 ? m_config.getMaximallyReturnedResults().intValue() 485 : super.getMaxReturnedResults(indexName); 486 } 487 488 /** 489 * @see org.opencms.jsp.search.config.parser.CmsJSONSearchConfigurationParser#getQueryModifier() 490 */ 491 @Override 492 protected String getQueryModifier() { 493 494 String modifier = super.getQueryModifier(); 495 if (CmsStringUtil.isEmptyOrWhitespaceOnly(modifier)) { 496 modifier = "{!type=edismax qf=\"" 497 + CmsSearchField.FIELD_CONTENT 498 + "_" 499 + getSearchLocale().toString() 500 + " " 501 + CmsPropertyDefinition.PROPERTY_TITLE 502 + CmsSearchField.FIELD_DYNAMIC_PROPERTIES 503 + " " 504 + CmsPropertyDefinition.PROPERTY_DESCRIPTION 505 + CmsSearchField.FIELD_DYNAMIC_PROPERTIES_DIRECT 506 + " " 507 + CmsPropertyDefinition.PROPERTY_DESCRIPTION_HTML 508 + CmsSearchField.FIELD_DYNAMIC_PROPERTIES_DIRECT 509 + "\"}%(query)"; 510 } 511 return modifier; 512 } 513 514 /** 515 * @see org.opencms.jsp.search.config.parser.CmsJSONSearchConfigurationParser#getSearchForEmptyQuery() 516 */ 517 @Override 518 protected Boolean getSearchForEmptyQuery() { 519 520 if (m_configObject.has(JSON_KEY_SEARCH_FOR_EMPTY_QUERY)) { 521 return super.getSearchForEmptyQuery(); 522 } else { 523 return Boolean.TRUE; 524 } 525 } 526 527 /** 528 * @see org.opencms.jsp.search.config.parser.CmsJSONSearchConfigurationParser#getSortOptions() 529 */ 530 @Override 531 protected List<I_CmsSearchConfigurationSortOption> getSortOptions() { 532 533 if (m_configObject.has(JSON_KEY_SORTOPTIONS)) { 534 return super.getSortOptions(); 535 } else { 536 List<I_CmsSearchConfigurationSortOption> options = new LinkedList<I_CmsSearchConfigurationSortOption>(); 537 538 CmsSimpleSearchConfigurationParser.SortOption currentOption = CmsSimpleSearchConfigurationParser.SortOption.valueOf( 539 m_config.getSortOrder()); 540 if (m_sortOrder != null) { 541 currentOption = m_sortOrder; 542 } 543 Locale locale = getSearchLocale(); 544 options.add(currentOption.getOption(locale)); 545 CmsSimpleSearchConfigurationParser.SortOption[] sortOptions = CmsSimpleSearchConfigurationParser.SortOption.values(); 546 for (int i = 0; i < sortOptions.length; i++) { 547 CmsSimpleSearchConfigurationParser.SortOption option = sortOptions[i]; 548 if (!Objects.equals(currentOption, option)) { 549 options.add(option.getOption(locale)); 550 } 551 } 552 return options; 553 } 554 } 555 556 /** 557 * Generates the query part for the preconfigured restrictions for the type. 558 * @param type the type to generate the restriction for. 559 * @param restrictionsForType the preconfigured restrictions for the type. 560 * @return the part of the Solr query for the restriction. 561 */ 562 String generatePreconfiguredRestriction( 563 String type, 564 Map<CmsRestrictionRule, Collection<FieldValues>> restrictionsForType) { 565 566 String result = ""; 567 if ((null != restrictionsForType) && (restrictionsForType.size() > 0)) { 568 Collection<String> ruleRestrictions = new HashSet<>(restrictionsForType.size()); 569 for (Map.Entry<CmsRestrictionRule, Collection<FieldValues>> ruleEntry : restrictionsForType.entrySet()) { 570 ruleRestrictions.add(generatePreconfiguredRestrictionForRule(ruleEntry.getKey(), ruleEntry.getValue())); 571 } 572 result = ruleRestrictions.size() > 1 573 ? ruleRestrictions.stream().reduce((r1, r2) -> (r1 + " " + CombinationMode.AND + " " + r2)).get() 574 : ruleRestrictions.iterator().next(); 575 if (null != type) { 576 result = "type:\"" + type + "\" AND (" + result + ")"; 577 } 578 } else if (null != type) { 579 result = "type:\"" + type + "\""; 580 } 581 return result.isEmpty() ? result : "(" + result + ")"; 582 } 583 584 /** 585 * Generates the query part for the preconfigured restriction for a single rule. 586 * @param rule the rule to generate the restriction for. 587 * @param values the values provided for the rule. 588 * @return the part of the Solr query for the restriction. 589 */ 590 String generatePreconfiguredRestrictionForRule(CmsRestrictionRule rule, Collection<FieldValues> values) { 591 592 Collection<String> resolvedFieldValues = values.stream().map(v -> resolveFieldValues(rule, v)).collect( 593 Collectors.toSet()); 594 595 String seperator = " " + rule.getCombinationModeBetweenFields().toString() + " "; 596 return rule.getFieldForLocale(getSearchLocale()) 597 + ":(" 598 + resolvedFieldValues.stream().reduce((v1, v2) -> v1 + seperator + v2).get() 599 + ")"; 600 601 } 602 603 /** 604 * Returns the blacklist filter.<p> 605 * 606 * @return the blacklist filter 607 */ 608 String getBlacklistFilter() { 609 610 if (m_ignoreBlacklist) { 611 return ""; 612 } 613 String result = ""; 614 List<CmsUUID> blacklist = m_config.getBlacklist(); 615 List<String> blacklistStrings = Lists.newArrayList(); 616 for (CmsUUID id : blacklist) { 617 blacklistStrings.add("\"" + id.toString() + "\""); 618 } 619 if (!blacklistStrings.isEmpty()) { 620 result = "&fq=" + CmsEncoder.encode("-id:(" + CmsStringUtil.listAsString(blacklistStrings, " OR ") + ")"); 621 } 622 return result; 623 } 624 625 /** 626 * Returns the category filter string.<p> 627 * 628 * @return the category filter 629 */ 630 String getCategoryFilterPart() { 631 632 String result = ""; 633 if (!m_config.getCategories().isEmpty()) { 634 List<String> categoryVals = Lists.newArrayList(); 635 for (String path : m_config.getCategories()) { 636 try { 637 path = CmsCategoryService.getInstance().getCategory( 638 m_cms, 639 m_cms.getRequestContext().addSiteRoot(path)).getPath(); 640 categoryVals.add("\"" + path + "\""); 641 } catch (CmsException e) { 642 LOG.warn(e.getLocalizedMessage(), e); 643 } 644 } 645 if (!categoryVals.isEmpty()) { 646 String operator = " " + m_config.getCategoryMode() + " "; 647 String valueExpression = CmsStringUtil.listAsString(categoryVals, operator); 648 result = "category_exact:(" + valueExpression + ")"; 649 650 } 651 } 652 return result; 653 } 654 655 /** 656 * Returns the category filter string.<p> 657 * 658 * @return the category filter 659 */ 660 String getCategoryFolderFilter() { 661 662 String result = ""; 663 String defaultPart = getFolderFilterPart(); 664 String categoryFilterPart = getCategoryFilterPart(); 665 if (!categoryFilterPart.isEmpty()) { 666 defaultPart = "((" + defaultPart + ") AND (" + categoryFilterPart + "))"; 667 } 668 for (CmsCategoryFolderRestrictionBean restriction : m_config.getCategoryFolderRestrictions()) { 669 String restrictionQuery = restriction.toString(); 670 if (!restrictionQuery.isEmpty()) { 671 restrictionQuery = "(" + restrictionQuery + " AND " + defaultPart + ")"; 672 if (!result.isEmpty()) { 673 result += " OR "; 674 } 675 result += restrictionQuery; 676 } 677 } 678 if (result.isEmpty()) { 679 result = defaultPart; 680 } 681 return "fq=" + CmsEncoder.encode(result); 682 } 683 684 /** 685 * The fields returned by default. Typically the output is done via display formatters and hence nearly no 686 * field is necessary. Returning all fields might cause performance problems. 687 * 688 * @return the default return fields. 689 */ 690 String getDefaultReturnFields() { 691 692 StringBuffer fields = new StringBuffer(""); 693 fields.append(CmsSearchField.FIELD_PATH); 694 fields.append(','); 695 fields.append(CmsSearchField.FIELD_INSTANCEDATE).append(CmsSearchField.FIELD_POSTFIX_DATE); 696 fields.append(','); 697 fields.append(CmsSearchField.FIELD_INSTANCEDATE_END).append(CmsSearchField.FIELD_POSTFIX_DATE); 698 fields.append(','); 699 fields.append(CmsSearchField.FIELD_INSTANCEDATE_CURRENT_TILL).append(CmsSearchField.FIELD_POSTFIX_DATE); 700 fields.append(','); 701 fields.append(CmsSearchField.FIELD_INSTANCEDATE).append('_').append(getSearchLocale().toString()).append( 702 CmsSearchField.FIELD_POSTFIX_DATE); 703 fields.append(','); 704 fields.append(CmsSearchField.FIELD_INSTANCEDATE_END).append('_').append(getSearchLocale().toString()).append( 705 CmsSearchField.FIELD_POSTFIX_DATE); 706 fields.append(','); 707 fields.append(CmsSearchField.FIELD_INSTANCEDATE_CURRENT_TILL).append('_').append( 708 getSearchLocale().toString()).append(CmsSearchField.FIELD_POSTFIX_DATE); 709 fields.append(','); 710 fields.append(CmsSearchField.FIELD_ID); 711 fields.append(','); 712 fields.append(CmsSearchField.FIELD_SOLR_ID); 713 fields.append(','); 714 fields.append(CmsSearchField.FIELD_DISPTITLE).append('_').append(getSearchLocale().toString()).append("_sort"); 715 fields.append(','); 716 fields.append(CmsSearchField.FIELD_LINK); 717 fields.append(','); 718 fields.append(CmsSearchField.FIELD_GEOCOORDS); 719 return fields.toString(); 720 } 721 722 /** 723 * Returns the filter query string.<p> 724 * 725 * @return the filter query 726 */ 727 String getFilterQuery() { 728 729 String result = m_config.getFilterQuery(); 730 if (result == null) { 731 result = ""; 732 } 733 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(result) && !result.startsWith("&")) { 734 result = "&" + result; 735 } 736 if (!result.contains(CommonParams.FL + "=")) { 737 result += "&" + CommonParams.FL + "=" + CmsEncoder.encode(getDefaultReturnFields()); 738 } 739 I_CmsDateRestriction dateRestriction = m_config.getDateRestriction(); 740 if (dateRestriction != null) { 741 result += "&fq=" 742 + CmsEncoder.encode( 743 CmsSearchField.FIELD_INSTANCEDATE_CURRENT_TILL 744 + "_" 745 + getSearchLocale().toString() 746 + "_dt:" 747 + dateRestriction.getRange()); 748 749 } 750 result += "&fq=con_locales:" + getSearchLocale().toString(); 751 return result; 752 } 753 754 /** 755 * Returns the folder filter string.<p> 756 * 757 * @return the folder filter 758 */ 759 String getFolderFilterPart() { 760 761 String result = ""; 762 List<String> parentFolderVals = Lists.newArrayList(); 763 if (!m_config.getFolders().isEmpty()) { 764 for (String value : m_config.getFolders()) { 765 parentFolderVals.add("\"" + value + "\""); 766 } 767 } 768 if (parentFolderVals.isEmpty()) { 769 result = "parent-folders:(\"/\")"; 770 } else { 771 result = "parent-folders:(" + CmsStringUtil.listAsString(parentFolderVals, " OR ") + ")"; 772 } 773 return result; 774 } 775 776 /** 777 * Returns the Geo filter query string.<p> 778 * 779 * @return the Geo filter query string 780 */ 781 String getGeoFilterQuery() { 782 783 String result = ""; 784 CmsGeoFilterBean geoFilterBean = m_config.getGeoFilter(); 785 if (geoFilterBean != null) { 786 String fq = CmsSolrQueryUtil.composeGeoFilterQuery( 787 CmsSearchField.FIELD_GEOCOORDS, 788 geoFilterBean.getCoordinates(), 789 geoFilterBean.getRadius(), 790 "km"); 791 result = "&fq=" + fq; 792 } 793 return result; 794 } 795 796 /** 797 * Returns the filter query string.<p> 798 * 799 * @return the filter query 800 */ 801 String getPreconfiguredFilterQuery() { 802 803 String result = ""; 804 if (m_config.hasPreconfiguredRestrictions()) { 805 CmsRestrictionsBean restrictions = m_config.getPreconfiguredRestrictions(); 806 String restriction = generatePreconfiguredRestriction(null, restrictions.getRestrictionsForType(null)); 807 if (!restriction.isEmpty()) { 808 result = "&fq=" + CmsEncoder.encode(restriction); 809 } 810 Collection<String> typedRestrictions = new HashSet<>(); 811 for (String type : m_config.getTypes()) { 812 restriction = generatePreconfiguredRestriction(type, restrictions.getRestrictionsForType(type)); 813 if (!restriction.isEmpty()) { 814 typedRestrictions.add(restriction); 815 } 816 } 817 if (!typedRestrictions.isEmpty()) { 818 result += "&fq=" 819 + CmsEncoder.encode( 820 "(" + typedRestrictions.stream().reduce((r1, r2) -> (r1 + " OR " + r2)).get() + ")"); 821 } 822 } 823 return result; 824 } 825 826 /** 827 * Returns the resource type filter string.<p> 828 * 829 * @return the folder filter 830 */ 831 String getResourceTypeFilter() { 832 833 String result = ""; 834 // When we have pre-configured restrictions, we need to combine the type filter with these restrictions. 835 if (!m_config.hasPreconfiguredRestrictions()) { 836 List<String> typeVals = Lists.newArrayList(); 837 for (String type : m_config.getTypes()) { 838 typeVals.add("\"" + type + "\""); 839 } 840 if (!typeVals.isEmpty()) { 841 result = "&fq=" + CmsEncoder.encode("type:(" + CmsStringUtil.listAsString(typeVals, " OR ") + ")"); 842 } 843 } 844 return result; 845 } 846 847 /** 848 * Generates the search string part for one input field value. 849 * @param rule the preconfigured rule. 850 * @param fieldValues the values in the field. 851 * @return the search term part for the value in the field. 852 */ 853 String resolveFieldValues(CmsRestrictionRule rule, FieldValues fieldValues) { 854 855 Collection<String> values = fieldValues.getValues(); 856 Collection<String> finalValues; 857 if (FieldType.PLAIN.equals(fieldValues.getFieldType())) { 858 // We are sure that there is exactly one value in that case. 859 return "(" + values.iterator().next() + ")"; 860 } else { 861 switch (rule.getMatchType()) { 862 case DEFAULT: 863 finalValues = values; 864 break; 865 case EXACT: 866 finalValues = values.stream().map(v -> ("\"" + v + "\"")).collect(Collectors.toSet()); 867 break; 868 case INFIX: 869 finalValues = values.stream().map( 870 v -> ("(" + v + " OR *" + v + " OR *" + v + "* OR " + v + "*)")).collect(Collectors.toSet()); 871 break; 872 case POSTFIX: 873 finalValues = values.stream().map(v -> ("(" + v + " OR *" + v + ")")).collect(Collectors.toSet()); 874 break; 875 case PREFIX: 876 finalValues = values.stream().map(v -> ("(" + v + " OR " + v + "*)")).collect(Collectors.toSet()); 877 break; 878 default: 879 throw new IllegalArgumentException("Unknown match type '" + rule.getMatchType() + "'."); 880 } 881 if (finalValues.size() > 1) { 882 String seperator = " " + rule.getCombinationModeInField().toString() + " "; 883 return "(" + finalValues.stream().reduce((v1, v2) -> v1 + seperator + v2).get() + ")"; 884 } else { 885 return finalValues.iterator().next(); 886 } 887 888 } 889 } 890 891 /** 892 * Returns a flag, indicating if the release and expiration date should be ignored. 893 * @return a flag, indicating if the release and expiration date should be ignored. 894 */ 895 private Boolean getIgnoreReleaseAndExpiration() { 896 897 return Boolean.valueOf(m_config.isShowExpired()); 898 } 899}