Skip to content

Commit

Permalink
Browse-by support for controlled vocabularies
Browse files Browse the repository at this point in the history
  • Loading branch information
MarieVerdonck committed May 4, 2023
1 parent 4cdb662 commit 66eb8a5
Show file tree
Hide file tree
Showing 13 changed files with 288 additions and 32 deletions.
Expand Up @@ -15,6 +15,7 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
Expand All @@ -30,6 +31,8 @@
import org.dspace.content.authority.service.ChoiceAuthorityService;
import org.dspace.core.Utils;
import org.dspace.core.service.PluginService;
import org.dspace.discovery.configuration.DiscoveryConfigurationService;
import org.dspace.discovery.configuration.DiscoverySearchFilterFacet;
import org.dspace.services.ConfigurationService;
import org.springframework.beans.factory.annotation.Autowired;

Expand Down Expand Up @@ -80,13 +83,18 @@ public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService
protected Map<String, Map<String, List<String>>> authoritiesFormDefinitions =
new HashMap<String, Map<String, List<String>>>();

// Map of vocabulary authorities to and their index info equivalent
protected Map<String, DSpaceControlledVocabularyIndex> vocabularyIndexMap = new HashMap<>();

// the item submission reader
private SubmissionConfigReader itemSubmissionConfigReader;

@Autowired(required = true)
protected ConfigurationService configurationService;
@Autowired(required = true)
protected PluginService pluginService;
@Autowired
private DiscoveryConfigurationService searchConfigurationService;

final static String CHOICES_PLUGIN_PREFIX = "choices.plugin.";
final static String CHOICES_PRESENTATION_PREFIX = "choices.presentation.";
Expand Down Expand Up @@ -540,4 +548,50 @@ public Choice getParentChoice(String authorityName, String vocabularyId, String
HierarchicalAuthority ma = (HierarchicalAuthority) getChoiceAuthorityByAuthorityName(authorityName);
return ma.getParentChoice(authorityName, vocabularyId, locale);
}

@Override
public DSpaceControlledVocabularyIndex getVocabularyIndex(String nameVocab) {
if (this.vocabularyIndexMap.containsKey(nameVocab)) {
return this.vocabularyIndexMap.get(nameVocab);
} else {
init();
ChoiceAuthority source = this.getChoiceAuthorityByAuthorityName(nameVocab);
if (source != null && source instanceof DSpaceControlledVocabulary) {
Set<String> metadataFields = new HashSet<>();
Map<String, List<String>> formsToFields = this.authoritiesFormDefinitions.get(nameVocab);
for (Map.Entry<String, List<String>> formToField : formsToFields.entrySet()) {
metadataFields.addAll(formToField.getValue().stream().map(value ->
StringUtils.replace(value, "_", "."))
.collect(Collectors.toList()));
}
DiscoverySearchFilterFacet matchingFacet = null;
for (DiscoverySearchFilterFacet facetConfig : searchConfigurationService.getAllFacetsConfig()) {
boolean coversAllFieldsFromVocab = true;
for (String fieldFromVocab: metadataFields) {
boolean coversFieldFromVocab = false;
for (String facetMdField: facetConfig.getMetadataFields()) {
if (facetMdField.startsWith(fieldFromVocab)) {
coversFieldFromVocab = true;
break;
}
}
if (!coversFieldFromVocab) {
coversAllFieldsFromVocab = false;
break;
}
}
if (coversAllFieldsFromVocab) {
matchingFacet = facetConfig;
break;
}
}
DSpaceControlledVocabularyIndex vocabularyIndex =
new DSpaceControlledVocabularyIndex((DSpaceControlledVocabulary) source, metadataFields,
matchingFacet);
this.vocabularyIndexMap.put(nameVocab, vocabularyIndex);
return vocabularyIndex;
}
return null;
}
}
}
@@ -0,0 +1,45 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.content.authority;

import java.util.Set;

import org.dspace.discovery.configuration.DiscoverySearchFilterFacet;

