This commit is contained in:
chenejac 2022-08-24 17:51:25 +02:00
commit c6b9524b74
18 changed files with 346 additions and 35 deletions

View file

@ -6,9 +6,11 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individuallist.ListedIndividualBuilder; import javax.servlet.annotation.WebServlet;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@ -16,6 +18,7 @@ import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.beans.Individual; import edu.cornell.mannlib.vitro.webapp.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.beans.VClass; import edu.cornell.mannlib.vitro.webapp.beans.VClass;
import edu.cornell.mannlib.vitro.webapp.beans.VClassGroup; import edu.cornell.mannlib.vitro.webapp.beans.VClassGroup;
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ExceptionResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ExceptionResponseValues;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
@ -27,8 +30,7 @@ import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngineExcepti
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchQuery; import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchQuery;
import edu.cornell.mannlib.vitro.webapp.utils.searchengine.SearchQueryUtils; import edu.cornell.mannlib.vitro.webapp.utils.searchengine.SearchQueryUtils;
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individuallist.ListedIndividual; import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individuallist.ListedIndividual;
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individuallist.ListedIndividualBuilder;
import javax.servlet.annotation.WebServlet;
/** /**
* Generates a list of individuals for display in a template * Generates a list of individuals for display in a template
@ -43,6 +45,7 @@ public class IndividualListController extends FreemarkerHttpServlet {
private static final int MAX_PAGES = 40; // must be even private static final int MAX_PAGES = 40; // must be even
private static final String TEMPLATE_DEFAULT = "individualList.ftl"; private static final String TEMPLATE_DEFAULT = "individualList.ftl";
private static final String LANGUAGE_FILTER_PROPERTY = "RDFService.languageFilter";
@Override @Override
protected ResponseValues processRequest(VitroRequest vreq) { protected ResponseValues processRequest(VitroRequest vreq) {
@ -152,12 +155,12 @@ public class IndividualListController extends FreemarkerHttpServlet {
return SearchQueryUtils.getPageParameter(request); return SearchQueryUtils.getPageParameter(request);
} }
public static IndividualListResults getResultsForVClass(String vclassURI, int page, String alpha, VitroRequest vreq) public static IndividualListResults getResultsForVClass(String vclassURI,
int page, String alpha, VitroRequest vreq)
throws SearchException{ throws SearchException{
try{ try{
List<String> classUris = Collections.singletonList(vclassURI); List<String> classUris = Collections.singletonList(vclassURI);
IndividualListQueryResults results = buildAndExecuteVClassQuery(classUris, alpha, page, INDIVIDUALS_PER_PAGE, vreq.getWebappDaoFactory().getIndividualDao()); return buildAndExecuteVClassQuery(classUris, page, INDIVIDUALS_PER_PAGE, alpha, vreq);
return getResultsForVClassQuery(results, page, INDIVIDUALS_PER_PAGE, alpha, vreq);
} catch (SearchEngineException e) { } catch (SearchEngineException e) {
String msg = "An error occurred retrieving results for vclass query"; String msg = "An error occurred retrieving results for vclass query";
log.error(msg, e); log.error(msg, e);
@ -169,16 +172,27 @@ public class IndividualListController extends FreemarkerHttpServlet {
} }
} }
public static IndividualListResults getResultsForVClassIntersections(List<String> vclassURIs, int page, int pageSize, String alpha, VitroRequest vreq) { public static IndividualListResults getResultsForVClassIntersections(
List<String> classUris, int page, int pageSize, String alpha, VitroRequest vreq) {
try{ try{
IndividualListQueryResults results = buildAndExecuteVClassQuery(vclassURIs, alpha, page, pageSize, vreq.getWebappDaoFactory().getIndividualDao()); return buildAndExecuteVClassQuery(classUris, page, pageSize, alpha, vreq);
return getResultsForVClassQuery(results, page, pageSize, alpha, vreq);
} catch(Throwable th) { } catch(Throwable th) {
log.error("Error retrieving individuals corresponding to intersection multiple classes." + vclassURIs.toString(), th); log.error("Error retrieving individuals corresponding to intersection multiple classes." + classUris.toString(), th);
return IndividualListResults.EMPTY; return IndividualListResults.EMPTY;
} }
} }
private static IndividualListResults buildAndExecuteVClassQuery(List<String> classUris, int page, int pageSize,
String alpha, VitroRequest vreq) throws SearchEngineException {
ConfigurationProperties props = ConfigurationProperties.getBean(vreq);
boolean languageFilter = Boolean.valueOf(props.getProperty(LANGUAGE_FILTER_PROPERTY, "false"));
IndividualListQueryResults results = buildAndExecuteVClassQuery(classUris, alpha,
((languageFilter) ? vreq.getLocale() : null), page, pageSize,
vreq.getWebappDaoFactory().getIndividualDao());
IndividualListResults indListResults = getResultsForVClassQuery(results, page, pageSize, alpha, vreq);
return indListResults;
}
public static IndividualListResults getRandomResultsForVClass(String vclassURI, int page, int pageSize, VitroRequest vreq) { public static IndividualListResults getRandomResultsForVClass(String vclassURI, int page, int pageSize, VitroRequest vreq) {
try{ try{
List<String> classUris = Collections.singletonList(vclassURI); List<String> classUris = Collections.singletonList(vclassURI);
@ -201,9 +215,10 @@ public class IndividualListController extends FreemarkerHttpServlet {
private static IndividualListQueryResults buildAndExecuteVClassQuery( private static IndividualListQueryResults buildAndExecuteVClassQuery(
List<String> vclassURIs, String alpha, int page, int pageSize, IndividualDao indDao) List<String> vclassURIs, String alpha, Locale locale, int page,
int pageSize, IndividualDao indDao)
throws SearchEngineException { throws SearchEngineException {
SearchQuery query = SearchQueryUtils.getQuery(vclassURIs, alpha, page, pageSize); SearchQuery query = SearchQueryUtils.getQuery(vclassURIs, alpha, locale, page, pageSize);
IndividualListQueryResults results = IndividualListQueryResults.runQuery(query, indDao); IndividualListQueryResults results = IndividualListQueryResults.runQuery(query, indDao);
log.debug("Executed search query for " + vclassURIs); log.debug("Executed search query for " + vclassURIs);
if (results.getIndividuals().isEmpty()) { if (results.getIndividuals().isEmpty()) {

View file

@ -643,7 +643,7 @@ public class JenaBaseDao extends JenaBaseDaoCon {
if (existingValue == null ) { if (existingValue == null ) {
model.add(res, prop, model.createResource(uri)); model.add(res, prop, model.createResource(uri));
} else if (!(existingValue.getURI()).equals(uri)) { } else if (!isEqual(uri, existingValue)) {
model.removeAll(res, prop, null); model.removeAll(res, prop, null);
model.add(res, prop, model.createResource(uri)); model.add(res, prop, model.createResource(uri));
} }
@ -651,6 +651,15 @@ public class JenaBaseDao extends JenaBaseDaoCon {
} }
} }
private boolean isEqual(String uri, Resource existingValue) {
if (existingValue.asNode().isBlank()) {
final String blankNodeId = existingValue.asNode().getBlankNodeId().toString();
return uri.endsWith(blankNodeId);
} else {
return existingValue.getURI().equals(uri);
}
}
/** /**
* convenience method for use with functional object properties * convenience method for use with functional object properties
*/ */

