KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > opencms > search > CmsSearch


1 /*
2  * File : $Source: /usr/local/cvs/opencms/src/org/opencms/search/CmsSearch.java,v $
3  * Date : $Date: 2006/03/27 14:52:54 $
4  * Version: $Revision: 1.41 $
5  *
6  * This library is part of OpenCms -
7  * the Open Source Content Mananagement System
8  *
9  * Copyright (c) 2005 Alkacon Software GmbH (http://www.alkacon.com)
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * For further information about Alkacon Software GmbH, please see the
22  * company website: http://www.alkacon.com
23  *
24  * For further information about OpenCms, please see the
25  * project website: http://www.opencms.org
26  *
27  * You should have received a copy of the GNU Lesser General Public
28  * License along with this library; if not, write to the Free Software
29  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30  */

31
32 package org.opencms.search;
33
34 import org.opencms.file.CmsObject;
35 import org.opencms.i18n.CmsEncoder;
36 import org.opencms.main.CmsException;
37 import org.opencms.main.CmsIllegalArgumentException;
38 import org.opencms.main.CmsLog;
39 import org.opencms.main.OpenCms;
40 import org.opencms.util.CmsStringUtil;
41
42 import java.util.ArrayList JavaDoc;
43 import java.util.Arrays JavaDoc;
44 import java.util.Collections JavaDoc;
45 import java.util.Iterator JavaDoc;
46 import java.util.LinkedList JavaDoc;
47 import java.util.List JavaDoc;
48 import java.util.Map JavaDoc;
49 import java.util.TreeMap JavaDoc;
50
51 import org.apache.commons.logging.Log;
52 import org.apache.lucene.search.Sort;
53
54 /**
55  * Helper class to access the search facility within a jsp.<p>
56  *
57  * Typically, the following fields are available for searching:
58  * <ul>
59  * <li>title - the title of a resource</li>
60  * <li>keywords - the keywords of a resource</li>
61  * <li>description - the description of a resource</li>
62  * <li>content - the aggregated content of a resource</li>
63  * <li>created - the creation date of a resource</li>
64  * <li>lastmodified - the date of the last modification of a resource</li>
65  * <li>path - the path to display the resource</li>
66  * <li>channel - the channel of a resource</li>
67  * <li>contentdefinition - the name of the content definition class of a resource</li>
68  * </ul>
69  *
70  * @author Carsten Weinholz
71  * @author Thomas Weckert
72  *
73  * @version $Revision: 1.41 $
74  *
75  * @since 6.0.0
76  */

