001: /*
002: Licensed to the Apache Software Foundation (ASF) under one or more
003: contributor license agreements. See the NOTICE file distributed with
004: this work for additional information regarding copyright ownership.
005: The ASF licenses this file to You under the Apache License, Version 2.0
006: (the "License"); you may not use this file except in compliance with
007: the License. You may obtain a copy of the License at
008:
009: http://www.apache.org/licenses/LICENSE-2.0
010:
011: Unless required by applicable law or agreed to in writing, software
012: distributed under the License is distributed on an "AS IS" BASIS,
013: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: See the License for the specific language governing permissions and
015: limitations under the License.
016: */
017:
018: package org.apache.cocoon.bean.query;
019:
020: import java.io.IOException;
021: import java.io.Serializable;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.Date;
025: import java.util.ArrayList;
026: import java.util.HashMap;
027: import java.util.Enumeration;
028:
029: import org.apache.lucene.document.Document;
030: import org.apache.lucene.document.Field;
031: import org.apache.lucene.search.Hits;
032: import org.apache.lucene.search.Query;
033: import org.apache.lucene.search.BooleanQuery;
034: import org.apache.cocoon.components.search.LuceneCocoonSearcher;
035: import org.apache.cocoon.ProcessingException;
036:
037: /**
038: * The query bean.
039: * <p>
040: * This object defines a <code>Bean</code> for searching.<br/>
041: * The idea is to abstract the process of searching into a Bean to be manipulated by CForms.<br/>
042: * This Bean is designed to be persistable.
043: * </p>
044: *
045: * @version CVS $Id: SimpleLuceneQueryBean.java 433543 2006-08-22 06:22:54Z crossley $
046: */
047: public class SimpleLuceneQueryBean implements SimpleLuceneQuery,
048: Cloneable, Serializable {
049:
050: /**
051: * The DEFAULT_PAGE_SIZE of this bean.
052: * ie. <code>20</code>
053: */
054: public static Long DEFAULT_PAGE_SIZE = new Long(20);
055:
056: /**
057: * The DEFAULT_PAGE of this bean.
058: * ie. <code>0</code>
059: */
060: public static Long DEFAULT_PAGE = new Long(0);
061:
062: /**
063: * The SCORE_FIELD of this bean.
064: * This is the key of the Lucene Score as output by this Bean in each hit.
065: * ie. <code>_lucene-score_</code>
066: */
067: public static String SCORE_FIELD = "_lucene-score_";
068:
069: /**
070: * The INDEX_FIELD of this bean.
071: * This is the key of the hit index as output by this Bean in each hit.
072: * ie. <code>_lucene-index_</code>
073: */
074: public static String INDEX_FIELD = "_lucene-index_";
075:
076: /**
077: * The date this Query was created.
078: */
079: private Date date;
080:
081: /**
082: * The Bean's list of Criteria.
083: */
084: private List criteria;
085:
086: /**
087: * The Bean's ID.
088: */
089: private Long id;
090:
091: /**
092: * The Bean's current page.
093: */
094: private Long page;
095:
096: /**
097: * The Bean's page isze.
098: */
099: private Long size;
100:
101: /**
102: * The Bean's hit count.
103: */
104: private Long total;
105:
106: /**
107: * The Bean's query boolean.
108: */
109: private String bool;
110:
111: /**
112: * The Bean's query name.
113: */
114: private String name;
115:
116: /**
117: * The Bean's query type.
118: */
119: private String type;
120:
121: /**
122: * The Bean's owner.
123: */
124: private String user;
125:
126: /**
127: * Default constructor.
128: */
129: public SimpleLuceneQueryBean() {
130: }
131:
132: /**
133: * Utility constructor.
134: *
135: * @param type the type of this query
136: * @param bool the kind of boolean opperation to apply to each of it's Criteria
137: * @param match the kind of match to use for the generated <code>Criterion</code>
138: * @param field the field to search for the generated <code>Criterion</code>
139: * @param query the terms to search for the generated <code>Criterion</code>
140: */
141: public SimpleLuceneQueryBean(String type, String bool,
142: String match, String field, String query) {
143: this .name = "My Query";
144: this .type = type;
145: this .bool = bool;
146: this .size = DEFAULT_PAGE_SIZE;
147: this .page = DEFAULT_PAGE;
148: this .total = null;
149: this .user = null;
150: this .id = null;
151: this .addCriterion(new SimpleLuceneCriterionBean(field, match,
152: query));
153: }
154:
155: public Object clone() throws CloneNotSupportedException {
156: SimpleLuceneQueryBean query = (SimpleLuceneQueryBean) super
157: .clone();
158: query.setCriteria(new ArrayList(this .criteria.size()));
159: Iterator it = this .getCriteria().iterator();
160: while (it.hasNext())
161: query
162: .addCriterion((SimpleLuceneCriterionBean) ((SimpleLuceneCriterionBean) it
163: .next()).clone());
164: return query;
165: }
166:
167: /**
168: * Gets the Bean to perform it's query
169: * <p>
170: * The searcher specifies which LuceneCocoonSearcher to use for this search.<br/>
171: * It needs to have been initialised properly before use.<br/>
172: * Each <code>Map</code> in the <code>List</code> returned by this method contains:
173: * <ul>
174: * <li>Each stored field from the Index</li>
175: * <li><code>SCORE_FIELD</code> the Lucene score</li>
176: * <li><code>INDEX_FIELD</code> the index of the hit</li>
177: * </ul>
178: * </p>
179: *
180: * @param searcher The <code>LuceneCocoonSearcher</code> to use for this search
181: * @return a List of Maps, each representing a Hit.
182: * @exception ProcessingException thrown by the searcher
183: * @exception IOException thrown when the searcher's directory cannot be found
184: */
185: public List search(LuceneCocoonSearcher searcher)
186: throws IOException, ProcessingException {
187: BooleanQuery query = new BooleanQuery();
188: Iterator it = criteria.iterator();
189: boolean required = false;
190: if (AND_BOOL.equals(this .bool))
191: required = true;
192: while (it.hasNext()) {
193: SimpleLuceneCriterion criterion = (SimpleLuceneCriterion) it
194: .next();
195: Query subquery = criterion.getQuery(searcher.getAnalyzer());
196: query.add(subquery, required, criterion.isProhibited());
197: }
198: Hits hits = searcher.search(query);
199: this .total = new Long(hits.length());
200: this .date = new Date();
201: return page(hits);
202: }
203:
204: /**
205: * Outputs part of a Hit List according to the Bean's paging properties.
206: *
207: * @param hits The Lucene Hits you want to page
208: * @return a List of Maps, each representing a Hit.
209: * @exception IOException thrown when the searcher's directory cannot be found
210: */
211: private List page(Hits hits) throws java.io.IOException {
212: ArrayList results = new ArrayList();
213: int start = getPage().intValue() * getSize().intValue();
214: if (start > this .total.intValue())
215: start = this .total.intValue();
216: int end = start + getSize().intValue();
217: if (end > this .total.intValue())
218: end = this .total.intValue();
219: for (int i = start; i < end; i++) {
220: HashMap hit = new HashMap();
221: hit.put(SCORE_FIELD, new Float(hits.score(i)));
222: hit.put(INDEX_FIELD, new Long(i));
223: Document doc = hits.doc(i);
224: for (Enumeration e = doc.fields(); e.hasMoreElements();) {
225: Field field = (Field) e.nextElement();
226: if (field.name().equals(SCORE_FIELD))
227: continue;
228: if (field.name().equals(INDEX_FIELD))
229: continue;
230: hit.put(field.name(), field.stringValue());
231: }
232: results.add(hit);
233: }
234: return (results);
235: }
236:
237: /**
238: * Gets the Bean's ID.
239: *
240: * @return the <code>Long</code> ID of the Bean.
241: */
242: public Long getId() {
243: return this .id;
244: }
245:
246: /**
247: * Sets the Bean's ID.
248: *
249: * @param id the <code>Long</code> ID of the Bean.
250: */
251: public void setId(Long id) {
252: this .id = id;
253: }
254:
255: /**
256: * Gets the Bean's name.
257: *
258: * @return the <code>String</code> name of the Bean.
259: */
260: public String getName() {
261: return this .name;
262: }
263:
264: /**
265: * Sets the Bean's Name.
266: *
267: * @param name the <code>String</code> name of the Bean.
268: */
269: public void setName(String name) {
270: this .name = name;
271: }
272:
273: /**
274: * Gets the Bean's type.
275: *
276: * @return the <code>String</code> type of the Bean.
277: */
278: public String getType() {
279: return this .type;
280: }
281:
282: /**
283: * Sets the Bean's type.
284: *
285: * @param type the <code>String</code> type of the Bean.
286: */
287: public void setType(String type) {
288: this .type = type;
289: }
290:
291: /**
292: * Gets the Bean's boolean operator.
293: *
294: * @return the <code>String</code> boolean of the Bean.
295: */
296: public String getBool() {
297: return this .bool;
298: }
299:
300: /**
301: * Sets the Bean's boolean operator.
302: * ie. which kind of boolean operation do you want performed on each <code>Criterion</code>.
303: *
304: * @param bool the <code>String</code> boolean of the Bean.
305: */
306: public void setBool(String bool) {
307: this .bool = bool;
308: }
309:
310: /**
311: * Gets the Bean's owner.
312: *
313: * @return the <code>String</code> owner of the Bean.
314: */
315: public String getUser() {
316: return this .user;
317: }
318:
319: /**
320: * Sets the Bean's owner.
321: *
322: * @param user the <code>String</code> owner of the Bean.
323: */
324: public void setUser(String user) {
325: this .user = user;
326: }
327:
328: /**
329: * Gets the Bean's page size
330: *
331: * @return the <code>Long</code> page size of the Bean.
332: */
333: public Long getSize() {
334: if (this .size == null) {
335: return DEFAULT_PAGE_SIZE;
336: } else {
337: return this .size;
338: }
339: }
340:
341: /**
342: * Sets the Bean's page size.
343: * ie. how many hits do you want this Bean to show on in page.
344: *
345: * @param size the <code>Long</code> page size of the Bean.
346: */
347: public void setSize(Long size) {
348: this .size = size;
349: }
350:
351: /**
352: * Gets the Bean's page index
353: *
354: * @return the <code>Long</code> page index of the Bean.
355: */
356: public Long getPage() {
357: if (this .page == null) {
358: return DEFAULT_PAGE;
359: } else {
360: return this .page;
361: }
362: }
363:
364: /**
365: * Sets the Bean's page index.
366: * ie. which page do you want this Bean to show.
367: *
368: * @param page the <code>Long</code> page index of the Bean.
369: */
370: public void setPage(Long page) {
371: this .page = page;
372: }
373:
374: /**
375: * Gets the Bean's hit count.
376: *
377: * @return the <code>Long</code> hit count of the Bean.
378: */
379: public Long getTotal() {
380: return this .total;
381: }
382:
383: /**
384: * Sets the Bean's hit count.
385: *
386: * @param total the <code>Long</code> hit count of the Bean.
387: */
388: public void setTotal(Long total) {
389: this .total = total;
390: }
391:
392: /**
393: * Gets the Bean's inception date.
394: *
395: * @return the <code>Date</code> of the Bean.
396: */
397: public Date getDate() {
398: return this .date;
399: }
400:
401: /**
402: * Sets the Bean's inception date.
403: *
404: * @param date the <code>Date</code> inception date of the Bean.
405: */
406: public void setDate(Date date) {
407: this .date = date;
408: }
409:
410: /**
411: * Gets the Bean's criteria.
412: *
413: * @return the <code>List</code> of Bean Query criteria.
414: */
415: public List getCriteria() {
416: return this .criteria;
417: }
418:
419: /**
420: * Sets the Bean's criteria.
421: *
422: * @param criteria the <code>List</code> of Bean Query criteria.
423: */
424: public void setCriteria(List criteria) {
425: this .criteria = criteria;
426: }
427:
428: /**
429: * Adds a <code>Criterion</code> the Bean.
430: *
431: * @param criterion the <code>SimpleLuceneCriterionBean</code> to add to the Bean.
432: */
433: public void addCriterion(SimpleLuceneCriterionBean criterion) {
434: if (this .criteria == null)
435: this .criteria = new ArrayList();
436: this .criteria.add(criterion);
437: }
438:
439: /**
440: * Removes a <code>Criterion</code> from the Bean.
441: *
442: * @param criterion the <code>SimpleLuceneCriterionBean</code> to remove from the Bean.
443: */
444: public void removeCriterion(SimpleLuceneCriterionBean criterion) {
445: if (this.criteria != null)
446: this.criteria.remove(criterion);
447: }
448:
449: }
|