Skip to content

Commit

Permalink
Show all values for the Subject facet in the search page. (#569)
Browse files Browse the repository at this point in the history
* Updated fast building app after config change.

* Added a new search facet to return facet values with and without splitter. With - search page side bar, without - homepage.

* Show values in search page and do not show them on Home page, added tests.

* Fixed tests which were failing because of Solr indexing updates
  • Loading branch information
milanmajchrak authored Mar 28, 2024
1 parent fe63fdb commit 10ecfb7
Show file tree
Hide file tree
Showing 10 changed files with 402 additions and 19 deletions.
28 changes: 28 additions & 0 deletions dspace-api/src/main/java/org/dspace/discovery/SolrServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ public class SolrServiceImpl implements SearchService, IndexingService {
// facet by indexing "each word to end of value' partial value
public static final String SOLR_FIELD_SUFFIX_FACET_PREFIXES = "_prefix";

// Suffix of the solr field used to index the facet/filter so that the facet search can search all word in a
// facet.
private static final String SOLR_FACET_FIELD_ALL_VALUES_SUFFIX = "_filter";

// List of all facets which will return facet value with splitter.
private ArrayList<String> allValuesFacetList = new ArrayList<>();

@Autowired
protected ContentServiceFactory contentServiceFactory;
@Autowired
Expand Down Expand Up @@ -1400,12 +1407,22 @@ protected String transformFacetField(DiscoverFacetField facetFieldConfig, String
}
} else if (facetFieldConfig.getType().equals(DiscoveryConfigurationParameters.TYPE_HIERARCHICAL)) {
if (removePostfix) {
// If the current field is configured to show all values instead of only the top level values
if (this.getFacetsToShowAllValues().contains(
StringUtils.substringBeforeLast(field, SOLR_FACET_FIELD_ALL_VALUES_SUFFIX))) {
return StringUtils.substringBeforeLast(field, SOLR_FACET_FIELD_ALL_VALUES_SUFFIX);
}
return StringUtils.substringBeforeLast(field, "_tax_");
} else {
// If the current field is configured to show all values instead of only the top level values
if (this.getFacetsToShowAllValues().contains(field)) {
return field + SOLR_FACET_FIELD_ALL_VALUES_SUFFIX;
}
//Only display top level filters !
return field + "_tax_0_filter";
}
} else if (facetFieldConfig.getType().equals(DiscoveryConfigurationParameters.TYPE_AUTHORITY)) {

if (removePostfix) {
return field.substring(0, field.lastIndexOf("_acid"));
} else {
Expand Down Expand Up @@ -1603,4 +1620,15 @@ public String calculateExtremeValue(Context context, String valueField,
return null;
}

/**
* Return or load the configuration property `discovery.solr.facets.allvalues` as a list.
*/
private ArrayList<String> getFacetsToShowAllValues() {
if (CollectionUtils.isEmpty(allValuesFacetList)) {
String[] allValuesFacetArray = configurationService.getArrayProperty("discovery.solr.facets.allvalues");
Collections.addAll(allValuesFacetList, allValuesFacetArray);
}
return allValuesFacetList;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<!-- Same as the "default" configuration, but does NOT filter out older versions of items -->
<!-- Used to display related items on single-item pages, because a relationship does not always point to the latest version of the related item -->
<entry key="default-relationships" value-ref="defaultRelationshipsConfiguration" />
<entry key="homepage" value-ref="homepageConfiguration" />
<!--<entry key="123456789/7621" value-ref="defaultConfiguration"/>-->
<!-- Used to show filters and results on MyDSpace -->
<!-- Do not change the id of special entries or else they won't work -->
Expand Down Expand Up @@ -144,6 +145,166 @@
</property>
</bean>

<bean id="homepageConfiguration" class="org.dspace.discovery.configuration.DiscoveryConfiguration" scope="prototype">
<!--Which sidebar facets are to be displayed-->
<property name="sidebarFacets">
<list>
<ref bean="searchFilterAuthor" />
<ref bean="searchFilterSubjectFirstValue" />
<!-- <ref bean="searchFilterIssued" />-->
<ref bean="searchFilterRights" />
<ref bean="searchFilterLanguage" />
<ref bean="searchFilterContentInOriginalBundle"/>
<ref bean="searchFilterEntityType"/>
<ref bean="searchFilterItemsCommunity"/>
</list>
</property>
<!-- Set TagCloud configuration per discovery configuration -->
<property name="tagCloudFacetConfiguration" ref="defaultTagCloudFacetConfiguration"/>
<!--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="searchFilterSubjectFirstValue" />
<!-- <ref bean="searchFilterIssued" />-->
<ref bean="searchFilterContentInOriginalBundle"/>
<ref bean="searchFilterFileNameInOriginalBundle" />
<ref bean="searchFilterFileDescriptionInOriginalBundle" />
<ref bean="searchFilterEntityType"/>
<ref bean="searchFilterIsAuthorOfPublicationRelation"/>
<ref bean="searchFilterIsProjectOfPublicationRelation"/>
<ref bean="searchFilterIsOrgUnitOfPublicationRelation"/>
<ref bean="searchFilterIsPublicationOfJournalIssueRelation"/>
<ref bean="searchFilterIsJournalOfPublicationRelation"/>
<ref bean="searchFilterRights" />
<ref bean="searchFilterLanguage" />
<ref bean="searchFilterItemsCommunity"/>
<ref bean="searchFilterType"/>
<ref bean="searchFilterPublisher"/>
</list>
</property>
<!--The sort filters for the discovery search-->
<property name="searchSortConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoverySortConfiguration">
<property name="sortFields">
<list>
<ref bean="sortScore" />
<ref bean="sortTitleAsc" />
<ref bean="sortTitleDesc" />
<ref bean="sortDateIssuedAsc" />
<ref bean="sortDateIssuedDesc" />
<ref bean="sortDateAccessioned"/>
</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, communities and collections-->
<value>(search.resourcetype:Item) OR search.resourcetype:Collection OR search.resourcetype:Community</value>
<value>-withdrawn:true AND -discoverable:false</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="20"/>
<!-- If enabled the collection home page will not display metadata but show a pageable list of recent submissions -->
<property name="useAsHomePage" value="false"/>
</bean>
</property>
<!--Default result per page -->
<property name="defaultRpp" value="10" />
<property name="hitHighlightingConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoveryHitHighlightingConfiguration">
<property name="metadataFields">
<list>
<bean class="org.dspace.discovery.configuration.DiscoveryHitHighlightFieldConfiguration">
<property name="field" value="dc.contributor.author"/>
<property name="snippets" value="5"/>
</bean>
<bean class="org.dspace.discovery.configuration.DiscoveryHitHighlightFieldConfiguration">
<property name="field" value="dspace.entity.type"/>
<property name="snippets" value="5"/>
</bean>
<bean class="org.dspace.discovery.configuration.DiscoveryHitHighlightFieldConfiguration">
<property name="field" value="person.identifier.jobtitle"/>
<property name="snippets" value="5"/>
</bean>
<bean class="org.dspace.discovery.configuration.DiscoveryHitHighlightFieldConfiguration">
<property name="field" value="project.identifier.name"/>
<property name="snippets" value="5"/>
</bean>
<bean class="org.dspace.discovery.configuration.DiscoveryHitHighlightFieldConfiguration">
<property name="field" value="dc.description.abstract"/>
<property name="maxSize" value="250"/>
<property name="snippets" value="2"/>
</bean>
<bean class="org.dspace.discovery.configuration.DiscoveryHitHighlightFieldConfiguration">
<property name="field" value="dc.title"/>
<property name="snippets" value="5"/>
</bean>
<bean class="org.dspace.discovery.configuration.DiscoveryHitHighlightFieldConfiguration">
<property name="field" value="organization.legalName"/>
<property name="snippets" value="5"/>
</bean>
<bean class="org.dspace.discovery.configuration.DiscoveryHitHighlightFieldConfiguration">
<property name="field" value="person.givenName"/>
<property name="snippets" value="5"/>
</bean>
<bean class="org.dspace.discovery.configuration.DiscoveryHitHighlightFieldConfiguration">
<property name="field" value="person.familyName"/>
<property name="snippets" value="5"/>
</bean>
<!-- By default, full text snippets are disabled, as snippets of embargoed/restricted bitstreams
may appear in search results when the Item is public. See DS-3498
<bean class="org.dspace.discovery.configuration.DiscoveryHitHighlightFieldConfiguration">
<property name="field" value="project.identifier.status"/>
<property name="snippets" value="5"/>
</bean>
<bean class="org.dspace.discovery.configuration.DiscoveryHitHighlightFieldConfiguration">
<property name="field" value="orgunit.identifier.name"/>
<property name="snippets" value="5"/>
</bean>
<bean class="org.dspace.discovery.configuration.DiscoveryHitHighlightFieldConfiguration">
<property name="field" value="orgunit.identifier.description"/>
<property name="maxSize" value="250"/>
<property name="snippets" value="5"/>
</bean>
-->
</list>
</property>
</bean>
</property>
<property name="moreLikeThisConfiguration">
<bean class="org.dspace.discovery.configuration.DiscoveryMoreLikeThisConfiguration">
<property name="similarityMetadataFields">
<list>
<value>dc.title</value>
<value>dc.contributor.author</value>
<value>dc.creator</value>
<value>dc.contributor.other</value>
<value>dc.subject</value>
</list>
</property>
<!--The minimum number of matching terms across the metadata fields above before an item is found as related -->
<property name="minTermFrequency" value="5"/>
<!--The maximum number of related items displayed-->
<property name="max" value="3"/>
<!--The minimum word length below which words will be ignored-->
<property name="minWordLength" value="5"/>
</bean>
</property>
<!-- When true a "did you mean" example will be displayed, value can be true or false -->
<property name="spellCheckEnabled" value="true"/>
</bean>

<bean id="discovery-parent-community-1" class="org.dspace.discovery.configuration.DiscoveryConfiguration" scope="prototype">
<property name="id" value="discovery-parent-community-1"/>
<!--Which sidebar facets are to be displayed-->
Expand Down Expand Up @@ -1114,5 +1275,18 @@
<property name="pageSize" value="10"/>
<property name="exposeMinAndMaxValue" value="true"/>
</bean>
<bean id="searchFilterSubjectFirstValue" class="org.dspace.discovery.configuration.HierarchicalSidebarFacetConfiguration">
<property name="indexFieldName" value="subjectFirstValue"/>
<property name="metadataFields">
<list>
<value>dc.subject.*</value>
</list>
</property>
<property name="facetLimit" value="5"/>
<property name="sortOrderSidebar" value="COUNT"/>
<property name="sortOrderFilterPage" value="COUNT"/>
<property name="splitter" value="::"/>
<property name="skipFirstNodeLevel" value="false"/>

</bean>
</beans>
Original file line number Diff line number Diff line change
Expand Up @@ -1001,7 +1001,8 @@ public void discoverSearchTest() throws Exception {
SearchFilterMatcher.clarinLicenseRightsFilter(),
SearchFilterMatcher.clarinItemsLanguageFilter(),
SearchFilterMatcher.clarinItemsCommunityFilter(),
SearchFilterMatcher.clarinItemsTypeFilter()
SearchFilterMatcher.clarinItemsTypeFilter(),
SearchFilterMatcher.clarinSubjectFirstValueFilter()
)))
//These sortOptions need to be present as it's the default in the configuration
.andExpect(jsonPath("$.sortOptions", contains(
Expand Down Expand Up @@ -5947,4 +5948,112 @@ public void discoverFacetsTestWithDsoTypeTest() throws Exception {
.andExpect(jsonPath("$._embedded.values").value(Matchers.hasSize(1)));

}

@Test
public void showFacetValuesWithSplitterInSearchPage() throws Exception {
context.turnOffAuthorisationSystem();

parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community").build();

Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
.withName("Collection 1").build();

ItemBuilder.createItem(context, col1)
.withTitle("Public item 1")
.withIssueDate("2017-10-17")
.withAuthor("Smith, Donald")
.withSubject("People")
.build();

ItemBuilder.createItem(context, col1)
.withTitle("Public item 2")
.withIssueDate("2020-02-13")
.withAuthor("Doe, Jane")
.withSubject("People::Jane")
.build();

ItemBuilder.createItem(context, col1)
.withTitle("Public item 2")
.withIssueDate("2020-02-13")
.withAuthor("Doe, Jane")
.withSubject("People::Jane")
.build();

context.restoreAuthSystemState();

getClient().perform(get("/api/discover/facets/subject"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.type", is("discover")))
.andExpect(jsonPath("$.name", is("subject")))
.andExpect(jsonPath("$.facetType", is("hierarchical")))
.andExpect(jsonPath("$.scope", is(emptyOrNullString())))
.andExpect(jsonPath("$._links.self.href",
containsString("api/discover/facets/subject")))
.andExpect(jsonPath("$._embedded.values[0].label", is("People")))
.andExpect(jsonPath("$._embedded.values[0].count", is(3)))
.andExpect(jsonPath("$._embedded.values[1].label", is("People::Jane")))
.andExpect(jsonPath("$._embedded.values[1].count", is(2)))
.andExpect(jsonPath("$", JsonPathMatchers.hasNoJsonPath("_embedded.values[2].label")))
.andExpect(jsonPath("$", JsonPathMatchers.hasNoJsonPath("_embedded.values[2].count")))
.andExpect(jsonPath("$._embedded.values").value(Matchers.hasSize(2)));
}

@Test
public void doNotShowFacetValuesWithSplitterInHomePage() throws Exception {
context.turnOffAuthorisationSystem();

parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community").build();

Collection col1 = CollectionBuilder.createCollection(context, parentCommunity)
.withName("Collection 1").build();

ItemBuilder.createItem(context, col1)
.withTitle("Public item 1")
.withIssueDate("2017-10-17")
.withAuthor("Smith, Donald")
.withSubject("People")
.build();

ItemBuilder.createItem(context, col1)
.withTitle("Public item 2")
.withIssueDate("2020-02-13")
.withAuthor("Doe, Jane")
.withSubject("People::Jane")
.build();

ItemBuilder.createItem(context, col1)
.withTitle("Public item 2")
.withIssueDate("2020-02-13")
.withAuthor("Doe, Jane")
.withSubject("People::Jane")
.build();

ItemBuilder.createItem(context, col1)
.withTitle("Public item 2")
.withIssueDate("2020-02-13")
.withAuthor("Doe, Jane")
.withSubject("Another subject")
.build();

context.restoreAuthSystemState();

getClient().perform(get("/api/discover/facets/subjectFirstValue")
.param("configuration", "homepage"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.type", is("discover")))
.andExpect(jsonPath("$.name", is("subjectFirstValue")))
.andExpect(jsonPath("$.facetType", is("hierarchical")))
.andExpect(jsonPath("$.scope", is(emptyOrNullString())))
.andExpect(jsonPath("$._links.self.href",
containsString("api/discover/facets/subjectFirstValue")))
.andExpect(jsonPath("$._embedded.values[0].label", is("People")))
.andExpect(jsonPath("$._embedded.values[0].count", is(3)))
.andExpect(jsonPath("$._embedded.values[1].label", is("Another subject")))
.andExpect(jsonPath("$._embedded.values[1].count", is(1)))
.andExpect(jsonPath("$", JsonPathMatchers.hasNoJsonPath("_embedded.values[2].label")))
.andExpect(jsonPath("$", JsonPathMatchers.hasNoJsonPath("_embedded.values[2].count")))
.andExpect(jsonPath("$._embedded.values").value(Matchers.hasSize(2)));
}
}
Loading

0 comments on commit 10ecfb7

Please sign in to comment.