View file

@ -42,7 +42,7 @@ public class DefaultDataPropertyFormValidator implements N3ValidatorVTwo{
private final Pattern ymPattern = Pattern.compile(ymRegex); private final Pattern ymPattern = Pattern.compile(ymRegex);
private final String monthRegex = "^--(0[1-9]|1[012])"; private final String monthRegex = "^--(0[1-9]|1[012])";
private final Pattern monthPattern = Pattern.compile(monthRegex); private final Pattern monthPattern = Pattern.compile(monthRegex);
private final String floatRegex = "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?."; private final String floatRegex = "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?";
private final Pattern floatPattern = Pattern.compile(floatRegex); private final Pattern floatPattern = Pattern.compile(floatRegex);
private final String intRegex = "^-?\\d+$"; private final String intRegex = "^-?\\d+$";
private final Pattern intPattern = Pattern.compile(intRegex); private final Pattern intPattern = Pattern.compile(intRegex);

View file

@ -71,4 +71,10 @@ public class VitroSearchTermNames {
/** Source institution name */ /** Source institution name */
public static final String SITE_NAME = "siteName"; public static final String SITE_NAME = "siteName";
/** Multilingual sort field suffix */
public static final String LABEL_SORT_SUFFIX = "_label_sort";
/** Multilingual label field suffix */
public static final String LABEL_DISPLAY_SUFFIX = "_label_display";
} }