77 public class CmsSearch implements Cloneable JavaDoc {
78
79     /** The log object for this class. */
80     private static final Log LOG = CmsLog.getLog(CmsSearch.class);
81
82     /** The result categories of a search. */
83     protected Map JavaDoc m_categoriesFound;
84
85     /** The cms object. */
86     protected transient CmsObject m_cms;
87
88     /** The number of displayed pages returned by getPageLinks(). */
89     protected int m_displayPages;
90
91     /** The latest exception. */
92     protected Exception JavaDoc m_lastException;
93
94     /** The number of matches per page. */
95     protected int m_matchesPerPage;
96
97     /** The URL which leads to the next result page. */
98     protected String JavaDoc m_nextUrl;
99
100     /** The number of pages for the result list. */
101     protected int m_pageCount;
102
103     /** The restriction for the search parameters, used for "search in seach result". */
104     protected CmsSearchParameters m_parameterRestriction;
105
106     /** The search parameters used for searching, build out of the given individual parameter values. */
107     protected CmsSearchParameters m_parameters;
108
109     /** The URL which leads to the previous result page. */
110     protected String JavaDoc m_prevUrl;
111
112     /** The current search result. */
113     protected List JavaDoc m_result;
114
115     /** The search parameter String. */
116     protected String JavaDoc m_searchParameters;
117
118     /** The total number of search results matching the query. */
119     protected int m_searchResultCount;
120
121     /**
122      * Default constructor, used to instanciate the search facility as a bean.<p>
123      */

124     public CmsSearch() {
125
126         super();
127
128         m_parameters = new CmsSearchParameters();
129         m_parameters.setSearchRoots("");
130         m_parameters.setSearchPage(1);
131         m_searchResultCount = 0;
132         m_matchesPerPage = 10;
133         m_displayPages = 10;
134         m_parameters.setSort(CmsSearchParameters.SORT_DEFAULT);
135         List JavaDoc fields = new ArrayList JavaDoc(2);
136         fields.add(CmsSearchIndex.DOC_META_FIELDS[0]);
137         fields.add(CmsSearchIndex.DOC_META_FIELDS[1]);
138         m_parameters.setFields(fields);
139     }
140
141     /**
142      * Returns <code>true</code> if a category overview should be shown as part of the result.<p>
143      *
144      * <b>Please note:</b> The calculation of the category count slows down the search time by an order
145      * of magnitude. Make sure that you only use this feature if it's really required!
146      * Be especially careful if your search result list can become large (> 1000 documents), since in this case
147      * overall system performance will certainly be impacted considerably when calculating the categories.<p>
148      *
149      * @return <code>true</code> if a category overview should be shown as part of the result
150      */

151     public boolean getCalculateCategories() {
152
153         return m_parameters.getCalculateCategories();
154     }
155
156     /**
157      * Returns the search categories.<p>
158      *
159      * @return the search categories
160      */

161     public String JavaDoc[] getCategories() {
162
163         List JavaDoc l = m_parameters.getCategories();
164         return (String JavaDoc[])l.toArray(new String JavaDoc[l.size()]);
165     }
166
167     /**
168      * Returns the maximum number of pages which should be shown.<p>
169      *
170      * @return the maximum number of pages which should be shown
171      */

172     public int getDisplayPages() {
173
174         return m_displayPages;
175     }
176
177     /**
178      * Gets the current fields list.<p>
179      *
180      * @return the fields to search
181      */

182     public String JavaDoc getFields() {
183
184         if (m_parameters.getFields() == null) {
185             return "";
186         }
187         StringBuffer JavaDoc result = new StringBuffer JavaDoc();
188         Iterator JavaDoc it = m_parameters.getFields().iterator();
189         while (it.hasNext()) {
190             result.append(it.next());
191             result.append(" ");
192         }
193         return result.toString();
194     }
195
196     /**
197      * Gets the name of the current search index.<p>
198      *
199      * @return the name of the index
200      */

201     public String JavaDoc getIndex() {
202
203         return m_parameters.getSearchIndex().getName();
204     }
205
206     /**
207      * Gets the last exception after a search operation.<p>
208      *
209      * @return the exception occured in a search operation or null
210      */

211     public Exception JavaDoc getLastException() {
212
213         return m_lastException;
214     }
215
216     /**
217      * Gets the number of matches displayed on each page.<p>
218      *
219      * @return matches per result page
220      */

221     public int getMatchesPerPage() {
222
223         return m_matchesPerPage;
224     }
225
226     /**
227      * Gets the URL for the link to the next result page.<p>
228      *
229      * @return the URL to the next result page
230      */

231     public String JavaDoc getNextUrl() {
232
233         return m_nextUrl;
234     }
235
236     /**
237      * Creates a sorted map of URLs to link to other search result pages.<p>
238      *
239      * The key values are Integers representing the page number, the entry
240      * holds the corresponding link.<p>
241      *
242      * @return a map with String URLs
243      */

244     public Map JavaDoc getPageLinks() {
245
246         Map JavaDoc links = new TreeMap JavaDoc();
247         if (m_pageCount <= 1) {
248             return links;
249         }
250         int startIndex, endIndex;
251         String JavaDoc link = m_cms.getRequestContext().getUri() + getSearchParameters() + "&searchPage=";
252         if (getDisplayPages() < 1) {
253             // number of displayed pages not limited, build a map with all available page links
254
startIndex = 1;
255             endIndex = m_pageCount;
256         } else {
257             // limited number of displayed pages, calculate page range
258
int currentPage = getSearchPage();
259             int countBeforeCurrent = getDisplayPages() / 2;
260             int countAfterCurrent;
261             if ((currentPage - countBeforeCurrent) < 1) {
262                 // set count before to number of available pages
263
countBeforeCurrent = currentPage - 1;
264             }
265             // set count after to number of remaining pages (- 1 for current page)
266
countAfterCurrent = getDisplayPages() - countBeforeCurrent - 1;
267             // calculate start and end index
268
startIndex = currentPage - countBeforeCurrent;
269             endIndex = currentPage + countAfterCurrent;
270             // check end index
271
if (endIndex > m_pageCount) {
272                 int delta = endIndex - m_pageCount;
273                 // decrease start index with delta to get the right number of displayed pages
274
startIndex -= delta;
275                 // check start index to avoid values < 1
276
if (startIndex < 1) {
277                     startIndex = 1;
278                 }
279                 endIndex = m_pageCount;
280             }
281         }
282
283         // build the sorted tree map of page links
284
for (int i = startIndex; i <= endIndex; i++) {
285             links.put(new Integer JavaDoc(i), (link + i));
286         }
287         return links;
288     }
289
290     /**
291      * Returns the search parameters used for searching, build out of the given individual parameter values.<p>
292      *
293      * @return the search parameters used for searching, build out of the given individual parameter values
294      */

295     public CmsSearchParameters getParameters() {
296
297         if (m_parameterRestriction != null) {
298             m_parameters = m_parameters.restrict(m_parameterRestriction);
299         }
300         return m_parameters;
301
302     }
303
304     /**
305      * Gets the URL for the link to the previous result page.<p>
306      *
307      * @return the URL to the previous result page
308      */

309     public String JavaDoc getPreviousUrl() {
310
311         return m_prevUrl;
312     }
313
314     /**
315      * Gets the current search query.<p>
316      *
317      * @return the current query string or null if no query was set before
318      */

319     public String JavaDoc getQuery() {
320
321         return m_parameters.getQuery();
322     }
323
324     /**
325      * Gets the minimum search query length.<p>
326      *
327      * @return the minimum search query length
328      */

329     public int getQueryLength() {
330
331         return m_parameters.getQueryLength();
332     }
333
334     /**
335      * Gets the current result page.<p>
336      *
337      * @return the current result page
338      */

339     public int getSearchPage() {
340
341         return m_parameters.getSearchPage();
342     }
343
344     /**
345      * Creates a String with the necessary search parameters for page links.<p>
346      *
347      * @return String with search parameters
348      */

349     public String JavaDoc getSearchParameters() {
350
351         // if (m_searchParameters == null) {
352
StringBuffer JavaDoc params = new StringBuffer JavaDoc(128);
353         params.append("?action=search&query=");
354         params.append(CmsEncoder.encodeParameter(m_parameters.getQuery()));
355
356         params.append("&matchesPerPage=");
357         params.append(getMatchesPerPage());
358         params.append("&displayPages=");
359         params.append(getDisplayPages());
360         params.append("&index=");
361         params.append(CmsEncoder.encodeParameter(m_parameters.getIndex()));
362
363         Sort sort = m_parameters.getSort();
364         if (sort != CmsSearchParameters.SORT_DEFAULT) {
365             params.append("&sort=");
366             // TODO: find a better way to name sort
367
if (sort == CmsSearchParameters.SORT_TITLE) {
368                 params.append("title");
369             } else if (sort == CmsSearchParameters.SORT_DATE_CREATED) {
370                 params.append("date-created");
371             } else if (sort == CmsSearchParameters.SORT_DATE_LASTMODIFIED) {
372                 params.append("date-lastmodified");
373             }
374         }
375
376         if (m_parameters.getCategories() != null) {
377             params.append("&category=");
378             Iterator JavaDoc it = m_parameters.getCategories().iterator();
379             while (it.hasNext()) {
380                 params.append(it.next());
381                 if (it.hasNext()) {
382                     params.append(',');
383                 }
384             }
385         }
386
387         if (m_parameters.getRoots() != null) {
388             params.append("&searchRoots=");
389             Iterator JavaDoc it = m_parameters.getRoots().iterator();
390             while (it.hasNext()) {
391                 params.append(CmsEncoder.encode((String JavaDoc)it.next()));
392                 if (it.hasNext()) {
393                     params.append(',');
394                 }
395             }
396         }
397
398         // TODO: Better move this whole method into class "CmsSearchParameters"
399
int todo = 0;
400         // TODO: handle the multiple search roots (could be easy, just use multiple parameters?)
401
// TODO: handle the categories
402
// TODO: handle the search fields
403
// params.append("&searchRoot=");
404
// params.append(CmsEncoder.encode(m_searchRoots[0]));
405

406         return params.toString();
407         // cw: cannot store searchParameters any longer since resultRestrictions changes query
408
// m_searchParameters = params.toString();
409
// return m_searchParameters;
410
/* } else {
411          return m_searchParameters;
412          } */

413     }
414
415     /**
416      * Returns the search result for the current query, as a list of <code>{@link CmsSearchResult}</code> objects.<p>
417      *
418      * @return the search result (may be empty) or null if no index or query was set before
419      */

420     public List JavaDoc getSearchResult() {
421
422         if (m_cms != null
423             && m_result == null
424             && m_parameters.getIndex() != null
425             && CmsStringUtil.isNotEmpty(m_parameters.getQuery())) {
426
427             if ((getQueryLength() > 0) && (m_parameters.getQuery().trim().length() < getQueryLength())) {
428
429                 m_lastException = new CmsSearchException(Messages.get().container(
430                     Messages.ERR_QUERY_TOO_SHORT_1,
431                     new Integer JavaDoc(getQueryLength())));
432
433                 return m_result;
434             }
435
436             try {
437
438                 CmsSearchResultList result = m_parameters.getSearchIndex().search(
439                     m_cms,
440                     getParameters(),
441                     m_matchesPerPage);
442
443                 if (result.size() > 0) {
444
445                     m_result = result;
446                     m_searchResultCount = result.getHitCount();
447                     m_categoriesFound = result.getCategories();
448
449                     // re-caluclate the number of pages for this search result
450
m_pageCount = m_searchResultCount / m_matchesPerPage;
451                     if ((m_searchResultCount % m_matchesPerPage) != 0) {
452                         m_pageCount++;
453                     }
454
455                     // re-calculate the URLs to browse forward and backward in the search result
456
String JavaDoc url = m_cms.getRequestContext().getUri() + getSearchParameters() + "&searchPage=";
457                     if (m_parameters.getSearchPage() > 1) {
458                         m_prevUrl = url + (m_parameters.getSearchPage() - 1);
459                     }
460                     if (m_parameters.getSearchPage() < m_pageCount) {
461                         m_nextUrl = url + (m_parameters.getSearchPage() + 1);
462                     }
463                 } else {
464                     m_result = Collections.EMPTY_LIST;
465                     m_searchResultCount = 0;
466                     m_categoriesFound = null;
467                     m_pageCount = 0;
468                     m_prevUrl = null;
469                     m_nextUrl = null;
470                 }
471             } catch (Exception JavaDoc exc) {
472
473                 if (LOG.isDebugEnabled()) {
474                     LOG.debug(Messages.get().getBundle().key(Messages.LOG_SEARCHING_FAILED_0), exc);
475                 }
476
477                 m_result = null;
478                 m_searchResultCount = 0;
479                 m_pageCount = 0;
480
481                 m_lastException = exc;
482             }
483         }
484
485         return m_result;
486     }
487
488     /**
489      * Returns a map of categories (Strings) for the last search result, mapped to the hit count (Integer) of
490      * the documents in this category, or <code>null</code> if the categories have not been calculated.<p>
491      *
492      * @return a map of categories for the last search result
493      *
494      * @see CmsSearch#getCalculateCategories()
495      * @see CmsSearch#setCalculateCategories(boolean)
496      */

497     public Map JavaDoc getSearchResultCategories() {
498
499         return m_categoriesFound;
500     }
501
502     /**
503      * Returns the total number of search results matching the query.<p>
504      *
505      * @return the total number of search results matching the query
506      */

507     public int getSearchResultCount() {
508
509         return m_searchResultCount;
510     }
511
512     /**
513      * Returns the search roots.<p>
514      *
515      * Only resources that are sub-resources of one of the search roots
516      * are included in the search result.<p>
517      *
518      * The search roots are used <i>in addition to</i> the current site root
519      * of the user performing the search.<p>
520      *
521      * By default, the search roots contain only one entry with an empty string.<p>
522      *
523      * @return the search roots
524      */

525     public String JavaDoc[] getSearchRoots() {
526
527         List JavaDoc l = m_parameters.getRoots();
528         return (String JavaDoc[])l.toArray(new String JavaDoc[l.size()]);
529     }
530
531     /**
532      * Returns the sort order used for sorting the results of s search.<p>
533      *
534      * @return the sort order used for sorting the results of s search
535      */

536     public Sort getSortOrder() {
537
538         return m_parameters.getSort();
539     }
540
541     /**
542      * Initializes the bean with the cms object.<p>
543      *
544      * @param cms the cms object
545      */

546     public void init(CmsObject cms) {
547
548         m_cms = cms;
549         m_result = null;
550         m_lastException = null;
551         m_pageCount = 0;
552         m_nextUrl = null;
553         m_prevUrl = null;
554     }
555
556     /**
557      * Sets the flag that controls calculation of result categories for the next search,
558      * use this only if it's really required since the search can become very slow using this option.<p>
559      *
560      * <b>Please note:</b> The calculation of the category count slows down the search time by an order
561      * of magnitude. Make sure that you only use this feature if it's really required!
562      * Be especially careful if your search result list can become large (> 1000 documents), since in this case
563      * overall system performance will certainly be impacted considerably when calculating the categories.<p>
564      *
565      * @param calculateCategories if <code>true</code>, the category count will be calculated for the next search
566      */

567     public void setCalculateCategories(boolean calculateCategories) {
568
569         m_parameters.setCalculateCategories(calculateCategories);
570     }
571
572     /**
573      * Sets the search categories, all search results must be in one of the categories,
574      * the category set must match the indexed category exactly.<p>
575      *
576      * All categories will automatically be trimmed and lowercased, since search categories
577      * are also stored this way in the index.<p>
578      *
579      * @param categories the categories to set
580      */

581     public void setCategories(String JavaDoc[] categories) {
582
583         List JavaDoc setCategories = new LinkedList JavaDoc();
584         if (categories != null) {
585             if (categories.length != 0) {
586                 // ensure all categories are not null, trimmed, not-empty and lowercased
587
String JavaDoc cat;
588                 for (int i = 0; i < categories.length; i++) {
589                     cat = categories[i];
590                     if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(cat)) {
591                         // all categories must internally be lower case,
592
// since the index keywords are lowercased as well
593
cat = cat.trim().toLowerCase();
594                         setCategories.add(cat);
595                     }
596                 }
597             }
598         }
599         m_parameters.setCategories(setCategories);
600         resetLastResult();
601     }
602
603     /**
604      * Sets the maximum number of pages which should be shown.<p>
605      *
606      * Enter an odd value to achieve a nice, "symmetric" output.<p>
607      *
608      * @param value the maximum number of pages which should be shown
609      */

