/*************************************************************************** * * * PunkSearch - Searching over LAN * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ package org.punksearch.web; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.lucene.document.Document; import org.apache.lucene.search.Filter; import org.apache.lucene.search.Query; import org.punksearch.common.IndexFields; import org.punksearch.searcher.EasyQueryParser; import org.punksearch.searcher.SearcherResult; import org.punksearch.searcher.filters.CompositeFilter; import org.punksearch.searcher.filters.FilterFactory; import org.punksearch.searcher.filters.NumberRangeFilter; import org.punksearch.web.filters.OnlineResultFilter; import org.punksearch.web.filters.TypeFilters; /** * @author Yury Soldak (ysoldak@gmail.com) */ public class SearchAction { private static final int MAX_DOCS = 10000; private static Log __log = LogFactory.getLog(SearchParams.class); private SearchParams params; // private SearcherConfig config = SearcherConfig.getInstance(); private long searchTime = 0; private long presentationTime = 0; private int overallCount = 0; public SearchAction(SearchParams params) { this.params = params; } private Filter makeFilter() { if (params.type.equals("advanced")) { NumberRangeFilter sizeFilter = null; if (params.minSize != null || params.maxSize != null) { sizeFilter = FilterFactory.createNumberFilter(IndexFields.SIZE, params.minSize, params.maxSize); } NumberRangeFilter dateFilter = null; if (params.fromDate != null || params.toDate != null) { dateFilter = FilterFactory.createNumberFilter(IndexFields.DATE, params.fromDate, params.toDate); } if (sizeFilter != null || dateFilter != null) { CompositeFilter resultFilter = new CompositeFilter(); if (sizeFilter != null) resultFilter.add(sizeFilter); if (dateFilter != null) resultFilter.add(dateFilter); return resultFilter; } else { return null; } } else { return TypeFilters.get(params.type); } } private Query makeQuery() { EasyQueryParser parser = new EasyQueryParser(); if (params.type.equals("advanced")) { return parser.makeAdvancedQuery(params.dir, params.file, params.ext); } else { return parser.makeSimpleQuery(params.query); } } @Deprecated public List doSearch() { Query query = makeQuery(); Filter filter = makeFilter(); List searchResults = new ArrayList(); if (query != null) { __log.info("query constructed: " + query.toString()); Date startDate = new Date(); SearcherResult result = SearcherWrapper.search(query, params.first, params.last, filter); Date stopDate = new Date(); searchTime = stopDate.getTime() - startDate.getTime(); overallCount = result.count(); searchResults = prepareResults(result.items()); } return searchResults; } public List doSearchGroupped() { return doSearchGroupped(0, MAX_DOCS); } public List doSearchGroupped(int start, int stop) { Query query = makeQuery(); if (query == null) { return new ArrayList(0); } __log.info("query constructed: " + query.toString()); Filter filter = makeFilter(); Date startDate = new Date(); SearcherResult result = SearcherWrapper.search(query, filter, MAX_DOCS); Date stopDate = new Date(); List docs = sortDocsOnlineFirst(result.items()); List searchResults = makeGroupsFromDocs(docs); searchTime = stopDate.getTime() - startDate.getTime(); presentationTime = new Date().getTime() - stopDate.getTime(); overallCount = searchResults.size(); int min = (start > 0) ? Math.max(0, start) : 0; int max = (stop > 0) ? Math.min(overallCount, stop) : 0; if (min > max || min > overallCount) { min = 0; max = 0; } return searchResults.subList(min, max); } // following should be effective while list is linked and not array-based // profile this anyway, since removal still can be expensive (must traverse list until index met) private static List sortDocsOnlineFirst(List docs) { List result = new ArrayList(docs.size()); ResultFilter resultFilter = new OnlineResultFilter(); List idxs = resultFilter.filter(docs); // __log.info("Online docs: " + idxs.size()); Collections.sort(idxs); int count = 0; for (Integer idx : idxs) { Document onlineDoc = docs.get(idx); // docs.remove(idx.intValue()); result.add(onlineDoc); count++; } for (int i = 0; i < docs.size(); i++) { if (!idxs.contains(i)) { result.add(docs.get(i)); } } return result; } public int getOverallCount() { return overallCount; } public long getSearchTime() { return searchTime; } public long getPresentationTime() { return presentationTime; } /** * Groups items (lucene documents) into groups by size and extension (see ItemGroup for details) * * Returns groups sorted according to the order of first group items sort order * * @param docs * the sorted list of lucene documents (sorted by relevance and by online/offline status) * @return list of item groups */ private static List makeGroupsFromDocs(List docs) { List result = new LinkedList(); // following hash was created just in order to speedup the grouping // this enables us to geach potential group quickly and check if item matches it // the hash key is the item size Map> hash = new HashMap>(); for (Document doc : docs) { String size = doc.get(IndexFields.SIZE); Set groups = hash.get(size); if (groups == null) { groups = new HashSet(); hash.put(size, groups); } boolean added = false; for (ItemGroup group : groups) { if (group.matches(doc)) { group.add(doc); added = true; break; } } if (!added) { ItemGroup group = new ItemGroup(doc); groups.add(group); result.add(group); // we have to put the group in result here, since we want to preserve order of items } } return result; } private static List prepareResults(List results) { List searchResults = new ArrayList(results.size()); for (Document doc : results) { searchResults.add(new SearchResult(doc)); } return searchResults; } }