View file

@ -36,6 +36,7 @@ import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchResultDocumen
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils; import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils;
import edu.cornell.mannlib.vitro.webapp.search.VitroSearchTermNames; import edu.cornell.mannlib.vitro.webapp.search.VitroSearchTermNames;
import edu.cornell.mannlib.vitro.webapp.utils.searchengine.SearchQueryUtils;
/** /**
* AutocompleteController generates autocomplete content * AutocompleteController generates autocomplete content
@ -127,7 +128,10 @@ public class AutocompleteController extends VitroAjaxController {
for (SearchResultDocument doc : docs) { for (SearchResultDocument doc : docs) {
try { try {
String uri = doc.getStringValue(VitroSearchTermNames.URI); String uri = doc.getStringValue(VitroSearchTermNames.URI);
String name = doc.getStringValue(VitroSearchTermNames.NAME_RAW); String name = doc.getStringValue(SearchQueryUtils.getLabelFieldNameForLocale(vreq.getLocale()));
if (name == null) {
name = doc.getStringValue(VitroSearchTermNames.NAME_RAW);
}
//There may be multiple most specific types, sending them all back //There may be multiple most specific types, sending them all back
String mst = doc.getStringValue(VitroSearchTermNames.MOST_SPECIFIC_TYPE_URIS); String mst = doc.getStringValue(VitroSearchTermNames.MOST_SPECIFIC_TYPE_URIS);
//Assuming these will get me string values //Assuming these will get me string values
@ -184,7 +188,9 @@ public class AutocompleteController extends VitroAjaxController {
addFilterQuery(query, typeParam, multipleTypesParam); addFilterQuery(query, typeParam, multipleTypesParam);
} }
query.addFields(VitroSearchTermNames.NAME_RAW, VitroSearchTermNames.URI, VitroSearchTermNames.MOST_SPECIFIC_TYPE_URIS); // fields to retrieve query.addFields(SearchQueryUtils.getLabelFieldNameForLocale(vreq.getLocale()),
VitroSearchTermNames.NAME_RAW, VitroSearchTermNames.URI,
VitroSearchTermNames.MOST_SPECIFIC_TYPE_URIS); // fields to retrieve
// Can't sort on multivalued field, so we sort the results in Java when we get them. // Can't sort on multivalued field, so we sort the results in Java when we get them.
// query.addSortField(VitroSearchTermNames.NAME_LOWERCASE, Order.ASC); // query.addSortField(VitroSearchTermNames.NAME_LOWERCASE, Order.ASC);

View file

@ -5,8 +5,8 @@ package edu.cornell.mannlib.vitro.webapp.searchengine.base;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -21,7 +21,7 @@ public class BaseSearchQuery implements SearchQuery {
private int rows = -1; private int rows = -1;
private final Set<String> fieldsToReturn = new HashSet<>(); private final Set<String> fieldsToReturn = new HashSet<>();
private final Map<String, SearchQuery.Order> sortFields = new HashMap<>(); private final Map<String, SearchQuery.Order> sortFields = new LinkedHashMap <>();
private final Set<String> filters = new HashSet<>(); private final Set<String> filters = new HashSet<>();
private final Set<String> facetFields = new HashSet<>(); private final Set<String> facetFields = new HashSet<>();

View file

@ -0,0 +1,72 @@
package edu.cornell.mannlib.vitro.webapp.searchengine.solr;
import static edu.cornell.mannlib.vitro.webapp.search.VitroSearchTermNames.LABEL_DISPLAY_SUFFIX;
import static edu.cornell.mannlib.vitro.webapp.search.VitroSearchTermNames.LABEL_SORT_SUFFIX;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.impl.ConcurrentUpdateSolrClient;
import org.apache.solr.client.solrj.request.schema.SchemaRequest;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.schema.SchemaResponse;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.util.SimpleOrderedMap;
public class SolrFieldInitializer {
static void initializeFields(SolrClient queryEngine, ConcurrentUpdateSolrClient updateEngine) throws Exception {
Set<String> fieldSuffixes = new HashSet<>(Arrays.asList(LABEL_SORT_SUFFIX, LABEL_DISPLAY_SUFFIX));
excludeMatchedFields(fieldSuffixes, queryEngine, "dynamicFields");
excludeMatchedFields(fieldSuffixes, queryEngine, "fields");
createMissingFields(fieldSuffixes, updateEngine);
}
private static void createMissingFields(Set<String> fieldSuffixes, ConcurrentUpdateSolrClient updateEngine)
throws Exception {
for (String suffix : fieldSuffixes) {
Map<String, Object> fieldAttributes = getFieldAttributes(suffix);
SchemaRequest.AddDynamicField request = new SchemaRequest.AddDynamicField(fieldAttributes);
SchemaResponse.UpdateResponse response = request.process(updateEngine);
if (response.getStatus() != 0) {
throw new Exception("Creation of missing solr field '*" + suffix + "' failed");
}
}
}
private static Map<String, Object> getFieldAttributes(String suffix) {
Map<String, Object> fieldAttributes = new HashMap<String, Object>();
fieldAttributes.put("type", "string");
fieldAttributes.put("stored", "true");
fieldAttributes.put("indexed", "true");
fieldAttributes.put("name", "*" + suffix);
return fieldAttributes;
}
private static void excludeMatchedFields(Set<String> fieldSuffixes, SolrClient queryEngine, String fieldType)
throws Exception {
SolrQuery query = new SolrQuery();
query.add(CommonParams.QT, "/schema/" + fieldType.toLowerCase());
QueryResponse response = queryEngine.query(query);
ArrayList<SimpleOrderedMap> fieldList = (ArrayList<SimpleOrderedMap>) response.getResponse().get(fieldType);
if (fieldList == null) {
return;
}
Set<String> it = new HashSet<>(fieldSuffixes);
for (String target : it) {
for (SimpleOrderedMap field : fieldList) {
String fieldName = (String) field.get("name");
if (fieldName.endsWith(target)) {
fieldSuffixes.remove(target);
}
}
}
}
}

View file

@ -77,6 +77,8 @@ public class SolrSearchEngine implements SearchEngine {
updateEngine = updateBuilder.build(); updateEngine = updateBuilder.build();
SolrFieldInitializer.initializeFields(queryEngine, updateEngine);
css.info("Set up the Solr search engine; URL = '" + solrServerUrlString + "'."); css.info("Set up the Solr search engine; URL = '" + solrServerUrlString + "'.");
} catch (Exception e) { } catch (Exception e) {
css.fatal("Could not set up the Solr search engine", e); css.fatal("Could not set up the Solr search engine", e);

View file

@ -52,13 +52,13 @@ public class SelectQueryDocumentModifier implements DocumentModifier,
private static final Log log = LogFactory private static final Log log = LogFactory
.getLog(SelectQueryDocumentModifier.class); .getLog(SelectQueryDocumentModifier.class);
private RDFService rdfService; protected RDFService rdfService;
/** A name to be used in logging, to identify this instance. */ /** A name to be used in logging, to identify this instance. */
private String label; protected String label;
/** The queries to be executed. There must be at least one. */ /** The queries to be executed. There must be at least one. */
private List<String> queries = new ArrayList<>(); protected List<String> queries = new ArrayList<>();
/** /**
* The names of the fields where the results of the queries will be stored. * The names of the fields where the results of the queries will be stored.
@ -128,7 +128,7 @@ public class SelectQueryDocumentModifier implements DocumentModifier,
} }
} }
private boolean passesTypeRestrictions(Individual ind) { protected boolean passesTypeRestrictions(Individual ind) {
if (typeRestrictions.isEmpty()) { if (typeRestrictions.isEmpty()) {
return true; return true;
} else { } else {

View file

@ -0,0 +1,111 @@
/* $This file is distributed under the terms of the license in LICENSE$ */
package edu.cornell.mannlib.vitro.webapp.searchindex.documentBuilding;
import static edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.SparqlQueryRunner.createSelectQueryContext;
import static edu.cornell.mannlib.vitro.webapp.i18n.selection.LocaleSelectionSetup.PROPERTY_SELECTABLE_LOCALES;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchInputDocument;
import edu.cornell.mannlib.vitro.webapp.rdfservice.filter.LanguageFilteringRDFService;
import edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationReader;
import edu.cornell.mannlib.vitro.webapp.utils.configuration.ContextModelsUser;
import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property;
import edu.cornell.mannlib.vitro.webapp.utils.sparqlrunner.QueryHolder;
/**
* A variation on SelectQueryDocumentModifier where the suffix of target field is defined.
* Multiple queries are performed for each of locales configured in runtime.properties
*
* Target field names are composed of locale + fieldSuffix.
*
* Each query should contain a ?uri variable, which will be replaced by the URI
* of the individual.
*
* All of the other result fields in each row of each query will be converted to
* strings and added to the field.
*
*/
public class SelectQueryI18nDocumentModifier extends SelectQueryDocumentModifier
implements DocumentModifier, ContextModelsUser, ConfigurationReader {
private static final Log log = LogFactory.getLog(SelectQueryI18nDocumentModifier.class);
private String fieldSuffix = "";
private ArrayList<String> locales = new ArrayList<>();
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasTargetSuffix")
public void setTargetSuffix(String fieldSuffix) {
this.fieldSuffix = fieldSuffix;
}
@Override
public void modifyDocument(Individual ind, SearchInputDocument doc) {
if (passesTypeRestrictions(ind) && StringUtils.isNotBlank(fieldSuffix)) {
List<Map<String, List<String>>> maps = getTextForQueries(ind);
for (Map<String, List<String>> map : maps) {
for (String locale : map.keySet()) {
List<String> values = map.get(locale);
String fieldName = locale + fieldSuffix;
doc.addField(fieldName, values);
}
}
}
}
protected List<Map<String, List<String>>> getTextForQueries(Individual ind) {
List<Map<String, List<String>>> list = new ArrayList<>();
for (String query : queries) {
list.add(getTextForQuery(query, ind));
}
return list;
}
protected Map<String, List<String>> getTextForQuery(String query, Individual ind) {
try {
QueryHolder queryHolder = new QueryHolder(query).bindToUri("uri", ind.getURI());
Map<String, List<String>> mapLocaleToFields = new HashMap<>();
for (String locale : locales) {
LanguageFilteringRDFService lfrs = new LanguageFilteringRDFService(rdfService,
Collections.singletonList(locale));
List<String> list = createSelectQueryContext(lfrs, queryHolder).execute().toStringFields().flatten();
mapLocaleToFields.put(locale, list);
log.debug(label + " for locale " + locale + " - query: '" + query + "' returns " + list);
}
return mapLocaleToFields;
} catch (Throwable t) {
log.error("problem while running query '" + query + "'", t);
return Collections.emptyMap();
}
}
@Override
public void setConfigurationProperties(ConfigurationProperties config) {
String property = config.getProperty(PROPERTY_SELECTABLE_LOCALES);
if (!StringUtils.isBlank(property)) {
String[] values = property.trim().split("\\s*,\\s*");
for (String value : values) {
String locale = value.replace("_", "-");
addLocale(locale);
}
}
}
private void addLocale(String localeString) {
if (StringUtils.isBlank(localeString)) {
return;
}
locales.add(localeString);
}
}