610     public void setDisplayPages(int value) {
611
612         m_displayPages = value;
613     }
614
615     /**
616      * Sets the fields to search.<p>
617      *
618      * If the fields are set to <code>null</code>,
619      * or not set at all, the default fields "content" and "meta" are used.<p>
620      *
621      * For a list of valid field names, see the Interface constants of
622      * <code>{@link org.opencms.search.documents.I_CmsDocumentFactory}</code>.
623      *
624      * @param fields the fields to search
625      */

626     public void setField(String JavaDoc[] fields) {
627
628         List JavaDoc l = new LinkedList JavaDoc(Arrays.asList(fields));
629         m_parameters.setFields(l);
630         resetLastResult();
631     }
632
633     /**
634      * Set the name of the index to search.<p>
635      *
636      * A former search result will be deleted.<p>
637      *
638      * @param indexName the name of the index
639      */

640     public void setIndex(String JavaDoc indexName) {
641
642         resetLastResult();
643         CmsSearchIndex index;
644         if (CmsStringUtil.isNotEmpty(indexName)) {
645             try {
646                 index = OpenCms.getSearchManager().getIndex(indexName);
647                 if (index == null) {
648                     throw new CmsException(Messages.get().container(Messages.ERR_INDEX_NOT_FOUND_1, indexName));
649                 }
650                 m_parameters.setSearchIndex(index);
651             } catch (Exception JavaDoc exc) {
652                 if (LOG.isDebugEnabled()) {
653                     LOG.debug(Messages.get().getBundle().key(Messages.LOG_INDEX_ACCESS_FAILED_1, indexName), exc);
654                 }
655                 m_lastException = exc;
656             }
657         }
658     }
659
660     /**
661      * Sets the number of matches per page.<p>
662      *
663      * @param matches the number of matches per page
664      */

