Reimplement search controller and search results display template in Freemarker. Error cases not handled yet.
This commit is contained in:
parent
3dbbf13cd7
commit
73024a4d03
13 changed files with 1042 additions and 26 deletions
|
@ -843,7 +843,7 @@
|
||||||
|
|
||||||
<servlet>
|
<servlet>
|
||||||
<servlet-name>SearchController</servlet-name>
|
<servlet-name>SearchController</servlet-name>
|
||||||
<servlet-class>edu.cornell.mannlib.vitro.webapp.search.controller.PagedSearchController</servlet-class>
|
<servlet-class>edu.cornell.mannlib.vitro.webapp.search.controller.FreemarkerPagedSearchController</servlet-class>
|
||||||
</servlet>
|
</servlet>
|
||||||
<servlet-mapping>
|
<servlet-mapping>
|
||||||
<servlet-name>SearchController</servlet-name>
|
<servlet-name>SearchController</servlet-name>
|
||||||
|
@ -876,6 +876,15 @@
|
||||||
<url-pattern>/populateselect</url-pattern>
|
<url-pattern>/populateselect</url-pattern>
|
||||||
</servlet-mapping>
|
</servlet-mapping>
|
||||||
|
|
||||||
|
<servlet>
|
||||||
|
<servlet-name>ReorderController</servlet-name>
|
||||||
|
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.edit.ReorderController</servlet-class>
|
||||||
|
</servlet>
|
||||||
|
<servlet-mapping>
|
||||||
|
<servlet-name>ReorderController</servlet-name>
|
||||||
|
<url-pattern>/edit/reorder</url-pattern>
|
||||||
|
</servlet-mapping>
|
||||||
|
|
||||||
<servlet>
|
<servlet>
|
||||||
<servlet-name>AdminController</servlet-name>
|
<servlet-name>AdminController</servlet-name>
|
||||||
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.AdminController</servlet-class>
|
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.AdminController</servlet-class>
|
||||||
|
|
|
@ -251,6 +251,7 @@ public class FreemarkerHttpServlet extends VitroHttpServlet {
|
||||||
urls.put("siteAdmin", urlBuilder.getPortalUrl(Route.LOGIN));
|
urls.put("siteAdmin", urlBuilder.getPortalUrl(Route.LOGIN));
|
||||||
|
|
||||||
urls.put("siteIcons", urlBuilder.getPortalUrl(themeDir + "/site_icons"));
|
urls.put("siteIcons", urlBuilder.getPortalUrl(themeDir + "/site_icons"));
|
||||||
|
|
||||||
return urls;
|
return urls;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
@ -143,14 +144,19 @@ public class UrlBuilder {
|
||||||
public Params() { }
|
public Params() { }
|
||||||
|
|
||||||
public Params(String...strings) {
|
public Params(String...strings) {
|
||||||
this();
|
|
||||||
int stringCount = strings.length;
|
int stringCount = strings.length;
|
||||||
for (int i = 0; i < stringCount; i=i+2) {
|
for (int i = 0; i < stringCount; i=i+2) {
|
||||||
// Skip the last item if there's an odd number
|
// Skip the last item if there's an odd number
|
||||||
if (i == stringCount-1) { break; }
|
if (i == stringCount-1) { break; }
|
||||||
|
// Skip a param with a null value
|
||||||
|
if (strings[i+1] == null) { continue; }
|
||||||
this.put(strings[i], strings[i+1]);
|
this.put(strings[i], strings[i+1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Params(Map<String, String> map) {
|
||||||
|
this.putAll(map);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/********** Static utility methods **********/
|
/********** Static utility methods **********/
|
||||||
|
@ -164,6 +170,11 @@ public class UrlBuilder {
|
||||||
return path.isEmpty() ? "/" : path;
|
return path.isEmpty() ? "/" : path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getUrl(String path, String...params) {
|
||||||
|
Params urlParams = new Params(params);
|
||||||
|
return getUrl(path, urlParams);
|
||||||
|
}
|
||||||
|
|
||||||
public static String getUrl(String path, Params params) {
|
public static String getUrl(String path, Params params) {
|
||||||
path = getPath(path, params);
|
path = getPath(path, params);
|
||||||
return getUrl(path);
|
return getUrl(path);
|
||||||
|
|
|
@ -0,0 +1,866 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.search.controller;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.servlet.RequestDispatcher;
|
||||||
|
import javax.servlet.ServletConfig;
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.apache.lucene.analysis.Analyzer;
|
||||||
|
import org.apache.lucene.document.Document;
|
||||||
|
import org.apache.lucene.document.Field;
|
||||||
|
import org.apache.lucene.index.CorruptIndexException;
|
||||||
|
import org.apache.lucene.index.Term;
|
||||||
|
import org.apache.lucene.queryParser.QueryParser;
|
||||||
|
import org.apache.lucene.search.BooleanClause;
|
||||||
|
import org.apache.lucene.search.BooleanQuery;
|
||||||
|
import org.apache.lucene.search.IndexSearcher;
|
||||||
|
import org.apache.lucene.search.Query;
|
||||||
|
import org.apache.lucene.search.TermQuery;
|
||||||
|
import org.apache.lucene.search.TopDocs;
|
||||||
|
import org.apache.lucene.search.WildcardQuery;
|
||||||
|
import org.apache.lucene.store.Directory;
|
||||||
|
import org.apache.lucene.store.FSDirectory;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.DataProperty;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatement;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.IndividualImpl;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.ObjectProperty;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.Portal;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.VClassGroup;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.Controllers;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.Params;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.DataPropertyDao;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyDao;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.VClassDao;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.VClassGroupDao;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.flags.PortalFlag;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.search.SearchException;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.search.beans.Searcher;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.search.beans.VitroHighlighter;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.search.beans.VitroQuery;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.search.beans.VitroQueryFactory;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.search.lucene.Entity2LuceneDoc;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.search.lucene.LuceneIndexer;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.search.lucene.LuceneSetup;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.search.lucene.SimpleLuceneHighlighter;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.utils.FlagMathUtils;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.utils.Html2Text;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.utils.StringUtils;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.IndividualTemplateModel;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.LinkTemplateModel;
|
||||||
|
import freemarker.template.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PagedSearchController is the new search controller that interacts
|
||||||
|
* directly with the lucene API and returns paged, relevance ranked results.
|
||||||
|
*
|
||||||
|
* @author bdc34
|
||||||
|
*
|
||||||
|
* Rewritten to use Freemarker: rjy7
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class FreemarkerPagedSearchController extends FreemarkerHttpServlet implements Searcher {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private IndexSearcher searcher = null;
|
||||||
|
private static final Log log = LogFactory.getLog(FreemarkerPagedSearchController.class.getName());
|
||||||
|
String NORESULT_MSG = "The search returned no results.";
|
||||||
|
private int defaultHitsPerPage = 25;
|
||||||
|
private int defaultMaxSearchSize= 1000;
|
||||||
|
|
||||||
|
public void init(ServletConfig config) throws ServletException {
|
||||||
|
super.init(config);
|
||||||
|
LuceneIndexer indexer=(LuceneIndexer)getServletContext()
|
||||||
|
.getAttribute(LuceneIndexer.class.getName());
|
||||||
|
indexer.addSearcher(this);
|
||||||
|
|
||||||
|
try{
|
||||||
|
String indexDir = getIndexDir(getServletContext());
|
||||||
|
getIndexSearcher(indexDir);
|
||||||
|
}catch(Exception ex){
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getBody(VitroRequest vreq, Map<String, Object> body, Configuration config) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
Portal portal = vreq.getPortal();
|
||||||
|
PortalFlag portalFlag = vreq.getPortalFlag();
|
||||||
|
|
||||||
|
//make sure an IndividualDao is available
|
||||||
|
if( vreq.getWebappDaoFactory() == null
|
||||||
|
|| vreq.getWebappDaoFactory().getIndividualDao() == null ){
|
||||||
|
log.error("makeUsableBeans() could not get IndividualDao ");
|
||||||
|
//doSearchError(request, response, "Could not access Model", portalFlag);
|
||||||
|
//return;
|
||||||
|
}
|
||||||
|
IndividualDao iDao = vreq.getWebappDaoFactory().getIndividualDao();
|
||||||
|
VClassGroupDao grpDao = vreq.getWebappDaoFactory().getVClassGroupDao();
|
||||||
|
VClassDao vclassDao = vreq.getWebappDaoFactory().getVClassDao();
|
||||||
|
String alphaFilter = vreq.getParameter("alpha");
|
||||||
|
|
||||||
|
int startIndex = 0;
|
||||||
|
try{
|
||||||
|
startIndex = Integer.parseInt(vreq.getParameter("startIndex"));
|
||||||
|
}catch (Throwable e) {
|
||||||
|
startIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hitsPerPage = defaultHitsPerPage;
|
||||||
|
try{
|
||||||
|
hitsPerPage = Integer.parseInt(vreq.getParameter("hitsPerPage"));
|
||||||
|
} catch (Throwable e) {
|
||||||
|
hitsPerPage = defaultHitsPerPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxHitSize = defaultMaxSearchSize;
|
||||||
|
if( startIndex >= defaultMaxSearchSize - hitsPerPage )
|
||||||
|
maxHitSize = startIndex + defaultMaxSearchSize;
|
||||||
|
if( alphaFilter != null ){
|
||||||
|
maxHitSize = maxHitSize * 2;
|
||||||
|
hitsPerPage = maxHitSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
String indexDir = getIndexDir(getServletContext());
|
||||||
|
|
||||||
|
String qtxt = vreq.getParameter(VitroQuery.QUERY_PARAMETER_NAME);
|
||||||
|
Analyzer analyzer = getAnalyzer(getServletContext());
|
||||||
|
Query query = getQuery(vreq, portalFlag, analyzer, indexDir, qtxt);
|
||||||
|
log.debug("query for '" + qtxt +"' is " + query.toString());
|
||||||
|
|
||||||
|
if (query == null ) {
|
||||||
|
//doNoQuery(vreq, response);
|
||||||
|
//return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IndexSearcher searcherForRequest = getIndexSearcher(indexDir);
|
||||||
|
|
||||||
|
TopDocs topDocs = null;
|
||||||
|
try{
|
||||||
|
topDocs = searcherForRequest.search(query,null,maxHitSize);
|
||||||
|
}catch(Throwable t){
|
||||||
|
log.error("in first pass at search: " + t);
|
||||||
|
// this is a hack to deal with odd cases where search and index threads interact
|
||||||
|
try{
|
||||||
|
wait(150);
|
||||||
|
topDocs = searcherForRequest.search(query,null,maxHitSize);
|
||||||
|
}catch (Exception ex){
|
||||||
|
log.error(ex);
|
||||||
|
String msg = makeBadSearchMessage(qtxt,ex.getMessage());
|
||||||
|
if(msg == null ) msg = "<p>The search request contained errors.</p>";
|
||||||
|
//doFailedSearch(vreq, response, msg, qtxt);
|
||||||
|
//return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( topDocs == null || topDocs.scoreDocs == null){
|
||||||
|
log.error("topDocs for a search was null");
|
||||||
|
String msg = "<p>The search request contained errors.</p>";
|
||||||
|
//doFailedSearch(request, response, msg, qtxt);
|
||||||
|
//return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hitsLength = topDocs.scoreDocs.length;
|
||||||
|
if ( hitsLength < 1 ){
|
||||||
|
//doFailedSearch(request, response, NORESULT_MSG, qtxt);
|
||||||
|
//return;
|
||||||
|
}
|
||||||
|
log.debug("found "+hitsLength+" hits");
|
||||||
|
|
||||||
|
int lastHitToShow = 0;
|
||||||
|
if((startIndex + hitsPerPage) > hitsLength ) {
|
||||||
|
lastHitToShow = hitsLength;
|
||||||
|
} else {
|
||||||
|
lastHitToShow = startIndex + hitsPerPage - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Individual> beans = new LinkedList<Individual>();
|
||||||
|
for(int i=startIndex; i<topDocs.scoreDocs.length ;i++){
|
||||||
|
try{
|
||||||
|
if( (i >= startIndex) && (i <= lastHitToShow) ){
|
||||||
|
Document doc = searcherForRequest.doc(topDocs.scoreDocs[i].doc);
|
||||||
|
String uri = doc.get(Entity2LuceneDoc.term.URI);
|
||||||
|
Individual ent = new IndividualImpl();
|
||||||
|
ent.setURI(uri);
|
||||||
|
ent = iDao.getIndividualByURI(uri);
|
||||||
|
if(ent!=null)
|
||||||
|
beans.add(ent);
|
||||||
|
}
|
||||||
|
}catch(Exception e){
|
||||||
|
log.error("problem getting usable Individuals from search " +
|
||||||
|
"hits" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Params pagingLinkParams = new Params();
|
||||||
|
pagingLinkParams.put("querytext", qtxt);
|
||||||
|
|
||||||
|
String classGroupParam = vreq.getParameter("classgroup");
|
||||||
|
String typeParam = vreq.getParameter("type");
|
||||||
|
|
||||||
|
// Search request includes no classgroup and no type, so add classgroup search refinement links.
|
||||||
|
if ( classGroupParam == null && typeParam == null) {
|
||||||
|
List<VClassGroup> classgroups = getClassGroups(grpDao, topDocs, searcherForRequest);
|
||||||
|
List<VClassGroupSearchLink> classGroupLinks = new ArrayList<VClassGroupSearchLink>(classgroups.size());
|
||||||
|
for (VClassGroup vcg : classgroups) {
|
||||||
|
classGroupLinks.add(new VClassGroupSearchLink(qtxt, vcg));
|
||||||
|
}
|
||||||
|
body.put("classGroupLinks", classGroupLinks);
|
||||||
|
|
||||||
|
// Search request is for a classgroup, so add rdf:type search refinement links
|
||||||
|
// but try to filter out classes that are subclasses
|
||||||
|
} else if ( classGroupParam != null && typeParam == null ) {
|
||||||
|
List<VClass> vClasses = getVClasses(vclassDao,topDocs,searcherForRequest);
|
||||||
|
List<VClassSearchLink> vClassLinks = new ArrayList<VClassSearchLink>(vClasses.size());
|
||||||
|
for (VClass vc : vClasses) {
|
||||||
|
vClassLinks.add(new VClassSearchLink(qtxt, vc));
|
||||||
|
}
|
||||||
|
body.put("classLinks", vClassLinks);
|
||||||
|
pagingLinkParams.put("classgroup", classGroupParam);
|
||||||
|
|
||||||
|
// This case is never displayed
|
||||||
|
} else if ( !StringUtils.isEmpty(alphaFilter) ) {
|
||||||
|
body.put("alphas", getAlphas(topDocs, searcherForRequest));
|
||||||
|
alphaSortIndividuals(beans);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
pagingLinkParams.put("type", typeParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
beans = highlightBeans( beans ,
|
||||||
|
vreq.getWebappDaoFactory().getDataPropertyDao(),
|
||||||
|
vreq.getWebappDaoFactory().getObjectPropertyDao(),
|
||||||
|
new SimpleLuceneHighlighter(query,analyzer) );
|
||||||
|
|
||||||
|
// Convert search result individuals to template model objects
|
||||||
|
List<IndividualTemplateModel> individuals = new ArrayList<IndividualTemplateModel>(beans.size());
|
||||||
|
for (Individual i : beans) {
|
||||||
|
individuals.add(new IndividualTemplateModel(i));
|
||||||
|
}
|
||||||
|
body.put("individuals", individuals);
|
||||||
|
|
||||||
|
body.put("querytext", qtxt);
|
||||||
|
body.put("title", qtxt+" - "+portal.getAppName()+" Search Results" );
|
||||||
|
|
||||||
|
if ( !StringUtils.isEmpty(classGroupParam) ) {
|
||||||
|
VClassGroup grp = grpDao.getGroupByURI(classGroupParam);
|
||||||
|
if( grp != null && grp.getPublicName() != null )
|
||||||
|
body.put("classgroupName", grp.getPublicName());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !StringUtils.isEmpty(typeParam) ) {
|
||||||
|
VClass type = vclassDao.getVClassByURI(typeParam);
|
||||||
|
if( type != null && type.getName() != null )
|
||||||
|
body.put("typeName", type.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
body.put("pagingLinks", getPagingLinks(startIndex, hitsPerPage, hitsLength, maxHitSize, vreq.getServletPath(), pagingLinkParams));
|
||||||
|
|
||||||
|
} catch (Throwable e) {
|
||||||
|
log.error(e, e);
|
||||||
|
//doSearchError(request, response, e.getMessage(), null);
|
||||||
|
//return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mergeBodyToTemplate("pagedSearchResults.ftl", body, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void alphaSortIndividuals(List<Individual> beans) {
|
||||||
|
Collections.sort(beans, new Comparator< Individual >(){
|
||||||
|
public int compare(Individual o1, Individual o2) {
|
||||||
|
if( o1 == null || o1.getName() == null )
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return o1.getName().compareTo(o2.getName());
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getAlphas(TopDocs topDocs, IndexSearcher searcher) {
|
||||||
|
Set<String> alphas = new HashSet<String>();
|
||||||
|
for(int i=0;i<topDocs.scoreDocs.length; i++){
|
||||||
|
Document doc;
|
||||||
|
try {
|
||||||
|
doc = searcher.doc(topDocs.scoreDocs[i].doc);
|
||||||
|
String name =doc.get(Entity2LuceneDoc.term.NAME);
|
||||||
|
if( name != null && name.length() > 0)
|
||||||
|
alphas.add( name.substring(0, 1));
|
||||||
|
} catch (CorruptIndexException e) {
|
||||||
|
log.debug("Could not get alphas for document",e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.debug("Could not get alphas for document",e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return new ArrayList<String>(alphas);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the class groups represented for the individuals in the topDocs.
|
||||||
|
*/
|
||||||
|
private List<VClassGroup> getClassGroups(VClassGroupDao grpDao, TopDocs topDocs,
|
||||||
|
IndexSearcher searcherForRequest) {
|
||||||
|
LinkedHashMap<String,VClassGroup> grpMap = grpDao.getClassGroupMap();
|
||||||
|
int n = grpMap.size();
|
||||||
|
|
||||||
|
HashSet<String> classGroupsInHits = new HashSet<String>(n);
|
||||||
|
int grpsFound = 0;
|
||||||
|
|
||||||
|
for(int i=0; i<topDocs.scoreDocs.length && n > grpsFound ;i++){
|
||||||
|
try{
|
||||||
|
Document doc = searcherForRequest.doc(topDocs.scoreDocs[i].doc);
|
||||||
|
Field[] grps = doc.getFields(Entity2LuceneDoc.term.CLASSGROUP_URI);
|
||||||
|
if(grps != null || grps.length > 0){
|
||||||
|
for(int j=0;j<grps.length;j++){
|
||||||
|
String groupUri = grps[j].stringValue();
|
||||||
|
if( groupUri != null && ! classGroupsInHits.contains(groupUri)){
|
||||||
|
classGroupsInHits.add(groupUri);
|
||||||
|
grpsFound++;
|
||||||
|
if( grpsFound >= n )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch(Exception e){
|
||||||
|
log.error("problem getting VClassGroups from search hits "
|
||||||
|
+ e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> classgroupURIs= Collections.list(Collections.enumeration(classGroupsInHits));
|
||||||
|
List<VClassGroup> classgroups = new ArrayList<VClassGroup>( classgroupURIs.size() );
|
||||||
|
for(String cgUri: classgroupURIs){
|
||||||
|
if( cgUri != null && ! "".equals(cgUri) ){
|
||||||
|
VClassGroup vcg = grpDao.getGroupByURI( cgUri );
|
||||||
|
if( vcg == null ){
|
||||||
|
log.debug("could not get classgroup for URI " + cgUri);
|
||||||
|
}else{
|
||||||
|
classgroups.add(vcg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
grpDao.sortGroupList(classgroups);
|
||||||
|
|
||||||
|
return classgroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class VClassGroupSearchLink extends LinkTemplateModel {
|
||||||
|
|
||||||
|
VClassGroupSearchLink(String querytext, VClassGroup classgroup) {
|
||||||
|
super(classgroup.getPublicName(), "/search", "querytext", querytext, "classgroup", classgroup.getURI());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class VClassSearchLink extends LinkTemplateModel {
|
||||||
|
|
||||||
|
VClassSearchLink(String querytext, VClass type) {
|
||||||
|
super(type.getName(), "/search", "querytext", querytext, "type", type.getURI());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<PagingLink> getPagingLinks(int startIndex, int hitsPerPage, int hitsLength, int maxHitSize, String baseUrl, Params params) {
|
||||||
|
|
||||||
|
List<PagingLink> pagingLinks = new ArrayList<PagingLink>();
|
||||||
|
|
||||||
|
// No paging links if only one page of results
|
||||||
|
if (hitsLength <= hitsPerPage) {
|
||||||
|
return pagingLinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pageNumber;
|
||||||
|
|
||||||
|
params.put("hitsPerPage", String.valueOf(hitsPerPage));
|
||||||
|
|
||||||
|
for (int i = 0; i < hitsLength; i += hitsPerPage) {
|
||||||
|
params.put("startIndex", String.valueOf(i));
|
||||||
|
if ( i < maxHitSize - hitsPerPage) {
|
||||||
|
pageNumber = i/hitsPerPage + 1;
|
||||||
|
if (i >= startIndex && i < (startIndex + hitsPerPage)) {
|
||||||
|
pagingLinks.add(new PagingLink(pageNumber));
|
||||||
|
} else {
|
||||||
|
pagingLinks.add(new PagingLink(pageNumber, baseUrl, params));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pagingLinks.add(new PagingLink("more...", baseUrl, params));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pagingLinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PagingLink extends LinkTemplateModel {
|
||||||
|
|
||||||
|
PagingLink(int pageNumber, String baseUrl, Params params) {
|
||||||
|
super(String.valueOf(pageNumber), baseUrl, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor for current page item: not a link, so no url value.
|
||||||
|
PagingLink(int pageNumber) {
|
||||||
|
setText(String.valueOf(pageNumber));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor for "more..." item
|
||||||
|
PagingLink(String text, String baseUrl, Params params) {
|
||||||
|
super(text, baseUrl, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<VClass> getVClasses(VClassDao vclassDao, TopDocs topDocs,
|
||||||
|
IndexSearcher searherForRequest){
|
||||||
|
HashSet<String> typesInHits = getVClassUrisForHits(topDocs,searherForRequest);
|
||||||
|
List<VClass> classes = new ArrayList<VClass>(typesInHits.size());
|
||||||
|
|
||||||
|
Iterator<String> it = typesInHits.iterator();
|
||||||
|
while(it.hasNext()){
|
||||||
|
String typeUri = it.next();
|
||||||
|
try{
|
||||||
|
if( VitroVocabulary.OWL_THING.equals(typeUri))
|
||||||
|
continue;
|
||||||
|
VClass type = vclassDao.getVClassByURI(typeUri);
|
||||||
|
if( ! type.isAnonymous() &&
|
||||||
|
type.getName() != null && !"".equals(type.getName()) &&
|
||||||
|
type.getGroupURI() != null ) //don't display classes that aren't in classgroups
|
||||||
|
classes.add(type);
|
||||||
|
}catch(Exception ex){
|
||||||
|
if( log.isDebugEnabled() )
|
||||||
|
log.debug("could not add type " + typeUri, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Collections.sort(classes, new Comparator<VClass>(){
|
||||||
|
public int compare(VClass o1, VClass o2) {
|
||||||
|
return o1.compareTo(o2);
|
||||||
|
}});
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashSet<String> getVClassUrisForHits(TopDocs topDocs,
|
||||||
|
IndexSearcher searcherForRequest){
|
||||||
|
HashSet<String> typesInHits = new HashSet<String>();
|
||||||
|
for(int i=0; i<topDocs.scoreDocs.length; i++){
|
||||||
|
try{
|
||||||
|
Document doc=searcherForRequest.doc(topDocs.scoreDocs[i].doc);
|
||||||
|
Field[] types = doc.getFields(Entity2LuceneDoc.term.RDFTYPE);
|
||||||
|
if(types != null ){
|
||||||
|
for(int j=0;j<types.length;j++){
|
||||||
|
String typeUri = types[j].stringValue();
|
||||||
|
typesInHits.add(typeUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch(Exception e){
|
||||||
|
log.error("problems getting rdf:type for search hits",e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return typesInHits;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getIndexDir(ServletContext servletContext) throws SearchException {
|
||||||
|
Object obj = servletContext.getAttribute(LuceneSetup.INDEX_DIR);
|
||||||
|
if( obj == null || !(obj instanceof String) )
|
||||||
|
throw new SearchException("Could not get IndexDir for luecene index");
|
||||||
|
else
|
||||||
|
return (String)obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Analyzer getAnalyzer(ServletContext servletContext) throws SearchException {
|
||||||
|
Object obj = servletContext.getAttribute(LuceneSetup.ANALYZER);
|
||||||
|
if( obj == null || !(obj instanceof Analyzer) )
|
||||||
|
throw new SearchException("Could not get anlyzer");
|
||||||
|
else
|
||||||
|
return (Analyzer)obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Query getQuery(VitroRequest request, PortalFlag portalState,
|
||||||
|
Analyzer analyzer, String indexDir, String querystr ) throws SearchException{
|
||||||
|
Query query = null;
|
||||||
|
try{
|
||||||
|
//String querystr = request.getParameter(VitroQuery.QUERY_PARAMETER_NAME);
|
||||||
|
if( querystr == null){
|
||||||
|
log.error("There was no Parameter '"+VitroQuery.QUERY_PARAMETER_NAME
|
||||||
|
+"' in the request.");
|
||||||
|
return null;
|
||||||
|
}else if( querystr.length() > MAX_QUERY_LENGTH ){
|
||||||
|
log.debug("The search was too long. The maximum " +
|
||||||
|
"query length is " + MAX_QUERY_LENGTH );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
QueryParser parser = getQueryParser(analyzer);
|
||||||
|
query = parser.parse(querystr);
|
||||||
|
|
||||||
|
String alpha = request.getParameter("alpha");
|
||||||
|
if( alpha != null && !"".equals(alpha) && alpha.length() == 1){
|
||||||
|
BooleanQuery boolQuery = new BooleanQuery();
|
||||||
|
boolQuery.add( query, BooleanClause.Occur.MUST );
|
||||||
|
boolQuery.add(
|
||||||
|
new WildcardQuery(new Term(Entity2LuceneDoc.term.NAME, alpha+'*')),
|
||||||
|
BooleanClause.Occur.MUST);
|
||||||
|
query = boolQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check if this is classgroup filtered
|
||||||
|
Object param = request.getParameter("classgroup");
|
||||||
|
if( param != null && !"".equals(param)){
|
||||||
|
BooleanQuery boolQuery = new BooleanQuery();
|
||||||
|
boolQuery.add( query, BooleanClause.Occur.MUST);
|
||||||
|
boolQuery.add( new TermQuery(
|
||||||
|
new Term(Entity2LuceneDoc.term.CLASSGROUP_URI,
|
||||||
|
(String)param)),
|
||||||
|
BooleanClause.Occur.MUST);
|
||||||
|
query = boolQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check if this is rdf:type filtered
|
||||||
|
param = request.getParameter("type");
|
||||||
|
if( param != null && !"".equals(param)){
|
||||||
|
BooleanQuery boolQuery = new BooleanQuery();
|
||||||
|
boolQuery.add( query, BooleanClause.Occur.MUST);
|
||||||
|
boolQuery.add( new TermQuery(
|
||||||
|
new Term(Entity2LuceneDoc.term.RDFTYPE,
|
||||||
|
(String)param)),
|
||||||
|
BooleanClause.Occur.MUST);
|
||||||
|
query = boolQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if we have a flag/portal query then we add
|
||||||
|
//it by making a BooelanQuery.
|
||||||
|
Query flagQuery = makeFlagQuery( portalState );
|
||||||
|
if( flagQuery != null ){
|
||||||
|
BooleanQuery boolQuery = new BooleanQuery();
|
||||||
|
boolQuery.add( query, BooleanClause.Occur.MUST);
|
||||||
|
boolQuery.add( flagQuery, BooleanClause.Occur.MUST);
|
||||||
|
query = boolQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("Query: " + query);
|
||||||
|
|
||||||
|
}catch (Exception ex){
|
||||||
|
throw new SearchException(ex.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("static-access")
|
||||||
|
private QueryParser getQueryParser(Analyzer analyzer){
|
||||||
|
//defaultSearchField indicates which field search against when there is no term
|
||||||
|
//indicated in the query string.
|
||||||
|
//The analyzer is needed so that we use the same analyzer on the search queries as
|
||||||
|
//was used on the text that was indexed.
|
||||||
|
QueryParser qp = new QueryParser(defaultSearchField,analyzer);
|
||||||
|
//this sets the query parser to AND all of the query terms it finds.
|
||||||
|
qp.setDefaultOperator(QueryParser.AND_OPERATOR);
|
||||||
|
//set up the map of stemmed field names -> unstemmed field names
|
||||||
|
// HashMap<String,String> map = new HashMap<String, String>();
|
||||||
|
// map.put(Entity2LuceneDoc.term.ALLTEXT,Entity2LuceneDoc.term.ALLTEXTUNSTEMMED);
|
||||||
|
// qp.setStemmedToUnstemmed(map);
|
||||||
|
return qp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a flag based query clause. This is where searches can filtered
|
||||||
|
* by portal.
|
||||||
|
*
|
||||||
|
* If you think that search is not working correctly with protals and
|
||||||
|
* all that kruft then this is a method you want to look at.
|
||||||
|
*
|
||||||
|
* It only takes into account "the portal flag" and flag1Exclusive must
|
||||||
|
* be set. Where does that stuff get set? Look in vitro.flags.PortalFlag
|
||||||
|
*
|
||||||
|
* One thing to keep in mind with portal filtering and search is that if
|
||||||
|
* you want to search a portal that is different then the portal the user
|
||||||
|
* is 'in' then the home parameter should be set to force the user into
|
||||||
|
* the new portal.
|
||||||
|
*
|
||||||
|
* Ex. Bob requests the search page for vivo in portal 3. You want to
|
||||||
|
* have a drop down menu so bob can search the all CALS protal, id 60.
|
||||||
|
* You need to have a home=60 on your search form. If you don't set
|
||||||
|
* home=60 with your search query, then the search will not be in the
|
||||||
|
* all portal AND the WebappDaoFactory will be filtered to only show
|
||||||
|
* things in portal 3.
|
||||||
|
*
|
||||||
|
* Notice: flag1 as a parameter is ignored. bdc34 2009-05-22.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("static-access")
|
||||||
|
private Query makeFlagQuery( PortalFlag flag){
|
||||||
|
if( flag == null || !flag.isFilteringActive()
|
||||||
|
|| flag.getFlag1DisplayStatus() == flag.SHOW_ALL_PORTALS )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// make one term for each bit in the numeric flag that is set
|
||||||
|
Collection<TermQuery> terms = new LinkedList<TermQuery>();
|
||||||
|
int portalNumericId = flag.getFlag1Numeric();
|
||||||
|
Long[] bits = FlagMathUtils.numeric2numerics(portalNumericId);
|
||||||
|
for (Long bit : bits) {
|
||||||
|
terms.add(new TermQuery(new Term(Entity2LuceneDoc.term.PORTAL, Long
|
||||||
|
.toString(bit))));
|
||||||
|
}
|
||||||
|
|
||||||
|
// make a boolean OR query for all of those terms
|
||||||
|
BooleanQuery boolQuery = new BooleanQuery();
|
||||||
|
if (terms.size() > 0) {
|
||||||
|
for (TermQuery term : terms) {
|
||||||
|
boolQuery.add(term, BooleanClause.Occur.SHOULD);
|
||||||
|
}
|
||||||
|
return boolQuery;
|
||||||
|
} else {
|
||||||
|
//we have no flags set, so no flag filtering
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized IndexSearcher getIndexSearcher(String indexDir) {
|
||||||
|
if( searcher == null ){
|
||||||
|
try {
|
||||||
|
Directory fsDir = FSDirectory.getDirectory(indexDir);
|
||||||
|
searcher = new IndexSearcher(fsDir);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("LuceneSearcher: could not make indexSearcher "+e);
|
||||||
|
log.error("It is likely that you have not made a directory for the lucene index. "+
|
||||||
|
"Create the directory indicated in the error and set permissions/ownership so"+
|
||||||
|
" that the tomcat server can read/write to it.");
|
||||||
|
//The index directory is created by LuceneIndexer.makeNewIndex()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return searcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Individual> highlightBeans(List<Individual> beans,
|
||||||
|
DataPropertyDao dpDao, ObjectPropertyDao opDao, VitroHighlighter highlighter) {
|
||||||
|
if( beans == null ){
|
||||||
|
log.debug("List of beans passed to highlightBeans() was null");
|
||||||
|
return Collections.EMPTY_LIST;
|
||||||
|
}else if( highlighter == null ){
|
||||||
|
log.debug("Null highlighter passed to highlightBeans()");
|
||||||
|
return beans;
|
||||||
|
}
|
||||||
|
Iterator<Individual> it = beans.iterator();
|
||||||
|
while(it.hasNext()){
|
||||||
|
Individual ent = it.next();
|
||||||
|
try{
|
||||||
|
dpDao.fillDataPropertiesForIndividual(ent);
|
||||||
|
opDao.fillObjectPropertiesForIndividual(ent);
|
||||||
|
fragmentHighlight(ent, highlighter);
|
||||||
|
}catch( Exception ex ){
|
||||||
|
log.debug("Error while doing search highlighting" , ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return beans;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlights the name and then replaces the description with
|
||||||
|
* highlighted fragments.
|
||||||
|
* @param ent
|
||||||
|
* @param highlighter
|
||||||
|
*/
|
||||||
|
public void fragmentHighlight(Individual ent, VitroHighlighter hl){
|
||||||
|
try{
|
||||||
|
if( ent == null ) return;
|
||||||
|
|
||||||
|
Html2Text h2t = new Html2Text();
|
||||||
|
StringBuffer sb = new StringBuffer("");
|
||||||
|
if(ent.getBlurb() != null)
|
||||||
|
sb.append(ent.getBlurb()).append(' ');
|
||||||
|
|
||||||
|
if(ent.getDescription() != null )
|
||||||
|
sb.append(ent.getDescription()).append(' ');
|
||||||
|
|
||||||
|
if(ent.getDataPropertyList() != null) {
|
||||||
|
Iterator edIt = ent.getDataPropertyList().iterator();
|
||||||
|
while (edIt.hasNext()) {
|
||||||
|
try{
|
||||||
|
DataProperty dp = (DataProperty)edIt.next();
|
||||||
|
if( getDataPropertyBlacklist().contains(dp.getURI()))
|
||||||
|
continue;
|
||||||
|
for(DataPropertyStatement dps : dp.getDataPropertyStatements()){
|
||||||
|
sb.append(dp.getPublicName()).append(' ')
|
||||||
|
.append(dps.getData()).append(' ');
|
||||||
|
}
|
||||||
|
}catch(Throwable e){
|
||||||
|
log.debug("Error highlighting data property statment " +
|
||||||
|
"for individual "+ent.getURI());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ent.getObjectPropertyList() != null) {
|
||||||
|
Iterator edIt = ent.getObjectPropertyList().iterator();
|
||||||
|
String t = null;
|
||||||
|
while (edIt.hasNext()) {
|
||||||
|
try {
|
||||||
|
ObjectProperty op = (ObjectProperty)edIt.next();
|
||||||
|
if( getObjectPropertyBlacklist().contains(op.getURI()))
|
||||||
|
continue;
|
||||||
|
for( ObjectPropertyStatement stmt : op.getObjectPropertyStatements()){
|
||||||
|
sb.append( ( (t = op.getDomainPublic()) != null) ? t : "" );
|
||||||
|
sb.append(' ');
|
||||||
|
sb.append( ( (t = stmt.getObject().getName()) != null) ? t : "" );
|
||||||
|
sb.append(' ');
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
log.debug("Error highlighting object property " +
|
||||||
|
"statement for individual "+ent.getURI());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String keywords = ent.getKeywordString();
|
||||||
|
if( keywords != null )
|
||||||
|
sb.append(keywords);
|
||||||
|
|
||||||
|
ent.setDescription(hl.getHighlightFragments( h2t.stripHtml( sb.toString() )));
|
||||||
|
}catch(Throwable th){
|
||||||
|
log.debug("could not hightlight for entity " + ent.getURI(),th);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doNoQuery(HttpServletRequest request,
|
||||||
|
HttpServletResponse response)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
Portal portal = (new VitroRequest(request)).getPortal();
|
||||||
|
request.setAttribute("title", "Search "+portal.getAppName());
|
||||||
|
request.setAttribute("bodyJsp", Controllers.SEARCH_FORM_JSP);
|
||||||
|
|
||||||
|
RequestDispatcher rd = request
|
||||||
|
.getRequestDispatcher(Controllers.BASIC_JSP);
|
||||||
|
rd.forward(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doFailedSearch(HttpServletRequest request,
|
||||||
|
HttpServletResponse response, String message, String querytext)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
Portal portal = (new VitroRequest(request)).getPortal();
|
||||||
|
if( querytext != null ){
|
||||||
|
request.setAttribute("querytext", querytext);
|
||||||
|
request.setAttribute("title", querytext+" - "+portal.getAppName()+" Search" );
|
||||||
|
}else{
|
||||||
|
request.setAttribute("title", portal.getAppName()+" Search" );
|
||||||
|
request.setAttribute("querytext", "");
|
||||||
|
}
|
||||||
|
if( message != null && message.length() > 0)
|
||||||
|
request.setAttribute("message", message);
|
||||||
|
|
||||||
|
request.setAttribute("bodyJsp", Controllers.SEARCH_FAILED_JSP);
|
||||||
|
RequestDispatcher rd = request.getRequestDispatcher(Controllers.BASIC_JSP);
|
||||||
|
rd.forward(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a message to display to user for a bad search term.
|
||||||
|
* @param query
|
||||||
|
* @param exceptionMsg
|
||||||
|
*/
|
||||||
|
private String makeBadSearchMessage(String querytext, String exceptionMsg){
|
||||||
|
String rv = "";
|
||||||
|
try{
|
||||||
|
//try to get the column in the search term that is causing the problems
|
||||||
|
int coli = exceptionMsg.indexOf("column");
|
||||||
|
if( coli == -1) return "";
|
||||||
|
int numi = exceptionMsg.indexOf(".", coli+7);
|
||||||
|
if( numi == -1 ) return "";
|
||||||
|
String part = exceptionMsg.substring(coli+7,numi );
|
||||||
|
int i = Integer.parseInt(part) - 1;
|
||||||
|
|
||||||
|
// figure out where to cut preview and post-view
|
||||||
|
int errorWindow = 5;
|
||||||
|
int pre = i - errorWindow;
|
||||||
|
if (pre < 0)
|
||||||
|
pre = 0;
|
||||||
|
int post = i + errorWindow;
|
||||||
|
if (post > querytext.length())
|
||||||
|
post = querytext.length();
|
||||||
|
// log.warn("pre: " + pre + " post: " + post + " term len:
|
||||||
|
// " + term.length());
|
||||||
|
|
||||||
|
// get part of the search term before the error and after
|
||||||
|
String before = querytext.substring(pre, i);
|
||||||
|
String after = "";
|
||||||
|
if (post > i)
|
||||||
|
after = querytext.substring(i + 1, post);
|
||||||
|
|
||||||
|
rv = "The search term had an error near <span class='searchQuote'>"
|
||||||
|
+ before + "<span class='searchError'>" + querytext.charAt(i)
|
||||||
|
+ "</span>" + after + "</span>";
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private HashSet<String> getDataPropertyBlacklist(){
|
||||||
|
HashSet<String>dpBlacklist = (HashSet<String>)
|
||||||
|
getServletContext().getAttribute(LuceneSetup.SEARCH_DATAPROPERTY_BLACKLIST);
|
||||||
|
return dpBlacklist;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private HashSet<String> getObjectPropertyBlacklist(){
|
||||||
|
HashSet<String>opBlacklist = (HashSet<String>)
|
||||||
|
getServletContext().getAttribute(LuceneSetup.SEARCH_OBJECTPROPERTY_BLACKLIST);
|
||||||
|
return opBlacklist;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doSearchError(HttpServletRequest request,
|
||||||
|
HttpServletResponse response, String message, Object object)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
Portal portal = (new VitroRequest(request)).getPortal();
|
||||||
|
|
||||||
|
request.setAttribute("bodyJsp", Controllers.SEARCH_ERROR_JSP);
|
||||||
|
RequestDispatcher rd = request.getRequestDispatcher(Controllers.BASIC_JSP);
|
||||||
|
rd.forward(request, response);
|
||||||
|
}
|
||||||
|
private final String defaultSearchField = "ALLTEXT";
|
||||||
|
public static final int MAX_QUERY_LENGTH = 500;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Need to accept notification from indexer that the index has been changed.
|
||||||
|
*/
|
||||||
|
public void close() {
|
||||||
|
searcher = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VitroHighlighter getHighlighter(VitroQuery q) {
|
||||||
|
throw new Error("PagedSearchController.getHighlighter() is unimplemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
public VitroQueryFactory getQueryFactory() {
|
||||||
|
throw new Error("PagedSearchController.getQueryFactory() is unimplemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
public List search(VitroQuery query) throws SearchException {
|
||||||
|
throw new Error("PagedSearchController.search() is unimplemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -89,7 +89,7 @@ public class IndexController extends FreemarkerHttpServlet {
|
||||||
! "authenticated".equalsIgnoreCase(loginHandler.getLoginStatus()) ||
|
! "authenticated".equalsIgnoreCase(loginHandler.getLoginStatus()) ||
|
||||||
Integer.parseInt(loginHandler.getLoginRole()) <= 5 ){
|
Integer.parseInt(loginHandler.getLoginRole()) <= 5 ){
|
||||||
|
|
||||||
body.put("message","You must login to rebuild the search index.");
|
body.put("message","You must log in to rebuild the search index.");
|
||||||
return mergeBodyToTemplate("message.ftl", body, config);
|
return mergeBodyToTemplate("message.ftl", body, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ public class IndexController extends FreemarkerHttpServlet {
|
||||||
return mergeBodyToTemplate("errorMessage.ftl", body, config);
|
return mergeBodyToTemplate("errorMessage.ftl", body, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
body.put("message","Rebuilding of index started.");
|
body.put("message","Rebuilding of index started.");
|
||||||
return mergeBodyToTemplate("message.ftl", body, config);
|
return mergeBodyToTemplate("message.ftl", body, config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -220,7 +220,7 @@ public class PagedSearchController extends VitroHttpServlet implements Searcher{
|
||||||
if( request.getParameter("classgroup") == null && request.getParameter("type") == null){
|
if( request.getParameter("classgroup") == null && request.getParameter("type") == null){
|
||||||
request.setAttribute("classgroups",
|
request.setAttribute("classgroups",
|
||||||
getClassGroups(grpDao, topDocs, searcherForRequest));
|
getClassGroups(grpDao, topDocs, searcherForRequest));
|
||||||
request.setAttribute("refinment", "");
|
request.setAttribute("refinement", "");
|
||||||
}else{
|
}else{
|
||||||
|
|
||||||
//search request for a classgroup so add rdf:type search refinement links
|
//search request for a classgroup so add rdf:type search refinement links
|
||||||
|
@ -228,14 +228,14 @@ public class PagedSearchController extends VitroHttpServlet implements Searcher{
|
||||||
if( request.getParameter("classgroup") != null && request.getParameter("type") == null ){
|
if( request.getParameter("classgroup") != null && request.getParameter("type") == null ){
|
||||||
request.setAttribute("types",
|
request.setAttribute("types",
|
||||||
getVClasses(vclassDao,topDocs,searcherForRequest));
|
getVClasses(vclassDao,topDocs,searcherForRequest));
|
||||||
request.setAttribute("refinment", "&classgroup="
|
request.setAttribute("refinement", "&classgroup="
|
||||||
+ URLEncoder.encode(request.getParameter("classgroup"),"UTF-8"));
|
+ URLEncoder.encode(request.getParameter("classgroup"),"UTF-8"));
|
||||||
}else{
|
}else{
|
||||||
if( alphaFilter != null && !"".equals(alphaFilter)){
|
if( alphaFilter != null && !"".equals(alphaFilter)){
|
||||||
request.setAttribute("alphas", getAlphas(topDocs, searcherForRequest));
|
request.setAttribute("alphas", getAlphas(topDocs, searcherForRequest));
|
||||||
alphaSortIndividuals(beans);
|
alphaSortIndividuals(beans);
|
||||||
}else{
|
}else{
|
||||||
request.setAttribute("refinment", "&type="
|
request.setAttribute("refinement", "&type="
|
||||||
+ URLEncoder.encode(request.getParameter("type"),"UTF-8"));
|
+ URLEncoder.encode(request.getParameter("type"),"UTF-8"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
package edu.cornell.mannlib.vitro.webapp.web.templatemodels;
|
package edu.cornell.mannlib.vitro.webapp.web.templatemodels;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
|
@ -13,7 +16,7 @@ import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.Params;
|
||||||
|
|
||||||
public abstract class BaseTemplateModel {
|
public abstract class BaseTemplateModel {
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog(BaseTemplateModel.class.getName());
|
private static final Log log = LogFactory.getLog(BaseTemplateModel.class);
|
||||||
|
|
||||||
protected static ServletContext servletContext = null;
|
protected static ServletContext servletContext = null;
|
||||||
protected static VitroRequest vreq = null;
|
protected static VitroRequest vreq = null;
|
||||||
|
@ -23,8 +26,13 @@ public abstract class BaseTemplateModel {
|
||||||
return UrlBuilder.getUrl(path);
|
return UrlBuilder.getUrl(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap UrlBuilder method so templates can call ${item.url}
|
// Convenience method so subclasses can call getUrl(path, params)
|
||||||
public String getUrl(String path, Params params) {
|
protected String getUrl(String path, Params params) {
|
||||||
|
return UrlBuilder.getUrl(path, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience method so subclasses can call getUrl(path, params)
|
||||||
|
protected String getUrl(String path, String... params) {
|
||||||
return UrlBuilder.getUrl(path, params);
|
return UrlBuilder.getUrl(path, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ import edu.cornell.mannlib.vitro.webapp.web.ViewFinder.ClassView;
|
||||||
|
|
||||||
public class IndividualTemplateModel extends BaseTemplateModel {
|
public class IndividualTemplateModel extends BaseTemplateModel {
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog(IndividualTemplateModel.class.getName());
|
private static final Log log = LogFactory.getLog(IndividualTemplateModel.class);
|
||||||
|
|
||||||
private static final String PATH = Route.INDIVIDUAL.path();
|
private static final String PATH = Route.INDIVIDUAL.path();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.web.templatemodels;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringEscapeUtils;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.Params;
|
||||||
|
|
||||||
|
public class LinkTemplateModel extends BaseTemplateModel {
|
||||||
|
|
||||||
|
private String url;
|
||||||
|
private String text;
|
||||||
|
|
||||||
|
public LinkTemplateModel() { }
|
||||||
|
|
||||||
|
public LinkTemplateModel(String text, String path) {
|
||||||
|
setText(text);
|
||||||
|
setUrl(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LinkTemplateModel(String text, String path, String...params) {
|
||||||
|
setText(text);
|
||||||
|
setUrl(path, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LinkTemplateModel(String text, String path, Params params) {
|
||||||
|
setText(text);
|
||||||
|
setUrl(path, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setUrl(String path) {
|
||||||
|
url = UrlBuilder.getUrl(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setUrl(String path, String... params) {
|
||||||
|
url = UrlBuilder.getUrl(path, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setUrl(String path, Params params) {
|
||||||
|
url = UrlBuilder.getUrl(path, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setText(String text) {
|
||||||
|
this.text = StringEscapeUtils.escapeHtml(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
<#-- List individual members of a class. -->
|
<#-- List individual members of a class. -->
|
||||||
|
|
||||||
|
<#import "listMacros.ftl" as l>
|
||||||
|
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="individualList">
|
<div class="individualList">
|
||||||
<h2>${title}</h2>
|
<h2>${title}</h2>
|
||||||
|
@ -15,8 +17,20 @@
|
||||||
<ul>
|
<ul>
|
||||||
<#list individuals as individual>
|
<#list individuals as individual>
|
||||||
<li>
|
<li>
|
||||||
<#-- Currently we just use the search view here; there's no custom list view defined. -->
|
<#-- The old JSP version uses a custom search view if one is defined, but it doesn't make sense
|
||||||
<#include "${individual.searchView}">
|
to do that in the current system, because we don't use the default search view. We could define
|
||||||
|
a custom list or use the custom short view, but for now just hard-code the view here; it will not be
|
||||||
|
customizable.
|
||||||
|
<#include "${individual.searchView}"> -->
|
||||||
|
<a href="${individual.profileUrl}">${individual.name}</a>
|
||||||
|
<ul class="individualData">
|
||||||
|
<@l.firstLastList>
|
||||||
|
<#if individual.moniker??><li>${individual.moniker}</li>,</#if>
|
||||||
|
<#list individual.links as link>
|
||||||
|
<li><a class="externalLink" href="${link.url}">${link.anchor}</a></li>,
|
||||||
|
</#list>
|
||||||
|
</@l.firstLastList>
|
||||||
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</#list>
|
</#list>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
55
webapp/web/templates/freemarker/body/pagedSearchResults.ftl
Normal file
55
webapp/web/templates/freemarker/body/pagedSearchResults.ftl
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
|
||||||
|
|
||||||
|
<#-- Template for displaying paged search results -->
|
||||||
|
|
||||||
|
<h2>
|
||||||
|
Search Results for '${querytext}'
|
||||||
|
<#if classgroupName?has_content>limited to type '${classgroupName}'</#if>
|
||||||
|
<#if typeName?has_content>limited to type '${typeName}'</#if>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div class="contentsBrowseGroup">
|
||||||
|
|
||||||
|
<#-- Refinement links -->
|
||||||
|
<#if classGroupLinks?has_content>
|
||||||
|
<div class="searchTOC">
|
||||||
|
<span class="jumpText">Show only results of this <b>type</b>:</span>
|
||||||
|
<#list classGroupLinks as link>
|
||||||
|
<a href="${link.url}">${link.text}</a>
|
||||||
|
</#list>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<#if classLinks?has_content>
|
||||||
|
<div class="searchTOC">
|
||||||
|
<span class="jumpText">Show only results of this <b>subtype</b>:</span>
|
||||||
|
<#list classLinks as link>
|
||||||
|
<a href="${link.url}">${link.text}</a>
|
||||||
|
</#list>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
<#-- Search results -->
|
||||||
|
<ul class="searchhits">
|
||||||
|
<#list individuals as individual>
|
||||||
|
<li>
|
||||||
|
<#include "${individual.searchView}">
|
||||||
|
</li>
|
||||||
|
</#list>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<#-- Paging controls -->
|
||||||
|
<#if (pagingLinks?size > 0)>
|
||||||
|
<div class="searchpages">
|
||||||
|
Pages:
|
||||||
|
<#list pagingLinks as link>
|
||||||
|
<#if link.url??>
|
||||||
|
<a href="${link.url}">${link.text}</a>
|
||||||
|
<#else>
|
||||||
|
${link.text} <#-- no link if current page -->
|
||||||
|
</#if>
|
||||||
|
</#list>
|
||||||
|
</div>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
</div> <!-- end contentsBrowseGroup -->
|
|
@ -2,14 +2,9 @@
|
||||||
|
|
||||||
<#-- Default individual search view -->
|
<#-- Default individual search view -->
|
||||||
|
|
||||||
<#import "listMacros.ftl" as l>
|
|
||||||
|
|
||||||
<a href="${individual.profileUrl}">${individual.name}</a>
|
<a href="${individual.profileUrl}">${individual.name}</a>
|
||||||
<ul class="individualData">
|
<#if individual.moniker?has_content> | ${individual.moniker}</#if>
|
||||||
<@l.firstLastList>
|
|
||||||
<#if individual.moniker??><li>${individual.moniker}</li>,</#if>
|
<#if individual.description?has_content>
|
||||||
<#list individual.links as link>
|
<div class="searchFragment">${individual.description}</div>
|
||||||
<li><a class="externalLink" href="${link.url}">${link.anchor}</a></li>,
|
</#if>
|
||||||
</#list>
|
|
||||||
</@l.firstLastList>
|
|
||||||
</ul>
|
|
|
@ -7,6 +7,7 @@
|
||||||
<%@ page import="edu.cornell.mannlib.vitro.webapp.beans.Portal" %>
|
<%@ page import="edu.cornell.mannlib.vitro.webapp.beans.Portal" %>
|
||||||
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
|
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
|
||||||
<%@ page errorPage="/error.jsp"%>
|
<%@ page errorPage="/error.jsp"%>
|
||||||
|
|
||||||
<%
|
<%
|
||||||
/***********************************************
|
/***********************************************
|
||||||
Display Paged Search Results
|
Display Paged Search Results
|
||||||
|
@ -153,7 +154,7 @@ if( request.getAttribute("types") != null ){
|
||||||
String basePageUrl =
|
String basePageUrl =
|
||||||
request.getContextPath() + "/search?querytext="
|
request.getContextPath() + "/search?querytext="
|
||||||
+URLEncoder.encode(request.getParameter("querytext"),"UTF-8") +
|
+URLEncoder.encode(request.getParameter("querytext"),"UTF-8") +
|
||||||
request.getAttribute("refinment");
|
request.getAttribute("refinement");
|
||||||
|
|
||||||
out.println("<div class='searchpages'>");
|
out.println("<div class='searchpages'>");
|
||||||
out.println("Pages:");
|
out.println("Pages:");
|
||||||
|
|
Loading…
Add table
Reference in a new issue