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 GmbH & Co. KG, 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.search; 029 030import org.opencms.i18n.CmsEncoder; 031import org.opencms.main.CmsException; 032import org.opencms.main.CmsIllegalArgumentException; 033import org.opencms.main.CmsLog; 034import org.opencms.main.OpenCms; 035import org.opencms.search.fields.CmsSearchField; 036import org.opencms.util.CmsStringUtil; 037 038import java.util.ArrayList; 039import java.util.Arrays; 040import java.util.Iterator; 041import java.util.List; 042 043import org.apache.commons.collections.ListUtils; 044import org.apache.commons.logging.Log; 045import org.apache.lucene.search.BooleanClause; 046import org.apache.lucene.search.BooleanClause.Occur; 047import org.apache.lucene.search.Sort; 048import org.apache.lucene.search.SortField; 049 050/** 051 * Contains the search parameters for a call to <code>{@link org.opencms.search.CmsSearchIndex#search(org.opencms.file.CmsObject, CmsSearchParameters)}</code>.<p> 052 * 053 * Primary purpose is translation of search arguments to response parameters and from request parameters as 054 * well as support for creation of restrictions of several search query parameter sets. <p> 055 * 056 * @since 6.0.0 057 */ 058public class CmsSearchParameters { 059 060 /** 061 * Describes a specific search field query.<p> 062 */ 063 public static class CmsSearchFieldQuery { 064 065 /** The field name. */ 066 private String m_fieldName; 067 068 /** The occur parameter for this field. */ 069 private Occur m_fieldOccur; 070 071 /** The search term list. */ 072 private List<String> m_searchTerms; 073 074 /** The occur parameter used for the search term combination. */ 075 private Occur m_termOccur; 076 077 /** 078 * Creates a new search field query with a variable length search term list.<p> 079 * 080 * @param fieldName the field name 081 * @param fieldOccur the occur parameter for this field 082 * @param termList the search term list 083 * @param termOccur the occur parameter used for the search term combination 084 */ 085 public CmsSearchFieldQuery(String fieldName, Occur fieldOccur, List<String> termList, Occur termOccur) { 086 087 super(); 088 m_fieldName = fieldName; 089 m_fieldOccur = fieldOccur; 090 m_searchTerms = termList; 091 m_termOccur = termOccur; 092 } 093 094 /** 095 * Creates a new search field query with just a single search term.<p> 096 * 097 * Please note: Since there is only one term, the ocucr parameter for the term combination is 098 * not required and set to <code>null</code>.<p> 099 * 100 * @param fieldName the field name 101 * @param searchTerm the search term 102 * @param fieldOccur the occur parameter for this field 103 */ 104 public CmsSearchFieldQuery(String fieldName, String searchTerm, Occur fieldOccur) { 105 106 this(fieldName, fieldOccur, Arrays.asList(searchTerm), null); 107 } 108 109 /** 110 * Returns the field name.<p> 111 * 112 * @return the field name 113 */ 114 public String getFieldName() { 115 116 return m_fieldName; 117 } 118 119 /** 120 * Returns the occur parameter for this field query.<p> 121 * 122 * @return the occur parameter for this field query 123 */ 124 public Occur getOccur() { 125 126 return m_fieldOccur; 127 } 128 129 /** 130 * Returns the first entry from the term list.<p> 131 * 132 * @return the search query 133 * 134 * @deprecated use {@link #getSearchTerms()} instead 135 */ 136 @Deprecated 137 public String getSearchQuery() { 138 139 return m_searchTerms.get(0); 140 } 141 142 /** 143 * Returns the search term list.<p> 144 * 145 * @return the search term list 146 */ 147 public List<String> getSearchTerms() { 148 149 return m_searchTerms; 150 } 151 152 /** 153 * Returns the occur parameter used for the search term combination of this field query.<p> 154 * 155 * @return the occur parameter used for the search term combination of this field query 156 */ 157 public Occur getTermOccur() { 158 159 return m_termOccur; 160 } 161 162 /** 163 * Sets the name of the field to use this query for.<p> 164 * 165 * @param fieldName the name of the field to use this query for 166 */ 167 public void setFieldName(String fieldName) { 168 169 m_fieldName = fieldName; 170 } 171 172 /** 173 * Sets the occur parameter for this field query.<p> 174 * 175 * @param occur the occur parameter to set 176 */ 177 public void setOccur(BooleanClause.Occur occur) { 178 179 m_fieldOccur = occur; 180 } 181 182 /** 183 * Sets the search keywords to just a single entry.<p> 184 * 185 * @param searchQuery the single search keyword to set 186 * 187 * @deprecated use {@link #setSearchTerms(List)} instead 188 */ 189 @Deprecated 190 public void setSearchQuery(String searchQuery) { 191 192 setSearchTerms(Arrays.asList(searchQuery)); 193 } 194 195 /** 196 * Sets the search terms.<p> 197 * 198 * @param searchTerms the search terms to set 199 */ 200 public void setSearchTerms(List<String> searchTerms) { 201 202 m_searchTerms = searchTerms; 203 } 204 } 205 206 /** Sort result documents by date of creation, then score. */ 207 public static final Sort SORT_DATE_CREATED = new Sort( 208 new SortField(CmsSearchField.FIELD_DATE_CREATED, SortField.Type.STRING, true)); 209 210 /** Sort result documents by date of last modification, then score. */ 211 public static final Sort SORT_DATE_LASTMODIFIED = new Sort( 212 new SortField(CmsSearchField.FIELD_DATE_LASTMODIFIED, SortField.Type.STRING, true)); 213 214 /** Default sort order (by document score). */ 215 public static final Sort SORT_DEFAULT = Sort.RELEVANCE; 216 217 /** Names of the default sort options. */ 218 public static final String[] SORT_NAMES = { 219 "SORT_DEFAULT", 220 "SORT_DATE_CREATED", 221 "SORT_DATE_LASTMODIFIED", 222 "SORT_TITLE"}; 223 224 /** Sort result documents by title, then score. */ 225 public static final Sort SORT_TITLE = new Sort( 226 new SortField[] {new SortField(CmsSearchField.FIELD_TITLE, SortField.Type.STRING), SortField.FIELD_SCORE}); 227 228 /** The log object for this class. */ 229 private static final Log LOG = CmsLog.getLog(CmsSearchParameters.class); 230 231 /** The number of displayed pages returned by getPageLinks(). */ 232 protected int m_displayPages; 233 234 /** The number of matches per page. */ 235 protected int m_matchesPerPage; 236 237 /** If <code>true</code>, the category count is calculated for all search results. */ 238 private boolean m_calculateCategories; 239 240 /** The list of categories to limit the search to. */ 241 private List<String> m_categories; 242 243 /** Indicates if all fields should be used for generating the excerpt, regardless if they have been searched or not. */ 244 private boolean m_excerptOnlySearchedFields; 245 246 /** The map of individual search field queries. */ 247 private List<CmsSearchFieldQuery> m_fieldQueries; 248 249 /** The list of search index fields to search in. */ 250 private List<String> m_fields; 251 252 /** The index to search. */ 253 private CmsSearchIndex m_index; 254 255 /** Indicates if the query part should be ignored so that only filters are used for searching. */ 256 private boolean m_isIgnoreQuery; 257 258 /** The creation date the resources have to have as maximum. */ 259 private long m_maxDateCreated; 260 261 /** The last modification date the resources have to have as maximum. */ 262 private long m_maxDateLastModified; 263 264 /** The creation date the resources have to have as minimum. */ 265 private long m_minDateCreated; 266 267 /** The last modification date the resources have to have as minimum. */ 268 private long m_minDateLastModified; 269 270 /** The current result page. */ 271 private int m_page; 272 273 /** The search query to use. */ 274 private String m_query; 275 276 /** The minimum length of the search query. */ 277 private int m_queryLength; 278 279 /** The list of resource types to limit the search to. */ 280 private List<String> m_resourceTypes; 281 282 /** Only resource that are sub-resource of one of the search roots are included in the search result. */ 283 private List<String> m_roots; 284 285 /** The sort order for the search. */ 286 private Sort m_sort; 287 288 /** 289 * Creates a new search parameter instance with no search query and 290 * default values for the remaining parameters. <p> 291 * 292 * Before using this search parameters for a search method 293 * <code>{@link #setQuery(String)}</code> has to be invoked. <p> 294 * 295 */ 296 public CmsSearchParameters() { 297 298 this(""); 299 } 300 301 /** 302 * Creates a new search parameter instance with the provided search query and 303 * default values for the remaining parameters. <p> 304 * 305 * Only the "meta" field (combination of content and title) will be used for search. 306 * No search root restriction is chosen. 307 * No category restriction is used. 308 * No category counts are calculated for the result. 309 * Sorting is turned off. This is a simple but fast setup. <p> 310 * 311 * @param query the query to search for 312 */ 313 public CmsSearchParameters(String query) { 314 315 this(query, null, null, null, null, false, null); 316 317 } 318 319 /** 320 * Creates a new search parameter instance with the provided parameter values.<p> 321 * 322 * @param query the search term to search the index 323 * @param fields the list of fields to search 324 * @param roots only resource that are sub-resource of one of the search roots are included in the search result 325 * @param categories the list of categories to limit the search to 326 * @param resourceTypes the list of resource types to limit the search to 327 * @param calculateCategories if <code>true</code>, the category count is calculated for all search results 328 * (use with caution, this option uses much performance) 329 * @param sort the sort order for the search 330 */ 331 public CmsSearchParameters( 332 String query, 333 List<String> fields, 334 List<String> roots, 335 List<String> categories, 336 List<String> resourceTypes, 337 boolean calculateCategories, 338 Sort sort) { 339 340 super(); 341 m_query = (query == null) ? "" : query; 342 if (fields == null) { 343 fields = new ArrayList<String>(2); 344 fields.add(CmsSearchIndex.DOC_META_FIELDS[0]); 345 fields.add(CmsSearchIndex.DOC_META_FIELDS[1]); 346 } 347 m_fields = fields; 348 if (roots == null) { 349 roots = new ArrayList<String>(2); 350 } 351 m_roots = roots; 352 m_categories = (categories == null) ? new ArrayList<String>() : categories; 353 m_resourceTypes = (resourceTypes == null) ? new ArrayList<String>() : resourceTypes; 354 m_calculateCategories = calculateCategories; 355 // null sort is allowed default 356 m_sort = sort; 357 m_page = 1; 358 m_queryLength = -1; 359 m_matchesPerPage = 10; 360 m_displayPages = 10; 361 m_isIgnoreQuery = false; 362 363 m_minDateCreated = Long.MIN_VALUE; 364 m_maxDateCreated = Long.MAX_VALUE; 365 m_minDateLastModified = Long.MIN_VALUE; 366 m_maxDateLastModified = Long.MAX_VALUE; 367 } 368 369 /** 370 * Adds an individual query for a search field.<p> 371 * 372 * If this is used, any setting made with {@link #setQuery(String)} and {@link #setFields(List)} 373 * will be ignored and only the individual field search settings will be used.<p> 374 * 375 * @param query the query to add 376 * 377 * @since 7.5.1 378 */ 379 public void addFieldQuery(CmsSearchFieldQuery query) { 380 381 if (m_fieldQueries == null) { 382 m_fieldQueries = new ArrayList<CmsSearchFieldQuery>(); 383 m_fields = new ArrayList<String>(); 384 } 385 m_fieldQueries.add(query); 386 // add the used field used in the fields query to the list of fields used in the search 387 if (!m_fields.contains(query.getFieldName())) { 388 m_fields.add(query.getFieldName()); 389 } 390 } 391 392 /** 393 * Adds an individual query for a search field.<p> 394 * 395 * If this is used, any setting made with {@link #setQuery(String)} and {@link #setFields(List)} 396 * will be ignored and only the individual field search settings will be used.<p> 397 * 398 * @param fieldName the field name 399 * @param searchQuery the search query 400 * @param occur the occur parameter for the query in the field 401 * 402 * @since 7.5.1 403 */ 404 public void addFieldQuery(String fieldName, String searchQuery, Occur occur) { 405 406 CmsSearchFieldQuery newQuery = new CmsSearchFieldQuery(fieldName, searchQuery, occur); 407 addFieldQuery(newQuery); 408 } 409 410 /** 411 * Returns whether category counts are calculated for search results or not. <p> 412 * 413 * @return a boolean that tells whether category counts are calculated for search results or not 414 */ 415 public boolean getCalculateCategories() { 416 417 return m_calculateCategories; 418 } 419 420 /** 421 * Returns the list of categories to limit the search to.<p> 422 * 423 * @return the list of categories to limit the search to 424 */ 425 public List<String> getCategories() { 426 427 return m_categories; 428 } 429 430 /** 431 * Returns the maximum number of pages which should be shown.<p> 432 * 433 * @return the maximum number of pages which should be shown 434 */ 435 public int getDisplayPages() { 436 437 return m_displayPages; 438 } 439 440 /** 441 * Returns the list of individual field queries.<p> 442 * 443 * @return the list of individual field queries 444 * 445 * @since 7.5.1 446 */ 447 public List<CmsSearchFieldQuery> getFieldQueries() { 448 449 return m_fieldQueries; 450 } 451 452 /** 453 * Returns the list of search index field names (Strings) to search in.<p> 454 * 455 * @return the list of search index field names (Strings) to search in 456 */ 457 public List<String> getFields() { 458 459 return m_fields; 460 } 461 462 /** 463 * Get the name of the index for the search.<p> 464 * 465 * @return the name of the index for the search 466 */ 467 public String getIndex() { 468 469 return m_index.getName(); 470 } 471 472 /** 473 * Gets the number of matches displayed on each page.<p> 474 * 475 * @return matches per result page 476 */ 477 public int getMatchesPerPage() { 478 479 return m_matchesPerPage; 480 } 481 482 /** 483 * Returns the maximum creation date a resource must have to be included in the search result.<p> 484 * 485 * @return the maximum creation date a resource must have to be included in the search result 486 */ 487 public long getMaxDateCreated() { 488 489 return m_maxDateCreated; 490 } 491 492 /** 493 * Returns the maximum last modification date a resource must have to be included in the search result.<p> 494 * 495 * @return the maximum last modification date a resource must have to be included in the search result 496 */ 497 public long getMaxDateLastModified() { 498 499 return m_maxDateLastModified; 500 } 501 502 /** 503 * Returns the minimum creation date a resource must have to be included in the search result.<p> 504 * 505 * @return the minimum creation date a resource must have to be included in the search result 506 */ 507 public long getMinDateCreated() { 508 509 return m_minDateCreated; 510 } 511 512 /** 513 * Returns the minimum last modification date a resource must have to be included in the search result.<p> 514 * 515 * @return the minimum last modification date a resource must have to be included in the search result 516 */ 517 public long getMinDateLastModified() { 518 519 return m_minDateLastModified; 520 } 521 522 /** 523 * Returns the search query to use.<p> 524 * 525 * @return the search query to use 526 */ 527 public String getQuery() { 528 529 return m_query; 530 } 531 532 /** 533 * Gets the minimum search query length.<p> 534 * 535 * @return the minimum search query length 536 */ 537 public int getQueryLength() { 538 539 return m_queryLength; 540 } 541 542 /** 543 * Returns the list of resource types to limit the search to.<p> 544 * 545 * @return the list of resource types to limit the search to 546 * 547 * @since 7.5.1 548 */ 549 public List<String> getResourceTypes() { 550 551 return m_resourceTypes; 552 } 553 554 /** 555 * Returns the list of strings of search roots to use.<p> 556 * 557 * Only resource that are sub-resource of one of the search roots are included in the search result.<p> 558 * 559 * @return the list of strings of search roots to use 560 */ 561 public List<String> getRoots() { 562 563 return m_roots; 564 } 565 566 /** 567 * Returns the list of categories to limit the search to.<p> 568 * 569 * @return the list of categories to limit the search to 570 */ 571 public String getSearchCategories() { 572 573 return toSeparatedString(getCategories(), ','); 574 } 575 576 /** 577 * Returns the search index to search in or null if not set before 578 * (<code>{@link #setSearchIndex(CmsSearchIndex)}</code>). <p> 579 * 580 * @return the search index to search in or null if not set before (<code>{@link #setSearchIndex(CmsSearchIndex)}</code>) 581 */ 582 public CmsSearchIndex getSearchIndex() { 583 584 return m_index; 585 } 586 587 /** 588 * Returns the search page to display.<p> 589 * 590 * @return the search page to display 591 */ 592 public int getSearchPage() { 593 594 return m_page; 595 } 596 597 /** 598 * Returns the comma separated lists of root folder names to restrict search to.<p> 599 * 600 * This method is a "sibling" to method <code>{@link #getRoots()}</code> but with 601 * the support of being usable with widget technology. <p> 602 * 603 * @return the comma separated lists of field names to search in 604 * 605 * @see #setSortName(String) 606 */ 607 public String getSearchRoots() { 608 609 return toSeparatedString(m_roots, ','); 610 } 611 612 /** 613 * Returns the instance that defines the sort order for the results. 614 * 615 * @return the instance that defines the sort order for the results 616 */ 617 public Sort getSort() { 618 619 return m_sort; 620 } 621 622 /** 623 * Returns the name of the sort option being used.<p> 624 * @return the name of the sort option being used 625 * 626 * @see #SORT_NAMES 627 * @see #setSortName(String) 628 */ 629 public String getSortName() { 630 631 if (m_sort == SORT_DATE_CREATED) { 632 return SORT_NAMES[1]; 633 } 634 if (m_sort == SORT_DATE_LASTMODIFIED) { 635 return SORT_NAMES[2]; 636 } 637 if (m_sort == SORT_TITLE) { 638 return SORT_NAMES[3]; 639 } 640 return SORT_NAMES[0]; 641 } 642 643 /** 644 * Returns <code>true</code> if the category count is calculated for all search results.<p> 645 * 646 * @return <code>true</code> if the category count is calculated for all search results 647 */ 648 public boolean isCalculateCategories() { 649 650 return m_calculateCategories; 651 } 652 653 /** 654 * Returns <code>true</code> if fields configured for the excerpt should be used for generating the excerpt only 655 * if they have been actually searched in.<p> 656 * 657 * The default setting is <code>false</code>, which means all text fields configured for the excerpt will 658 * be used to generate the excerpt, regardless if they have been searched in or not.<p> 659 * 660 * Please note: A field will only be included in the excerpt if it has been configured as <code>excerpt="true"</code> 661 * in <code>opencms-search.xml</code>. This method controls if so configured fields are used depending on the 662 * fields searched, see {@link #setFields(List)}.<p> 663 * 664 * @return <code>true</code> if fields configured for the excerpt should be used for generating the excerpt only 665 * if they have been actually searched in 666 */ 667 public boolean isExcerptOnlySearchedFields() { 668 669 return m_excerptOnlySearchedFields; 670 } 671 672 /** 673 * Returns <code>true</code> if the query part should be ignored so that only filters are used for searching.<p> 674 * 675 * @return <code>true</code> if the query part should be ignored so that only filters are used for searching 676 * 677 * @since 8.0.0 678 */ 679 public boolean isIgnoreQuery() { 680 681 return m_isIgnoreQuery; 682 } 683 684 /** 685 * Creates a merged parameter set from this parameters, restricted by the given other parameters.<p> 686 * 687 * This is mainly intended for "search in search result" functions.<p> 688 * 689 * The restricted query is build of the queries of both parameters, appended with AND.<p> 690 * 691 * The lists in the restriction for <code>{@link #getFields()}</code>, <code>{@link #getRoots()}</code> and 692 * <code>{@link #getCategories()}</code> are <b>intersected</b> with the lists of this search parameters. Only 693 * elements contained in both lists are included for the created search parameters. 694 * If a list in either the restriction or in this search parameters is <code>null</code>, 695 * the list from the other search parameters is used directly.<p> 696 * 697 * The values for 698 * <code>{@link #isCalculateCategories()}</code> 699 * and <code>{@link #getSort()}</code> of this parameters are used for the restricted parameters.<p> 700 * 701 * @param restriction the parameters to restrict this parameters with 702 * @return the restricted parameters 703 */ 704 public CmsSearchParameters restrict(CmsSearchParameters restriction) { 705 706 // append queries 707 StringBuffer query = new StringBuffer(256); 708 if (getQuery() != null) { 709 // don't blow up unnecessary closures (if CmsSearch is reused and restricted several times) 710 boolean closure = !getQuery().startsWith("+("); 711 if (closure) { 712 query.append("+("); 713 } 714 query.append(getQuery()); 715 if (closure) { 716 query.append(")"); 717 } 718 } 719 if (restriction.getQuery() != null) { 720 // don't let Lucene max terms be exceeded in case someone reuses a CmsSearch and continuously restricts 721 // query with the same restrictions... 722 if (query.indexOf(restriction.getQuery()) < 0) { 723 query.append(" +("); 724 query.append(restriction.getQuery()); 725 query.append(")"); 726 } 727 } 728 729 // restrict fields 730 List<String> fields = null; 731 if ((m_fields != null) && (m_fields.size() > 0)) { 732 if ((restriction.getFields() != null) && (restriction.getFields().size() > 0)) { 733 fields = ListUtils.intersection(m_fields, restriction.getFields()); 734 } else { 735 fields = m_fields; 736 } 737 } else { 738 fields = restriction.getFields(); 739 } 740 741 // restrict roots 742 List<String> roots = null; 743 if ((m_roots != null) && (m_roots.size() > 0)) { 744 if ((restriction.getRoots() != null) && (restriction.getRoots().size() > 0)) { 745 roots = ListUtils.intersection(m_roots, restriction.getRoots()); 746 // TODO: This only works if there are equal paths in both parameter sets - for two distinct sets 747 // all root restrictions are dropped with an empty list. 748 } else { 749 roots = m_roots; 750 } 751 } else { 752 roots = restriction.getRoots(); 753 } 754 755 // restrict categories 756 List<String> categories = null; 757 if ((m_categories != null) && (m_categories.size() > 0)) { 758 if ((restriction.getCategories() != null) && (restriction.getCategories().size() > 0)) { 759 categories = ListUtils.intersection(m_categories, restriction.getCategories()); 760 } else { 761 categories = m_categories; 762 } 763 } else { 764 categories = restriction.getCategories(); 765 } 766 767 // restrict resource types 768 List<String> resourceTypes = null; 769 if ((m_resourceTypes != null) && (m_resourceTypes.size() > 0)) { 770 if ((restriction.getResourceTypes() != null) && (restriction.getResourceTypes().size() > 0)) { 771 resourceTypes = ListUtils.intersection(m_resourceTypes, restriction.getResourceTypes()); 772 } else { 773 resourceTypes = m_resourceTypes; 774 } 775 } else { 776 resourceTypes = restriction.getResourceTypes(); 777 } 778 779 // create the new search parameters 780 CmsSearchParameters result = new CmsSearchParameters( 781 query.toString(), 782 fields, 783 roots, 784 categories, 785 resourceTypes, 786 m_calculateCategories, 787 m_sort); 788 result.setIndex(getIndex()); 789 return result; 790 } 791 792 /** 793 * Set whether category counts shall be calculated for the corresponding search results or not.<p> 794 * 795 * @param flag true if category counts shall be calculated for the corresponding search results or false if not 796 */ 797 public void setCalculateCategories(boolean flag) { 798 799 m_calculateCategories = flag; 800 } 801 802 /** 803 * Set the list of categories (strings) to this parameters. <p> 804 * 805 * @param categories the list of categories (strings) of this parameters 806 */ 807 public void setCategories(List<String> categories) { 808 809 m_categories = categories; 810 } 811 812 /** 813 * Sets the maximum number of pages which should be shown.<p> 814 * 815 * Enter an odd value to achieve a nice, "symmetric" output.<p> 816 * 817 * @param value the maximum number of pages which should be shown 818 */ 819 public void setDisplayPages(int value) { 820 821 m_displayPages = value; 822 } 823 824 /** 825 * Controls if the excerpt from a field is generated only for searched fields, or for all fields (the default).<p> 826 * 827 * @param excerptOnlySearchedFields if <code>true</code>, the excerpt is generated only from the fields actually searched in 828 * 829 * @see #isExcerptOnlySearchedFields() 830 */ 831 public void setExcerptOnlySearchedFields(boolean excerptOnlySearchedFields) { 832 833 m_excerptOnlySearchedFields = excerptOnlySearchedFields; 834 } 835 836 /** 837 * Sets the list of strings of names of fields to search in. <p> 838 * 839 * @param fields the list of strings of names of fields to search in to set 840 */ 841 public void setFields(List<String> fields) { 842 843 m_fields = fields; 844 } 845 846 /** 847 * Sets the flag to indicate if the query part should be ignored so that only filters are used for searching.<p> 848 * 849 * @param isIgnoreQuery the flag to indicate if the query part should be ignored 850 * 851 * @since 8.0.0 852 */ 853 public void setIgnoreQuery(boolean isIgnoreQuery) { 854 855 m_isIgnoreQuery = isIgnoreQuery; 856 } 857 858 /** 859 * Set the name of the index to search.<p> 860 * 861 * @param indexName the name of the index 862 */ 863 public void setIndex(String indexName) { 864 865 CmsSearchIndex index; 866 if (CmsStringUtil.isNotEmpty(indexName)) { 867 try { 868 I_CmsSearchIndex idx = OpenCms.getSearchManager().getIndex(indexName); 869 index = idx instanceof CmsSearchIndex ? (CmsSearchIndex)idx : null; 870 if (index == null) { 871 throw new CmsException(Messages.get().container(Messages.ERR_INDEX_NOT_FOUND_1, indexName)); 872 } 873 setSearchIndex(index); 874 } catch (Exception exc) { 875 if (LOG.isErrorEnabled()) { 876 LOG.error(Messages.get().getBundle().key(Messages.LOG_INDEX_ACCESS_FAILED_1, indexName), exc); 877 } 878 } 879 } 880 } 881 882 /** 883 * Sets the number of matches per page.<p> 884 * 885 * @param matches the number of matches per page 886 */ 887 public void setMatchesPerPage(int matches) { 888 889 m_matchesPerPage = matches; 890 } 891 892 /** 893 * Sets the maximum creation date a resource must have to be included in the search result.<p> 894 * 895 * @param maxDateCreated the maximum creation date to set 896 */ 897 public void setMaxDateCreated(long maxDateCreated) { 898 899 m_maxDateCreated = maxDateCreated; 900 } 901 902 /** 903 * Sets the maximum last modification date a resource must have to be included in the search result.<p> 904 * 905 * @param maxDateLastModified the maximum last modification date to set 906 */ 907 public void setMaxDateLastModified(long maxDateLastModified) { 908 909 m_maxDateLastModified = maxDateLastModified; 910 } 911 912 /** 913 * Sets the minimum creation date a resource must have to be included in the search result.<p> 914 * 915 * @param minDateCreated the minimum creation date to set 916 */ 917 public void setMinDateCreated(long minDateCreated) { 918 919 m_minDateCreated = minDateCreated; 920 } 921 922 /** 923 * Sets the minimum last modification date a resource must have to be included in the search result.<p> 924 * 925 * @param minDateLastModified he minimum last modification date to set 926 */ 927 public void setMinDateLastModified(long minDateLastModified) { 928 929 m_minDateLastModified = minDateLastModified; 930 } 931 932 /** 933 * Sets the query to search for. <p> 934 * 935 * The decoding here is tailored for query strings that are 936 * additionally manually UTF-8 encoded at client side (javascript) to get around an 937 * issue with special chars in applications that use non- UTF-8 encoding 938 * (e.g. ISO-8859-1) OpenCms applications. It is not recommended to use this with 939 * front ends that don't encode manually as characters like sole "%" (without number suffix) 940 * will cause an Exception.<p> 941 * 942 * @param query the query to search for to set 943 */ 944 public void setQuery(String query) { 945 946 // for use with widgets the exception is thrown here to enforce the error message next to the widget 947 if (query.trim().length() < getQueryLength()) { 948 throw new CmsIllegalArgumentException( 949 Messages.get().container(Messages.ERR_QUERY_TOO_SHORT_1, Integer.valueOf(getQueryLength()))); 950 } 951 m_query = query; 952 } 953 954 /** 955 * Sets the minimum length of the search query.<p> 956 * 957 * @param length the minimum search query length 958 */ 959 public void setQueryLength(int length) { 960 961 m_queryLength = length; 962 } 963 964 /** 965 * Set the list of resource types (strings) to limit the search to. <p> 966 * 967 * @param resourceTypes the list of resource types (strings) to limit the search to 968 * 969 * @since 7.5.1 970 */ 971 public void setResourceTypes(List<String> resourceTypes) { 972 973 m_resourceTypes = resourceTypes; 974 } 975 976 /** 977 * Sets the list of strings of roots to search under for the search.<p> 978 * 979 * @param roots the list of strings of roots to search under for the search to set 980 */ 981 public void setRoots(List<String> roots) { 982 983 m_roots = roots; 984 } 985 986 /** 987 * Set the comma separated search root names to restrict search to.<p> 988 * 989 * @param categories the comma separated category names to restrict search to 990 */ 991 public void setSearchCategories(String categories) { 992 993 setCategories(CmsStringUtil.splitAsList(categories, ',')); 994 } 995 996 /** 997 * Sets the search index to use for the search. <p> 998 * 999 * @param index the search index to use for the search to set. 1000 * 1001 * @throws CmsIllegalArgumentException if null is given as argument 1002 */ 1003 public void setSearchIndex(CmsSearchIndex index) throws CmsIllegalArgumentException { 1004 1005 if (index == null) { 1006 throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_INDEX_NULL_0)); 1007 } 1008 m_index = index; 1009 } 1010 1011 /** 1012 * Set the search page to display. <p> 1013 * 1014 * @param page the search page to display 1015 */ 1016 public void setSearchPage(int page) { 1017 1018 m_page = page; 1019 } 1020 1021 /** 1022 * Set the comma separated search root names to restrict search to.<p> 1023 * 1024 * @param rootNameList the comma separated search root names to restrict search to 1025 */ 1026 public void setSearchRoots(String rootNameList) { 1027 1028 m_roots = CmsStringUtil.splitAsList(rootNameList, ','); 1029 } 1030 1031 /** 1032 * Set the instance that defines the sort order for search results. 1033 * 1034 * @param sortOrder the instance that defines the sort order for search results to set 1035 */ 1036 public void setSort(Sort sortOrder) { 1037 1038 m_sort = sortOrder; 1039 } 1040 1041 /** 1042 * Sets the internal member of type <code>{@link Sort}</code> according to 1043 * the given sort name. <p> 1044 * 1045 * For a list of valid sort names, please see <code>{@link #SORT_NAMES}</code>.<p> 1046 * 1047 * @param sortName the name of the sort to use 1048 * 1049 * @see #SORT_NAMES 1050 */ 1051 public void setSortName(String sortName) { 1052 1053 if (sortName.equals(SORT_NAMES[1])) { 1054 m_sort = SORT_DATE_CREATED; 1055 } else if (sortName.equals(SORT_NAMES[2])) { 1056 m_sort = SORT_DATE_LASTMODIFIED; 1057 } else if (sortName.equals(SORT_NAMES[3])) { 1058 m_sort = SORT_TITLE; 1059 } else { 1060 m_sort = SORT_DEFAULT; 1061 } 1062 } 1063 1064 /** 1065 * Creates a query String build from this search parameters for HTML links.<p> 1066 * 1067 * @return a query String build from this search parameters for HTML links 1068 */ 1069 public String toQueryString() { 1070 1071 StringBuffer result = new StringBuffer(128); 1072 result.append("?action=search"); 1073 result.append("&query="); 1074 result.append(CmsEncoder.encodeParameter(getQuery())); 1075 1076 result.append("&matchesPerPage="); 1077 result.append(getMatchesPerPage()); 1078 result.append("&displayPages="); 1079 result.append(getDisplayPages()); 1080 result.append("&index="); 1081 result.append(CmsEncoder.encodeParameter(getIndex())); 1082 1083 Sort sort = getSort(); 1084 if (sort != CmsSearchParameters.SORT_DEFAULT) { 1085 result.append("&sort="); 1086 if (sort == CmsSearchParameters.SORT_TITLE) { 1087 result.append("title"); 1088 } else if (sort == CmsSearchParameters.SORT_DATE_CREATED) { 1089 result.append("date-created"); 1090 } else if (sort == CmsSearchParameters.SORT_DATE_LASTMODIFIED) { 1091 result.append("date-lastmodified"); 1092 } 1093 } 1094 1095 if ((getCategories() != null) && (getCategories().size() > 0)) { 1096 result.append("&category="); 1097 Iterator<String> it = getCategories().iterator(); 1098 while (it.hasNext()) { 1099 result.append(it.next()); 1100 if (it.hasNext()) { 1101 result.append(','); 1102 } 1103 } 1104 } 1105 1106 if (getMinDateCreated() > Long.MIN_VALUE) { 1107 result.append("&minDateCreated="); 1108 result.append(getMinDateCreated()); 1109 } 1110 if (getMinDateLastModified() > Long.MIN_VALUE) { 1111 result.append("&minDateLastModified="); 1112 result.append(getMinDateLastModified()); 1113 } 1114 if (getMaxDateCreated() < Long.MAX_VALUE) { 1115 result.append("&maxDateCreated="); 1116 result.append(getMaxDateCreated()); 1117 } 1118 if (getMaxDateLastModified() < Long.MAX_VALUE) { 1119 result.append("&maxDateLastModified="); 1120 result.append(getMaxDateLastModified()); 1121 } 1122 1123 if ((getRoots() != null) && (getRoots().size() > 0)) { 1124 result.append("&searchRoots="); 1125 Iterator<String> it = getRoots().iterator(); 1126 while (it.hasNext()) { 1127 result.append(CmsEncoder.encode(it.next())); 1128 if (it.hasNext()) { 1129 result.append(','); 1130 } 1131 } 1132 } 1133 1134 if (isExcerptOnlySearchedFields()) { 1135 result.append("&excerptOnlySearchedFields=true"); 1136 } 1137 1138 return result.toString(); 1139 } 1140 1141 /** 1142 * @see java.lang.Object#toString() 1143 */ 1144 @Override 1145 public String toString() { 1146 1147 StringBuffer result = new StringBuffer(); 1148 result.append("query:["); 1149 result.append(m_query); 1150 result.append("] "); 1151 if ((m_fields != null) && (m_fields.size() > 0)) { 1152 result.append("fields:["); 1153 for (int i = 0; i < m_fields.size(); i++) { 1154 result.append(m_fields.get(i)); 1155 if ((i + 1) < m_fields.size()) { 1156 result.append(", "); 1157 } 1158 } 1159 result.append("] "); 1160 } 1161 if ((m_roots != null) && (m_roots.size() > 0)) { 1162 result.append("roots:["); 1163 for (int i = 0; i < m_roots.size(); i++) { 1164 result.append(m_roots.get(i)); 1165 if ((i + 1) < m_roots.size()) { 1166 result.append(", "); 1167 } 1168 } 1169 result.append("] "); 1170 } 1171 if ((m_categories != null) && (m_categories.size() > 0)) { 1172 result.append("categories:["); 1173 for (int i = 0; i < m_categories.size(); i++) { 1174 result.append(m_categories.get(i)); 1175 if ((i + 1) < m_categories.size()) { 1176 result.append(", "); 1177 } 1178 } 1179 result.append("] "); 1180 } 1181 if ((m_resourceTypes != null) && (m_resourceTypes.size() > 0)) { 1182 result.append("resourceTypes:["); 1183 for (int i = 0; i < m_resourceTypes.size(); i++) { 1184 result.append(m_resourceTypes.get(i)); 1185 if ((i + 1) < m_resourceTypes.size()) { 1186 result.append(", "); 1187 } 1188 } 1189 result.append("] "); 1190 } 1191 if (m_calculateCategories) { 1192 result.append("calculate-categories "); 1193 } 1194 if (m_excerptOnlySearchedFields) { 1195 result.append("excerpt-searched-fields-only "); 1196 } 1197 result.append("sort:["); 1198 if (m_sort == CmsSearchParameters.SORT_DEFAULT) { 1199 result.append("default"); 1200 } else if (m_sort == CmsSearchParameters.SORT_TITLE) { 1201 result.append("title"); 1202 } else if (m_sort == CmsSearchParameters.SORT_DATE_CREATED) { 1203 result.append("date-created"); 1204 } else if (m_sort == CmsSearchParameters.SORT_DATE_LASTMODIFIED) { 1205 result.append("date-lastmodified"); 1206 } else { 1207 result.append("unknown"); 1208 } 1209 result.append("]"); 1210 1211 return result.toString(); 1212 } 1213 1214 /** 1215 * Concatenates the elements of the string list separated by the given separator character.<p> 1216 * 1217 * @param stringList the list 1218 * @param separator the separator 1219 * 1220 * @return the concatenated string 1221 */ 1222 private String toSeparatedString(List<String> stringList, char separator) { 1223 1224 StringBuffer result = new StringBuffer(); 1225 Iterator<String> it = stringList.iterator(); 1226 while (it.hasNext()) { 1227 result.append(it.next()); 1228 if (it.hasNext()) { 1229 result.append(separator); 1230 } 1231 } 1232 return result.toString(); 1233 } 1234}