665     public void setMatchesPerPage(int matches) {
666
667         m_matchesPerPage = matches;
668         resetLastResult();
669     }
670
671     /**
672      * Set the parameters to use if a non null instance is provided. <p>
673      *
674      * @param parameters the parameters to use for the search if a non null instance is provided
675      *
676      */

677     public void setParameters(CmsSearchParameters parameters) {
678
679         if (parameters != null) {
680             m_parameters = parameters;
681         }
682     }
683
684     /**
685      * Sets the search query.<p>
686      *
687      * The syntax of the query depends on the search engine used.
688      * A former search result will be deleted.<p>
689      *
690      * @param query the search query (escaped format)
691      */

692     public void setQuery(String JavaDoc query) {
693
694         try {
695             m_parameters.setQuery(CmsEncoder.decodeParameter(query));
696         } catch (CmsIllegalArgumentException iae) {
697             m_lastException = iae;
698         }
699         resetLastResult();
700     }
701
702     /**
703      * Sets the minimum length of the search query.<p>
704      *
705      * @param length the minimum search query length
706      */

707     public void setQueryLength(int length) {
708
709         m_parameters.setQueryLength(length);
710     }
711
712     /**
713      * Restrict the result of the next search to the results of the last search,
714      * restricted with the provided parameters.<p>
715      *
716      * Use this for "seach in search result" functions.<p>
717      *
718      * @param restriction the restriction to use
719      *
720      * @see CmsSearchParameters#restrict(CmsSearchParameters)
721      */

