/** * 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.browse; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.apache.solr.common.SolrInputDocument; import org.dspace.content.Metadatum; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; import org.dspace.content.authority.ChoiceAuthorityManager; import org.dspace.content.authority.MetadataAuthorityManager; import org.dspace.core.ConfigurationManager; import org.dspace.core.Context; import org.dspace.discovery.SolrServiceImpl; import org.dspace.discovery.SolrServiceIndexPlugin; import org.dspace.sort.OrderFormat; import org.dspace.sort.SortException; import org.dspace.sort.SortOption; import org.dspace.storage.rdbms.DatabaseManager; import org.dspace.utils.DSpace; /** * * @author Andrea Bollini (CILEA) * */ public class SolrBrowseCreateDAO implements BrowseCreateDAO, SolrServiceIndexPlugin { private static final String INFO_NOSQL_TO_RUN = "No SQL to run: data are stored in the SOLR Search Core. PLEASE NOTE THAT YOU MUST UPDATE THE DISCOVERY INDEX AFTER ANY CHANGES TO THE BROWSE CONFIGURATION"; // reference to a DBMS BrowseCreateDAO needed to remove old tables when // switching from DBMS to SOLR private BrowseCreateDAO dbCreateDAO; private static final Logger log = Logger .getLogger(SolrBrowseCreateDAO.class); private BrowseIndex[] bis; public SolrBrowseCreateDAO() { try { bis = BrowseIndex.getBrowseIndices(); } catch (BrowseException e) { log.error(e.getMessage(), e); throw new IllegalStateException(e); } for (BrowseIndex bi : bis) bi.generateMdBits(); } public SolrBrowseCreateDAO(Context context) throws BrowseException { // For compatibility with previous versions if (! DatabaseManager.isOracle()) { dbCreateDAO = new BrowseCreateDAOPostgres(context); } else { dbCreateDAO = new BrowseCreateDAOOracle(context); } try { bis = BrowseIndex.getBrowseIndices(); } catch (BrowseException e) { log.error(e.getMessage(), e); throw new IllegalStateException(e); } for (BrowseIndex bi : bis) bi.generateMdBits(); } @Override public void additionalIndex(Context context, DSpaceObject dso, SolrInputDocument doc) { if (!(dso instanceof Item)) { return; } Item item = (Item) dso; // faceting for metadata browsing. It is different than search facet // because if there are authority with variants support we want all the // variants to go in the facet... they are sorted by count so just the // prefered label is relevant for (BrowseIndex bi : bis) { log.debug("Indexing for item " + item.getID() + ", for index: " + bi.getTableName()); if (bi.isMetadataIndex()) { // values to show in the browse list Set<String> distFValues = new HashSet<String>(); // value for lookup without authority Set<String> distFVal = new HashSet<String>(); // value for lookup with authority Set<String> distFAuths = new HashSet<String>(); // value for lookup when partial search (the item mapper tool use it) Set<String> distValuesForAC = new HashSet<String>(); // now index the new details - but only if it's archived or // withdrawn if (item.isArchived() || item.isWithdrawn()) { // get the metadata from the item for (int mdIdx = 0; mdIdx < bi.getMetadataCount(); mdIdx++) { String[] md = bi.getMdBits(mdIdx); Metadatum[] values = item.getMetadata(md[0], md[1], md[2], Item.ANY); // if we have values to index on, then do so if (values != null && values.length > 0) { int minConfidence = MetadataAuthorityManager .getManager().getMinConfidence( values[0].schema, values[0].element, values[0].qualifier); boolean ignoreAuthority = new DSpace() .getConfigurationService() .getPropertyAsType( "discovery.browse.authority.ignore." + bi.getName(), new DSpace() .getConfigurationService() .getPropertyAsType( "discovery.browse.authority.ignore", new Boolean(false)), true); for (int x = 0; x < values.length; x++) { // Ensure that there is a value to index before // inserting it if (StringUtils.isEmpty(values[x].value)) { log.error("Null metadata value for item " + item.getID() + ", field: " + values[x].schema + "." + values[x].element + (values[x].qualifier == null ? "" : "." + values[x].qualifier)); } else { if (bi.isAuthorityIndex() && (values[x].authority == null || values[x].confidence < minConfidence)) { // if we have an authority index only // authored metadata will go here! log.debug("Skipping item=" + item.getID() + ", field=" + values[x].schema + "." + values[x].element + "." + values[x].qualifier + ", value=" + values[x].value + ", authority=" + values[x].authority + ", confidence=" + values[x].confidence + " (BAD AUTHORITY)"); continue; } // is there any valid (with appropriate // confidence) authority key? if ((ignoreAuthority && !bi.isAuthorityIndex()) || (values[x].authority != null && values[x].confidence >= minConfidence)) { distFAuths.add(values[x].authority); distValuesForAC.add(values[x].value); String preferedLabel = null; boolean ignorePrefered = new DSpace() .getConfigurationService() .getPropertyAsType( "discovery.browse.authority.ignore-prefered." + bi.getName(), new DSpace() .getConfigurationService() .getPropertyAsType( "discovery.browse.authority.ignore-prefered", new Boolean( false)), true); if (!ignorePrefered) { preferedLabel = ChoiceAuthorityManager .getManager() .getLabel( values[x].schema, values[x].element, values[x].qualifier, values[x].authority, values[x].language); } List<String> variants = null; boolean ignoreVariants = new DSpace() .getConfigurationService() .getPropertyAsType( "discovery.browse.authority.ignore-variants." + bi.getName(), new DSpace() .getConfigurationService() .getPropertyAsType( "discovery.browse.authority.ignore-variants", new Boolean( false)), true); if (!ignoreVariants) { variants = ChoiceAuthorityManager .getManager() .getVariants( values[x].schema, values[x].element, values[x].qualifier, values[x].authority, values[x].language); } if (StringUtils .isNotBlank(preferedLabel)) { String nLabel = OrderFormat .makeSortString( preferedLabel, values[x].language, bi.getDataType()); distFValues .add(nLabel + SolrServiceImpl.FILTER_SEPARATOR + preferedLabel + SolrServiceImpl.AUTHORITY_SEPARATOR + values[x].authority); distValuesForAC.add(preferedLabel); } if (variants != null) { for (String var : variants) { String nVal = OrderFormat .makeSortString( var, values[x].language, bi.getDataType()); distFValues .add(nVal + SolrServiceImpl.FILTER_SEPARATOR + var + SolrServiceImpl.AUTHORITY_SEPARATOR + values[x].authority); distValuesForAC.add(var); } } } else // put it in the browse index as if it // hasn't have an authority key { // get the normalised version of the // value String nVal = OrderFormat .makeSortString( values[x].value, values[x].language, bi.getDataType()); distFValues .add(nVal + SolrServiceImpl.FILTER_SEPARATOR + values[x].value); distFVal.add(values[x].value); distValuesForAC.add(values[x].value); } } } } } } for (String facet : distFValues) { doc.addField(bi.getDistinctTableName() + "_filter", facet); } for (String facet : distFAuths) { doc.addField(bi.getDistinctTableName() + "_authority_filter", facet); } for (String facet : distValuesForAC) { doc.addField(bi.getDistinctTableName() + "_partial", facet); } for (String facet : distFVal) { doc.addField(bi.getDistinctTableName()+"_value_filter", facet); } } } // Add sorting options as configurated for the browse system try { for (SortOption so : SortOption.getSortOptions()) { Metadatum[] dcvalue = item.getMetadataByMetadataString(so.getMetadata()); if (dcvalue != null && dcvalue.length > 0) { String nValue = OrderFormat .makeSortString(dcvalue[0].value, dcvalue[0].language, so.getType()); doc.addField("bi_sort_" + so.getNumber() + "_sort", nValue); } } } catch (SortException e) { // we can't solve it so rethrow as runtime exception throw new RuntimeException(e.getMessage(), e); } } @Override public void deleteByItemID(String table, int itemID) throws BrowseException { } @Override public void deleteCommunityMappings(int itemID) throws BrowseException { } @Override public void updateCommunityMappings(int itemID) throws BrowseException { } @Override public void insertIndex(String table, int itemID, Map sortCols) throws BrowseException { // this is required to be sure that communities2item will be cleaned // after the switch to SOLRBrowseDAOs. See DS-1619 dbCreateDAO.deleteCommunityMappings(itemID); } @Override public boolean updateIndex(String table, int itemID, Map sortCols) throws BrowseException { return false; } @Override public int getDistinctID(String table, String value, String authority, String sortValue) throws BrowseException { // TODO Auto-generated method stub return 0; } @Override public int insertDistinctRecord(String table, String value, String authority, String sortValue) throws BrowseException { // TODO Auto-generated method stub return 0; } @Override public String dropIndexAndRelated(String table, boolean execute) throws BrowseException { return dbCreateDAO.dropIndexAndRelated(table, execute); } @Override public String dropSequence(String sequence, boolean execute) throws BrowseException { return dbCreateDAO.dropSequence(sequence, execute); } @Override public String dropView(String view, boolean execute) throws BrowseException { return dbCreateDAO.dropView(view, execute); } @Override public String createSequence(String sequence, boolean execute) throws BrowseException { return INFO_NOSQL_TO_RUN; } @Override public String createPrimaryTable(String table, List sortCols, boolean execute) throws BrowseException { return INFO_NOSQL_TO_RUN; } @Override public String[] createDatabaseIndices(String table, List<Integer> sortCols, boolean value, boolean execute) throws BrowseException { return new String[] { INFO_NOSQL_TO_RUN }; } @Override public String[] createMapIndices(String disTable, String mapTable, boolean execute) throws BrowseException { return new String[] { INFO_NOSQL_TO_RUN }; } @Override public String createCollectionView(String table, String view, boolean execute) throws BrowseException { return INFO_NOSQL_TO_RUN; } @Override public String createCommunityView(String table, String view, boolean execute) throws BrowseException { return INFO_NOSQL_TO_RUN; } @Override public String createDistinctTable(String table, boolean execute) throws BrowseException { return INFO_NOSQL_TO_RUN; } @Override public String createDistinctMap(String table, String map, boolean execute) throws BrowseException { return INFO_NOSQL_TO_RUN; } public MappingResults updateDistinctMappings(String table, int itemID, Set<Integer> distinctIDs) throws BrowseException { return new MappingResults() { @Override public List<Integer> getRetainedDistinctIds() { return new ArrayList<Integer>(); } @Override public List<Integer> getRemovedDistinctIds() { return new ArrayList<Integer>(); } @Override public List<Integer> getAddedDistinctIds() { return new ArrayList<Integer>(); } }; } @Override public boolean testTableExistence(String table) throws BrowseException { return dbCreateDAO.testTableExistence(table); } @Override public List<Integer> deleteMappingsByItemID(String mapTable, int itemID) throws BrowseException { return new ArrayList<Integer>(); } @Override public void pruneExcess(String table, boolean withdrawn) throws BrowseException { } @Override public void pruneMapExcess(String map, boolean withdrawn, List<Integer> distinctIds) throws BrowseException { } @Override public void pruneDistinct(String table, String map, List<Integer> distinctIds) throws BrowseException { } }