diff --git a/doc/release-notes/12617-cvv-performance.md b/doc/release-notes/12617-cvv-performance.md new file mode 100644 index 00000000000..a1ab7f42517 --- /dev/null +++ b/doc/release-notes/12617-cvv-performance.md @@ -0,0 +1 @@ +The loading time for the dataset metadata and template editing pages has been significantly improved relative to the v 6.4+ performance when support for ~8K ISO 63903 langauge codes were added to the citation metadatablock. \ No newline at end of file diff --git a/src/main/java/edu/harvard/iq/dataverse/ControlledVocabularyAutocompleteBean.java b/src/main/java/edu/harvard/iq/dataverse/ControlledVocabularyAutocompleteBean.java new file mode 100644 index 00000000000..e41778c93e7 --- /dev/null +++ b/src/main/java/edu/harvard/iq/dataverse/ControlledVocabularyAutocompleteBean.java @@ -0,0 +1,62 @@ +package edu.harvard.iq.dataverse; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import jakarta.faces.component.UIComponent; +import jakarta.faces.context.FacesContext; +import jakarta.faces.view.ViewScoped; +import jakarta.inject.Named; + +@ViewScoped +@Named("ControlledVocabularyAutocompleteBean") +public class ControlledVocabularyAutocompleteBean implements Serializable { + + private final Set slowModeFieldTypeIds = new HashSet<>(); + + public boolean isSlowMode(Long fieldTypeId) { + return fieldTypeId != null && slowModeFieldTypeIds.contains(fieldTypeId); + } + + public void switchToSlowMode(Long fieldTypeId) { + if (fieldTypeId != null) { + slowModeFieldTypeIds.add(fieldTypeId); + } + } + + public List complete(String query) { + UIComponent component = UIComponent.getCurrentComponent(FacesContext.getCurrentInstance()); + DatasetField dsf = (DatasetField) component.getAttributes().get("dsf"); + DatasetFieldType dsft = (DatasetFieldType) component.getAttributes().get("dsft"); + + if (dsf != null) { + dsft = dsf.getDatasetFieldType(); + } + + if (dsft == null || dsft.getControlledVocabularyValues() == null || query == null) { + return Collections.emptyList(); + } + + List results = new ArrayList<>(); + String queryLower = query.toLowerCase(); + String mdLangCode = null; + + if (dsf != null && dsf.getDatasetVersion() != null && dsf.getDatasetVersion().getDataset() != null) { + mdLangCode = dsf.getDatasetVersion().getDataset().getMetadataLanguage(); + } + + for (ControlledVocabularyValue cvv : dsft.getControlledVocabularyValues()) { + String localeStrValue = cvv.getLocaleStrValue(mdLangCode); + if (localeStrValue != null && localeStrValue.toLowerCase().contains(queryLower)) { + results.add(cvv); + } + if (results.size() >= 101) { + break; + } + } + return results; + } +} diff --git a/src/main/java/edu/harvard/iq/dataverse/DatasetFieldType.java b/src/main/java/edu/harvard/iq/dataverse/DatasetFieldType.java index 0191b178795..c024bfdc73c 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DatasetFieldType.java +++ b/src/main/java/edu/harvard/iq/dataverse/DatasetFieldType.java @@ -4,6 +4,7 @@ import edu.harvard.iq.dataverse.util.BundleUtil; import edu.harvard.iq.dataverse.util.json.JsonLDTerm; +import java.util.ArrayList; import java.util.Collection; import java.io.Serializable; @@ -97,7 +98,7 @@ public void setId(Long id) { private String searchValue; @Transient - private List listValues; + private List listValues = new ArrayList<>(); @Transient private Map controlledVocabularyValuesByStrValue; @@ -424,11 +425,11 @@ public void setSearchValue(String searchValue) { this.searchValue = searchValue; } - public List getListValues() { + public List getListValues() { return listValues; } - public void setListValues(List listValues) { + public void setListValues(List listValues) { this.listValues = listValues; } /** diff --git a/src/main/java/edu/harvard/iq/dataverse/TemplatePage.java b/src/main/java/edu/harvard/iq/dataverse/TemplatePage.java index 279944beaa7..25e1f9198d7 100644 --- a/src/main/java/edu/harvard/iq/dataverse/TemplatePage.java +++ b/src/main/java/edu/harvard/iq/dataverse/TemplatePage.java @@ -12,12 +12,16 @@ import edu.harvard.iq.dataverse.util.JsfHelper; import static edu.harvard.iq.dataverse.util.JsfHelper.JH; import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.List; import java.util.logging.Logger; import jakarta.ejb.EJB; import jakarta.ejb.EJBException; import jakarta.faces.application.FacesMessage; +import jakarta.faces.component.UIComponent; +import jakarta.faces.context.FacesContext; import jakarta.faces.view.ViewScoped; import jakarta.inject.Inject; import jakarta.inject.Named; @@ -263,5 +267,4 @@ public String getInstructionsLabelFor(String fieldType) { String fieldInstructions = template.getInstructionsMap().get(fieldType); return (fieldInstructions!=null && !fieldInstructions.isBlank()) ? fieldInstructions : BundleUtil.getStringFromBundle("template.instructions.empty.label"); } - } diff --git a/src/main/java/edu/harvard/iq/dataverse/search/AdvancedSearchPage.java b/src/main/java/edu/harvard/iq/dataverse/search/AdvancedSearchPage.java index bc92959a5ac..59f653118d7 100644 --- a/src/main/java/edu/harvard/iq/dataverse/search/AdvancedSearchPage.java +++ b/src/main/java/edu/harvard/iq/dataverse/search/AdvancedSearchPage.java @@ -15,11 +15,16 @@ import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.logging.Logger; import jakarta.ejb.EJB; +import jakarta.faces.component.UIComponent; +import jakarta.faces.context.FacesContext; import jakarta.faces.view.ViewScoped; import jakarta.inject.Inject; import jakarta.inject.Named; @@ -51,7 +56,7 @@ public class AdvancedSearchPage implements java.io.Serializable { private String dvFieldAlias; private String dvFieldDescription; private String dvFieldAffiliation; - private List dvFieldSubject; + private List dvFieldSubject = new ArrayList<>(); private String dsPublicationDate; private String dsPersistentId; private String fileFieldName; @@ -111,8 +116,8 @@ private String constructDatasetQuery() { queryStrings.add(constructQuery(dsfType.getSolrField().getNameSearchable(), dsfType.getSearchValue(), getCVocConf().containsKey(dsfType.getId()))); } else if (dsfType.getListValues() != null && !dsfType.getListValues().isEmpty()) { List listQueryStrings = new ArrayList<>(); - for (String value : dsfType.getListValues()) { - listQueryStrings.add(dsfType.getSolrField().getNameSearchable() + ":" + "\"" + value + "\""); + for (ControlledVocabularyValue cvv : dsfType.getListValues()) { + listQueryStrings.add(dsfType.getSolrField().getNameSearchable() + ":" + "\"" + cvv.getStrValue() + "\""); } queryStrings.add(constructQuery(listQueryStrings, false)); } @@ -146,8 +151,8 @@ private String constructDataverseQuery() { if (dvFieldSubject != null && !dvFieldSubject.isEmpty()) { List listQueryStrings = new ArrayList<>(); - for (String value : dvFieldSubject) { - listQueryStrings.add(SearchFields.DATAVERSE_SUBJECT + ":" + "\"" + value + "\""); + for (ControlledVocabularyValue cvv : dvFieldSubject) { + listQueryStrings.add(SearchFields.DATAVERSE_SUBJECT + ":" + "\"" + cvv.getStrValue() + "\""); } queryStrings.add(constructQuery(listQueryStrings, false)); } @@ -252,11 +257,11 @@ public void setDvFieldAffiliation(String dvFieldAffiliation) { this.dvFieldAffiliation = dvFieldAffiliation; } - public List getDvFieldSubject() { + public List getDvFieldSubject() { return dvFieldSubject; } - public void setDvFieldSubject(List dvFieldSubject) { + public void setDvFieldSubject(List dvFieldSubject) { this.dvFieldSubject = dvFieldSubject; } @@ -265,6 +270,10 @@ public Collection getDvFieldSubjectValues() { return subjectType.getControlledVocabularyValues(); } + public DatasetFieldType getSubjectDatasetFieldType() { + return datasetFieldService.findByName(DatasetFieldConstant.subject); + } + public String getDsPublicationDate() { return dsPublicationDate; } diff --git a/src/main/java/propertyFiles/Bundle.properties b/src/main/java/propertyFiles/Bundle.properties index 0825816048d..0da3380fb47 100644 --- a/src/main/java/propertyFiles/Bundle.properties +++ b/src/main/java/propertyFiles/Bundle.properties @@ -3298,3 +3298,9 @@ abstractPaginatedCommand.errors.negativePaginationParam=The {0} parameter cannot #UpdateDatasetLicenseCommand.java updateDatasetLicenseCommand.errors.licenseNotActive=License {0} cannot be set because it is not active. updateDatasetLicenseCommand.errors.customTermsOfUseNotProvided=Terms of use text should be provided in custom terms. + +# Controlled Vocabulary Autocomplete +controlledvocabulary.autocomplete.watermark=Start typing to search... +controlledvocabulary.autocomplete.moreText=Use a longer search term to see more values. +controlledvocabulary.showAll=Load all {0} choices +controlledvocabulary.showAll.tooltip=This is slow to load but allows you to scroll through the entire list of choices diff --git a/src/main/webapp/metadataFragment.xhtml b/src/main/webapp/metadataFragment.xhtml index 37246ddf40f..bd061b857f8 100755 --- a/src/main/webapp/metadataFragment.xhtml +++ b/src/main/webapp/metadataFragment.xhtml @@ -7,6 +7,7 @@ xmlns:p="http://primefaces.org/ui" xmlns:o="http://omnifaces.org/ui" xmlns:jsf="http://xmlns.jcp.org/jsf"> + @@ -297,25 +298,50 @@ +
- - - - - - - - + + + + + + + + + + + + + + + + + + + +
#{dsf.validationMessage}
@@ -380,30 +406,50 @@
- - - - - - - - + + + + + + + + + + + rendered="#{subdsf.datasetFieldType.allowMultiples and (subVocabSize lt 501 or ControlledVocabularyAutocompleteBean.isSlowMode(subdsf.datasetFieldType.id))}" label="#{bundle.select}" multiple="true" + filter="#{(subVocabSize ge 10 and subVocabSize le 500) ? 'true':'false'}" filterMatchMode="contains" + showHeader="#{(subVocabSize ge 10) ? 'true':'false'}" + panelStyleClass="#{subVocabSize gt 500 ? 'no-selectall' : ''}"> + + + + + + + + +
#{subdsf.validationMessage} diff --git a/src/main/webapp/resources/css/structure.css b/src/main/webapp/resources/css/structure.css index 7fe66f7dcff..6538930f793 100644 --- a/src/main/webapp/resources/css/structure.css +++ b/src/main/webapp/resources/css/structure.css @@ -109,6 +109,22 @@ td.col-select-width, th.col-select-width {width:36px;} .clear-both {clear:both;} table.selectManyCheckboxBlock input {margin-right:4px;} + +/* Controlled Vocabulary Autocomplete / Slow Mode */ +.vocab-slow-link { + font-size: 0.85em; + color: #666; + display: block; + margin-top: 4px; + text-align: right; +} +.vocab-slow-link:hover { + color: #333; + text-decoration: underline; +} +body .no-selectall .ui-selectcheckboxmenu-header .ui-chkbox { + display: none; +} table.selectManyCheckboxBlock label {font-weight:normal;} .normal-text {font-weight:normal;} .normal-text input {margin-right:4px;} diff --git a/src/main/webapp/search/advanced.xhtml b/src/main/webapp/search/advanced.xhtml index c921fe930d5..5ac2b7cc91b 100644 --- a/src/main/webapp/search/advanced.xhtml +++ b/src/main/webapp/search/advanced.xhtml @@ -90,12 +90,41 @@ data-toggle="tooltip" data-placement="auto right" data-original-title="#{bundle['advanced.search.dataverses.subject.tip']}">
-
- - - -
+ + + + + + + + + + + + + +
@@ -133,12 +162,41 @@ -
- + + + - -
+ itemLabel="#{cvs.localeStrValue}" itemValue="#{cvs}"/> + + + + + + + + + +