/** * The contents of this file are subject to the license and copyright * detailed in the LICENSE and NOTICE files at the root of the source * tree and available online at * * http://www.dspace.org/license/ */ package org.dspace.content.authority; import org.dspace.authority.AuthoritySearchService; import org.dspace.authority.AuthorityValue; import org.dspace.authority.factory.AuthorityServiceFactory; import org.dspace.authority.rest.RestSource; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.params.CommonParams; import org.dspace.authority.service.AuthorityValueService; import org.dspace.content.Collection; import org.dspace.core.ConfigurationManager; import org.dspace.services.factory.DSpaceServicesFactory; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; /** * * @author Antoine Snyers (antoine at atmire.com) * @author Kevin Van de Velde (kevin at atmire dot com) * @author Ben Bosman (ben at atmire dot com) * @author Mark Diggory (markd at atmire dot com) */ public class SolrAuthority implements ChoiceAuthority { private static final Logger log = Logger.getLogger(SolrAuthority.class); protected RestSource source = DSpaceServicesFactory.getInstance().getServiceManager().getServiceByName("AuthoritySource", RestSource.class); protected boolean externalResults = false; protected final AuthorityValueService authorityValueService = AuthorityServiceFactory.getInstance().getAuthorityValueService(); public Choices getMatches(String field, String text, Collection collection, int start, int limit, String locale, boolean bestMatch) { if(limit == 0) limit = 10; SolrQuery queryArgs = new SolrQuery(); if (text == null || text.trim().equals("")) { queryArgs.setQuery("*:*"); } else { String searchField = "value"; String localSearchField = ""; try { //A downside of the authors is that the locale is sometimes a number, make sure that this isn't one Integer.parseInt(locale); locale = null; } catch (NumberFormatException e) { //Everything is allright } if (locale != null && !"".equals(locale)) { localSearchField = searchField + "_" + locale; } String query = "(" + toQuery(searchField, text) + ") "; if (!localSearchField.equals("")) { query += " or (" + toQuery(localSearchField, text) + ")"; } queryArgs.setQuery(query); } queryArgs.addFilterQuery("field:" + field); queryArgs.set(CommonParams.START, start); //We add one to our facet limit so that we know if there are more matches int maxNumberOfSolrResults = limit + 1; if(externalResults){ maxNumberOfSolrResults = ConfigurationManager.getIntProperty("xmlui.lookup.select.size", 12); } queryArgs.set(CommonParams.ROWS, maxNumberOfSolrResults); String sortField = "value"; String localSortField = ""; if (StringUtils.isNotBlank(locale)) { localSortField = sortField + "_" + locale; queryArgs.setSortField(localSortField, SolrQuery.ORDER.asc); } else { queryArgs.setSortField(sortField, SolrQuery.ORDER.asc); } Choices result; try { int max = 0; boolean hasMore = false; QueryResponse searchResponse = getSearchService().search(queryArgs); SolrDocumentList authDocs = searchResponse.getResults(); ArrayList<Choice> choices = new ArrayList<Choice>(); if (authDocs != null) { max = (int) searchResponse.getResults().getNumFound(); int maxDocs = authDocs.size(); if (limit < maxDocs) maxDocs = limit; List<AuthorityValue> alreadyPresent = new ArrayList<AuthorityValue>(); for (int i = 0; i < maxDocs; i++) { SolrDocument solrDocument = authDocs.get(i); if (solrDocument != null) { AuthorityValue val = authorityValueService.fromSolr(solrDocument); Map<String, String> extras = val.choiceSelectMap(); extras.put("insolr", val.getId()); choices.add(new Choice(val.getId(), val.getValue(), val.getValue(), extras)); alreadyPresent.add(val); } } if (externalResults && StringUtils.isNotBlank(text)) { int sizeFromSolr = alreadyPresent.size(); int maxExternalResults = limit <= 10 ? Math.max(limit - sizeFromSolr, 2) : Math.max(limit - 10 - sizeFromSolr, 2) + limit - 10; addExternalResults(text, choices, alreadyPresent, maxExternalResults); } // hasMore = (authDocs.size() == (limit + 1)); hasMore = true; } int confidence; if (choices.size() == 0) confidence = Choices.CF_NOTFOUND; else if (choices.size() == 1) confidence = Choices.CF_UNCERTAIN; else confidence = Choices.CF_AMBIGUOUS; result = new Choices(choices.toArray(new Choice[choices.size()]), start, hasMore ? max : choices.size() + start, confidence, hasMore); } catch (Exception e) { log.error("Error while retrieving authority values {field: " + field + ", prefix:" + text + "}", e); result = new Choices(true); } return result; //To change body of implemented methods use File | Settings | File Templates. } protected void addExternalResults(String text, ArrayList<Choice> choices, List<AuthorityValue> alreadyPresent, int max) { if(source != null){ try { List<AuthorityValue> values = source.queryAuthorities(text, max * 2); // max*2 because results get filtered // filtering loop Iterator<AuthorityValue> iterator = values.iterator(); while (iterator.hasNext()) { AuthorityValue next = iterator.next(); if (alreadyPresent.contains(next)) { iterator.remove(); } } // adding choices loop int added = 0; iterator = values.iterator(); while (iterator.hasNext() && added < max) { AuthorityValue val = iterator.next(); Map<String, String> extras = val.choiceSelectMap(); extras.put("insolr", "false"); choices.add(new Choice(val.generateString(), val.getValue(), val.getValue(), extras)); added++; } } catch (Exception e) { log.error("Error", e); } this.externalResults = false; } else { log.warn("external source for authority not configured"); } } private String toQuery(String searchField, String text) { return searchField + ":\"" + text.toLowerCase().replaceAll(":", "\\:") + "*\" or " + searchField + ":\"" + text.toLowerCase().replaceAll(":", "\\:")+"\""; } @Override public Choices getMatches(String field, String text, Collection collection, int start, int limit, String locale) { return getMatches(field, text, collection, start, limit, locale, true); } @Override public Choices getBestMatch(String field, String text, Collection collection, String locale) { Choices matches = getMatches(field, text, collection, 0, 1, locale, false); if (matches.values.length !=0 && !matches.values[0].value.equalsIgnoreCase(text)) { matches = new Choices(false); } return matches; } @Override public String getLabel(String field, String key, String locale) { try { if (log.isDebugEnabled()) { log.debug("requesting label for key " + key + " using locale " + locale); } SolrQuery queryArgs = new SolrQuery(); queryArgs.setQuery("id:" + key); queryArgs.setRows(1); QueryResponse searchResponse = getSearchService().search(queryArgs); SolrDocumentList docs = searchResponse.getResults(); if (docs.getNumFound() == 1) { String label = null; try { label = (String) docs.get(0).getFieldValue("value_" + locale); } catch (Exception e) { //ok to fail here } if (label != null) { if (log.isDebugEnabled()) { log.debug("returning label " + label + " for key " + key + " using locale " + locale + " and fieldvalue " + "value_" + locale); } return label; } try { label = (String) docs.get(0).getFieldValue("value"); } catch (Exception e) { log.error("couldn't get field value for key " + key,e); } if (label != null) { if (log.isDebugEnabled()) { log.debug("returning label " + label + " for key " + key + " using locale " + locale + " and fieldvalue " + "value"); } return label; } try { label = (String) docs.get(0).getFieldValue("value_en"); } catch (Exception e) { log.error("couldn't get field value for key " + key,e); } if (label != null) { if (log.isDebugEnabled()) { log.debug("returning label " + label + " for key " + key + " using locale " + locale + " and fieldvalue " + "value_en"); } return label; } } } catch (Exception e) { log.error("error occurred while trying to get label for key " + key,e); } return key; } public static AuthoritySearchService getSearchService() { org.dspace.kernel.ServiceManager manager = DSpaceServicesFactory.getInstance().getServiceManager(); return manager.getServiceByName(AuthoritySearchService.class.getName(), AuthoritySearchService.class); } public void addExternalResultsInNextMatches() { this.externalResults = true; } }