[DS-1144] Maven Project Consolidation : Discovery

This commit is contained in:
Mark Diggory
2012-10-03 03:28:50 -07:00
parent 138abc8c24
commit e24aaffe83
87 changed files with 2716 additions and 3644 deletions

View File

@@ -0,0 +1,533 @@
/**
* 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.DCValue;
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.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
String db = ConfigurationManager.getProperty("db.name");
if ("postgres".equals(db))
{
dbCreateDAO = new BrowseCreateDAOPostgres(context);
}
else if ("oracle".equals(db))
{
dbCreateDAO = new BrowseCreateDAOOracle(context);
}
else
{
throw new BrowseException(
"The configuration for db.name is either invalid, or contains an unrecognised database");
}
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 wan't 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 and not
// 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);
DCValue[] 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())
{
DCValue[] dcvalue = item.getMetadata(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
{
}
@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
{
}
}

View File

@@ -0,0 +1,759 @@
/**
* 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.Collections;
import java.util.Comparator;
import java.util.List;
import org.apache.log4j.Logger;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.discovery.DiscoverFacetField;
import org.dspace.discovery.DiscoverQuery;
import org.dspace.discovery.DiscoverQuery.SORT_ORDER;
import org.dspace.discovery.DiscoverResult;
import org.dspace.discovery.DiscoverResult.FacetResult;
import org.dspace.discovery.DiscoverResult.SearchDocument;
import org.dspace.discovery.SearchService;
import org.dspace.discovery.SearchServiceException;
import org.dspace.discovery.configuration.DiscoveryConfigurationParameters;
import org.dspace.utils.DSpace;
/**
*
* @author Andrea Bollini (CILEA)
*
*/
public class SolrBrowseDAO implements BrowseDAO
{
public SolrBrowseDAO(Context context)
{
this.context = context;
}
static private class FacetValueComparator implements Comparator
{
@Override
public int compare(Object o1, Object o2)
{
String s1 = "", s2 = "";
if (o1 instanceof FacetResult && o2 instanceof String)
{
FacetResult c = (FacetResult) o1;
s1 = c.getSortValue();
s2 = (String) o2;
}
else if (o2 instanceof FacetResult && o1 instanceof String)
{
FacetResult c = (FacetResult) o2;
s1 = (String) o1;
s2 = c.getSortValue();
}
// both object are FacetResult so they are already sorted
return s1.compareTo(s2);
}
}
/** Log4j log */
private static Logger log = Logger.getLogger(SolrBrowseDAO.class);
/** The DSpace context */
private Context context;
// SQL query related attributes for this class
/** table(s) to select from */
private String table = null;
/** field to look for focus value in */
private String focusField = null;
/** value to start browse from in focus field */
private String focusValue = null;
/** field to look for value in */
private String valueField = null;
/** value to restrict browse to (e.g. author name) */
private String value = null;
private String authority = null;
/** exact or partial matching of the value */
private boolean valuePartial = false;
/** the table that defines the mapping for the relevant container */
private String containerTable = null;
/**
* the name of the field which contains the container id (e.g.
* collection_id)
*/
private String containerIDField = null;
/** the database id of the container we are constraining to */
private int containerID = -1;
/** the column that we are sorting results by */
private String orderField = null;
/** whether to sort results ascending or descending */
private boolean ascending = true;
/** the limit of number of results to return */
private int limit = -1;
/** the offset of the start point */
private int offset = 0;
/** whether to use the equals comparator in value comparisons */
private boolean equalsComparator = true;
/** whether this is a distinct browse or not */
private boolean distinct = false;
private String facetField;
// administrative attributes for this class
DSpace dspace = new DSpace();
SearchService searcher = dspace.getServiceManager().getServiceByName(
SearchService.class.getName(), SearchService.class);
private DiscoverResult sResponse = null;
private boolean itemsWithdrawn = false;
private boolean itemsPrivate = false;
private boolean showFrequencies;
private DiscoverResult getSolrResponse() throws BrowseException
{
if (sResponse == null)
{
DiscoverQuery query = new DiscoverQuery();
addLocationScopeFilter(query);
addStatusFilter(query);
if (distinct)
{
DiscoverFacetField dff = new DiscoverFacetField(facetField,
DiscoveryConfigurationParameters.TYPE_TEXT, -1,
DiscoveryConfigurationParameters.SORT.VALUE);
query.addFacetField(dff);
query.setFacetMinCount(1);
query.setMaxResults(0);
}
else
{
query.setMaxResults(limit > 0 ? limit : 20);
if (offset > 0)
{
query.setStart(offset);
}
// caution check first authority, value is always present!
if (authority != null)
{
query.addFilterQueries("{!field f="+facetField + "_authority_filter}"
+ authority);
}
else if (value != null && !valuePartial)
{
query.addFilterQueries("{!field f="+facetField + "_value_filter}" + value);
}
else if (valuePartial)
{
query.addFilterQueries("{!field f="+facetField + "_partial}" + value);
}
// filter on item to be sure to don't include any other object
// indexed in the Discovery Search core
query.addFilterQueries("search.resourcetype:" + Constants.ITEM);
if (orderField != null)
{
query.setSortField("bi_" + orderField + "_sort",
ascending ? SORT_ORDER.asc : SORT_ORDER.desc);
}
}
try
{
sResponse = searcher.search(context, query, itemsWithdrawn);
}
catch (SearchServiceException e)
{
throw new BrowseException(e);
}
}
return sResponse;
}
private void addStatusFilter(DiscoverQuery query)
{
if (itemsWithdrawn)
{
query.addFilterQueries("withdrawn:true");
if (itemsPrivate)
{
query.addFilterQueries("discoverable:false");
}
else
{
query.addFilterQueries("NOT(discoverable:false)");
}
}
else
{
query.addFilterQueries("NOT(withdrawn:true)");
}
}
private void addLocationScopeFilter(DiscoverQuery query)
{
if (containerID > 0)
{
if (containerIDField.startsWith("collection"))
{
query.addFilterQueries("location.coll:" + containerID);
}
else if (containerIDField.startsWith("community"))
{
query.addFilterQueries("location.comm:" + containerID);
}
}
}
@Override
public int doCountQuery() throws BrowseException
{
DiscoverResult resp = getSolrResponse();
int count = 0;
if (distinct)
{
List<FacetResult> facetResults = resp.getFacetResult(facetField);
count = facetResults.size();
}
else
{
// we need to cast to int to respect the BrowseDAO contract...
count = (int) resp.getTotalSearchResults();
// FIXME null the response cache
// the BrowseEngine send fake argument to the BrowseDAO for the
// count...
sResponse = null;
}
return count;
}
@Override
public List doValueQuery() throws BrowseException
{
DiscoverResult resp = getSolrResponse();
List<FacetResult> facet = resp.getFacetResult(facetField);
int count = doCountQuery();
int start = offset > 0 ? offset : 0;
int max = limit > 0 ? limit : 20;
List<String[]> result = new ArrayList<String[]>();
if (ascending)
{
for (int i = start; i < (start + max) && i < count; i++)
{
FacetResult c = facet.get(i);
String freq = showFrequencies ? String.valueOf(c.getCount())
: "";
result.add(new String[] { c.getDisplayedValue(),
c.getAuthorityKey(), freq });
}
}
else
{
for (int i = count - start - 1; i >= count - (start + max)
&& i >= 0; i--)
{
FacetResult c = facet.get(i);
String freq = showFrequencies ? String.valueOf(c.getCount())
: "";
result.add(new String[] { c.getDisplayedValue(),
c.getAuthorityKey(), freq });
}
}
return result;
}
@Override
public List doQuery() throws BrowseException
{
DiscoverResult resp = getSolrResponse();
List<BrowseItem> bitems = new ArrayList<BrowseItem>();
for (DSpaceObject solrDoc : resp.getDspaceObjects())
{
// FIXME introduce project, don't retrieve Item immediately when
// processing the query...
Item item = (Item) solrDoc;
BrowseItem bitem = new BrowseItem(context, item.getID(),
item.isArchived(), item.isWithdrawn());
bitems.add(bitem);
}
return bitems;
}
@Override
public String doMaxQuery(String column, String table, int itemID)
throws BrowseException
{
DiscoverQuery query = new DiscoverQuery();
query.setQuery("search.resourceid:" + itemID
+ " AND search.resourcetype:" + Constants.ITEM);
query.setMaxResults(1);
DiscoverResult resp = null;
try
{
resp = searcher.search(context, query);
}
catch (SearchServiceException e)
{
throw new BrowseException(e);
}
if (resp.getTotalSearchResults() > 0)
{
SearchDocument doc = resp.getSearchDocument(
resp.getDspaceObjects().get(0)).get(0);
return (String) doc.getSearchFieldValues(column).get(0);
}
return null;
}
@Override
public int doOffsetQuery(String column, String value, boolean isAscending)
throws BrowseException
{
DiscoverQuery query = new DiscoverQuery();
addLocationScopeFilter(query);
addStatusFilter(query);
query.setMaxResults(0);
query.addFilterQueries("search.resourcetype:" + Constants.ITEM);
if (isAscending)
{
query.setQuery("bi_"+column + "_sort" + ": [* TO \"" + value + "\"]");
}
else
{
query.setQuery("bi_" + column + "_sort" + ": [\"" + value + "\" TO *]");
}
DiscoverResult resp = null;
try
{
resp = searcher.search(context, query);
}
catch (SearchServiceException e)
{
throw new BrowseException(e);
}
return (int) resp.getTotalSearchResults();
}
@Override
public int doDistinctOffsetQuery(String column, String value,
boolean isAscending) throws BrowseException
{
DiscoverResult resp = getSolrResponse();
List<FacetResult> facets = resp.getFacetResult(facetField);
Comparator comparator = new SolrBrowseDAO.FacetValueComparator();
Collections.sort(facets, comparator);
int x = Collections.binarySearch(facets, value, comparator);
int ascValue = (x >= 0) ? x : -(x + 1);
if (isAscending)
{
return ascValue;
}
else
{
return doCountQuery() - ascValue;
}
}
@Override
public boolean isEnableBrowseFrequencies()
{
return showFrequencies;
}
@Override
public void setEnableBrowseFrequencies(boolean enableBrowseFrequencies)
{
showFrequencies = enableBrowseFrequencies;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#getContainerID()
*/
public int getContainerID()
{
return containerID;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#getContainerIDField()
*/
public String getContainerIDField()
{
return containerIDField;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#getContainerTable()
*/
public String getContainerTable()
{
return containerTable;
}
// FIXME is this in use?
public String[] getCountValues()
{
return null;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#getFocusField()
*/
public String getJumpToField()
{
return focusField;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#getFocusValue()
*/
public String getJumpToValue()
{
return focusValue;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#getLimit()
*/
public int getLimit()
{
return limit;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#getOffset()
*/
public int getOffset()
{
return offset;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#getOrderField()
*/
public String getOrderField()
{
return orderField;
}
// is this in use?
public String[] getSelectValues()
{
return null;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#getTable()
*/
public String getTable()
{
return table;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#getValue()
*/
public String getFilterValue()
{
return value;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#getValueField()
*/
public String getFilterValueField()
{
return valueField;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#isAscending()
*/
public boolean isAscending()
{
return ascending;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#isDistinct()
*/
public boolean isDistinct()
{
return this.distinct;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#setAscending(boolean)
*/
public void setAscending(boolean ascending)
{
this.ascending = ascending;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#setContainerID(int)
*/
public void setContainerID(int containerID)
{
this.containerID = containerID;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#setContainerIDField(java.lang.String)
*/
public void setContainerIDField(String containerIDField)
{
this.containerIDField = containerIDField;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#setContainerTable(java.lang.String)
*/
public void setContainerTable(String containerTable)
{
this.containerTable = containerTable;
}
// is this in use?
public void setCountValues(String[] fields)
{
// this.countValues = fields;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#setDistinct(boolean)
*/
public void setDistinct(boolean bool)
{
this.distinct = bool;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#setEqualsComparator(boolean)
*/
public void setEqualsComparator(boolean equalsComparator)
{
this.equalsComparator = equalsComparator;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#setFocusField(java.lang.String)
*/
public void setJumpToField(String focusField)
{
this.focusField = focusField;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#setFocusValue(java.lang.String)
*/
public void setJumpToValue(String focusValue)
{
this.focusValue = focusValue;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#setLimit(int)
*/
public void setLimit(int limit)
{
this.limit = limit;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#setOffset(int)
*/
public void setOffset(int offset)
{
this.offset = offset;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#setOrderField(java.lang.String)
*/
public void setOrderField(String orderField)
{
this.orderField = orderField;
}
// is this in use?
public void setSelectValues(String[] selectValues)
{
// this.selectValues = selectValues;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#setTable(java.lang.String)
*/
public void setTable(String table)
{
if (table.equals(BrowseIndex.getWithdrawnBrowseIndex().getTableName()))
{
itemsWithdrawn = true;
itemsPrivate = false;
}
else if (table.equals(BrowseIndex.getPrivateBrowseIndex().getTableName()))
{
itemsPrivate = true;
// items private are also withdrawn
itemsWithdrawn = true;
}
facetField = table;
}
public void setFilterMappingTables(String tableDis, String tableMap)
{
if (tableDis != null)
{
this.facetField = tableDis;
}
// this.fields = tableDis;
// this.tableMap = tableMap;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#setValue(java.lang.String)
*/
public void setFilterValue(String value)
{
this.value = value;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#setFilterValuePartial(boolean)
*/
public void setFilterValuePartial(boolean part)
{
this.valuePartial = part;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#setValueField(java.lang.String)
*/
public void setFilterValueField(String valueField)
{
this.valueField = valueField;
}
/*
* (non-Javadoc)
*
* @see org.dspace.browse.BrowseDAO#useEqualsComparator()
*/
public boolean useEqualsComparator()
{
return equalsComparator;
}
@Override
public String getAuthorityValue()
{
return authority;
}
@Override
public void setAuthorityValue(String value)
{
this.authority = value;
}
}

View File

@@ -0,0 +1,86 @@
/**
* 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.discovery;
import org.dspace.discovery.configuration.DiscoveryConfigurationParameters;
/**
* Class contains facet query information
*
* @author Kevin Van de Velde (kevin at atmire dot com)
*/
public class DiscoverFacetField {
private String field;
private int limit;
private int offset=-1;
/* The facet prefix, all facet values will have to start with the given prefix */
private String prefix;
private String type;
private DiscoveryConfigurationParameters.SORT sortOrder;
public DiscoverFacetField(String field, String type, int limit, DiscoveryConfigurationParameters.SORT sortOrder) {
this.field = field;
this.type = type;
this.limit = limit;
this.sortOrder = sortOrder;
}
public DiscoverFacetField(String field, String type, int limit, DiscoveryConfigurationParameters.SORT sortOrder, int offset) {
this.field = field;
this.type = type;
this.limit = limit;
this.sortOrder = sortOrder;
this.offset = offset;
}
public DiscoverFacetField(String field, String type, int limit, DiscoveryConfigurationParameters.SORT sortOrder, String prefix) {
this.prefix = prefix;
this.limit = limit;
this.type = type;
this.sortOrder = sortOrder;
this.field = field;
}
public DiscoverFacetField(String field, String type, int limit, DiscoveryConfigurationParameters.SORT sortOrder, String prefix, int offset) {
this.prefix = prefix;
this.limit = limit;
this.type = type;
this.sortOrder = sortOrder;
this.field = field;
this.offset = offset;
}
public String getField() {
return field;
}
public String getPrefix() {
return prefix;
}
public int getLimit() {
return limit;
}
public String getType() {
return type;
}
public DiscoveryConfigurationParameters.SORT getSortOrder() {
return sortOrder;
}
public int getOffset()
{
return offset;
}
public void setOffset(int offset)
{
this.offset = offset;
}
}

View File

@@ -0,0 +1,58 @@
/**
* 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.discovery;
/**
* This class represents a filter query for discovery and can contain the following objects:
* The field in which we are searching
* The query the query which the filter query is using
* The displayed value
*
* @author Kevin Van de Velde (kevin at atmire dot com)
*
*/
public class DiscoverFilterQuery {
private String field;
private String filterQuery;
private String displayedValue;
public DiscoverFilterQuery() {
}
public DiscoverFilterQuery(String field, String filterQuery, String displayedValue) {
this.field = field;
this.filterQuery = filterQuery;
this.displayedValue = displayedValue;
}
public void setField(String field) {
this.field = field;
}
public void setFilterQuery(String filterQuery) {
this.filterQuery = filterQuery;
}
public void setDisplayedValue(String displayedValue) {
this.displayedValue = displayedValue;
}
public String getField() {
return field;
}
public String getFilterQuery() {
return filterQuery;
}
public String getDisplayedValue() {
return displayedValue;
}
}

View File

@@ -0,0 +1,53 @@
/**
* 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.discovery;
/**
* Configuration for one field that is to be highlighted
* Giving 0 as max chars ensures that the entire field is returned !
*
* @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 DiscoverHitHighlightingField {
public static final int UNLIMITED_FRAGMENT_LENGTH = 0;
private String field;
private int maxChars;
private int maxSnippets;
public DiscoverHitHighlightingField(String field, int maxChars, int maxSnippets)
{
this.field = field;
this.maxChars = maxChars;
this.maxSnippets = maxSnippets;
}
public String getField()
{
return field;
}
/**
* The max number of characters that should be shown for a
* field containing a matching hit. e.g. If maxChars = 200
* and a hit is found in the full-text the 200 chars
* surrounding the hit will be shown
*/
public int getMaxChars()
{
return maxChars;
}
public int getMaxSnippets()
{
return maxSnippets;
}
}

View File

@@ -0,0 +1,267 @@
/**
* 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.discovery;
import java.util.*;
/**
* This class represents a query which the discovery backend can use
*
* @author Kevin Van de Velde (kevin at atmire dot com)
*
*/
public class DiscoverQuery {
/** Main attributes for the discovery query **/
private String query;
private List<String> filterQueries;
private int DSpaceObjectFilter = -1;
private List<String> fieldPresentQueries;
private int start = 0;
private int maxResults = -1;
/** Attributes used for sorting of results **/
public enum SORT_ORDER {
desc,
asc
}
private String sortField;
private SORT_ORDER sortOrder;
/** Attributes required for the faceting of values **/
private List<DiscoverFacetField> facetFields;
private List<String> facetQueries;
private int facetLimit = -1;
private int facetMinCount = -1;
private int facetOffset = 0;
private Map<String, DiscoverHitHighlightingField> hitHighlighting;
/** Used when you want to search for a specific field value **/
private List<String> searchFields;
/** Misc attributes can be implementation dependent **/
private Map<String, List<String>> properties;
public DiscoverQuery() {
//Initialize all our lists
this.filterQueries = new ArrayList<String>();
this.fieldPresentQueries = new ArrayList<String>();
this.facetFields = new ArrayList<DiscoverFacetField>();
this.facetQueries = new ArrayList<String>();
this.searchFields = new ArrayList<String>();
this.hitHighlighting = new HashMap<String, DiscoverHitHighlightingField>();
//Use a linked hashmap since sometimes insertion order might matter
this.properties = new LinkedHashMap<String, List<String>>();
}
public void setQuery(String query) {
this.query = query;
}
public String getQuery() {
return query;
}
public int getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public void setSortField(String sortField, SORT_ORDER sortOrder){
this.sortField = sortField;
this.sortOrder = sortOrder;
}
public String getSortField() {
return sortField;
}
public SORT_ORDER getSortOrder() {
return sortOrder;
}
/**
* Sets the DSpace object filter, must be an DSpace Object type integer
* can be used to only return objects from a certain DSpace Object type
* @param DSpaceObjectFilter the DSpace object filer
*/
public void setDSpaceObjectFilter(int DSpaceObjectFilter) {
this.DSpaceObjectFilter = DSpaceObjectFilter;
}
/**
* Gets the DSpace object filter
* can be used to only return objects from a certain DSpace Object type
* @return the DSpace object filer
*/
public int getDSpaceObjectFilter() {
return DSpaceObjectFilter;
}
/**
* The maximum number of results returned by this query
* @return the number of results
*/
public int getMaxResults() {
return maxResults;
}
/**
* Sets the maximum number of results by this query
* @param maxResults the number of results
*/
public void setMaxResults(int maxResults) {
this.maxResults = maxResults;
}
/**
* Adds new filter queries
* @param filterQueries the filter queries to be added
*/
public void addFilterQueries(String ...filterQueries){
this.filterQueries.addAll(Arrays.asList(filterQueries));
}
/**
* Returns the filter queries
* @return the filter queries in a list
*/
public List<String> getFilterQueries() {
return filterQueries;
}
/**
* Adds a query that will ensure that a certain field is present in the index
* @param fieldPresentQueries the queries to be added
*/
public void addFieldPresentQueries(String ...fieldPresentQueries){
this.fieldPresentQueries.addAll(Arrays.asList(fieldPresentQueries));
}
public List<String> getFieldPresentQueries() {
return fieldPresentQueries;
}
/**
* Adds a new facet query
* @param facetQuery the new facet query to be added
*/
public void addFacetQuery(String facetQuery){
this.facetQueries.add(facetQuery);
}
/**
* Returns the facet queries
* @return the facet queries for this query
*/
public List<String> getFacetQueries() {
return facetQueries;
}
/**
* Adds a new facet field
* @param facetField the new facet field to be added
*/
public void addFacetField(DiscoverFacetField facetField){
facetFields.add(facetField);
}
/**
* Gets the facets fields configured
* @return the facet fields for this query
*/
public List<DiscoverFacetField> getFacetFields() {
return facetFields;
}
/**
* Gets the minimum number of values that need to be present before a valid facet value has been found
* @return facetMinCount the minimum number of values to be present for a valid facet
*/
public int getFacetMinCount() {
return facetMinCount;
}
/**
* Set the minimum number of values that need to be present before a valid facet value has been found
* @param facetMinCount the minimum number of values to be present for a valid facet
*/
public void setFacetMinCount(int facetMinCount) {
this.facetMinCount = facetMinCount;
}
/**
* Gets the facet field offset
* @return the facet field offset
*/
public int getFacetOffset() {
return facetOffset;
}
/**
* Sets the facet field offset, one facet offset will be used for all the facet fields
* @param facetOffset an integer representing the offset
*/
public void setFacetOffset(int facetOffset) {
this.facetOffset = facetOffset;
}
public void addSearchField(String field){
this.searchFields.add(field);
}
public List<String> getSearchFields() {
return searchFields;
}
/**
* Returns the misc search properties
* @return a map containing the properties
*/
public Map<String, List<String>> getProperties() {
return properties;
}
/**
* Adds a new search property to the misc search properties
* @param property the name of the property
* @param value the value of the property
*/
public void addProperty(String property, String value){
List<String> toAddList = properties.get(property);
if(toAddList == null)
{
toAddList = new ArrayList<String>();
}
toAddList.add(value);
properties.put(property, toAddList);
}
public DiscoverHitHighlightingField getHitHighlightingField(String field)
{
return hitHighlighting.get(field);
}
public List<DiscoverHitHighlightingField> getHitHighlightingFields()
{
return new ArrayList<DiscoverHitHighlightingField>(hitHighlighting.values());
}
public void addHitHighlightingField(DiscoverHitHighlightingField hitHighlighting)
{
this.hitHighlighting.put(hitHighlighting.getField(), hitHighlighting);
}
}

View File

@@ -0,0 +1,234 @@
/**
* 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.discovery;
import org.dspace.content.DSpaceObject;
import java.util.*;
/**
* This class represents the result that the discovery search impl returns
*
* @author Kevin Van de Velde (kevin at atmire dot com)
*/
public class DiscoverResult {
private long totalSearchResults;
private int start;
private List<DSpaceObject> dspaceObjects;
private Map<String, List<FacetResult>> facetResults;
/** A map that contains all the documents sougth after, the key is a string representation of the DSpace object */
private Map<String, List<SearchDocument>> searchDocuments;
private int maxResults = -1;
private int searchTime;
private Map<String, DSpaceObjectHighlightResult> highlightedResults;
public DiscoverResult() {
dspaceObjects = new ArrayList<DSpaceObject>();
facetResults = new LinkedHashMap<String, List<FacetResult>>();
searchDocuments = new LinkedHashMap<String, List<SearchDocument>>();
highlightedResults = new HashMap<String, DSpaceObjectHighlightResult>();
}
public void addDSpaceObject(DSpaceObject dso){
this.dspaceObjects.add(dso);
}
public List<DSpaceObject> getDspaceObjects() {
return dspaceObjects;
}
public long getTotalSearchResults() {
return totalSearchResults;
}
public void setTotalSearchResults(long totalSearchResults) {
this.totalSearchResults = totalSearchResults;
}
public int getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public int getMaxResults() {
return maxResults;
}
public void setMaxResults(int maxResults) {
this.maxResults = maxResults;
}
public int getSearchTime()
{
return searchTime;
}
public void setSearchTime(int searchTime)
{
this.searchTime = searchTime;
}
public void addFacetResult(String facetField, FacetResult ...facetResults){
List<FacetResult> facetValues = this.facetResults.get(facetField);
if(facetValues == null)
{
facetValues = new ArrayList<FacetResult>();
}
facetValues.addAll(Arrays.asList(facetResults));
this.facetResults.put(facetField, facetValues);
}
public Map<String, List<FacetResult>> getFacetResults() {
return facetResults;
}
public List<FacetResult> getFacetResult(String facet){
return facetResults.get(facet) == null ? new ArrayList<FacetResult>() : facetResults.get(facet);
}
public DSpaceObjectHighlightResult getHighlightedResults(DSpaceObject dso)
{
return highlightedResults.get(dso.getHandle());
}
public void addHighlightedResult(DSpaceObject dso, DSpaceObjectHighlightResult highlightedResult)
{
this.highlightedResults.put(dso.getHandle(), highlightedResult);
}
public static final class FacetResult{
private String asFilterQuery;
private String displayedValue;
private String authorityKey;
private String sortValue;
private long count;
public FacetResult(String asFilterQuery, String displayedValue, String authorityKey, String sortValue, long count) {
this.asFilterQuery = asFilterQuery;
this.displayedValue = displayedValue;
this.authorityKey = authorityKey;
this.sortValue = sortValue;
this.count = count;
}
public String getAsFilterQuery() {
return asFilterQuery;
}
public String getDisplayedValue() {
return displayedValue;
}
public String getSortValue()
{
return sortValue;
}
public long getCount() {
return count;
}
public String getAuthorityKey()
{
return authorityKey;
}
public String getFilterType()
{
return authorityKey != null?"authority":"equals";
}
}
public static final class DSpaceObjectHighlightResult
{
private DSpaceObject dso;
private Map<String, List<String>> highlightResults;
public DSpaceObjectHighlightResult(DSpaceObject dso, Map<String, List<String>> highlightResults)
{
this.dso = dso;
this.highlightResults = highlightResults;
}
public DSpaceObject getDso()
{
return dso;
}
public List<String> getHighlightResults(String metadataKey)
{
return highlightResults.get(metadataKey);
}
}
public void addSearchDocument(DSpaceObject dso, SearchDocument searchDocument){
String dsoString = SearchDocument.getDspaceObjectStringRepresentation(dso);
List<SearchDocument> docs = searchDocuments.get(dsoString);
if(docs == null){
docs = new ArrayList<SearchDocument>();
}
docs.add(searchDocument);
searchDocuments.put(dsoString, docs);
}
/**
* Returns all the sought after search document values
* @param dso the dspace object we want our search documents for
* @return the search documents list
*/
public List<SearchDocument> getSearchDocument(DSpaceObject dso){
String dsoString = SearchDocument.getDspaceObjectStringRepresentation(dso);
List<SearchDocument> result = searchDocuments.get(dsoString);
if(result == null){
return new ArrayList<SearchDocument>();
}else{
return result;
}
}
/**
* This class contains values from the fields searched for in DiscoveryQuery.java
*/
public static final class SearchDocument{
private Map<String, List<String>> searchFields;
public SearchDocument() {
this.searchFields = new LinkedHashMap<String, List<String>>();
}
public void addSearchField(String field, String ...values){
List<String>searchFieldValues = searchFields.get(field);
if(searchFieldValues == null){
searchFieldValues = new ArrayList<String>();
}
searchFieldValues.addAll(Arrays.asList(values));
searchFields.put(field, searchFieldValues);
}
public Map<String, List<String>> getSearchFields() {
return searchFields;
}
public List<String> getSearchFieldValues(String field){
if(searchFields.get(field) == null)
return new ArrayList<String>();
else
return searchFields.get(field);
}
public static String getDspaceObjectStringRepresentation(DSpaceObject dso){
return dso.getType() + ":" + dso.getID();
}
}
}

View File

@@ -0,0 +1,125 @@
/**
* 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.discovery;
import org.apache.log4j.Logger;
import org.apache.commons.cli.*;
import org.dspace.core.Context;
import org.dspace.utils.DSpace;
import java.io.IOException;
import java.sql.SQLException;
/**
* Class used to reindex dspace communities/collections/items into discovery
*
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class IndexClient {
private static final Logger log = Logger.getLogger(IndexClient.class);
/**
* When invoked as a command-line tool, creates, updates, removes content
* from the whole index
*
* @param args the command-line arguments, none used
* @throws java.io.IOException
* @throws java.sql.SQLException
*
*/
public static void main(String[] args) throws SQLException, IOException, SearchServiceException {
Context context = new Context();
context.setIgnoreAuthorization(true);
String usage = "org.dspace.discovery.IndexClient [-cbhf[r <item handle>]] or nothing to update/clean an existing index.";
Options options = new Options();
HelpFormatter formatter = new HelpFormatter();
CommandLine line = null;
options
.addOption(OptionBuilder
.withArgName("item handle")
.hasArg(true)
.withDescription(
"remove an Item, Collection or Community from index based on its handle")
.create("r"));
options
.addOption(OptionBuilder
.isRequired(false)
.withDescription(
"clean existing index removing any documents that no longer exist in the db")
.create("c"));
options.addOption(OptionBuilder.isRequired(false).withDescription(
"(re)build index, wiping out current one if it exists").create(
"b"));
options
.addOption(OptionBuilder
.isRequired(false)
.withDescription(
"if updating existing index, force each handle to be reindexed even if uptodate")
.create("f"));
options.addOption(OptionBuilder.isRequired(false).withDescription(
"print this help message").create("h"));
options.addOption(OptionBuilder.isRequired(false).withDescription(
"optimize search core").create("o"));
try {
line = new PosixParser().parse(options, args);
} catch (Exception e) {
// automatically generate the help statement
formatter.printHelp(usage, e.getMessage(), options, "");
System.exit(1);
}
if (line.hasOption("h")) {
// automatically generate the help statement
formatter.printHelp(usage, options);
System.exit(1);
}
/** Acquire from dspace-services in future */
/**
* new DSpace.getServiceManager().getServiceByName("org.dspace.discovery.SolrIndexer");
*/
DSpace dspace = new DSpace();
IndexingService indexer = dspace.getServiceManager().getServiceByName(IndexingService.class.getName(),IndexingService.class);
if (line.hasOption("r")) {
log.info("Removing " + line.getOptionValue("r") + " from Index");
indexer.unIndexContent(context, line.getOptionValue("r"));
} else if (line.hasOption("c")) {
log.info("Cleaning Index");
indexer.cleanIndex(line.hasOption("f"));
} else if (line.hasOption("b")) {
log.info("(Re)building index from scratch.");
indexer.createIndex(context);
} else if (line.hasOption("o")) {
log.info("Optimizing search core.");
indexer.optimize();
} else {
log.info("Updating and Cleaning Index");
indexer.cleanIndex(line.hasOption("f"));
indexer.updateIndex(context, line.hasOption("f"));
}
log.info("Done with indexing");
}
}

View File

@@ -0,0 +1,208 @@
/**
* 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.discovery;
import org.apache.log4j.Logger;
import org.dspace.content.Bundle;
import org.dspace.content.DSpaceObject;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.event.Consumer;
import org.dspace.event.Event;
import org.dspace.utils.DSpace;
import java.util.HashSet;
import java.util.Set;
/**
* Class for updating search indices in discovery from content events.
*
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class IndexEventConsumer implements Consumer {
/**
* log4j logger
*/
private static Logger log = Logger.getLogger(IndexEventConsumer.class);
// collect Items, Collections, Communities that need indexing
private Set<DSpaceObject> objectsToUpdate = null;
// handles to delete since IDs are not useful by now.
private Set<String> handlesToDelete = null;
DSpace dspace = new DSpace();
IndexingService indexer = dspace.getServiceManager().getServiceByName(IndexingService.class.getName(),IndexingService.class);
public void initialize() throws Exception {
}
/**
* Consume a content event -- just build the sets of objects to add (new) to
* the index, update, and delete.
*
* @param ctx DSpace context
* @param event Content event
*/
public void consume(Context ctx, Event event) throws Exception {
if (objectsToUpdate == null) {
objectsToUpdate = new HashSet<DSpaceObject>();
handlesToDelete = new HashSet<String>();
}
int st = event.getSubjectType();
if (!(st == Constants.ITEM || st == Constants.BUNDLE
|| st == Constants.COLLECTION || st == Constants.COMMUNITY)) {
log
.warn("IndexConsumer should not have been given this kind of Subject in an event, skipping: "
+ event.toString());
return;
}
DSpaceObject subject = event.getSubject(ctx);
DSpaceObject object = event.getObject(ctx);
// If event subject is a Bundle and event was Add or Remove,
// transform the event to be a Modify on the owning Item.
// It could be a new bitstream in the TEXT bundle which
// would change the index.
int et = event.getEventType();
if (st == Constants.BUNDLE) {
if ((et == Event.ADD || et == Event.REMOVE) && subject != null
&& ((Bundle) subject).getName().equals("TEXT")) {
st = Constants.ITEM;
et = Event.MODIFY;
subject = ((Bundle) subject).getItems()[0];
if (log.isDebugEnabled())
{
log.debug("Transforming Bundle event into MODIFY of Item "
+ subject.getHandle());
}
} else
{
return;
}
}
switch (et) {
case Event.CREATE:
case Event.MODIFY:
case Event.MODIFY_METADATA:
if (subject == null)
{
log.warn(event.getEventTypeAsString() + " event, could not get object for "
+ event.getSubjectTypeAsString() + " id="
+ String.valueOf(event.getSubjectID())
+ ", perhaps it has been deleted.");
}
else {
log.debug("consume() adding event to update queue: " + event.toString());
objectsToUpdate.add(subject);
}
break;
case Event.REMOVE:
case Event.ADD:
if (object == null)
{
log.warn(event.getEventTypeAsString() + " event, could not get object for "
+ event.getObjectTypeAsString() + " id="
+ String.valueOf(event.getObjectID())
+ ", perhaps it has been deleted.");
}
else {
log.debug("consume() adding event to update queue: " + event.toString());
objectsToUpdate.add(object);
}
break;
case Event.DELETE:
String detail = event.getDetail();
if (detail == null)
{
log.warn("got null detail on DELETE event, skipping it.");
}
else {
log.debug("consume() adding event to delete queue: " + event.toString());
handlesToDelete.add(detail);
}
break;
default:
log
.warn("IndexConsumer should not have been given a event of type="
+ event.getEventTypeAsString()
+ " on subject="
+ event.getSubjectTypeAsString());
break;
}
}
/**
* Process sets of objects to add, update, and delete in index. Correct for
* interactions between the sets -- e.g. objects which were deleted do not
* need to be added or updated, new objects don't also need an update, etc.
*/
public void end(Context ctx) throws Exception {
if (objectsToUpdate != null && handlesToDelete != null) {
// update the changed Items not deleted because they were on create list
for (DSpaceObject iu : objectsToUpdate) {
/* we let all types through here and
* allow the search DSIndexer to make
* decisions on indexing and/or removal
*/
String hdl = iu.getHandle();
if (hdl != null && !handlesToDelete.contains(hdl)) {
try {
indexer.indexContent(ctx, iu, true);
log.debug("Indexed "
+ Constants.typeText[iu.getType()]
+ ", id=" + String.valueOf(iu.getID())
+ ", handle=" + hdl);
}
catch (Exception e) {
log.error("Failed while indexing object: ", e);
}
}
}
for (String hdl : handlesToDelete) {
try {
indexer.unIndexContent(ctx, hdl, true);
if (log.isDebugEnabled())
{
log.debug("UN-Indexed Item, handle=" + hdl);
}
}
catch (Exception e) {
log.error("Failed while UN-indexing object: " + hdl, e);
}
}
}
// "free" the resources
objectsToUpdate = null;
handlesToDelete = null;
}
public void finish(Context ctx) throws Exception {
// No-op
}
}

View File

@@ -0,0 +1,61 @@
/**
* 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.discovery;
import org.dspace.content.DSpaceObject;
import org.dspace.core.Context;
import java.io.IOException;
import java.sql.SQLException;
/**
* Interface used for indexing dspaceobject into discovery
*
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public interface IndexingService {
void indexContent(Context context, DSpaceObject dso)
throws SQLException;
void indexContent(Context context, DSpaceObject dso,
boolean force) throws SQLException;
void indexContent(Context context, DSpaceObject dso,
boolean force, boolean commit) throws SQLException, SearchServiceException;
void unIndexContent(Context context, DSpaceObject dso)
throws SQLException, IOException;
void unIndexContent(Context context, DSpaceObject dso, boolean commit)
throws SQLException, IOException;
void unIndexContent(Context context, String handle)
throws SQLException, IOException;
void unIndexContent(Context context, String handle, boolean commit)
throws SQLException, IOException;
void reIndexContent(Context context, DSpaceObject dso)
throws SQLException, IOException;
void createIndex(Context context) throws SQLException, IOException;
void updateIndex(Context context);
void updateIndex(Context context, boolean force);
void cleanIndex(boolean force) throws IOException,
SQLException, SearchServiceException;
void commit() throws SearchServiceException;
void optimize() throws SearchServiceException;
}

View File

@@ -0,0 +1,124 @@
/**
* 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.discovery;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.dspace.discovery.configuration.DiscoveryMoreLikeThisConfiguration;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.List;
/**
* Search interface that discovery uses
*
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public interface SearchService {
/**
* Convenient method to call @see #search(Context, DSpaceObject,
* DiscoverQuery) with a null DSpace Object as scope (i.e. all the
* repository)
*
* @param context
* DSpace Context object
* @param dso
* a DSpace Object to use as scope of the search (only results
* within this object)
* @param query
* the discovery query object
* @return
* @throws SearchServiceException
*/
DiscoverResult search(Context context, DiscoverQuery query)
throws SearchServiceException;
/**
* Convenient method to call @see #search(Context, DSpaceObject,
* DiscoverQuery, boolean) with includeWithdrawn=false
*
* @param context
* DSpace Context object
* @param dso
* a DSpace Object to use as scope of the search (only results
* within this object)
* @param query
* the discovery query object
* @return
* @throws SearchServiceException
*/
DiscoverResult search(Context context, DSpaceObject dso, DiscoverQuery query)
throws SearchServiceException;
/**
*
* @param context
* DSpace Context object
* @param dso
* a DSpace Object to use as scope of the search (only results
* within this object)
* @param includeWithdrawn
* use <code>true</code> to include in the results also withdrawn
* items that match the query
* @return
* @throws SearchServiceException
*/
DiscoverResult search(Context context, DiscoverQuery query,
boolean includeWithdrawn) throws SearchServiceException;
/**
*
* @param context
* DSpace Context object
* @param dso
* a DSpace Object to use as scope of the search (only results
* within this object)
* @param query
* the discovery query object
* @param includeWithdrawn
* use <code>true</code> to include in the results also withdrawn
* items that match the query
*
* @return
* @throws SearchServiceException
*/
DiscoverResult search(Context context, DSpaceObject dso, DiscoverQuery query, boolean includeWithdrawn) throws SearchServiceException;
InputStream searchJSON(Context context, DiscoverQuery query, String jsonIdentifier) throws SearchServiceException;
InputStream searchJSON(Context context, DiscoverQuery query, DSpaceObject dso, String jsonIdentifier) throws SearchServiceException;
List<DSpaceObject> search(Context context, String query, String orderfield, boolean ascending, int offset, int max, String... filterquery);
/**
* Transforms the given string field and value into a filter query
* @param context the DSpace context
* @param field the field of the filter query
* @param value the filter query value
* @return a filter query
* @throws SQLException ...
*/
DiscoverFilterQuery toFilterQuery(Context context, String field, String operator, String value) throws SQLException;
List<Item> getRelatedItems(Context context, Item item, DiscoveryMoreLikeThisConfiguration moreLikeThisConfiguration);
/**
* Transforms the metadata field of the given sort configuration into the indexed field which we can then use in our solr queries
* @param metadataField the metadata field
* @return the indexed field
*/
String toSortFieldIndex(String metadataField, String type);
}

View File

@@ -0,0 +1,34 @@
/**
* 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.discovery;
/**
* Exception used by discovery when discovery search exceptions occur
*
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class SearchServiceException extends Exception {
public SearchServiceException() {
}
public SearchServiceException(String s) {
super(s);
}
public SearchServiceException(String s, Throwable throwable) {
super(s, throwable);
}
public SearchServiceException(Throwable throwable) {
super(throwable);
}
}

View File

@@ -0,0 +1,96 @@
/**
* 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.discovery;
import org.dspace.content.*;
import org.dspace.content.Collection;
import org.dspace.discovery.configuration.DiscoveryConfiguration;
import org.dspace.discovery.configuration.DiscoveryConfigurationService;
import org.dspace.kernel.ServiceManager;
import org.dspace.utils.DSpace;
import java.sql.SQLException;
import java.util.*;
/**
* Util methods used by discovery
*
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class SearchUtils {
/** Cached search service **/
private static SearchService searchService;
public static SearchService getSearchService()
{
if(searchService == null){
DSpace dspace = new DSpace();
org.dspace.kernel.ServiceManager manager = dspace.getServiceManager() ;
searchService = manager.getServiceByName(SearchService.class.getName(),SearchService.class);
}
return searchService;
}
public static DiscoveryConfiguration getDiscoveryConfiguration(){
return getDiscoveryConfiguration(null);
}
public static DiscoveryConfiguration getDiscoveryConfiguration(DSpaceObject dso){
DiscoveryConfigurationService configurationService = getConfigurationService();
DiscoveryConfiguration result = null;
if(dso == null){
result = configurationService.getMap().get("site");
}else{
result = configurationService.getMap().get(dso.getHandle());
}
if(result == null){
//No specific configuration, get the default one
result = configurationService.getMap().get("default");
}
return result;
}
public static DiscoveryConfigurationService getConfigurationService() {
DSpace dspace = new DSpace();
ServiceManager manager = dspace.getServiceManager();
return manager.getServiceByName(DiscoveryConfigurationService.class.getName(), DiscoveryConfigurationService.class);
}
/**
* Method that retrieves a list of all the configuration objects from the given item
* A configuration object can be returned for each parent community/collection
* @param item the DSpace item
* @return a list of configuration objects
*/
public static List<DiscoveryConfiguration> getAllDiscoveryConfigurations(Item item) throws SQLException {
Map<String, DiscoveryConfiguration> result = new HashMap<String, DiscoveryConfiguration>();
Collection[] collections = item.getCollections();
for (Collection collection : collections) {
DiscoveryConfiguration configuration = getDiscoveryConfiguration(collection);
if(!result.containsKey(configuration.getId())){
result.put(configuration.getId(), configuration);
}
}
//Also add one for the default
DiscoveryConfiguration configuration = getDiscoveryConfiguration(null);
if(!result.containsKey(configuration.getId())){
result.put(configuration.getId(), configuration);
}
return Arrays.asList(result.values().toArray(new DiscoveryConfiguration[result.size()]));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
/**
* 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.discovery;
import org.apache.solr.common.SolrInputDocument;
import org.dspace.content.DSpaceObject;
import org.dspace.core.Context;
/**
* Example class that prints out the handle of the DSpace Object currently being indexed
*
* @author Kevin Van de Velde (kevin at atmire dot com)
*/
public class SolrServiceIndexOutputPlugin implements SolrServiceIndexPlugin{
@Override
public void additionalIndex(Context context, DSpaceObject dso, SolrInputDocument document) {
System.out.println("Currently indexing: " + dso.getHandle());
}
}

View File

@@ -0,0 +1,24 @@
/**
* 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.discovery;
import org.apache.solr.common.SolrInputDocument;
import org.dspace.content.DSpaceObject;
import org.dspace.core.Context;
/**
* Indexing plugin used when indexing the communities/collections/items into DSpace
*
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public interface SolrServiceIndexPlugin {
public void additionalIndex(Context context, DSpaceObject dso, SolrInputDocument document);
}

View File

@@ -0,0 +1,90 @@
/**
* 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.discovery;
import org.apache.log4j.Logger;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.common.SolrInputDocument;
import org.dspace.authorize.AuthorizeManager;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.content.DSpaceObject;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.LogManager;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import java.sql.SQLException;
import java.util.List;
import java.util.Set;
/**
* Restriction plugin that ensures that indexes all the resource policies.
* When a search is performed extra filter queries are added to retrieve only results to which the user has READ access
*
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public class SolrServiceResourceRestrictionPlugin implements SolrServiceIndexPlugin, SolrServiceSearchPlugin{
private static final Logger log = Logger.getLogger(SolrServiceResourceRestrictionPlugin.class);
@Override
public void additionalIndex(Context context, DSpaceObject dso, SolrInputDocument document) {
try {
List<ResourcePolicy> policies = AuthorizeManager.getPoliciesActionFilter(context, dso, Constants.READ);
for (ResourcePolicy resourcePolicy : policies) {
String fieldValue;
if(resourcePolicy.getGroupID() != -1){
//We have a group add it to the value
fieldValue = "g" + resourcePolicy.getGroupID();
}else{
//We have an eperson add it to the value
fieldValue = "e" + resourcePolicy.getEPersonID();
}
document.addField("read", fieldValue);
}
} catch (SQLException e) {
log.error(LogManager.getHeader(context, "Error while indexing resource policies", "DSpace object: (id " + dso.getID() + " type " + dso.getType() + ")"));
}
}
@Override
public void additionalSearchParameters(Context context, DiscoverQuery discoveryQuery, SolrQuery solrQuery) {
StringBuilder resourceQuery = new StringBuilder();
//Always add the anonymous group id to the query
resourceQuery.append("read:(g0");
EPerson currentUser = context.getCurrentUser();
if(currentUser != null){
try {
resourceQuery.append(" OR e").append(currentUser.getID());
//Retrieve all the groups the current user is a member of !
Set<Integer> groupIds = Group.allMemberGroupIDs(context, currentUser);
for (Integer groupId : groupIds) {
resourceQuery.append(" OR g").append(groupId);
}
} catch (SQLException e) {
log.error(LogManager.getHeader(context, "Error while adding resource policy information to query", "") ,e);
}
}
resourceQuery.append(")");
try {
if(AuthorizeManager.isAdmin(context)){
//Admins always have read access even if no policies are present !
resourceQuery.append(" OR (!read[* TO *])");
}
} catch (SQLException e) {
log.error(LogManager.getHeader(context, "Error while verifying if current user is admin !", ""), e);
}
solrQuery.addFilterQuery(resourceQuery.toString());
}
}

View File

@@ -0,0 +1,23 @@
/**
* 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.discovery;
import org.apache.solr.client.solrj.SolrQuery;
import org.dspace.core.Context;
/**
* Plugin from which users can add additional search parameters for every search that occurs in discovery
*
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Mark Diggory (markd at atmire dot com)
* @author Ben Bosman (ben at atmire dot com)
*/
public interface SolrServiceSearchPlugin {
public void additionalSearchParameters(Context context, DiscoverQuery discoveryQuery, SolrQuery solrQuery);
}

View File

@@ -0,0 +1,150 @@
/**
* 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.discovery.configuration;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Required;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @author Kevin Van de Velde (kevin at atmire dot com)
*/
public class DiscoveryConfiguration implements InitializingBean{
/** The configuration for the sidebar facets **/
private List<DiscoverySearchFilterFacet> sidebarFacets = new ArrayList<DiscoverySearchFilterFacet>();
/** The default filter queries which will be applied to any search & the recent submissions **/
private List<String> defaultFilterQueries;
/** Configuration object for the recent submissions **/
private DiscoveryRecentSubmissionsConfiguration recentSubmissionConfiguration;
/** The search filters which can be selected on the search page**/
private List<DiscoverySearchFilter> searchFilters = new ArrayList<DiscoverySearchFilter>();
private DiscoverySortConfiguration searchSortConfiguration;
private int defaultRpp = 10;
private String id;
private DiscoveryHitHighlightingConfiguration hitHighlightingConfiguration;
private DiscoveryMoreLikeThisConfiguration moreLikeThisConfiguration;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<DiscoverySearchFilterFacet> getSidebarFacets() {
return sidebarFacets;
}
@Required
public void setSidebarFacets(List<DiscoverySearchFilterFacet> sidebarFacets) {
this.sidebarFacets = sidebarFacets;
}
public List<String> getDefaultFilterQueries() {
//Since default filter queries are not mandatory we will return an empty list
if(defaultFilterQueries == null){
return new ArrayList<String>();
}else{
return defaultFilterQueries;
}
}
public void setDefaultFilterQueries(List<String> defaultFilterQueries) {
this.defaultFilterQueries = defaultFilterQueries;
}
public DiscoveryRecentSubmissionsConfiguration getRecentSubmissionConfiguration() {
return recentSubmissionConfiguration;
}
public void setRecentSubmissionConfiguration(DiscoveryRecentSubmissionsConfiguration recentSubmissionConfiguration) {
this.recentSubmissionConfiguration = recentSubmissionConfiguration;
}
public List<DiscoverySearchFilter> getSearchFilters() {
return searchFilters;
}
@Required
public void setSearchFilters(List<DiscoverySearchFilter> searchFilters) {
this.searchFilters = searchFilters;
}
public DiscoverySortConfiguration getSearchSortConfiguration() {
return searchSortConfiguration;
}
@Required
public void setSearchSortConfiguration(DiscoverySortConfiguration searchSortConfiguration) {
this.searchSortConfiguration = searchSortConfiguration;
}
public void setDefaultRpp(int defaultRpp)
{
this.defaultRpp = defaultRpp;
}
public int getDefaultRpp()
{
return defaultRpp;
}
public void setHitHighlightingConfiguration(DiscoveryHitHighlightingConfiguration hitHighlightingConfiguration) {
this.hitHighlightingConfiguration = hitHighlightingConfiguration;
}
public DiscoveryHitHighlightingConfiguration getHitHighlightingConfiguration() {
return hitHighlightingConfiguration;
}
public void setMoreLikeThisConfiguration(DiscoveryMoreLikeThisConfiguration moreLikeThisConfiguration) {
this.moreLikeThisConfiguration = moreLikeThisConfiguration;
}
public DiscoveryMoreLikeThisConfiguration getMoreLikeThisConfiguration() {
return moreLikeThisConfiguration;
}
/**
* After all the properties are set check that the sidebar facets are a subset of our search filters
*
* @throws Exception throws an exception if this isn't the case
*/
@Override
public void afterPropertiesSet() throws Exception
{
Collection missingSearchFilters = CollectionUtils.subtract(getSidebarFacets(), getSearchFilters());
if(CollectionUtils.isNotEmpty(missingSearchFilters))
{
StringBuilder error = new StringBuilder();
error.append("The following sidebar facet configurations are not present in the search filters list: ");
for (Object missingSearchFilter : missingSearchFilters)
{
DiscoverySearchFilter searchFilter = (DiscoverySearchFilter) missingSearchFilter;
error.append(searchFilter.getIndexFieldName()).append(" ");
}
error.append("all the sidebar facets MUST be a part of the search filters list.");
throw new DiscoveryConfigurationException(error.toString());
}
}
}

View File

@@ -0,0 +1,33 @@
/**
* 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.discovery.configuration;
/**
* Exception that can be thrown if there are issues with the discovery configuration
*
* @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 DiscoveryConfigurationException extends Exception{
public DiscoveryConfigurationException() {
}
public DiscoveryConfigurationException(String message, Throwable cause) {
super(message, cause);
}
public DiscoveryConfigurationException(String message) {
super(message);
}
public DiscoveryConfigurationException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,27 @@
/**
* 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.discovery.configuration;
/**
* Class contains parameters used for the configuration of discovery
*
* @author Kevin Van de Velde (kevin at atmire dot com)
*/
public class DiscoveryConfigurationParameters {
public static final String TYPE_TEXT = "text";
public static final String TYPE_DATE = "date";
public static final String TYPE_HIERARCHICAL = "hierarchical";
public static final String TYPE_AC = "ac";
public static final String TYPE_AUTHORITY = "authority";
public static final String TYPE_STANDARD = "standard";
public static enum SORT {VALUE, COUNT}
}

View File

@@ -0,0 +1,73 @@
/**
* 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.discovery.configuration;
import org.dspace.utils.DSpace;
import java.util.List;
import java.util.Map;
/**
* @author Kevin Van de Velde (kevin at atmire dot com)
*/
public class DiscoveryConfigurationService {
private Map<String, DiscoveryConfiguration> map;
public Map<String, DiscoveryConfiguration> getMap() {
return map;
}
public void setMap(Map<String, DiscoveryConfiguration> map) {
this.map = map;
}
public static void main(String[] args) {
DSpace dspace = new DSpace();
System.out.println(dspace.getServiceManager().getServicesNames().size());
DiscoveryConfigurationService mainService = dspace.getServiceManager().getServiceByName(DiscoveryConfigurationService.class.getName(), DiscoveryConfigurationService.class);
for(String key : mainService.getMap().keySet()){
System.out.println(key);
System.out.println("Facets:");
DiscoveryConfiguration discoveryConfiguration = mainService.getMap().get(key);
for (int i = 0; i < discoveryConfiguration.getSidebarFacets().size(); i++) {
DiscoverySearchFilterFacet sidebarFacet = discoveryConfiguration.getSidebarFacets().get(i);
System.out.println("\t" + sidebarFacet.getIndexFieldName());
for (int j = 0; j < sidebarFacet.getMetadataFields().size(); j++) {
String metadataField = sidebarFacet.getMetadataFields().get(j);
System.out.println("\t\t" + metadataField);
}
}
System.out.println("Search filters");
List<DiscoverySearchFilter> searchFilters = discoveryConfiguration.getSearchFilters();
for (DiscoverySearchFilter searchFilter : searchFilters) {
for (int i = 0; i < searchFilter.getMetadataFields().size(); i++) {
String metadataField = searchFilter.getMetadataFields().get(i);
System.out.println("\t\t" + metadataField);
}
}
System.out.println("Recent submissions configuration:");
DiscoveryRecentSubmissionsConfiguration recentSubmissionConfiguration = discoveryConfiguration.getRecentSubmissionConfiguration();
System.out.println("\tMetadata sort field: " + recentSubmissionConfiguration.getMetadataSortField());
System.out.println("\tMax recent submissions: " + recentSubmissionConfiguration.getMax());
List<String> defaultFilterQueries = discoveryConfiguration.getDefaultFilterQueries();
if(0 < defaultFilterQueries.size()){
System.out.println("Default filter queries");
for (String fq : defaultFilterQueries) {
System.out.println("\t" + fq);
}
}
}
}
}

View File

@@ -0,0 +1,63 @@
/**
* 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.discovery.configuration;
import org.springframework.beans.factory.annotation.Required;
/**
* Configuration class that holds hit highlighting configuration for a single metadata field
*
* @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 DiscoveryHitHighlightFieldConfiguration
{
private String field;
private int maxSize = 0;
private int snippets = 3;
public String getField()
{
return field;
}
@Required
public void setField(String field)
{
this.field = field;
}
public int getMaxSize()
{
return maxSize;
}
public void setMaxSize(int maxSize)
{
this.maxSize = maxSize;
}
/**
* Set the maximum number of highlighted snippets to generate per field
* @param snippets the number of maximum snippets
*/
public void setSnippets(int snippets)
{
this.snippets = snippets;
}
/**
* Get the maximum number of highlighted snippets to generate per field
*/
public int getSnippets()
{
return snippets;
}
}

View File

@@ -0,0 +1,39 @@
/**
* 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.discovery.configuration;
import org.springframework.beans.factory.annotation.Required;
import java.util.List;
/**
* Class that contains all the configuration concerning the hit highlighting in search resutls
* This class can be configured in the [dspace.dir]/config/spring/discovery/spring-dspace-addon-discovery-configuration-services.xml
*
* @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 DiscoveryHitHighlightingConfiguration
{
/* A list of metadata fields for which the hit highlighting is possible */
private List<DiscoveryHitHighlightFieldConfiguration> metadataFields;
@Required
public void setMetadataFields(List<DiscoveryHitHighlightFieldConfiguration> metadataFields)
{
this.metadataFields = metadataFields;
}
public List<DiscoveryHitHighlightFieldConfiguration> getMetadataFields()
{
return metadataFields;
}
}

View File

@@ -0,0 +1,69 @@
/**
* 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.discovery.configuration;
import org.springframework.beans.factory.annotation.Required;
import java.util.List;
/**
* Class that contains the more like this configuration on item pages
*
* @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 DiscoveryMoreLikeThisConfiguration {
private List<String> similarityMetadataFields;
private int minTermFrequency;
private int max;
private int minWordLength;
@Required
public void setSimilarityMetadataFields(List<String> similarityMetadataFields)
{
this.similarityMetadataFields = similarityMetadataFields;
}
public List<String> getSimilarityMetadataFields()
{
return similarityMetadataFields;
}
@Required
public void setMinTermFrequency(int minTermFrequency)
{
this.minTermFrequency = minTermFrequency;
}
public int getMinTermFrequency()
{
return minTermFrequency;
}
@Required
public void setMax(int max)
{
this.max = max;
}
public int getMax()
{
return max;
}
public int getMinWordLength()
{
return minWordLength;
}
public void setMinWordLength(int minWordLength)
{
this.minWordLength = minWordLength;
}
}

View File

@@ -0,0 +1,47 @@
/**
* 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.discovery.configuration;
import org.springframework.beans.factory.annotation.Required;
/**
* @author Kevin Van de Velde (kevin at atmire dot com)
*/
public class DiscoveryRecentSubmissionsConfiguration {
private String metadataSortField;
private String type;
private int max = 5;
public String getMetadataSortField() {
return metadataSortField;
}
@Required
public void setMetadataSortField(String metadataSortField) {
this.metadataSortField = metadataSortField;
}
public int getMax() {
return max;
}
@Required
public void setMax(int max) {
this.max = max;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}

View File

@@ -0,0 +1,67 @@
/**
* 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.discovery.configuration;
import org.springframework.beans.factory.annotation.Required;
import java.util.List;
/**
* @author Kevin Van de Velde (kevin at atmire dot com)
*/
public class DiscoverySearchFilter {
protected String indexFieldName;
protected List<String> metadataFields;
protected String type = DiscoveryConfigurationParameters.TYPE_TEXT;
public static final String FILTER_TYPE_DEFAULT = "default";
public String getIndexFieldName() {
return indexFieldName;
}
@Required
public void setIndexFieldName(String indexFieldName) {
this.indexFieldName = indexFieldName;
}
public List<String> getMetadataFields() {
return metadataFields;
}
@Required
public void setMetadataFields(List<String> metadataFields) {
this.metadataFields = metadataFields;
}
public String getType() {
return type;
}
public void setType(String type) throws DiscoveryConfigurationException {
if(type.equalsIgnoreCase(DiscoveryConfigurationParameters.TYPE_TEXT))
{
this.type = DiscoveryConfigurationParameters.TYPE_TEXT;
} else
if(type.equalsIgnoreCase(DiscoveryConfigurationParameters.TYPE_DATE))
{
this.type = DiscoveryConfigurationParameters.TYPE_DATE;
} else
if(type.equalsIgnoreCase(DiscoveryConfigurationParameters.TYPE_HIERARCHICAL))
{
throw new DiscoveryConfigurationException("The " + type + " can't be used with a default side bar facet use the \"HierarchicalSidebarFacetConfiguration\" class instead.");
}else{
this.type = type;
}
}
public String getFilterType(){
return FILTER_TYPE_DEFAULT;
}
}

View File

@@ -0,0 +1,54 @@
/**
* 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.discovery.configuration;
/**
* An expanded class that allows a search filter to be used as a sidebar facet
*
* @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 DiscoverySearchFilterFacet extends DiscoverySearchFilter {
private static final int DEFAULT_FACET_LIMIT = 10;
private int facetLimit = -1;
private DiscoveryConfigurationParameters.SORT sortOrder = DiscoveryConfigurationParameters.SORT.COUNT;
public static final String FILTER_TYPE_FACET = "facet";
public int getFacetLimit()
{
if(facetLimit == -1){
return DEFAULT_FACET_LIMIT;
}else{
return facetLimit;
}
}
public void setFacetLimit(int facetLimit)
{
this.facetLimit = facetLimit;
}
public DiscoveryConfigurationParameters.SORT getSortOrder()
{
return sortOrder;
}
public void setSortOrder(DiscoveryConfigurationParameters.SORT sortOrder)
{
this.sortOrder = sortOrder;
}
@Override
public String getFilterType()
{
return FILTER_TYPE_FACET;
}
}

View File

@@ -0,0 +1,61 @@
/**
* 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/
*/
/**
* 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.discovery.configuration;
import java.util.ArrayList;
import java.util.List;
/**
* @author Kevin Van de Velde (kevin at atmire dot com)
*/
public class DiscoverySortConfiguration {
/** Attributes used for sorting of results **/
public enum SORT_ORDER {
desc,
asc
}
private DiscoverySortFieldConfiguration defaultSort = null;
private List<DiscoverySortFieldConfiguration> sortFields = new ArrayList<DiscoverySortFieldConfiguration>();
private SORT_ORDER defaultSortOrder = SORT_ORDER.desc;
public DiscoverySortFieldConfiguration getDefaultSort() {
return defaultSort;
}
public void setDefaultSort(DiscoverySortFieldConfiguration defaultSort) {
this.defaultSort = defaultSort;
}
public List<DiscoverySortFieldConfiguration> getSortFields() {
return sortFields;
}
public void setSortFields(List<DiscoverySortFieldConfiguration> sortFields) {
this.sortFields = sortFields;
}
public SORT_ORDER getDefaultSortOrder() {
return defaultSortOrder;
}
public void setDefaultSortOrder(SORT_ORDER defaultSortOrder) {
this.defaultSortOrder = defaultSortOrder;
}
}

View File

@@ -0,0 +1,52 @@
/**
* 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.discovery.configuration;
import org.springframework.beans.factory.annotation.Required;
/**
* Created by IntelliJ IDEA.
* User: Kevin
* Date: 28/08/11
* Time: 14:11
* To change this template use File | Settings | File Templates.
*/
public class DiscoverySortFieldConfiguration {
private String metadataField;
private String type = DiscoveryConfigurationParameters.TYPE_TEXT;
public String getMetadataField() {
return metadataField;
}
@Required
public void setMetadataField(String metadataField) {
this.metadataField = metadataField;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public boolean equals(Object obj) {
if(obj != null && obj instanceof DiscoverySortFieldConfiguration){
DiscoverySortFieldConfiguration compareConfig = (DiscoverySortFieldConfiguration) obj;
if(compareConfig.getMetadataField().equals(getMetadataField()) && compareConfig.getType().equals(getType())){
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,41 @@
/**
* 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.discovery.configuration;
import org.dspace.kernel.config.SpringLoader;
import org.dspace.services.ConfigurationService;
import java.io.File;
import java.net.MalformedURLException;
/**
* User: kevin (kevin at atmire.com)
* Date: 3-aug-2011
* Time: 11:39:37
*/
public class DiscoverySpringLoader implements SpringLoader {
@Override
public String[] getResourcePaths(ConfigurationService configurationService) {
StringBuffer filePath = new StringBuffer();
filePath.append(configurationService.getProperty("dspace.dir"));
filePath.append(File.separator);
filePath.append("config");
filePath.append(File.separator);
filePath.append("spring");
filePath.append(File.separator);
filePath.append("discovery");
filePath.append(File.separator);
try {
return new String[]{new File(filePath.toString()).toURI().toURL().toString() + XML_SUFFIX};
} catch (MalformedURLException e) {
return new String[0];
}
}
}

View File

@@ -0,0 +1,56 @@
/**
* 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.discovery.configuration;
import org.springframework.beans.factory.annotation.Required;
/**
* Special sidebar facet configuration used for hierarchical facets
*
* @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 HierarchicalSidebarFacetConfiguration extends DiscoverySearchFilterFacet{
private String splitter;
private boolean skipFirstNodeLevel = true;
public HierarchicalSidebarFacetConfiguration() {
//Our default type is the hieracrhical, can be overridden by the configuration
this.type = DiscoveryConfigurationParameters.TYPE_HIERARCHICAL;
}
public String getSplitter() {
return splitter;
}
@Required
public void setSplitter(String splitter) {
this.splitter = splitter;
}
public boolean isSkipFirstNodeLevel() {
return skipFirstNodeLevel;
}
public void setSkipFirstNodeLevel(boolean skipFirstNodeLevel) {
this.skipFirstNodeLevel = skipFirstNodeLevel;
}
@Override
public void setType(String type) throws DiscoveryConfigurationException {
if(type.equalsIgnoreCase(DiscoveryConfigurationParameters.TYPE_HIERARCHICAL)){
this.type = type;
}else{
throw new DiscoveryConfigurationException("The " + type + " can't be used with a hierarchical facet side bar facet use the \"DiscoverySearchFilterFacet\" class instead.");
}
}
}

View File

@@ -0,0 +1,79 @@
/**
* 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.discovery.configuration;
import org.springframework.beans.factory.annotation.Required;
import java.util.List;
/**
* @author Kevin Van de Velde (kevin at atmire dot com)
*/
public class SidebarFacetConfiguration {
//Let the configuration manager retrieve this !
private static final int DEFAULT_FACET_LIMIT = 10;
private String indexFieldName;
private List<String> metadataFields;
private int facetLimit = -1;
private DiscoveryConfigurationParameters.SORT sortOrder = DiscoveryConfigurationParameters.SORT.COUNT;
private String type = DiscoveryConfigurationParameters.TYPE_TEXT;
public String getIndexFieldName() {
return indexFieldName;
}
@Required
public void setIndexFieldName(String indexFieldName) {
this.indexFieldName = indexFieldName;
}
public List<String> getMetadataFields() {
return metadataFields;
}
@Required
public void setMetadataFields(List<String> metadataFields) {
this.metadataFields = metadataFields;
}
public int getFacetLimit() {
if(facetLimit == -1){
return DEFAULT_FACET_LIMIT;
}else{
return facetLimit;
}
}
public void setFacetLimit(int facetLimit) {
this.facetLimit = facetLimit;
}
public String getType() {
return type;
}
public void setType(String type) {
if(type.equalsIgnoreCase(DiscoveryConfigurationParameters.TYPE_TEXT)){
this.type = DiscoveryConfigurationParameters.TYPE_TEXT;
} else
if(type.equalsIgnoreCase(DiscoveryConfigurationParameters.TYPE_DATE)){
this.type = DiscoveryConfigurationParameters.TYPE_DATE;
}else{
this.type = type;
}
}
public DiscoveryConfigurationParameters.SORT getSortOrder() {
return sortOrder;
}
public void setSortOrder(DiscoveryConfigurationParameters.SORT sortOrder) {
this.sortOrder = sortOrder;
}
}

View File

@@ -0,0 +1,182 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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/
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd"
default-autowire-candidates="*Service,*DAO,javax.sql.DataSource">
<context:annotation-config /> <!-- allows us to use spring annotations in beans -->
<!--Bean that is used for mapping communities/collections to certain discovery configurations.-->
<bean id="org.dspace.discovery.configuration.DiscoveryConfigurationService" class="org.dspace.discovery.configuration.DiscoveryConfigurationService">
<property name="map">
<map>
<!--The map containing all the settings,
the key is used to refer to the page (the "site" or a community/collection handle)
the value-ref is a reference to an identifier of the DiscoveryConfiguration format
-->
<!--The default entry, DO NOT REMOVE the system requires this-->
<entry key="default" value-ref="defaultConfiguration" />
<!--Use site to override the default configuration for the home page & default discovery page-->
<!--<entry key="site" value-ref="defaultConfiguration" />-->
<!--<entry key="123456789/7621" value-ref="defaultConfiguration"/>-->
</map>
</property>
</bean>
<!--The default configuration settings for discovery-->
<bean id="defaultConfiguration" class="org.dspace.discovery.configuration.DiscoveryConfiguration" scope="prototype">
<!--Which sidebar facets are to be displayed-->
<property name="sidebarFacets">
<list>
<ref bean="sidebarFacetAuthor" />
<ref bean="sidebarFacetSubject" />
<ref bean="sidebarFacetDateIssued" />
</list>
</property>
<!--The search filters which can be used on the discovery search page-->
<property name="searchFilters">
<list>
<ref bean="searchFilterTitle" />
<ref bean="searchFilterAuthor" />
<ref bean="searchFilterSubject" />
<ref bean="searchFilterIssued" />
</list>
</property>
<!--The sort filters for the discovery search-->
<property name="searchSortConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
<!--<property name="defaultSort" ref="sortDateIssued"/>-->
<!--DefaultSortOrder can either be desc or asc (desc is default)-->
<property name="defaultSortOrder" value="desc"/>
<property name="sortFields">
<list>
<ref bean="sortTitle" />
<ref bean="sortDateIssued" />
</list>
</property>
</bean>
</property>
<!--Any default filter queries, these filter queries will be used for all queries done by discovery for this configuration-->
<!--<property name="defaultFilterQueries">-->
<!--<list>-->
<!--Only find items-->
<!--<value>search.resourcetype:2</value>-->
<!--</list>-->
<!--</property>-->
<!--The configuration for the recent submissions-->
<property name="recentSubmissionConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoveryRecentSubmissionsConfiguration">
<property name="metadataSortField" value="dc.date.accessioned" />
<property name="type" value="date"/>
<property name="max" value="5"/>
</bean>
</property>
</bean>
<!--Search filter configuration beans-->
<bean id="searchFilterTitle" class="org.dspace.discovery.configuration.DiscoverySearchFilter">
<property name="indexFieldName" value="title"/>
<property name="metadataFields">
<list>
<value>dc.title</value>
</list>
</property>
<property name="fullAutoComplete" value="false"/>
</bean>
<bean id="searchFilterAuthor" class="org.dspace.discovery.configuration.DiscoverySearchFilter">
<property name="indexFieldName" value="author"/>
<property name="metadataFields">
<list>
<value>dc.contributor.author</value>
<value>dc.creator</value>
</list>
</property>
<property name="fullAutoComplete" value="true"/>
</bean>
<bean id="searchFilterSubject" class="org.dspace.discovery.configuration.DiscoverySearchFilter">
<property name="indexFieldName" value="subject"/>
<property name="metadataFields">
<list>
<value>dc.subject.*</value>
</list>
</property>
<property name="fullAutoComplete" value="true"/>
</bean>
<bean id="searchFilterIssued" class="org.dspace.discovery.configuration.DiscoverySearchFilter">
<property name="indexFieldName" value="dateIssued"/>
<property name="metadataFields">
<list>
<value>dc.date.issued</value>
</list>
</property>
<property name="type" value="date"/>
<property name="fullAutoComplete" value="false"/>
</bean>
<!--Sidebar facet configuration beans-->
<bean id="sidebarFacetAuthor" class="org.dspace.discovery.configuration.SidebarFacetConfiguration">
<property name="indexFieldName" value="author"/>
<property name="metadataFields">
<list>
<value>dc.contributor.author</value>
<value>dc.creator</value>
</list>
</property>
<property name="facetLimit" value="10"/>
<property name="sortOrder" value="COUNT"/>
</bean>
<bean id="sidebarFacetSubject" class="org.dspace.discovery.configuration.SidebarFacetConfiguration">
<property name="indexFieldName" value="subject"/>
<property name="metadataFields">
<list>
<value>dc.subject.*</value>
</list>
</property>
<property name="facetLimit" value="10"/>
<property name="sortOrder" value="COUNT"/>
</bean>
<bean id="sidebarFacetDateIssued" class="org.dspace.discovery.configuration.SidebarFacetConfiguration">
<property name="indexFieldName" value="dateIssued"/>
<property name="metadataFields">
<list>
<value>dc.date.issued</value>
</list>
</property>
<property name="type" value="date"/>
<property name="sortOrder" value="VALUE"/>
</bean>
<!--Sort properties-->
<bean id="sortTitle" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
<property name="metadataField" value="dc.title"/>
</bean>
<bean id="sortDateIssued" class="org.dspace.discovery.configuration.DiscoverySortFieldConfiguration">
<property name="metadataField" value="dc.date.issued"/>
<property name="type" value="date"/>
</bean>
</beans>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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/
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd"
default-autowire-candidates="*Service,*DAO,javax.sql.DataSource">
<context:annotation-config /> <!-- allows us to use spring annotations in beans -->
<!-- NOTE: I am not convinced this is a good idea, it is really slow and I think possibly dangerous -AZ -->
<!--
<context:component-scan base-package="org.dspace" name-generator="org.dspace.servicemanager.spring.FullPathBeanNameGenerator" />
-->
<bean class="org.dspace.discovery.SolrServiceImpl" id="org.dspace.discovery.SearchService"/>
<alias name="org.dspace.discovery.SearchService" alias="org.dspace.discovery.IndexingService"/>
<!--<bean class="org.dspace.discovery.SolrServiceIndexOutputPlugin" id="solrServiceIndexOutputPlugin"/>-->
</beans>