722     public void setResultRestriction(CmsSearchParameters restriction) {
723
724         resetLastResult();
725         m_parameterRestriction = restriction;
726     }
727
728     /**
729      * Sets the current result page.<p>
730      *
731      * Works with jsp bean mechanism for request parameter "searchPage"
732      * that is generated here for page links.<p>
733      *
734      * @param page the current result page
735      */

736     public void setSearchPage(int page) {
737
738         m_parameters.setSearchPage(page);
739         resetLastResult();
740     }
741
742     /**
743      * Convenience method to set exactly one search root.<p>
744      *
745      * @param searchRoot the search root to set
746      *
747      * @see #setSearchRoots(String[])
748      */

749     public void setSearchRoot(String JavaDoc searchRoot) {
750
751         setSearchRoots(CmsStringUtil.splitAsArray(searchRoot, ","));
752     }
753
754     /**
755      * Sets the search root list.<p>
756      *
757      * Only resources that are sub-resources of one of the search roots
758      * are included in the search result.<p>
759      *
760      * The search roots set here are used <i>in addition to</i> the current site root
761      * of the user performing the search.<p>
762      *
763      * By default, the search roots contain only one entry with an empty string.<p>
764      *
765      * @param searchRoots the search roots to set
766      */

767     public void setSearchRoots(String JavaDoc[] searchRoots) {
768
769         List JavaDoc l = new LinkedList JavaDoc(Arrays.asList(searchRoots));
770         m_parameters.setRoots(l);
771         resetLastResult();
772     }
773
774     /**
775      * Sets the sort order used for sorting the results of s search.<p>
776      *
777      * @param sortOrder the sort order to set
778      */

779     public void setSortOrder(Sort sortOrder) {
780
781         m_parameters.setSort(sortOrder);
782         resetLastResult();
783     }
784
785     /**
786      * Resets the last seach result.<p>
787      */

788     private void resetLastResult() {
789
790         m_result = null;
791         m_lastException = null;
792         m_categoriesFound = null;
793         m_parameterRestriction = null;
794     }
795
796 }
Popular Tags