NIHVIVO-2425 Write autocomplete results to the response's writer directly without merging to a Freemarker template.

This commit is contained in:
ryounes 2011-03-28 19:23:19 +00:00
parent 3745c37cc9
commit 12e68942fc
2 changed files with 54 additions and 45 deletions

View file

@ -28,22 +28,22 @@ import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.WildcardQuery; import org.apache.lucene.search.WildcardQuery;
import org.apache.lucene.util.Version;
import org.json.JSONArray;
import com.hp.hpl.jena.sparql.lib.org.json.JSONObject; import com.hp.hpl.jena.sparql.lib.org.json.JSONObject;
import edu.cornell.mannlib.vedit.beans.LoginStatusBean; import edu.cornell.mannlib.vedit.beans.LoginStatusBean;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.controller.ajax.VitroAjaxController; import edu.cornell.mannlib.vitro.webapp.controller.ajax.VitroAjaxController;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.TemplateProcessingHelper.TemplateProcessingException;
import edu.cornell.mannlib.vitro.webapp.flags.PortalFlag; import edu.cornell.mannlib.vitro.webapp.flags.PortalFlag;
import edu.cornell.mannlib.vitro.webapp.search.SearchException; import edu.cornell.mannlib.vitro.webapp.search.SearchException;
import edu.cornell.mannlib.vitro.webapp.search.lucene.Entity2LuceneDoc.VitroLuceneTermNames; import edu.cornell.mannlib.vitro.webapp.search.lucene.Entity2LuceneDoc.VitroLuceneTermNames;
import edu.cornell.mannlib.vitro.webapp.search.lucene.LuceneIndexFactory; import edu.cornell.mannlib.vitro.webapp.search.lucene.LuceneIndexFactory;
import edu.cornell.mannlib.vitro.webapp.search.lucene.LuceneSetup; import edu.cornell.mannlib.vitro.webapp.search.lucene.LuceneSetup;
import freemarker.template.Configuration;
/** /**
* AutocompleteController is used to generate autocomplete content * AutocompleteController generates autocomplete content
* through a Lucene search. * through a Lucene search.
*/ */
@ -52,7 +52,7 @@ public class AutocompleteController extends VitroAjaxController {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final Log log = LogFactory.getLog(AutocompleteController.class); private static final Log log = LogFactory.getLog(AutocompleteController.class);
private static final String TEMPLATE_DEFAULT = "autocompleteResults.ftl"; //private static final String TEMPLATE_DEFAULT = "autocompleteResults.ftl";
private static String QUERY_PARAMETER_NAME = "term"; private static String QUERY_PARAMETER_NAME = "term";
@ -69,9 +69,6 @@ public class AutocompleteController extends VitroAjaxController {
protected void doRequest(VitroRequest vreq, HttpServletResponse response) protected void doRequest(VitroRequest vreq, HttpServletResponse response)
throws IOException, ServletException { throws IOException, ServletException {
Map<String, Object> map = new HashMap<String, Object>();
Configuration config = getFreemarkerConfiguration(vreq);
PortalFlag portalFlag = vreq.getPortalFlag(); PortalFlag portalFlag = vreq.getPortalFlag();
try { try {
@ -84,7 +81,7 @@ public class AutocompleteController extends VitroAjaxController {
Query query = getQuery(vreq, portalFlag, analyzer, qtxt); Query query = getQuery(vreq, portalFlag, analyzer, qtxt);
if (query == null ) { if (query == null ) {
log.debug("query for '" + qtxt +"' is null."); log.debug("query for '" + qtxt +"' is null.");
doNoQuery(map, config, vreq, response); doNoQuery(response);
return; return;
} }
log.debug("query for '" + qtxt +"' is " + query.toString()); log.debug("query for '" + qtxt +"' is " + query.toString());
@ -100,22 +97,22 @@ public class AutocompleteController extends VitroAjaxController {
try{ try{
wait(150); wait(150);
topDocs = searcherForRequest.search(query,null,maxHitSize); topDocs = searcherForRequest.search(query,null,maxHitSize);
}catch (Exception ex){ }catch (Exception e){
log.error(ex); log.error(e, e);
doFailedSearch(map, config, vreq, response); doNoSearchResults(response);
return; return;
} }
} }
if( topDocs == null || topDocs.scoreDocs == null){ if( topDocs == null || topDocs.scoreDocs == null){
log.error("topDocs for a search was null"); log.error("topDocs for a search was null");
doFailedSearch(map, config, vreq, response); doNoSearchResults(response);
return; return;
} }
int hitsLength = topDocs.scoreDocs.length; int hitsLength = topDocs.scoreDocs.length;
if ( hitsLength < 1 ){ if ( hitsLength < 1 ){
doFailedSearch(map, config, vreq, response); doNoSearchResults(response);
return; return;
} }
log.debug("found "+hitsLength+" hits"); log.debug("found "+hitsLength+" hits");
@ -135,18 +132,20 @@ public class AutocompleteController extends VitroAjaxController {
} }
Collections.sort(results); Collections.sort(results);
map.put("results", results);
writeTemplate(TEMPLATE_DEFAULT, map, config, vreq, response);
} catch (TemplateProcessingException e) { // map.put("results", results);
log.error(e, e); // writeTemplate(TEMPLATE_DEFAULT, map, config, vreq, response);
} catch (Throwable e) {
log.error("AutocompleteController(): " + e); JSONArray jsonArray = new JSONArray();
try { for (SearchResult result : results) {
doSearchError(map, config, vreq, response); jsonArray.put(result.toMap());
} catch (TemplateProcessingException e1) {
log.error(e1.getMessage(), e1);
} }
response.getWriter().write(jsonArray.toString());
} catch (Throwable e) {
log.error(e, e);
doSearchError(response);
} }
} }
@ -265,27 +264,26 @@ public class AutocompleteController extends VitroAjaxController {
// indicated in the query string. // indicated in the query string.
// The analyzer is needed so that we use the same analyzer on the search queries as // The analyzer is needed so that we use the same analyzer on the search queries as
// was used on the text that was indexed. // was used on the text that was indexed.
QueryParser qp = new QueryParser(searchField,analyzer); QueryParser qp = new QueryParser(Version.LUCENE_29, searchField,analyzer);
//this sets the query parser to AND all of the query terms it finds. //this sets the query parser to AND all of the query terms it finds.
qp.setDefaultOperator(QueryParser.AND_OPERATOR); qp.setDefaultOperator(QueryParser.AND_OPERATOR);
return qp; return qp;
} }
private void doNoQuery(Map<String, Object> map, Configuration config, HttpServletRequest request, private void doNoQuery(HttpServletResponse response) throws IOException {
HttpServletResponse response) throws TemplateProcessingException {
writeTemplate(TEMPLATE_DEFAULT, map, config, request, response);
}
private void doFailedSearch(Map<String, Object> map, Configuration config, HttpServletRequest request,
HttpServletResponse response) throws TemplateProcessingException {
writeTemplate(TEMPLATE_DEFAULT, map, config, request, response);
}
private void doSearchError(Map<String, Object> map, Configuration config, HttpServletRequest request,
HttpServletResponse response) throws TemplateProcessingException {
// For now, we are not sending an error message back to the client because with the default autocomplete configuration it // For now, we are not sending an error message back to the client because with the default autocomplete configuration it
// chokes. // chokes.
writeTemplate(TEMPLATE_DEFAULT, map, config, request, response); doNoSearchResults(response);
}
private void doSearchError(HttpServletResponse response) throws IOException {
// For now, we are not sending an error message back to the client because with the default autocomplete configuration it
// chokes.
doNoSearchResults(response);
}
private void doNoSearchResults(HttpServletResponse response) throws IOException {
response.getWriter().write("[]");
} }
public static final int MAX_QUERY_LENGTH = 500; public static final int MAX_QUERY_LENGTH = 500;
@ -295,20 +293,31 @@ public class AutocompleteController extends VitroAjaxController {
private String uri; private String uri;
SearchResult(String label, String uri) { SearchResult(String label, String uri) {
this.label = JSONObject.quote(label); this.label = label;
this.uri = JSONObject.quote(uri); this.uri = uri;
} }
public String getLabel() { public String getLabel() {
return label; return label;
} }
public String getJsonLabel() {
return JSONObject.quote(label);
}
public String getUri() { public String getUri() {
return uri; return uri;
} }
public String getJson() { public String getJsonUri() {
return "{ \"label\": \"" + label + "\", " + "\"uri\": \"" + uri + "\" }"; return JSONObject.quote(uri);
}
Map<String, String> toMap() {
Map<String, String> map = new HashMap<String, String>();
map.put("label", label);
map.put("uri", uri);
return map;
} }
public int compareTo(Object o) throws ClassCastException { public int compareTo(Object o) throws ClassCastException {
@ -316,7 +325,7 @@ public class AutocompleteController extends VitroAjaxController {
throw new ClassCastException("Error in SearchResult.compareTo(): expected SearchResult object."); throw new ClassCastException("Error in SearchResult.compareTo(): expected SearchResult object.");
} }
SearchResult sr = (SearchResult) o; SearchResult sr = (SearchResult) o;
return label.compareTo(sr.getLabel()); return label.compareToIgnoreCase(sr.getLabel());
} }
} }

View file

@ -10,8 +10,8 @@
[ [
<#if results??> <#if results??>
<#list results as result> <#list results as result>
<#-- result.label and result.uri are already quoted --> <#-- result.jsonLabel and result.jsonUri are already quoted -->
{ "label": ${result.label}, "uri": ${result.uri} }<#if result_has_next>,</#if> { "label": ${result.jsonLabel}, "uri": ${result.jsonUri} }<#if result_has_next>,</#if>
</#list> </#list>
</#if> </#if>
] ]