/**
* Helper class to transform a {@link org.dspace.content.authority.DSpaceControlledVocabulary} into a
* {@code BrowseIndexRest}
* cached by {@link org.dspace.content.authority.service.ChoiceAuthorityService#getVocabularyIndex(String)}
*
* @author Marie Verdonck (Atmire) on 04/05/2023
*/
public class DSpaceControlledVocabularyIndex {

protected DSpaceControlledVocabulary vocabulary;
protected Set<String> metadataFields;
protected DiscoverySearchFilterFacet facetConfig;

public DSpaceControlledVocabularyIndex(DSpaceControlledVocabulary controlledVocabulary, Set<String> metadataFields,
DiscoverySearchFilterFacet facetConfig) {
this.vocabulary = controlledVocabulary;
this.metadataFields = metadataFields;
this.facetConfig = facetConfig;
}

public DSpaceControlledVocabulary getVocabulary() {
return vocabulary;
}

public Set<String> getMetadataFields() {
return this.metadataFields;
}

public DiscoverySearchFilterFacet getFacetConfig() {
return this.facetConfig;
}
}
Expand Up @@ -15,6 +15,7 @@
import org.dspace.content.authority.Choice;
import org.dspace.content.authority.ChoiceAuthority;
import org.dspace.content.authority.Choices;
import org.dspace.content.authority.DSpaceControlledVocabularyIndex;

/**
* Broker for ChoiceAuthority plugins, and for other information configured
Expand Down Expand Up @@ -220,4 +221,7 @@ public Choices getBestMatch(String fieldKey, String query, Collection collection
* @return the parent Choice object if any
*/
public Choice getParentChoice(String authorityName, String vocabularyId, String locale);

public DSpaceControlledVocabularyIndex getVocabularyIndex(String nameVocab);

}
Expand Up @@ -92,6 +92,18 @@ public List<DiscoveryConfiguration> getIndexAlwaysConfigurations() {
return configs;
}

/**
* @return All configurations for {@link org.dspace.discovery.configuration.DiscoverySearchFilterFacet}
*/
public List<DiscoverySearchFilterFacet> getAllFacetsConfig() {
List<DiscoverySearchFilterFacet> configs = new ArrayList<>();
for (String key : map.keySet()) {
DiscoveryConfiguration config = map.get(key);
configs.addAll(config.getSidebarFacets());
}
return configs;
}

