/*
* 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.searcher.response.json;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.nutch.searcher.Hit;
import org.apache.nutch.searcher.HitDetails;
import org.apache.nutch.searcher.Summary;
import org.apache.nutch.searcher.response.RequestUtils;
import org.apache.nutch.searcher.response.ResponseWriter;
import org.apache.nutch.searcher.response.SearchResults;
/**
* A ResponseWriter implementation that returns search results in JSON format.
*/
public class JSONResponseWriter
implements ResponseWriter {
private String contentType = null;
private Configuration conf;
private int maxAgeInSeconds;
private boolean prettyPrint = true;
public void setContentType(String contentType) {
this.contentType = contentType;
}
public Configuration getConf() {
return conf;
}
public void setConf(Configuration conf) {
this.conf = conf;
this.maxAgeInSeconds = conf.getInt("searcher.response.maxage", 86400);
this.prettyPrint = conf.getBoolean("searcher.response.prettyprint", true);
}
public void writeResponse(SearchResults results, HttpServletRequest request,
HttpServletResponse response)
throws IOException {
// the function name, if any wrapping the JSON output
String func = RequestUtils.getStringParameter(request, "func");
// create the JSON object and add common values
JSONObject jsonObj = new JSONObject();
jsonObj.accumulate("query", results.getQuery());
jsonObj.accumulate("lang", results.getLang());
jsonObj.accumulate("sort", results.getSort());
jsonObj.accumulate("reverse", results.isReverse());
jsonObj.accumulate("start", results.getStart());
jsonObj.accumulate("end", results.getEnd());
jsonObj.accumulate("rows", results.getRows());
jsonObj.accumulate("totalhits", results.getTotalHits());
jsonObj.accumulate("withSummary", results.isWithSummary());
String[] searchFields = results.getFields();
Set<String> fieldSet = new HashSet<String>();
if (searchFields != null && searchFields.length > 0) {
jsonObj.accumulate("fields", StringUtils.join(searchFields, ","));
for (int i = 0; i < searchFields.length; i++) {
fieldSet.add(searchFields[i]);
}
}
// add the documents from search hits
JSONArray docsAr = new JSONArray();
HitDetails[] details = results.getDetails();
Hit[] hits = results.getHits();
Summary[] summaries = results.getSummaries();
for (int i = 0; i < details.length; i++) {
// every document has an indexno and an indexdocno
JSONObject result = new JSONObject();
HitDetails detail = details[i];
Hit hit = hits[i];
result.accumulate("indexno", hit.getIndexNo());
result.accumulate("indexkey", hit.getUniqueKey());
// don't add summaries not including summaries
if (summaries != null && results.isWithSummary()) {
Summary summary = summaries[i];
result.accumulate("summary", summary.toString());
}
// add the fields from hit details
JSONObject fields = new JSONObject();
for (int k = 0; k < detail.getLength(); k++) {
String name = detail.getField(k);
String[] values = detail.getValues(name);
// if we specified fields to return, only return those fields
if (fieldSet.size() == 0 || fieldSet.contains(name)) {
JSONArray valuesAr = new JSONArray();
for (int m = 0; m < values.length; m++) {
valuesAr.add(values[m]);
}
fields.accumulate(name, valuesAr);
}
}
result.accumulate("fields", fields);
docsAr.add(result);
}
jsonObj.accumulate("documents", docsAr);
// pretty printing can be set through configuration, write out the wrapper
// function if there is one
StringBuilder builder = new StringBuilder();
if (StringUtils.isNotBlank(func)) {
builder.append(func + "(");
}
builder.append(prettyPrint ? jsonObj.toString(2) : jsonObj.toString());
if (StringUtils.isNotBlank(func)) {
builder.append(")");
}
// Cache control headers
SimpleDateFormat sdf = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'");
long relExpiresInMillis = System.currentTimeMillis()
+ (1000 * maxAgeInSeconds);
response.setContentType(contentType);
response.setHeader("Cache-Control", "max-age=" + maxAgeInSeconds);
response.setHeader("Expires", sdf.format(relExpiresInMillis));
// write out the content to the response
response.getOutputStream().write(builder.toString().getBytes());
response.flushBuffer();
}
}