001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. The ASF licenses this file to You
004: * under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License. For additional information regarding
015: * copyright in this work, please see the NOTICE file in the top level
016: * directory of this distribution.
017: */
018:
019: package org.apache.roller.ui.rendering.model;
020:
021: import java.io.IOException;
022: import java.sql.Timestamp;
023: import java.util.Date;
024: import java.util.Map;
025: import java.util.ResourceBundle;
026: import java.util.Set;
027: import java.util.TreeMap;
028: import java.util.TreeSet;
029: import org.apache.commons.collections.comparators.ReverseComparator;
030: import org.apache.commons.lang.StringUtils;
031: import org.apache.lucene.document.Document;
032: import org.apache.lucene.search.Hits;
033: import org.apache.roller.RollerException;
034: import org.apache.roller.business.search.FieldConstants;
035: import org.apache.roller.business.search.operations.SearchOperation;
036: import org.apache.roller.config.RollerRuntimeConfig;
037: import org.apache.roller.business.search.IndexManager;
038: import org.apache.roller.business.Roller;
039: import org.apache.roller.business.RollerFactory;
040: import org.apache.roller.business.WeblogManager;
041: import org.apache.roller.pojos.WeblogEntryData;
042: import org.apache.roller.pojos.WeblogEntryWrapperComparator;
043: import org.apache.roller.pojos.wrapper.WeblogEntryDataWrapper;
044: import org.apache.roller.ui.rendering.pagers.SearchResultsPager;
045: import org.apache.roller.ui.rendering.pagers.WeblogEntriesPager;
046: import org.apache.roller.ui.rendering.util.WeblogSearchRequest;
047: import org.apache.roller.util.DateUtil;
048:
049: /**
050: * Extends normal page renderer model to represent search results.
051: *
052: * Also adds some new methods which are specific only to search results.
053: */
054: public class SearchResultsModel extends PageModel {
055:
056: private static final ResourceBundle bundle = ResourceBundle
057: .getBundle("ApplicationResources");
058:
059: public static final int RESULTS_PER_PAGE = 10;
060:
061: // the original search request
062: WeblogSearchRequest searchRequest = null;
063:
064: // the actual search results mapped by Day -> Set of entries
065: private TreeMap results = new TreeMap(new ReverseComparator());
066:
067: // the pager used by the 3.0+ rendering system
068: private SearchResultsPager pager = null;
069:
070: private int hits = 0;
071: private int offset = 0;
072: private int limit = 0;
073: private Set categories = new TreeSet();
074: private boolean websiteSpecificSearch = true;
075: private String errorMessage = null;
076:
077: public void init(Map initData) throws RollerException {
078:
079: // we expect the init data to contain a searchRequest object
080: searchRequest = (WeblogSearchRequest) initData
081: .get("searchRequest");
082: if (searchRequest == null) {
083: throw new RollerException(
084: "expected searchRequest from init data");
085: }
086:
087: // let parent initialize
088: super .init(initData);
089:
090: // if there is no query, then we are done
091: if (searchRequest.getQuery() == null) {
092: pager = new SearchResultsPager(searchRequest, results,
093: false);
094: return;
095: }
096:
097: // setup the search
098: IndexManager indexMgr = RollerFactory.getRoller()
099: .getIndexManager();
100:
101: SearchOperation search = new SearchOperation(indexMgr);
102: search.setTerm(searchRequest.getQuery());
103:
104: if (RollerRuntimeConfig.isSiteWideWeblog(searchRequest
105: .getWeblogHandle())) {
106: this .websiteSpecificSearch = false;
107: } else {
108: search.setWebsiteHandle(searchRequest.getWeblogHandle());
109: }
110:
111: if (StringUtils.isNotEmpty(searchRequest
112: .getWeblogCategoryName())) {
113: search.setCategory(searchRequest.getWeblogCategoryName());
114: }
115:
116: // execute search
117: indexMgr.executeIndexOperationNow(search);
118:
119: if (search.getResultsCount() == -1) {
120: // this means there has been a parsing (or IO) error
121: this .errorMessage = bundle.getString("error.searchProblem");
122: } else {
123: Hits hits = search.getResults();
124: this .hits = search.getResultsCount();
125:
126: // Convert the Hits into WeblogEntryData instances.
127: convertHitsToEntries(hits);
128: }
129:
130: // search completed, setup pager based on results
131: pager = new SearchResultsPager(searchRequest, results,
132: (hits > (offset + limit)));
133: }
134:
135: /**
136: * Is this page showing search results?
137: */
138: public boolean isSearchResults() {
139: return true;
140: }
141:
142: // override page model and return search results pager
143: public WeblogEntriesPager getWeblogEntriesPager() {
144: return pager;
145: }
146:
147: // override page model and return search results pager
148: public WeblogEntriesPager getWeblogEntriesPager(String category) {
149: return pager;
150: }
151:
152: private void convertHitsToEntries(Hits hits) throws RollerException {
153:
154: // determine offset
155: this .offset = searchRequest.getPageNum() * RESULTS_PER_PAGE;
156: if (this .offset >= hits.length()) {
157: this .offset = 0;
158: }
159:
160: // determine limit
161: this .limit = RESULTS_PER_PAGE;
162: if (this .offset + this .limit > hits.length()) {
163: this .limit = hits.length() - this .offset;
164: }
165:
166: try {
167: TreeSet categories = new TreeSet();
168: Roller roller = RollerFactory.getRoller();
169: WeblogManager weblogMgr = roller.getWeblogManager();
170:
171: WeblogEntryData entry = null;
172: Document doc = null;
173: String handle = null;
174: Timestamp now = new Timestamp(new Date().getTime());
175: for (int i = offset; i < offset + limit; i++) {
176:
177: entry = null; // reset for each iteration
178:
179: doc = hits.doc(i);
180: handle = doc.getField(FieldConstants.WEBSITE_HANDLE)
181: .stringValue();
182:
183: if (websiteSpecificSearch
184: && handle.equals(searchRequest
185: .getWeblogHandle())) {
186:
187: entry = weblogMgr.getWeblogEntry(doc.getField(
188: FieldConstants.ID).stringValue());
189: } else {
190:
191: entry = weblogMgr.getWeblogEntry(doc.getField(
192: FieldConstants.ID).stringValue());
193:
194: if (doc.getField(FieldConstants.CATEGORY) != null) {
195: categories.add(doc.getField(
196: FieldConstants.CATEGORY).stringValue());
197: }
198: }
199:
200: // maybe null if search result returned inactive user
201: // or entry's user is not the requested user.
202: // but don't return future posts
203: if (entry != null && entry.getPubTime().before(now)) {
204: addEntryToResults(WeblogEntryDataWrapper
205: .wrap(entry));
206: }
207: }
208:
209: if (categories.size() > 0) {
210: this .categories = categories;
211: }
212: } catch (IOException e) {
213: throw new RollerException(e);
214: }
215: }
216:
217: private void addEntryToResults(WeblogEntryDataWrapper entry) {
218:
219: // convert entry's each date to midnight (00m 00h 00s)
220: Date midnight = DateUtil.getStartOfDay(entry.getPubTime());
221:
222: // ensure we do not get duplicates from Lucene by
223: // using a Set Collection. Entries sorted by pubTime.
224: TreeSet set = (TreeSet) this .results.get(midnight);
225: if (set == null) {
226: // date is not mapped yet, so we need a new Set
227: set = new TreeSet(new WeblogEntryWrapperComparator());
228: this .results.put(midnight, set);
229: }
230: set.add(entry);
231: }
232:
233: public String getTerm() {
234: return (searchRequest.getQuery() == null) ? "" : searchRequest
235: .getQuery();
236: }
237:
238: public int getHits() {
239: return hits;
240: }
241:
242: public int getOffset() {
243: return offset;
244: }
245:
246: public int getLimit() {
247: return limit;
248: }
249:
250: public TreeMap getResults() {
251: return results;
252: }
253:
254: public Set getCategories() {
255: return categories;
256: }
257:
258: public boolean isWebsiteSpecificSearch() {
259: return websiteSpecificSearch;
260: }
261:
262: public String getErrorMessage() {
263: return errorMessage;
264: }
265:
266: }
|