View file

@ -0,0 +1,13 @@
/* $This file is distributed under the terms of the license in LICENSE$ */
package edu.cornell.mannlib.vitro.webapp.utils.configuration;
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
/**
* When the ConfigurationBeanLoader creates an instance of this class, it will
* call this method, supplying ConfigurationProperties.
*/
public interface ConfigurationReader {
void setConfigurationProperties(ConfigurationProperties properties);
}

View file

@ -12,6 +12,7 @@ import java.util.Set;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess;
import edu.cornell.mannlib.vitro.webapp.utils.configuration.PropertyType.PropertyMethod; import edu.cornell.mannlib.vitro.webapp.utils.configuration.PropertyType.PropertyMethod;
import edu.cornell.mannlib.vitro.webapp.utils.configuration.PropertyType.PropertyStatement; import edu.cornell.mannlib.vitro.webapp.utils.configuration.PropertyType.PropertyStatement;
@ -62,6 +63,15 @@ public class WrappedInstance<T> {
rmu.setRequestModels(ModelAccess.on(req)); rmu.setRequestModels(ModelAccess.on(req));
} }
} }
if (instance instanceof ConfigurationReader) {
if (ctx == null) {
throw new ResourceUnavailableException("Cannot satisfy "
+ "ConfigurationReader interface: context not available.");
} else {
ConfigurationReader cr = (ConfigurationReader) instance;
cr.setConfigurationProperties(ConfigurationProperties.getBean(ctx));
}
}
} }
/** /**

View file

@ -5,6 +5,7 @@ package edu.cornell.mannlib.vitro.webapp.utils.searchengine;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -150,19 +151,38 @@ public class SearchQueryUtils {
} }
/** /**
* builds a query with a type clause for each type in vclassUris, NAME_LOWERCASE filetred by * builds a query with a type clause for each type in vclassUris,
* alpha, and just the hits for the page for pageSize. * NAME_LOWERCASE filtered by alpha, and just the hits for the page for pageSize.
* @param locale may be null. If null, default sort field will be used.
* Otherwise, query will be sorted by locale-specific sort field.
*/ */
public static SearchQuery getQuery(List<String> vclassUris, String alpha, int page, int pageSize){ public static SearchQuery getQuery(List<String> vclassUris, String alpha,
Locale locale, int page, int pageSize){
String queryText = ""; String queryText = "";
SearchEngine searchEngine = ApplicationUtils.instance().getSearchEngine(); SearchEngine searchEngine = ApplicationUtils.instance().getSearchEngine();
try { try {
queryText = makeMultiClassQuery(vclassUris); queryText = makeMultiClassQuery(vclassUris);
String localeSpecificField = null;
if (locale != null) {
localeSpecificField = getSortFieldNameForLocale(locale);
}
// Add alpha filter if applicable // Add alpha filter if applicable
if ( alpha != null && !"".equals(alpha) && alpha.length() == 1) { if ( alpha != null && !"".equals(alpha) && alpha.length() == 1) {
if (locale == null) {
queryText += VitroSearchTermNames.NAME_LOWERCASE + ":" + alpha.toLowerCase() + "*"; queryText += VitroSearchTermNames.NAME_LOWERCASE + ":" + alpha.toLowerCase() + "*";
} else {
// Retrieve items matching the appropriate alpha char
// on the i18ned field if that field exists. For records
// where the field does not exist, fall back to NAME_LOWERCASE
queryText += "(" + localeSpecificField + ":" + alpha.toLowerCase()
+ "* OR (-" + localeSpecificField + ":[* TO *] AND "
+ VitroSearchTermNames.NAME_LOWERCASE + ":" + alpha.toLowerCase() + "*))";
log.debug("Multiclass query text: " + queryText);
}
} }
SearchQuery query = searchEngine.createQuery(queryText); SearchQuery query = searchEngine.createQuery(queryText);
@ -172,6 +192,11 @@ public class SearchQueryUtils {
query.setStart( startRow ).setRows( pageSize ); query.setStart( startRow ).setRows( pageSize );
// Need a single-valued field for sorting // Need a single-valued field for sorting
// Sort first by sort field for locale; fall back to
// NAME_LOWERCASE_SINGLE_VALUED if not available.
if(locale != null) {
query.addSortField(localeSpecificField, Order.ASC);
}
query.addSortField(VitroSearchTermNames.NAME_LOWERCASE_SINGLE_VALUED, Order.ASC); query.addSortField(VitroSearchTermNames.NAME_LOWERCASE_SINGLE_VALUED, Order.ASC);
log.debug("Query is " + query.toString()); log.debug("Query is " + query.toString());
@ -183,6 +208,14 @@ public class SearchQueryUtils {
} }
} }
public static String getSortFieldNameForLocale(Locale locale) {
return locale.toString().replace('_', '-') + VitroSearchTermNames.LABEL_SORT_SUFFIX;
}
public static String getLabelFieldNameForLocale(Locale locale) {
return locale.toString().replace('_', '-') + VitroSearchTermNames.LABEL_DISPLAY_SUFFIX;
}
public static SearchQuery getRandomQuery(List<String> vclassUris, int page, int pageSize){ public static SearchQuery getRandomQuery(List<String> vclassUris, int page, int pageSize){
String queryText = ""; String queryText = "";
SearchEngine searchEngine = ApplicationUtils.instance().getSearchEngine(); SearchEngine searchEngine = ApplicationUtils.instance().getSearchEngine();

View file

@ -64,6 +64,7 @@ public class DataPropertyTemplateModel extends PropertyTemplateModel {
private DataPropertyListConfig config; private DataPropertyListConfig config;
private String objectKey; private String objectKey;
private String queryString; private String queryString;
private String publicDescription;
private String rangeDatatypeURI; private String rangeDatatypeURI;
private Set<String> constructQueries; private Set<String> constructQueries;
private int displayLimit; private int displayLimit;
@ -82,6 +83,7 @@ public class DataPropertyTemplateModel extends PropertyTemplateModel {
queryString = getSelectQuery(); queryString = getSelectQuery();
constructQueries = getConstructQueries(); constructQueries = getConstructQueries();
publicDescription = dp.getPublicDescription();
statements = new ArrayList<DataPropertyStatementTemplateModel>(); statements = new ArrayList<DataPropertyStatementTemplateModel>();
displayLimit = dp.getDisplayLimit(); displayLimit = dp.getDisplayLimit();
@ -158,6 +160,10 @@ public class DataPropertyTemplateModel extends PropertyTemplateModel {
return Route.DATA_PROPERTY_EDIT; return Route.DATA_PROPERTY_EDIT;
} }
public String getPublicDescription() {
return publicDescription;
}
@Override @Override
public int getDisplayLimit() { public int getDisplayLimit() {
return displayLimit; return displayLimit;

View file

@ -2,9 +2,6 @@
package edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual; package edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual;
import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.LanguageOption.LANGUAGE_NEUTRAL;
import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess.PolicyOption.POLICY_NEUTRAL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -280,8 +277,7 @@ public class GroupedPropertyList extends BaseTemplateModel {
} }
private ObjectProperty assembleObjectProperty(PropertyInstance pi) { private ObjectProperty assembleObjectProperty(PropertyInstance pi) {
WebappDaoFactory rawWadf = ModelAccess.on(vreq).getWebappDaoFactory( WebappDaoFactory rawWadf = ModelAccess.on(vreq).getWebappDaoFactory();
LANGUAGE_NEUTRAL, POLICY_NEUTRAL);
ObjectPropertyDao opDao = rawWadf.getObjectPropertyDao(); ObjectPropertyDao opDao = rawWadf.getObjectPropertyDao();
FauxPropertyDao fpDao = rawWadf.getFauxPropertyDao(); FauxPropertyDao fpDao = rawWadf.getFauxPropertyDao();

View file

@ -0,0 +1,16 @@
@prefix : <http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
:documentModifier_multilingual_label
a <java:edu.cornell.mannlib.vitro.webapp.searchindex.documentBuilding.SelectQueryI18nDocumentModifier> ,
<java:edu.cornell.mannlib.vitro.webapp.searchindex.documentBuilding.DocumentModifier> ;
rdfs:label "multilingual label document modifier" ;
:hasTargetSuffix "_label_display" ;
:hasSelectQuery """
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT (MIN(?label) AS ?singleLabel ) WHERE {
?uri rdfs:label ?label .
BIND (LANG(?label) as ?lang )
} GROUP BY ?lang ORDER BY ?lang
""" .

View file

@ -0,0 +1,16 @@
@prefix : <http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
:documentModifier_multilingual_sort
a <java:edu.cornell.mannlib.vitro.webapp.searchindex.documentBuilding.SelectQueryI18nDocumentModifier> ,
<java:edu.cornell.mannlib.vitro.webapp.searchindex.documentBuilding.DocumentModifier> ;
rdfs:label "multilingual sort document modifier" ;
:hasTargetSuffix "_label_sort" ;
:hasSelectQuery """
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT ( LCASE(MIN(?label)) AS ?singleLabel ) WHERE {
?uri rdfs:label ?label .
BIND (LANG(?label) as ?lang )
} GROUP BY ?lang ORDER BY ?lang
""" .

View file

@ -81,7 +81,7 @@
<span class="invalidFormatText">invalid format</span> <span class="invalidFormatText">invalid format</span>
<#break> <#break>
<#case "float"> <#case "float">
<#if !value?matches("^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?.") > <#if !value?matches("^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?") >
<img class="invalidFormatImg" src="${urls.base}/images/iconAlert.png" width="18" alt=" ${i18n().invalid_format}" title=" ${i18n().invalid_format}"> <img class="invalidFormatImg" src="${urls.base}/images/iconAlert.png" width="18" alt=" ${i18n().invalid_format}" title=" ${i18n().invalid_format}">
<span class="invalidFormatText">invalid format</span> <span class="invalidFormatText">invalid format</span>
</#if> </#if>