/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nutch.webapp.common;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.io.Writable;
import org.apache.nutch.html.Entities;
import org.apache.nutch.searcher.Hit;
import org.apache.nutch.searcher.HitDetails;
import org.apache.nutch.searcher.Hits;
import org.apache.nutch.searcher.Query;
import org.apache.nutch.searcher.Summary;
/**
* Search is a bean that represents an ongoing search.
*
* After search searchBean that represents the search user doing (also the
* results) might be a good candidate for caching ?
*
*/
public class Search implements Writable {
private static final long serialVersionUID = 1L;
public static final String REQ_ATTR_SEARCH="nutchSearch";
public static final Log LOG = LogFactory.getLog(Search.class);
String queryString;
String htmlQueryString;
Query query;
int startOffset;
int hitsPerDup;
// Number of results per page
int hitsPerPage;
// Number of hits required, for example clustering plugin might require more
// than hitsPerPage hits
int hitsRequired=0;
String sortColumn;
boolean sortDesc;
Hits hits;
Hit[] show;
HitDetails[] details;
Summary[] summaries;
ArrayList results = null;
NavigationHelper navigationHelper;
ServiceLocator locator;
SearchForm form;
String dupField;
/**
* Perform search described by this bean
*
* @param bean
*/
public void performSearch() {
LOG.info("performing search, requiring:" + getHitsRequired());
try {
LOG.info("query:" + getQuery());
LOG.info("endOffset:" + getStartOffset() + getHitsRequired());
LOG.info("hitsPerDup:" + getHitsPerDup());
LOG.info("dupField:" + getDupField());
LOG.info("sortField:" + getSortColumn());
LOG.info("descending:" + isSortDesc());
hits = locator.getNutchBean().search(getQuery(),
getStartOffset() + getHitsRequired(), getHitsPerDup(), getDupField(),
getSortColumn(), isSortDesc());
} catch (IOException e) {
hits = new Hits(0, new Hit[0]);
}
int realEnd = (int) Math.min(hits.getLength(), getStartOffset()
+ getHitsRequired());
init();
show = hits.getHits(getStartOffset(), realEnd - getStartOffset());
try {
details = locator.getNutchBean().getDetails(show);
summaries = locator.getNutchBean().getSummary(details, getQuery());
} catch (IOException e) {
LOG.info("Error getting hit information:" + e);
e.printStackTrace();
}
}
public void init(){
int endOffset=hits.getLength();
navigationHelper = new NavigationHelper(startOffset, endOffset, hitsPerPage, hits
.getTotal(), hits.totalIsExact());
// set offset to next page to form so it get's to ui
if (navigationHelper.hasNext()) {
form.setValue(SearchForm.NAME_START, Long.toString(navigationHelper
.getNextPageStart()));
}
}
/**
* Gets the results of search to display.
*
* @return
*/
public List getResults() {
int len=Math.min(details.length,getHitsPerPage());
if (results == null) {
results = new ArrayList(len);
for (int i = 0; i < len; i++) {
results.add(new SearchResultBean(this, show[i], details[i],
summaries[i]));
}
}
return results;
}
protected int parseInt(String value, int defaultValue) {
int ret = defaultValue;
if (value != null) {
try {
ret = Integer.parseInt(value);
} catch (Exception e) {
// ignore
}
}
return ret;
}
public Search(){
}
public Search(ServiceLocator locator) {
this.locator = locator;
Preferences prefs = locator.getPreferences();
form = locator.getSearchForm();
queryString = form.getValueString(SearchForm.NAME_QUERYSTRING);
if (queryString == null) {
queryString = "";
}
htmlQueryString = Entities.encode(queryString);
startOffset = parseInt(form.getValueString(SearchForm.NAME_START), 0);
hitsPerPage = parseInt(form.getValueString(SearchForm.NAME_HITSPERPAGE),
prefs.getInt(Preferences.KEY_RESULTS_PER_PAGE, 10));
hitsPerDup = parseInt(form.getValueString(SearchForm.NAME_HITSPERDUP),
prefs.getInt(
Preferences.KEY_HITS_PER_DUP, 2));
sortColumn = form.getValueString(SearchForm.NAME_SORTCOLUMN);
sortDesc = (sortColumn != null && "true".equals(form
.getValueString(SearchForm.NAME_SORTREVERSE)));
dupField = form.getValueString(SearchForm.NAME_DUPCOLUMN);
if (dupField == null) {
dupField = "site";
}
try {
query = Query.parse(queryString, locator.getConfiguration());
} catch (IOException e) {
LOG.info("Error parsing query:" + e);
e.printStackTrace();
}
}
/**
* @return Returns the hitsPerPage.
*/
public int getHitsPerPage() {
return hitsPerPage;
}
/**
* @param hitsPerPage
* The hitsPerPage to set.
*/
protected void setHitsPerPage(int hitsPerPage) {
this.hitsPerPage = hitsPerPage;
}
/**
* @return Returns the hitsPerSite.
*/
public int getHitsPerDup() {
return hitsPerDup;
}
/**
* @param hitsPerSite
* The hitsPerSite to set.
*/
protected void setHitsPerSite(int hitsPerSite) {
this.hitsPerDup = hitsPerSite;
}
/**
* @return Returns the query.
*/
public Query getQuery() {
return query;
}
/**
* @param query
* The query to set.
*/
public void setQuery(Query query) {
this.query = query;
}
/**
* @return Returns the queryString.
*/
public String getQueryString() {
return queryString;
}
/**
* @param queryString
* The queryString to set.
*/
protected void setQueryString(String queryString) {
this.queryString = queryString;
}
/**
* Returns true if sort is descending.
*
* @return Returns sort order
*/
public boolean isSortDesc() {
return sortDesc;
}
/**
* Set sort order.
*
* @param sortAsc
* The sortAsc to set.
*/
protected void setSortDesc(boolean sortAsc) {
this.sortDesc = sortAsc;
}
/**
* @return Returns the sortColumn.
*/
public String getSortColumn() {
return sortColumn;
}
/**
* @param sortColumn
* The sortColumn to set.
*/
protected void setSortColumn(String sortColumn) {
this.sortColumn = sortColumn;
}
/**
* @return Returns the startOffset.
*/
public int getStartOffset() {
return startOffset;
}
/**
* @param startOffset
* The startOffset to set.
*/
protected void setStartOffset(int startOffset) {
this.startOffset = startOffset;
}
/**
* @return Returns the htmlQueryString.
*/
public String getHtmlQueryString() {
return htmlQueryString;
}
/**
* @param htmlQueryString
* The htmlQueryString to set.
*/
protected void setHtmlQueryString(String htmlQueryString) {
this.htmlQueryString = htmlQueryString;
}
/**
* @return Returns the details.
*/
public HitDetails[] getDetails() {
return details;
}
/**
* @param details
* The details to set.
*/
protected void setDetails(HitDetails[] details) {
this.details = details;
}
/**
* @return Returns the hits.
*/
public Hits getHits() {
return hits;
}
/**
* @param hits
* The hits to set.
*/
protected void setHits(Hits hits) {
this.hits = hits;
}
/**
* @return Returns the show.
*/
public Hit[] getShow() {
return show;
}
/**
* @param show
* The show to set.
*/
protected void setShow(Hit[] show) {
this.show = show;
}
/**
* @return Returns the summaries.
*/
public Summary[] getSummaries() {
return summaries;
}
/**
* @param summaries
* The summaries to set.
*/
protected void setSummaries(Summary[] summaries) {
this.summaries = summaries;
}
/**
* returns start, end, total, used for printing message on search page, this
* is why offset is +1'd.
*/
public String[] getResultInfo() {
return new String[] { Long.toString(startOffset + 1),
Long.toString(navigationHelper.getEnd()),
Long.toString(hits.getTotal()), getHtmlQueryString() };
}
/**
* @return true if more results available
*/
public boolean getHasNextPage() {
return navigationHelper.hasNext();
}
/**
* @return true if previous page if available
*/
public boolean getHasPrevPage() {
return navigationHelper.hasPrev();
}
public String getDupField() {
return dupField;
}
protected SearchForm getForm() {
return form;
}
/**
* @return
*/
public List getFormProperties() {
return form.getActive();
}
public boolean getShowAllHits() {
if(navigationHelper.getShowAllHits()){
//remove start parameter from form
getForm().remove(SearchForm.NAME_START);
//add hitsPerDup=0
getForm().setValue(SearchForm.NAME_HITSPERDUP,"0");
}
return navigationHelper.getShowAllHits();
}
/**
* Return true if there are results.
*
* @return
*/
public boolean getHasResults() {
return hits.getTotal() > 0;
}
public boolean getIsSearch() {
return queryString != null && !queryString.trim().equals("");
}
/**
* Return number of hits required, if not specified defaults to HitsPerPage.
* @return
*/
public int getHitsRequired() {
if (hitsRequired != 0) {
return hitsRequired;
}
return getHitsPerPage();
}
/**
* Set number of hits required.
* @param hitsRequired
*/
public void setHitsRequired(int hitsRequired) {
this.hitsRequired = hitsRequired;
}
/**
* Launch search.
*/
public void launchSearch() {
BaseSearch bs = new BaseSearch(locator);
bs.doSearch();
}
public void write(DataOutput out) throws IOException {
LOG.info("writing hits");
hits.write(out);
out.writeInt(show.length);
for (int i = 0; i < show.length; i++) {
show[i].write(out);
}
out.writeInt(details.length);
for (int i = 0; i < details.length; i++) {
details[i].write(out);
}
out.writeInt(summaries.length);
for (int i = 0; i < summaries.length; i++) {
summaries[i].write(out);
}
}
public void readFields(DataInput in) throws IOException {
hits = new Hits();
hits.readFields(in);
int showlength = in.readInt();
show = new Hit[showlength];
for (int i = 0; i < showlength; i++) {
show[i] = new Hit();
show[i].readFields(in);
}
int detailsLength = in.readInt();
details = new HitDetails[detailsLength];
for (int i = 0; i < detailsLength; i++) {
details[i] = new HitDetails();
details[i].readFields(in);
}
int summariesLength = in.readInt();
summaries = new Summary[summariesLength];
for (int i = 0; i < summariesLength; i++) {
summaries[i] = new Summary();
summaries[i].readFields(in);
}
}
}