public static void main(String[] args) {
System.out.println(DSpaceServicesFactory.getInstance().getServiceManager().getServicesNames().size());
DiscoveryConfigurationService mainService = DSpaceServicesFactory.getInstance().getServiceManager()
Expand Down
Expand Up @@ -7,6 +7,9 @@
*/
package org.dspace.app.rest.converter;

import static org.dspace.app.rest.model.BrowseIndexRest.BROWSE_TYPE_FLAT;
import static org.dspace.app.rest.model.BrowseIndexRest.BROWSE_TYPE_VALUE_LIST;

import java.util.ArrayList;
import java.util.List;

Expand All @@ -33,14 +36,15 @@ public BrowseIndexRest convert(BrowseIndex obj, Projection projection) {
bir.setId(obj.getName());
bir.setDataType(obj.getDataType());
bir.setOrder(obj.getDefaultOrder());
bir.setMetadataBrowse(obj.isMetadataIndex());
List<String> metadataList = new ArrayList<String>();
if (obj.isMetadataIndex()) {
for (String s : obj.getMetadata().split(",")) {
metadataList.add(s.trim());
}
bir.setBrowseType(BROWSE_TYPE_VALUE_LIST);
} else {
metadataList.add(obj.getSortOption().getMetadata());
bir.setBrowseType(BROWSE_TYPE_FLAT);
}
bir.setMetadataList(metadataList);

Expand Down
@@ -0,0 +1,42 @@
/**
* 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.app.rest.converter;

import java.util.ArrayList;

import org.dspace.app.rest.model.BrowseIndexRest;
import org.dspace.app.rest.projection.Projection;
import org.dspace.content.authority.DSpaceControlledVocabularyIndex;
import org.springframework.stereotype.Component;

/**
* This is the converter from a {@link org.dspace.content.authority.DSpaceControlledVocabularyIndex} to a
* {@link org.dspace.app.rest.model.BrowseIndexRest#BROWSE_TYPE_HIERARCHICAL} {@link org.dspace.app.rest.model.BrowseIndexRest}
*
* @author Marie Verdonck (Atmire) on 04/05/2023
*/
@Component
public class HierarchicalBrowseConverter implements DSpaceConverter<DSpaceControlledVocabularyIndex, BrowseIndexRest> {

@Override
public BrowseIndexRest convert(DSpaceControlledVocabularyIndex obj, Projection projection) {
BrowseIndexRest bir = new BrowseIndexRest();
bir.setProjection(projection);
bir.setId(obj.getVocabulary().getPluginInstanceName());
bir.setBrowseType(BrowseIndexRest.BROWSE_TYPE_HIERARCHICAL);
bir.setFacetType(obj.getFacetConfig().getIndexFieldName());
bir.setVocabulary(obj.getVocabulary().getPluginInstanceName());
bir.setMetadataList(new ArrayList<>(obj.getMetadataFields()));
return bir;
}

@Override
public Class<DSpaceControlledVocabularyIndex> getModelClass() {
return DSpaceControlledVocabularyIndex.class;
}
}
Expand Up @@ -37,11 +37,11 @@ protected void addLinks(final BrowseEntryResource halResource, final Pageable pa
UriComponentsBuilder baseLink = uriBuilder(
getMethodOn(bix.getCategory(), bix.getType()).findRel(null, null, bix.getCategory(),
English.plural(bix.getType()), bix.getId(),
BrowseIndexRest.ITEMS, null, null));
BrowseIndexRest.LINK_ITEMS, null, null));

addFilterParams(baseLink, data);

list.add(buildLink(BrowseIndexRest.ITEMS,
list.add(buildLink(BrowseIndexRest.LINK_ITEMS,
baseLink.build().encode().toUriString()));
}
}
Expand Down
Expand Up @@ -10,6 +10,7 @@
import java.util.List;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.dspace.app.rest.RestResourceController;

Expand All @@ -20,11 +21,11 @@
*/
@LinksRest(links = {
@LinkRest(
name = BrowseIndexRest.ITEMS,
name = BrowseIndexRest.LINK_ITEMS,
method = "listBrowseItems"
),
@LinkRest(
name = BrowseIndexRest.ENTRIES,
name = BrowseIndexRest.LINK_ENTRIES,
method = "listBrowseEntries"
)
})
Expand All @@ -35,20 +36,38 @@ public class BrowseIndexRest extends BaseObjectRest<String> {

public static final String CATEGORY = RestAddressableModel.DISCOVER;

public static final String ITEMS = "items";
public static final String ENTRIES = "entries";

boolean metadataBrowse;

public static final String LINK_ITEMS = "items";
public static final String LINK_ENTRIES = "entries";
public static final String LINK_VOCABULARY = "vocabulary";

// if the browse index has two levels, the 1st level shows the list of entries like author names, subjects, types,
// etc. the second level is the actual list of items linked to a specific entry
public static final String BROWSE_TYPE_VALUE_LIST = "valueList";
// if the browse index has one level: the full list of items
public static final String BROWSE_TYPE_FLAT = "flatBrowse";
// if the browse index should display the vocabulary tree. The 1st level shows the tree.
// The second level is the actual list of items linked to a specific entry
public static final String BROWSE_TYPE_HIERARCHICAL = "hierarchicalBrowse";

// Shared fields
String browseType;
@JsonProperty(value = "metadata")
List<String> metadataList;

// Single browse index fields
@JsonInclude(JsonInclude.Include.NON_NULL)
String dataType;

@JsonInclude(JsonInclude.Include.NON_NULL)
List<SortOption> sortOptions;

@JsonInclude(JsonInclude.Include.NON_NULL)
String order;

// Hierarchical browse fields
@JsonInclude(JsonInclude.Include.NON_NULL)
String facetType;
@JsonInclude(JsonInclude.Include.NON_NULL)
String vocabulary;

@JsonIgnore
@Override
public String getCategory() {
Expand All @@ -60,14 +79,6 @@ public String getType() {
return NAME;
}

public boolean isMetadataBrowse() {
return metadataBrowse;
}

public void setMetadataBrowse(boolean metadataBrowse) {
this.metadataBrowse = metadataBrowse;
}

public List<String> getMetadataList() {
return metadataList;
}
Expand Down Expand Up @@ -100,6 +111,38 @@ public void setSortOptions(List<SortOption> sortOptions) {
this.sortOptions = sortOptions;
}

/**
* - valueList => if the browse index has two levels, the 1st level shows the list of entries like author names,
* subjects, types, etc. the second level is the actual list of items linked to a specific entry
* - flatBrowse if the browse index has one level: the full list of items
* - hierarchicalBrowse if the browse index should display the vocabulary tree. The 1st level shows the tree.
* The second level is the actual list of items linked to a specific entry
*/
public void setBrowseType(String browseType) {
this.browseType = browseType;
}

public String getBrowseType() {
return browseType;
}

public void setFacetType(String facetType) {
this.facetType = facetType;
}

public String getFacetType() {
return facetType;
}

public void setVocabulary(String vocabulary) {
this.vocabulary = vocabulary;
}


public String getVocabulary() {
return vocabulary;
}

@Override
public Class getController() {
return RestResourceController.class;
Expand Down

0 comments on commit 66eb8a5

Please sign in to comment.