diff --git a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/BaseItTest.java b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/BaseItTest.java index 02dae254f..1a319ea47 100644 --- a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/BaseItTest.java +++ b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/BaseItTest.java @@ -13,6 +13,11 @@ */ package org.gbif.registry.ws.it; +import static org.gbif.registry.ws.it.fixtures.TestConstants.IT_APP_KEY2; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import org.gbif.api.vocabulary.UserRole; import org.gbif.registry.database.BaseDBTest; import org.gbif.registry.search.test.EsManageServer; @@ -21,10 +26,6 @@ import org.gbif.ws.client.filter.SimplePrincipalProvider; import org.gbif.ws.json.JacksonJsonObjectMapperProvider; import org.gbif.ws.security.KeyStore; - -import java.util.Arrays; -import java.util.Collections; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; @@ -38,8 +39,6 @@ import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; -import static org.gbif.registry.ws.it.fixtures.TestConstants.IT_APP_KEY2; - /** Base class for IT tests that initializes data sources and basic security settings. */ @ExtendWith(SpringExtension.class) @SpringBootTest( @@ -125,6 +124,10 @@ protected T getService(ServiceType param, T resource, T client) { } } + protected List asList(T value) { + return Collections.singletonList(value); + } + @DynamicPropertySource static void properties(DynamicPropertyRegistry registry) { registry.add("registry.datasource.url", PG_CONTAINER::getJdbcUrl); diff --git a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/resource/CollectionResourceIT.java b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/resource/CollectionResourceIT.java index 065c410e1..68129dfe8 100644 --- a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/resource/CollectionResourceIT.java +++ b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/resource/CollectionResourceIT.java @@ -106,11 +106,11 @@ public void listTest() { .thenReturn(new PagingResponse<>(new PagingRequest(), Long.valueOf(views.size()), views)); CollectionSearchRequest req = CollectionSearchRequest.builder().build(); - req.setCity("city"); - req.setInstitution(UUID.randomUUID()); + req.setCity(Collections.singletonList("city")); + req.setInstitution(Collections.singletonList(UUID.randomUUID())); req.setCountry(Collections.singletonList(Country.DENMARK)); req.setGbifRegion(Collections.singletonList(GbifRegion.EUROPE)); - req.setPersonalCollection(true); + req.setPersonalCollection(Collections.singletonList(true)); req.setAccessionStatus(Collections.singletonList("Institutional")); req.setPreservationTypes(Arrays.asList("SampleCryopreserved", "SampleDried")); PagingResponse result = getClient().list(req); @@ -183,7 +183,7 @@ public void listDeletedTest() { .thenReturn(new PagingResponse<>(new PagingRequest(), Long.valueOf(views.size()), views)); CollectionSearchRequest request = CollectionSearchRequest.builder().build(); - request.setReplacedBy(UUID.randomUUID()); + request.setReplacedBy(Collections.singletonList(UUID.randomUUID())); PagingResponse result = getClient().listDeleted(request); assertEquals(views.size(), result.getResults().size()); } diff --git a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/resource/CollectionsSearchResourceTest.java b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/resource/CollectionsSearchResourceTest.java index 056ef54a5..e22b8b1a7 100644 --- a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/resource/CollectionsSearchResourceTest.java +++ b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/resource/CollectionsSearchResourceTest.java @@ -19,15 +19,20 @@ import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.UUID; import org.gbif.api.model.collections.request.CollectionDescriptorsSearchRequest; import org.gbif.api.model.collections.request.InstitutionSearchRequest; +import org.gbif.api.model.collections.search.CollectionFacet; import org.gbif.api.model.collections.search.CollectionSearchResponse; import org.gbif.api.model.collections.search.CollectionsFullSearchResponse; +import org.gbif.api.model.collections.search.FacetedSearchResponse; import org.gbif.api.model.collections.search.Highlight; import org.gbif.api.model.collections.search.InstitutionSearchResponse; -import org.gbif.api.model.common.paging.PagingResponse; +import org.gbif.api.model.common.paging.PagingRequest; import org.gbif.api.vocabulary.Country; +import org.gbif.api.vocabulary.collections.CollectionFacetParameter; +import org.gbif.api.vocabulary.collections.InstitutionFacetParameter; import org.gbif.registry.service.collections.CollectionsSearchService; import org.gbif.registry.ws.client.collections.CollectionsSearchClient; import org.gbif.registry.ws.it.fixtures.RequestTestFixture; @@ -69,11 +74,13 @@ public void searchTest() { highlight.setSnippet("snippet"); response.setHighlights(Collections.singleton(highlight)); - when(collectionsSearchService.search(q, hl, null, null, Country.SPAIN, limit)) + when(collectionsSearchService.search( + q, hl, null, null, Collections.singletonList(Country.SPAIN), limit)) .thenReturn(Collections.singletonList(response)); List responseReturned = - collectionsSearchClient.searchCrossEntities(q, hl, null, null, Country.SPAIN, limit); + collectionsSearchClient.searchCrossEntities( + q, hl, null, null, Collections.singletonList(Country.SPAIN), limit); assertEquals(1, responseReturned.size()); assertEquals(response, responseReturned.get(0)); } @@ -89,9 +96,9 @@ public void searchInstitutionsTest() { response.setHighlights(Collections.singleton(highlight)); when(collectionsSearchService.searchInstitutions(any())) - .thenReturn(new PagingResponse<>(0, 20, 1L, Collections.singletonList(response))); + .thenReturn(new FacetedSearchResponse<>(0, 20, 1L, Collections.singletonList(response))); - PagingResponse responseReturned = + FacetedSearchResponse responseReturned = collectionsSearchClient.searchInstitutions(InstitutionSearchRequest.builder().build()); assertEquals(1, responseReturned.getResults().size()); assertEquals(response, responseReturned.getResults().get(0)); @@ -108,12 +115,82 @@ public void searchCollectionsTest() { response.setHighlights(Collections.singleton(highlight)); when(collectionsSearchService.searchCollections(any())) - .thenReturn(new PagingResponse<>(0, 20, 1L, Collections.singletonList(response))); + .thenReturn(new FacetedSearchResponse<>(0, 20, 1L, Collections.singletonList(response))); - PagingResponse responseReturned = + FacetedSearchResponse responseReturned = collectionsSearchClient.searchCollections( CollectionDescriptorsSearchRequest.builder().build()); assertEquals(1, responseReturned.getResults().size()); assertEquals(response, responseReturned.getResults().get(0)); } + + @Test + public void facetSearchInstitutionsTest() { + InstitutionSearchRequest searchRequest = + InstitutionSearchRequest.builder() + .facets(Set.of(InstitutionFacetParameter.COUNTRY)) + .offset(0L) + .limit(20) + .build(); + + InstitutionSearchResponse response = new InstitutionSearchResponse(); + response.setCode("c1"); + response.setKey(UUID.randomUUID()); + + CollectionFacet facet = new CollectionFacet<>(); + facet.setCardinality(1); + facet.setField(InstitutionFacetParameter.COUNTRY); + facet.setCounts(List.of(new CollectionFacet.Count("ES", 2L))); + + FacetedSearchResponse searchResponse = + new FacetedSearchResponse<>( + new PagingRequest(), + 1L, + Collections.singletonList(response), + Collections.singletonList(facet)); + + when(collectionsSearchService.searchInstitutions(searchRequest)).thenReturn(searchResponse); + + FacetedSearchResponse responseReturned = + collectionsSearchClient.searchInstitutions(searchRequest); + assertEquals(1, responseReturned.getResults().size()); + assertEquals(response, responseReturned.getResults().get(0)); + assertEquals(1, responseReturned.getFacets().size()); + assertEquals(facet, responseReturned.getFacets().get(0)); + } + + @Test + public void facetSearchCollectionsTest() { + CollectionDescriptorsSearchRequest searchRequest = + CollectionDescriptorsSearchRequest.builder() + .facets(Set.of(CollectionFacetParameter.COUNTRY)) + .offset(0L) + .limit(20) + .build(); + + CollectionSearchResponse response = new CollectionSearchResponse(); + response.setCode("c1"); + response.setKey(UUID.randomUUID()); + + CollectionFacet facet = new CollectionFacet<>(); + facet.setCardinality(1); + facet.setField(CollectionFacetParameter.COUNTRY); + facet.setCounts(List.of(new CollectionFacet.Count("ES", 2L))); + + FacetedSearchResponse searchResponse = + new FacetedSearchResponse<>( + new PagingRequest(), + 1L, + Collections.singletonList(response), + Collections.singletonList(facet)); + + when(collectionsSearchService.searchCollections(searchRequest)).thenReturn(searchResponse); + + FacetedSearchResponse responseReturned = + collectionsSearchClient.searchCollections(searchRequest); + assertEquals(1, responseReturned.getResults().size()); + assertEquals(response, responseReturned.getResults().get(0)); + assertEquals(1, responseReturned.getFacets().size()); + assertEquals(facet, responseReturned.getFacets().get(0)); + } } diff --git a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/resource/InstitutionResourceIT.java b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/resource/InstitutionResourceIT.java index a0fd60794..6135caa83 100644 --- a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/resource/InstitutionResourceIT.java +++ b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/resource/InstitutionResourceIT.java @@ -95,11 +95,11 @@ public void listTest() { new PagingRequest(), Long.valueOf(institutions.size()), institutions)); InstitutionSearchRequest req = InstitutionSearchRequest.builder().build(); - req.setCity("city"); - req.setContact(UUID.randomUUID()); + req.setCity(Collections.singletonList("city")); + req.setContact(Collections.singletonList(UUID.randomUUID())); req.setCountry(Collections.singletonList(Country.DENMARK)); req.setGbifRegion(Collections.singletonList(GbifRegion.EUROPE)); - req.setActive(true); + req.setActive(Collections.singletonList(true)); req.setInstitutionalGovernance(Collections.singletonList("Academic")); req.setDisciplines(Arrays.asList("Archaeology", "Anthropology")); @@ -196,7 +196,7 @@ public void listDeletedTest() { new PagingRequest(), Long.valueOf(institutions.size()), institutions)); InstitutionSearchRequest request = InstitutionSearchRequest.builder().build(); - request.setReplacedBy(UUID.randomUUID()); + request.setReplacedBy(Collections.singletonList(UUID.randomUUID())); PagingResponse result = getClient().listDeleted(request); assertEquals(institutions.size(), result.getResults().size()); } diff --git a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/CollectionServiceIT.java b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/CollectionServiceIT.java index cfcb87cb2..485ed3b2e 100644 --- a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/CollectionServiceIT.java +++ b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/CollectionServiceIT.java @@ -38,6 +38,7 @@ import org.gbif.api.service.registry.NodeService; import org.gbif.api.service.registry.OrganizationService; import org.gbif.api.vocabulary.*; +import org.gbif.api.vocabulary.collections.CollectionsSortField; import org.gbif.api.vocabulary.collections.IdType; import org.gbif.api.vocabulary.collections.MasterSourceType; import org.gbif.api.vocabulary.collections.Source; @@ -139,7 +140,10 @@ public void listTest() { response = collectionService.list( - CollectionSearchRequest.builder().source(Source.IH_IRN).sourceId("test-123").build()); + CollectionSearchRequest.builder() + .source(Collections.singletonList(Source.IH_IRN)) + .sourceId(Collections.singletonList("test-123")) + .build()); assertEquals(1, response.getResults().size()); // empty queries are ignored and return all elements @@ -244,7 +248,7 @@ public void listTest() { collectionService .list( CollectionSearchRequest.builder() - .code("c1") + .code(Collections.singletonList("c1")) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()) @@ -255,7 +259,7 @@ public void listTest() { collectionService .list( CollectionSearchRequest.builder() - .name("n2") + .name(Collections.singletonList("n2")) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()) @@ -266,8 +270,8 @@ public void listTest() { collectionService .list( CollectionSearchRequest.builder() - .code("c1") - .name("n1") + .code(Collections.singletonList("c1")) + .name(Collections.singletonList("n1")) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()) @@ -278,8 +282,8 @@ public void listTest() { collectionService .list( CollectionSearchRequest.builder() - .code("c2") - .name("n1") + .code(Collections.singletonList("c2")) + .name(Collections.singletonList("n1")) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()) @@ -290,7 +294,7 @@ public void listTest() { collectionService .list( CollectionSearchRequest.builder() - .active(true) + .active(Collections.singletonList(true)) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()) @@ -356,7 +360,7 @@ public void listTest() { collectionService .list( CollectionSearchRequest.builder() - .personalCollection(true) + .personalCollection(Collections.singletonList(true)) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()) @@ -369,7 +373,7 @@ public void listTest() { collectionService .list( CollectionSearchRequest.builder() - .alternativeCode("alt") + .alternativeCode(Collections.singletonList("alt")) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()) @@ -380,7 +384,7 @@ public void listTest() { collectionService .list( CollectionSearchRequest.builder() - .alternativeCode("foo") + .alternativeCode(Collections.singletonList("foo")) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()) @@ -450,7 +454,7 @@ public void listTest() { collectionService .list( CollectionSearchRequest.builder() - .city("city2") + .city(Collections.singletonList("city2")) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()) @@ -462,7 +466,7 @@ public void listTest() { collectionService .list( CollectionSearchRequest.builder() - .city("foo") + .city(Collections.singletonList("foo")) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()) @@ -491,7 +495,7 @@ public void listTest() { collectionService .list( CollectionSearchRequest.builder() - .displayOnNHCPortal(true) + .displayOnNHCPortal(Collections.singletonList(true)) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()) @@ -618,7 +622,7 @@ public void listByInstitutionTest() { PagingResponse response = collectionService.list( CollectionSearchRequest.builder() - .institution(institutionKey1) + .institution(Collections.singletonList(institutionKey1)) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()); @@ -627,7 +631,7 @@ public void listByInstitutionTest() { response = collectionService.list( CollectionSearchRequest.builder() - .institution(institutionKey2) + .institution(Collections.singletonList(institutionKey2)) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()); @@ -636,7 +640,7 @@ public void listByInstitutionTest() { response = collectionService.list( CollectionSearchRequest.builder() - .institution(UUID.randomUUID()) + .institution(Collections.singletonList(UUID.randomUUID())) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()); @@ -877,7 +881,7 @@ public void listMultipleParamsTest() { collectionService.list( CollectionSearchRequest.builder() .q("code1") - .institution(institutionKey1) + .institution(Collections.singletonList(institutionKey1)) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()); @@ -887,7 +891,7 @@ public void listMultipleParamsTest() { collectionService.list( CollectionSearchRequest.builder() .q("foo") - .institution(institutionKey1) + .institution(Collections.singletonList(institutionKey1)) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()); @@ -897,7 +901,7 @@ public void listMultipleParamsTest() { collectionService.list( CollectionSearchRequest.builder() .q("code2") - .institution(institutionKey2) + .institution(Collections.singletonList(institutionKey2)) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()); @@ -907,7 +911,7 @@ public void listMultipleParamsTest() { collectionService.list( CollectionSearchRequest.builder() .q("code2") - .institution(institutionKey1) + .institution(Collections.singletonList(institutionKey1)) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()); @@ -928,7 +932,7 @@ public void listMultipleParamsTest() { collectionService.list( CollectionSearchRequest.builder() .q("Name1") - .institution(institutionKey1) + .institution(Collections.singletonList(institutionKey1)) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()); @@ -938,7 +942,7 @@ public void listMultipleParamsTest() { collectionService.list( CollectionSearchRequest.builder() .q("abcde") - .institution(institutionKey1) + .institution(Collections.singletonList(institutionKey1)) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()); @@ -948,7 +952,7 @@ public void listMultipleParamsTest() { collectionService.list( CollectionSearchRequest.builder() .q("aa1@aa.com") - .institution(institutionKey1) + .institution(Collections.singletonList(institutionKey1)) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()); @@ -958,7 +962,7 @@ public void listMultipleParamsTest() { collectionService.list( CollectionSearchRequest.builder() .q("aves") - .institution(institutionKey1) + .institution(Collections.singletonList(institutionKey1)) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()); @@ -1020,7 +1024,7 @@ public void listDeletedTest() { UUID key4 = collectionService.create(collection4); CollectionSearchRequest searchRequest = CollectionSearchRequest.builder().build(); - searchRequest.setReplacedBy(key4); + searchRequest.setReplacedBy(Collections.singletonList(key4)); assertEquals(0, collectionService.listDeleted(searchRequest).getResults().size()); collectionService.replace(key3, key4); assertEquals(1, collectionService.listDeleted(searchRequest).getResults().size()); diff --git a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/CollectionsSearchIT.java b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/CollectionsSearchIT.java index 62cd06b7f..69e8980f0 100644 --- a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/CollectionsSearchIT.java +++ b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/CollectionsSearchIT.java @@ -22,6 +22,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Set; import lombok.SneakyThrows; import org.gbif.api.model.collections.Address; import org.gbif.api.model.collections.AlternativeCode; @@ -29,20 +31,25 @@ import org.gbif.api.model.collections.Institution; import org.gbif.api.model.collections.request.CollectionDescriptorsSearchRequest; import org.gbif.api.model.collections.request.InstitutionSearchRequest; +import org.gbif.api.model.collections.search.CollectionFacet; import org.gbif.api.model.collections.search.CollectionSearchResponse; import org.gbif.api.model.collections.search.CollectionsFullSearchResponse; +import org.gbif.api.model.collections.search.FacetedSearchResponse; import org.gbif.api.model.collections.search.InstitutionSearchResponse; import org.gbif.api.model.common.export.ExportFormat; +import org.gbif.api.model.common.paging.PagingRequest; import org.gbif.api.model.common.paging.PagingResponse; import org.gbif.api.model.registry.Identifier; import org.gbif.api.service.collections.CollectionService; import org.gbif.api.service.collections.DescriptorsService; import org.gbif.api.service.collections.InstitutionService; -import org.gbif.api.vocabulary.CollectionsSortField; import org.gbif.api.vocabulary.Country; import org.gbif.api.vocabulary.IdentifierType; import org.gbif.api.vocabulary.SortOrder; import org.gbif.api.vocabulary.TypeStatus; +import org.gbif.api.vocabulary.collections.CollectionFacetParameter; +import org.gbif.api.vocabulary.collections.CollectionsSortField; +import org.gbif.api.vocabulary.collections.InstitutionFacetParameter; import org.gbif.registry.database.TestCaseDatabaseInitializer; import org.gbif.registry.service.collections.CollectionsSearchService; import org.gbif.registry.test.mocks.NubResourceClientMock; @@ -70,6 +77,7 @@ public class CollectionsSearchIT extends BaseServiceIT { private final Institution i2 = new Institution(); private final Collection c1 = new Collection(); private final Collection c2 = new Collection(); + private final Collection c3 = new Collection(); @Autowired public CollectionsSearchIT( @@ -90,6 +98,8 @@ public CollectionsSearchIT( public void loadData() { i1.setCode("I1"); i1.setName("Institution 1"); + i1.setTypes(Arrays.asList("t1", "t2")); + i1.setDisciplines(Arrays.asList("d1", "d2")); Address addressI1 = new Address(); addressI1.setCountry(Country.AFGHANISTAN); addressI1.setAddress("foo street"); @@ -104,8 +114,13 @@ public void loadData() { i2.setCode("I2"); i2.setName("Institution 2"); i2.setDescription("different than i1"); + i2.setTypes(Collections.singletonList("t2")); + i2.setDisciplines(Collections.singletonList("d2")); i2.setAlternativeCodes(Collections.singletonList(new AlternativeCode("II2", "test"))); i2.getIdentifiers().add(new Identifier(IdentifierType.LSID, "lsid-inst")); + Address addressI2 = new Address(); + addressI2.setCountry(Country.UNITED_STATES); + i2.setMailingAddress(addressI2); institutionService.create(i2); c1.setCode("C1"); @@ -117,6 +132,7 @@ public void loadData() { addressC1.setProvince("Asturias"); addressC1.setAddress("fake street"); c1.setAddress(addressC1); + c1.setPreservationTypes(Collections.singletonList("pType1")); c1.setNumberSpecimens(4000); c1.setDisplayOnNHCPortal(false); collectionService.create(c1); @@ -143,6 +159,7 @@ public void loadData() { c2.setInstitutionKey(i2.getKey()); c2.setAlternativeCodes(Collections.singletonList(new AlternativeCode("CC2", "test"))); c2.getIdentifiers().add(new Identifier(IdentifierType.LSID, "lsid-coll")); + c2.setPreservationTypes(Collections.singletonList("pType2")); collectionService.create(c2); descriptorsService.createDescriptorGroup( @@ -152,6 +169,16 @@ public void loadData() { "My descriptor set 3", "unusual description", c2.getKey()); + + c3.setCode("C3"); + c3.setName("Third"); + c3.setInstitutionKey(i2.getKey()); + c3.setPreservationTypes(Collections.singletonList("pType1")); + + Address addressC3 = new Address(); + addressC3.setCountry(Country.PORTUGAL); + c3.setMailingAddress(addressC3); + collectionService.create(c3); } @Test @@ -241,10 +268,10 @@ public void noMatchesTest() { @Test public void displayOnNHCPortalTest() { List responses = - searchService.search(null, false, null, true, null, 10); - assertEquals(3, responses.size()); + searchService.search(null, false, null, Collections.singletonList(true), null, 10); + assertEquals(4, responses.size()); - responses = searchService.search(null, false, null, false, null, 10); + responses = searchService.search(null, false, null, Collections.singletonList(false), null, 10); assertEquals(2, responses.size()); } @@ -256,35 +283,43 @@ public void typeParamTest() { assertTrue(responses.stream().allMatch(d -> d.getType().equals("institution"))); responses = searchService.search(null, false, COLLECTION, null, null, 10); - assertEquals(2, responses.size()); + assertEquals(3, responses.size()); assertTrue(responses.stream().allMatch(d -> d.getType().equals("collection"))); responses = searchService.search(null, false, null, null, null, 10); - assertEquals(5, responses.size()); + assertEquals(6, responses.size()); } @Test public void countryFilterTest() { List responses = - searchService.search(null, true, null, null, Country.SPAIN, 10); + searchService.search(null, true, null, null, Collections.singletonList(Country.SPAIN), 10); assertEquals(1, responses.size()); assertEquals(c1.getKey(), responses.get(0).getKey()); - responses = searchService.search(null, true, null, null, Country.DENMARK, 10); + responses = + searchService.search( + null, true, null, null, Collections.singletonList(Country.DENMARK), 10); assertEquals(0, responses.size()); - responses = searchService.search(null, true, INSTITUTION, null, Country.SPAIN, 10); + responses = + searchService.search( + null, true, INSTITUTION, null, Collections.singletonList(Country.SPAIN), 10); assertEquals(0, responses.size()); - responses = searchService.search("I1", true, null, null, Country.SPAIN, 10); + responses = + searchService.search("I1", true, null, null, Collections.singletonList(Country.SPAIN), 10); assertEquals(0, responses.size()); - responses = searchService.search("C1", true, null, null, Country.SPAIN, 10); + responses = + searchService.search("C1", true, null, null, Collections.singletonList(Country.SPAIN), 10); assertEquals(1, responses.size()); assertEquals(c1.getKey(), responses.get(0).getKey()); assertEquals(1, responses.get(0).getHighlights().size()); - responses = searchService.search("C1", true, null, null, Country.DENMARK, 10); + responses = + searchService.search( + "C1", true, null, null, Collections.singletonList(Country.DENMARK), 10); assertEquals(0, responses.size()); } @@ -322,7 +357,7 @@ public void searchInstitutionsTest() { @Test public void searchCollectionsTest() { assertEquals( - 2, + 3, searchService .searchCollections(CollectionDescriptorsSearchRequest.builder().build()) .getResults() @@ -480,6 +515,280 @@ public void searchCollectionsTest() { assertTrue(result.getResults().get(0).getDescriptorMatches().isEmpty()); } + @Test + public void collectionsFacetTest() { + FacetedSearchResponse searchResponse = + searchService.searchCollections( + CollectionDescriptorsSearchRequest.builder() + .facets(Collections.singleton(CollectionFacetParameter.COUNTRY)) + .build()); + assertEquals(3, searchResponse.getResults().size()); + assertEquals(1, searchResponse.getFacets().size()); + CollectionFacet facet = searchResponse.getFacets().get(0); + assertEquals(2, facet.getCardinality()); + assertEquals(CollectionFacetParameter.COUNTRY, facet.getField()); + assertEquals(2, facet.getCounts().size()); + assertEquals( + 1, + facet.getCounts().stream() + .filter(c -> c.getName().equals(Country.SPAIN.getIso2LetterCode())) + .count()); + assertEquals( + 1, + facet.getCounts().stream() + .filter(c -> c.getName().equals(Country.PORTUGAL.getIso2LetterCode())) + .count()); + assertTrue(facet.getCounts().stream().allMatch(c -> c.getCount() == 1)); + + searchResponse = + searchService.searchCollections( + CollectionDescriptorsSearchRequest.builder() + .country(Collections.singletonList(Country.SPAIN)) + .facets(Collections.singleton(CollectionFacetParameter.COUNTRY)) + .build()); + assertEquals(1, searchResponse.getResults().size()); + assertEquals(1, searchResponse.getFacets().size()); + facet = searchResponse.getFacets().get(0); + assertEquals(1, facet.getCardinality()); + assertEquals(CollectionFacetParameter.COUNTRY, facet.getField()); + assertEquals(1, facet.getCounts().size()); + assertEquals( + 1, + facet.getCounts().stream() + .filter(c -> c.getName().equals(Country.SPAIN.getIso2LetterCode())) + .count()); + assertTrue(facet.getCounts().stream().allMatch(c -> c.getCount() == 1)); + + searchResponse = + searchService.searchCollections( + CollectionDescriptorsSearchRequest.builder() + .country(Collections.singletonList(Country.SPAIN)) + .facets(Collections.singleton(CollectionFacetParameter.COUNTRY)) + .multiSelectFacets(true) + .build()); + assertEquals(1, searchResponse.getResults().size()); + assertEquals(1, searchResponse.getFacets().size()); + facet = searchResponse.getFacets().get(0); + assertEquals(2, facet.getCardinality()); + assertEquals(CollectionFacetParameter.COUNTRY, facet.getField()); + assertEquals(2, facet.getCounts().size()); + assertEquals( + 1, + facet.getCounts().stream() + .filter(c -> c.getName().equals(Country.SPAIN.getIso2LetterCode())) + .count()); + assertEquals( + 1, + facet.getCounts().stream() + .filter(c -> c.getName().equals(Country.PORTUGAL.getIso2LetterCode())) + .count()); + assertTrue(facet.getCounts().stream().allMatch(c -> c.getCount() == 1)); + + searchResponse = + searchService.searchCollections( + CollectionDescriptorsSearchRequest.builder() + .facets(Collections.singleton(CollectionFacetParameter.COUNTRY)) + .facetMinCount(2) + .build()); + assertEquals(3, searchResponse.getResults().size()); + assertEquals(1, searchResponse.getFacets().size()); + facet = searchResponse.getFacets().get(0); + assertEquals(2, facet.getCardinality()); + assertEquals(CollectionFacetParameter.COUNTRY, facet.getField()); + assertEquals(0, facet.getCounts().size()); + + searchResponse = + searchService.searchCollections( + CollectionDescriptorsSearchRequest.builder() + .facets(Collections.singleton(CollectionFacetParameter.PRESERVATION_TYPE)) + .build()); + assertEquals(3, searchResponse.getResults().size()); + assertEquals(1, searchResponse.getFacets().size()); + facet = searchResponse.getFacets().get(0); + assertEquals(2, facet.getCardinality()); + assertEquals(CollectionFacetParameter.PRESERVATION_TYPE, facet.getField()); + assertEquals(1, facet.getCounts().stream().filter(c -> c.getCount() == 1).count()); + assertEquals(1, facet.getCounts().stream().filter(c -> c.getCount() == 2).count()); + + searchResponse = + searchService.searchCollections( + CollectionDescriptorsSearchRequest.builder() + .facets( + Set.of( + CollectionFacetParameter.COUNTRY, + CollectionFacetParameter.PRESERVATION_TYPE)) + .build()); + assertEquals(2, searchResponse.getFacets().size()); + assertEquals( + 1, + searchResponse.getFacets().stream() + .filter(f -> f.getField() == CollectionFacetParameter.COUNTRY) + .count()); + assertEquals( + 1, + searchResponse.getFacets().stream() + .filter(f -> f.getField() == CollectionFacetParameter.PRESERVATION_TYPE) + .count()); + + searchResponse = + searchService.searchCollections( + CollectionDescriptorsSearchRequest.builder() + .facets( + Set.of( + CollectionFacetParameter.COUNTRY, + CollectionFacetParameter.PRESERVATION_TYPE)) + .facetLimit(1) + .build()); + assertEquals(2, searchResponse.getFacets().size()); + assertTrue(searchResponse.getFacets().stream().allMatch(f -> f.getCounts().size() == 1)); + + searchResponse = + searchService.searchCollections( + CollectionDescriptorsSearchRequest.builder() + .facets( + Set.of( + CollectionFacetParameter.COUNTRY, + CollectionFacetParameter.PRESERVATION_TYPE)) + .facetLimit(1) + .facetPages( + Map.of(CollectionFacetParameter.PRESERVATION_TYPE, new PagingRequest(0, 2))) + .build()); + assertEquals(2, searchResponse.getFacets().size()); + assertEquals( + 1, searchResponse.getFacets().stream().filter(f -> f.getCounts().size() == 1).count()); + assertEquals( + 1, searchResponse.getFacets().stream().filter(f -> f.getCounts().size() == 2).count()); + + searchResponse = + searchService.searchCollections( + CollectionDescriptorsSearchRequest.builder() + .facets( + Set.of( + CollectionFacetParameter.COUNTRY, + CollectionFacetParameter.PRESERVATION_TYPE)) + .facetLimit(1) + .facetPages( + Map.of(CollectionFacetParameter.PRESERVATION_TYPE, new PagingRequest(1, 2))) + .build()); + assertEquals(2, searchResponse.getFacets().size()); + assertTrue(searchResponse.getFacets().stream().allMatch(f -> f.getCounts().size() == 1)); + } + + @Test + public void institutionFacetsTest() { + FacetedSearchResponse searchResponse = + searchService.searchInstitutions( + InstitutionSearchRequest.builder() + .facets(Collections.singleton(InstitutionFacetParameter.TYPE)) + .build()); + assertEquals(3, searchResponse.getResults().size()); + assertEquals(1, searchResponse.getFacets().size()); + CollectionFacet facet = searchResponse.getFacets().get(0); + assertEquals(2, facet.getCardinality()); + assertEquals(InstitutionFacetParameter.TYPE, facet.getField()); + assertEquals(2, facet.getCounts().size()); + assertEquals( + 1L, + facet.getCounts().stream() + .filter(c -> c.getName().equals("t1")) + .map(CollectionFacet.Count::getCount) + .findFirst() + .orElse(0L)); + assertEquals( + 2L, + facet.getCounts().stream() + .filter(c -> c.getName().equals("t2")) + .map(CollectionFacet.Count::getCount) + .findFirst() + .orElse(0L)); + + searchResponse = + searchService.searchInstitutions( + InstitutionSearchRequest.builder() + .facets(Collections.singleton(InstitutionFacetParameter.TYPE)) + .country(Collections.singletonList(Country.UNITED_STATES)) + .build()); + assertEquals(1, searchResponse.getResults().size()); + assertEquals(1, searchResponse.getFacets().size()); + facet = searchResponse.getFacets().get(0); + assertEquals(1, facet.getCardinality()); + assertEquals(InstitutionFacetParameter.TYPE, facet.getField()); + assertEquals(1, facet.getCounts().size()); + assertEquals("t2", facet.getCounts().get(0).getName()); + assertEquals(1, facet.getCounts().get(0).getCount()); + + searchResponse = + searchService.searchInstitutions( + InstitutionSearchRequest.builder() + .facets(Collections.singleton(InstitutionFacetParameter.TYPE)) + .country(Collections.singletonList(Country.UNITED_STATES)) + .multiSelectFacets(true) + .build()); + assertEquals(1, searchResponse.getResults().size()); + assertEquals(1, searchResponse.getFacets().size()); + facet = searchResponse.getFacets().get(0); + assertEquals(2, facet.getCardinality()); + assertEquals(InstitutionFacetParameter.TYPE, facet.getField()); + assertEquals(2, facet.getCounts().size()); + assertEquals( + 1L, + facet.getCounts().stream() + .filter(c -> c.getName().equals("t1")) + .map(CollectionFacet.Count::getCount) + .findFirst() + .orElse(0L)); + assertEquals( + 2L, + facet.getCounts().stream() + .filter(c -> c.getName().equals("t2")) + .map(CollectionFacet.Count::getCount) + .findFirst() + .orElse(0L)); + + searchResponse = + searchService.searchInstitutions( + InstitutionSearchRequest.builder() + .facets( + Set.of(InstitutionFacetParameter.DISCIPLINE, InstitutionFacetParameter.COUNTRY)) + .multiSelectFacets(true) + .build()); + assertEquals(2, searchResponse.getFacets().size()); + assertEquals( + 1, + searchResponse.getFacets().stream() + .filter(f -> f.getField() == InstitutionFacetParameter.COUNTRY) + .count()); + assertEquals( + 1, + searchResponse.getFacets().stream() + .filter(f -> f.getField() == InstitutionFacetParameter.DISCIPLINE) + .count()); + + searchResponse = + searchService.searchInstitutions( + InstitutionSearchRequest.builder() + .facets( + Set.of(InstitutionFacetParameter.COUNTRY, InstitutionFacetParameter.DISCIPLINE)) + .facetLimit(1) + .build()); + assertEquals(2, searchResponse.getFacets().size()); + assertTrue(searchResponse.getFacets().stream().allMatch(f -> f.getCounts().size() == 1)); + + searchResponse = + searchService.searchInstitutions( + InstitutionSearchRequest.builder() + .facets( + Set.of(InstitutionFacetParameter.COUNTRY, InstitutionFacetParameter.DISCIPLINE)) + .facetLimit(1) + .facetPages(Map.of(InstitutionFacetParameter.DISCIPLINE, new PagingRequest(0, 2))) + .build()); + assertEquals(2, searchResponse.getFacets().size()); + assertEquals( + 1, searchResponse.getFacets().stream().filter(f -> f.getCounts().size() == 1).count()); + assertEquals( + 1, searchResponse.getFacets().stream().filter(f -> f.getCounts().size() == 2).count()); + } + private void assertDescriptorSearch( int expectedResults, Integer expectedDescriptors, diff --git a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/InstitutionServiceIT.java b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/InstitutionServiceIT.java index 3623e43cf..1b551d73c 100644 --- a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/InstitutionServiceIT.java +++ b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/collections/service/InstitutionServiceIT.java @@ -36,6 +36,7 @@ import org.gbif.api.service.registry.NodeService; import org.gbif.api.service.registry.OrganizationService; import org.gbif.api.vocabulary.*; +import org.gbif.api.vocabulary.collections.CollectionsSortField; import org.gbif.api.vocabulary.collections.IdType; import org.gbif.api.vocabulary.collections.MasterSourceType; import org.gbif.api.vocabulary.collections.Source; @@ -127,7 +128,10 @@ public void listTest() { response = institutionService.list( - InstitutionSearchRequest.builder().source(Source.IH_IRN).sourceId("test-123").build()); + InstitutionSearchRequest.builder() + .source(Collections.singletonList(Source.IH_IRN)) + .sourceId(Collections.singletonList("test-123")) + .build()); assertEquals(1, response.getResults().size()); // empty queries are ignored and return all elements response = @@ -191,7 +195,7 @@ public void listTest() { institutionService .list( InstitutionSearchRequest.builder() - .code("c1") + .code(Collections.singletonList("c1")) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()) @@ -202,7 +206,7 @@ public void listTest() { institutionService .list( InstitutionSearchRequest.builder() - .name("n2") + .name(Collections.singletonList("n2")) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()) @@ -213,8 +217,8 @@ public void listTest() { institutionService .list( InstitutionSearchRequest.builder() - .code("c1") - .name("n1") + .code(Collections.singletonList("c1")) + .name(Collections.singletonList("n1")) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()) @@ -225,8 +229,8 @@ public void listTest() { institutionService .list( InstitutionSearchRequest.builder() - .code("c2") - .name("n1") + .code(Collections.singletonList("c2")) + .name(Collections.singletonList("n1")) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()) @@ -304,7 +308,7 @@ public void listTest() { institutionService .list( InstitutionSearchRequest.builder() - .active(true) + .active(Collections.singletonList(true)) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()) @@ -359,7 +363,7 @@ public void listTest() { response = institutionService.list( InstitutionSearchRequest.builder() - .alternativeCode("alt") + .alternativeCode(Collections.singletonList("alt")) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()); @@ -368,7 +372,7 @@ public void listTest() { response = institutionService.list( InstitutionSearchRequest.builder() - .alternativeCode("foo") + .alternativeCode(Collections.singletonList("foo")) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()); @@ -428,7 +432,7 @@ public void listTest() { response = institutionService.list( InstitutionSearchRequest.builder() - .city("city2") + .city(Collections.singletonList("city2")) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()); @@ -437,7 +441,7 @@ public void listTest() { response = institutionService.list( InstitutionSearchRequest.builder() - .city("foo") + .city(Collections.singletonList("foo")) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()); @@ -465,7 +469,7 @@ public void listTest() { institutionService .list( InstitutionSearchRequest.builder() - .displayOnNHCPortal(true) + .displayOnNHCPortal(Collections.singletonList(true)) .limit(DEFAULT_PAGE.getLimit()) .offset(DEFAULT_PAGE.getOffset()) .build()) @@ -676,7 +680,7 @@ public void listDeletedTest() { UUID key4 = institutionService.create(institution4); InstitutionSearchRequest searchRequest = InstitutionSearchRequest.builder().build(); - searchRequest.setReplacedBy(key4); + searchRequest.setReplacedBy(Collections.singletonList(key4)); assertEquals(0, institutionService.listDeleted(searchRequest).getResults().size()); institutionService.replace(key3, key4); assertEquals(1, institutionService.listDeleted(searchRequest).getResults().size()); @@ -1061,7 +1065,8 @@ public void listAsGeoJsonTest() { .getFeatures() .size()); FeatureCollection featuresC1 = - institutionService.listGeojson(InstitutionSearchRequest.builder().code("c1").build()); + institutionService.listGeojson( + InstitutionSearchRequest.builder().code(Collections.singletonList("c1")).build()); assertEquals(1, featuresC1.getFeatures().size()); assertTrue(featuresC1.getFeatures().get(0).getGeometry() instanceof Point); assertEquals(2, featuresC1.getFeatures().get(0).getProperties().size()); diff --git a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/persistence/mapper/CollectionMapperIT.java b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/persistence/mapper/CollectionMapperIT.java index aeb5d3c6d..a7c20c841 100644 --- a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/persistence/mapper/CollectionMapperIT.java +++ b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/persistence/mapper/CollectionMapperIT.java @@ -13,6 +13,18 @@ */ package org.gbif.registry.ws.it.persistence.mapper; +import static org.gbif.registry.ws.it.fixtures.TestConstants.PAGE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; import org.gbif.api.model.collections.Address; import org.gbif.api.model.collections.AlternativeCode; import org.gbif.api.model.collections.Collection; @@ -40,24 +52,10 @@ import org.gbif.registry.search.test.EsManageServer; import org.gbif.registry.ws.it.BaseItTest; import org.gbif.ws.client.filter.SimplePrincipalProvider; - -import java.net.URI; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.UUID; - import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; -import static org.gbif.registry.ws.it.fixtures.TestConstants.PAGE; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - public class CollectionMapperIT extends BaseItTest { @RegisterExtension @@ -239,7 +237,8 @@ public void listTest() { masterSourceMetadata.setSourceId("test-123"); masterSourceMetadata.setCreatedBy("test"); metadataMapper.create(masterSourceMetadata); - collectionMapper.addMasterSourceMetadata(col5.getKey(),masterSourceMetadata.getKey(),MasterSourceType.GRSCICOLL); + collectionMapper.addMasterSourceMetadata( + col5.getKey(), masterSourceMetadata.getKey(), MasterSourceType.GRSCICOLL); Pageable page = PAGE.apply(2, 0L); List dtos = @@ -247,15 +246,26 @@ public void listTest() { assertEquals(2, dtos.size()); page = PAGE.apply(5, 0L); - assertSearch(CollectionListParams.builder().sourceId("test-123").source(Source.IH_IRN).build(),1,col5.getKey()); + assertSearch( + CollectionListParams.builder() + .sourceId(asList("test-123")) + .source(asList(Source.IH_IRN)) + .build(), + 1, + col5.getKey()); assertSearch(CollectionListParams.builder().page(page).build(), 5); - assertSearch(CollectionListParams.builder().code("c1").page(page).build(), 1); - assertSearch(CollectionListParams.builder().code("C1").page(page).build(), 1); - assertSearch(CollectionListParams.builder().name("n2").page(page).build(), 1); - assertSearch(CollectionListParams.builder().code("c3").name("n3").page(page).build(), 1); - assertSearch(CollectionListParams.builder().code("c1").name("n3").page(page).build(), 0); + assertSearch(CollectionListParams.builder().code(asList("c1")).page(page).build(), 1); + assertSearch(CollectionListParams.builder().code(asList("C1")).page(page).build(), 1); + assertSearch(CollectionListParams.builder().name(asList("n2")).page(page).build(), 1); + assertSearch( + CollectionListParams.builder().code(asList("c3")).name(asList("n3")).page(page).build(), 1); assertSearch( - CollectionListParams.builder().fuzzyName("nime of fourth collection").page(page).build(), + CollectionListParams.builder().code(asList("c1")).name(asList("n3")).page(page).build(), 0); + assertSearch( + CollectionListParams.builder() + .fuzzyName(asList("nime of fourth collection")) + .page(page) + .build(), 1); assertSearch( CollectionListParams.builder().query("nime of fourth collection").page(page).build(), 0); @@ -271,17 +281,17 @@ public void listTest() { .page(page) .build(), 0); - assertSearch(CollectionListParams.builder().city("Odense").page(page).build(), 1); + assertSearch(CollectionListParams.builder().city(asList("Odense")).page(page).build(), 1); assertSearch( CollectionListParams.builder() - .city("Copenhagen") + .city(asList("Copenhagen")) .countries(Collections.singletonList(Country.DENMARK)) .page(page) .build(), 1); assertSearch( CollectionListParams.builder() - .city("CPH") + .city(asList("CPH")) .countries(Collections.singletonList(Country.DENMARK)) .page(page) .build(), @@ -289,71 +299,80 @@ public void listTest() { // machine tags assertSearch( - CollectionListParams.builder().machineTagNamespace("dummy").page(page).build(), 0); + CollectionListParams.builder().machineTagNamespace(asList("dummy")).page(page).build(), 0); assertSearch( - CollectionListParams.builder().machineTagName(mt.getName()).page(page).build(), + CollectionListParams.builder().machineTagName(asList(mt.getName())).page(page).build(), 1, col1.getKey()); assertSearch( - CollectionListParams.builder().machineTagName(mt.getName()).page(page).build(), + CollectionListParams.builder().machineTagName(asList(mt.getName())).page(page).build(), 1, col1.getKey()); assertSearch( - CollectionListParams.builder().machineTagValue(mt.getValue()).page(page).build(), + CollectionListParams.builder().machineTagValue(asList(mt.getValue())).page(page).build(), 1, col1.getKey()); assertSearch( CollectionListParams.builder() - .machineTagName(mt.getName()) - .machineTagName(mt.getName()) - .machineTagValue(mt.getValue()) + .machineTagName(asList(mt.getName())) + .machineTagName(asList(mt.getName())) + .machineTagValue(asList(mt.getValue())) .page(page) .build(), 1, col1.getKey()); // identifiers - assertSearch(CollectionListParams.builder().identifier("dummy").page(page).build(), 0); + assertSearch(CollectionListParams.builder().identifier(asList("dummy")).page(page).build(), 0); assertSearch( CollectionListParams.builder() - .machineTagName(mt.getName()) - .machineTagName(mt.getName()) - .machineTagValue(mt.getValue()) + .machineTagName(asList(mt.getName())) + .machineTagName(asList(mt.getName())) + .machineTagValue(asList(mt.getValue())) .page(page) .build(), 1, col1.getKey()); assertSearch( - CollectionListParams.builder().identifierType(identifier.getType()).page(page).build(), + CollectionListParams.builder() + .identifierType(asList(identifier.getType())) + .page(page) + .build(), 1, col2.getKey()); assertSearch( - CollectionListParams.builder().identifier(identifier.getIdentifier()).page(page).build(), + CollectionListParams.builder() + .identifier(asList(identifier.getIdentifier())) + .page(page) + .build(), 1, col2.getKey()); assertSearch( CollectionListParams.builder() - .identifierType(identifier.getType()) - .identifier(identifier.getIdentifier()) + .identifierType(asList(identifier.getType())) + .identifier(asList(identifier.getIdentifier())) .page(page) .build(), 1, col2.getKey()); assertSearch( - CollectionListParams.builder().masterSourceType(MasterSourceType.IH).page(page).build(), + CollectionListParams.builder() + .masterSourceType(asList(MasterSourceType.IH)) + .page(page) + .build(), 1, col1.getKey()); assertSearch( CollectionListParams.builder() - .masterSourceType(MasterSourceType.GBIF_REGISTRY) + .masterSourceType(asList(MasterSourceType.GBIF_REGISTRY)) .page(page) .build(), 1, @@ -361,7 +380,7 @@ public void listTest() { assertSearch( CollectionListParams.builder() - .masterSourceType(MasterSourceType.GRSCICOLL) + .masterSourceType(asList(MasterSourceType.GRSCICOLL)) .page(page) .build(), 1); @@ -478,7 +497,7 @@ public void alternativeCodesTest() { assertSearch(CollectionListParams.builder().query("c2").page(page).build(), 2); assertSearch( - CollectionListParams.builder().alternativeCode("c1").page(page).build(), + CollectionListParams.builder().alternativeCode(asList("c1")).page(page).build(), 1, coll2.getKey()); } diff --git a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/persistence/mapper/CollectionsSearchMapperIT.java b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/persistence/mapper/CollectionsSearchMapperIT.java index 66ba7a408..d3e8ec036 100644 --- a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/persistence/mapper/CollectionsSearchMapperIT.java +++ b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/persistence/mapper/CollectionsSearchMapperIT.java @@ -29,6 +29,7 @@ import org.gbif.api.model.collections.descriptors.DescriptorGroup; import org.gbif.api.vocabulary.Country; import org.gbif.api.vocabulary.License; +import org.gbif.api.vocabulary.collections.CollectionFacetParameter; import org.gbif.registry.database.TestCaseDatabaseInitializer; import org.gbif.registry.persistence.mapper.collections.AddressMapper; import org.gbif.registry.persistence.mapper.collections.CollectionMapper; @@ -36,7 +37,8 @@ import org.gbif.registry.persistence.mapper.collections.DescriptorsMapper; import org.gbif.registry.persistence.mapper.collections.dto.CollectionSearchDto; import org.gbif.registry.persistence.mapper.collections.dto.DescriptorDto; -import org.gbif.registry.persistence.mapper.collections.params.DescriptorsParams; +import org.gbif.registry.persistence.mapper.collections.dto.FacetDto; +import org.gbif.registry.persistence.mapper.collections.params.DescriptorsListParams; import org.gbif.registry.search.test.EsManageServer; import org.gbif.registry.ws.it.BaseItTest; import org.gbif.ws.client.filter.SimplePrincipalProvider; @@ -121,22 +123,22 @@ public void searchTest() { collectionMapper.create(collection); - DescriptorGroup DescriptorGroup = new DescriptorGroup(); - DescriptorGroup.setCollectionKey(collectionKey); - DescriptorGroup.setTitle("title"); - DescriptorGroup.setCreatedBy("test"); - DescriptorGroup.setModifiedBy("test"); - descriptorsMapper.createDescriptorGroup(DescriptorGroup); + DescriptorGroup descriptorGroup = new DescriptorGroup(); + descriptorGroup.setCollectionKey(collectionKey); + descriptorGroup.setTitle("title"); + descriptorGroup.setCreatedBy("test"); + descriptorGroup.setModifiedBy("test"); + descriptorsMapper.createDescriptorGroup(descriptorGroup); DescriptorDto descriptorDto = new DescriptorDto(); - descriptorDto.setDescriptorGroupKey(DescriptorGroup.getKey()); + descriptorDto.setDescriptorGroupKey(descriptorGroup.getKey()); descriptorDto.setUsageName("aves"); descriptorDto.setCountry(Country.SPAIN); descriptorsMapper.createDescriptor(descriptorDto); List dtos = collectionsSearchMapper.searchCollections( - DescriptorsParams.builder().query("aves").build()); + DescriptorsListParams.builder().query("aves").build()); assertEquals(1, dtos.size()); assertEquals(Country.SPAIN, dtos.get(0).getCountry()); assertEquals(Country.SPAIN, dtos.get(0).getMailingCountry()); @@ -145,22 +147,158 @@ public void searchTest() { dtos = collectionsSearchMapper.searchCollections( - DescriptorsParams.builder().query("division").build()); + DescriptorsListParams.builder().query("division").build()); assertEquals(1, dtos.size()); assertEquals(0, dtos.get(0).getQueryDescriptorRank()); assertTrue(dtos.get(0).getQueryRank() > 0); dtos = collectionsSearchMapper.searchCollections( - DescriptorsParams.builder().query("aves").build()); + DescriptorsListParams.builder().query("aves").build()); assertEquals(1, dtos.size()); assertEquals(0, dtos.get(0).getQueryRank()); assertTrue(dtos.get(0).getQueryDescriptorRank() > 0); dtos = collectionsSearchMapper.searchCollections( - DescriptorsParams.builder().usageName(Collections.singletonList("aves")).build()); + DescriptorsListParams.builder().usageName(Collections.singletonList("aves")).build()); assertEquals(1, dtos.size()); assertNotNull(dtos.get(0).getDescriptorKey()); } + + @Test + public void facetsTest() { + UUID c1Key = UUID.randomUUID(); + Collection c1 = new Collection(); + c1.setKey(c1Key); + c1.setAccessionStatus("Institutional"); + c1.setCode("c1"); + c1.setName("name1"); + c1.setCreatedBy("test"); + c1.setModifiedBy("test"); + + List preservationTypes = new ArrayList<>(); + preservationTypes.add("StorageControlledAtmosphere"); + preservationTypes.add("SampleCryopreserved"); + c1.setPreservationTypes(preservationTypes); + + Address address = new Address(); + address.setCountry(Country.SPAIN); + addressMapper.create(address); + assertNotNull(address.getKey()); + c1.setAddress(address); + + Address mailingAddress = new Address(); + mailingAddress.setCountry(Country.SPAIN); + addressMapper.create(mailingAddress); + assertNotNull(mailingAddress.getKey()); + c1.setMailingAddress(mailingAddress); + + collectionMapper.create(c1); + + DescriptorGroup descriptorGroup = new DescriptorGroup(); + descriptorGroup.setCollectionKey(c1Key); + descriptorGroup.setTitle("title"); + descriptorGroup.setCreatedBy("test"); + descriptorGroup.setModifiedBy("test"); + descriptorsMapper.createDescriptorGroup(descriptorGroup); + + DescriptorDto descriptorDto1 = new DescriptorDto(); + descriptorDto1.setDescriptorGroupKey(descriptorGroup.getKey()); + descriptorDto1.setUsageName("aves"); + descriptorDto1.setCountry(Country.DENMARK); + descriptorDto1.setKingdomKey(1); + descriptorsMapper.createDescriptor(descriptorDto1); + + DescriptorDto descriptorDto2 = new DescriptorDto(); + descriptorDto2.setDescriptorGroupKey(descriptorGroup.getKey()); + descriptorDto2.setKingdomKey(1); + descriptorDto2.setCountry(Country.DENMARK); + descriptorsMapper.createDescriptor(descriptorDto2); + + UUID c2Key = UUID.randomUUID(); + Collection c2 = new Collection(); + c2.setKey(c2Key); + c2.setAccessionStatus("Institutional"); + c2.setCode("c2"); + c2.setName("name2"); + c2.setCreatedBy("test"); + c2.setModifiedBy("test"); + + address = new Address(); + address.setCountry(Country.SPAIN); + addressMapper.create(address); + assertNotNull(address.getKey()); + c2.setAddress(address); + + mailingAddress = new Address(); + mailingAddress.setCountry(Country.SPAIN); + addressMapper.create(mailingAddress); + assertNotNull(mailingAddress.getKey()); + c2.setMailingAddress(mailingAddress); + + collectionMapper.create(c2); + + DescriptorGroup descriptorGroupC2 = new DescriptorGroup(); + descriptorGroupC2.setCollectionKey(c2Key); + descriptorGroupC2.setTitle("title"); + descriptorGroupC2.setCreatedBy("test"); + descriptorGroupC2.setModifiedBy("test"); + descriptorsMapper.createDescriptorGroup(descriptorGroupC2); + + DescriptorDto descriptorDtoC2 = new DescriptorDto(); + descriptorDtoC2.setDescriptorGroupKey(descriptorGroupC2.getKey()); + descriptorDtoC2.setKingdomKey(2); + descriptorDtoC2.setCountry(Country.DENMARK); + descriptorsMapper.createDescriptor(descriptorDtoC2); + + List facetDtos = + collectionsSearchMapper.collectionFacet( + DescriptorsListParams.builder() + .facet(CollectionFacetParameter.PRESERVATION_TYPE) + .build()); + assertEquals(2, facetDtos.size()); + facetDtos.forEach(f -> assertEquals(1, f.getCount())); + assertEquals( + 2, + collectionsSearchMapper.collectionFacetCardinality( + DescriptorsListParams.builder() + .facet(CollectionFacetParameter.PRESERVATION_TYPE) + .build())); + + facetDtos = + collectionsSearchMapper.collectionFacet( + DescriptorsListParams.builder().facet(CollectionFacetParameter.COUNTRY).build()); + assertEquals(1, facetDtos.size()); + assertEquals(2, facetDtos.get(0).getCount()); + assertEquals( + 1, + collectionsSearchMapper.collectionFacetCardinality( + DescriptorsListParams.builder().facet(CollectionFacetParameter.COUNTRY).build())); + + facetDtos = + collectionsSearchMapper.collectionFacet( + DescriptorsListParams.builder() + .facet(CollectionFacetParameter.DESCRIPTOR_COUNTRY) + .build()); + assertEquals(1, facetDtos.size()); + assertEquals(2, facetDtos.get(0).getCount()); + assertEquals( + 1, + collectionsSearchMapper.collectionFacetCardinality( + DescriptorsListParams.builder() + .facet(CollectionFacetParameter.DESCRIPTOR_COUNTRY) + .build())); + + facetDtos = + collectionsSearchMapper.collectionFacet( + DescriptorsListParams.builder().facet(CollectionFacetParameter.KINGDOM_KEY).build()); + assertEquals(2, facetDtos.size()); + facetDtos.forEach(f -> assertEquals(1, f.getCount())); + + assertEquals( + 2, + collectionsSearchMapper.collectionFacetCardinality( + DescriptorsListParams.builder().facet(CollectionFacetParameter.KINGDOM_KEY).build())); + } } diff --git a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/persistence/mapper/InstitutionMapperIT.java b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/persistence/mapper/InstitutionMapperIT.java index 2d9bbd939..5d6feaf97 100644 --- a/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/persistence/mapper/InstitutionMapperIT.java +++ b/registry-integration-tests/src/test/java/org/gbif/registry/ws/it/persistence/mapper/InstitutionMapperIT.java @@ -13,6 +13,12 @@ */ package org.gbif.registry.ws.it.persistence.mapper; + +import static org.gbif.registry.ws.it.fixtures.TestConstants.PAGE; +import static org.junit.jupiter.api.Assertions.*; + +import java.net.URI; +import java.util.*; import org.gbif.api.model.collections.Address; import org.gbif.api.model.collections.AlternativeCode; import org.gbif.api.model.collections.Contact; @@ -39,17 +45,10 @@ import org.gbif.registry.search.test.EsManageServer; import org.gbif.registry.ws.it.BaseItTest; import org.gbif.ws.client.filter.SimplePrincipalProvider; - -import java.net.URI; -import java.util.*; - import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; -import static org.gbif.registry.ws.it.fixtures.TestConstants.PAGE; -import static org.junit.jupiter.api.Assertions.*; - public class InstitutionMapperIT extends BaseItTest { @RegisterExtension @@ -222,19 +221,33 @@ public void listTest() { masterSourceMetadata.setSourceId("test-123"); masterSourceMetadata.setCreatedBy("test"); metadataMapper.create(masterSourceMetadata); - institutionMapper.addMasterSourceMetadata(inst4.getKey(),masterSourceMetadata.getKey(),MasterSourceType.GRSCICOLL); + institutionMapper.addMasterSourceMetadata( + inst4.getKey(), masterSourceMetadata.getKey(), MasterSourceType.GRSCICOLL); Pageable page = PAGE.apply(5, 0L); - assertSearch(InstitutionListParams.builder().sourceId("test-123").source(Source.IH_IRN).build(),1,inst4.getKey()); + assertSearch( + InstitutionListParams.builder() + .sourceId(asList("test-123")) + .source(asList(Source.IH_IRN)) + .build(), + 1, + inst4.getKey()); assertSearch(InstitutionListParams.builder().page(page).build(), 4); - assertSearch(InstitutionListParams.builder().code("i1").page(page).build(), 1); - assertSearch(InstitutionListParams.builder().code("I1").page(page).build(), 1); - assertSearch(InstitutionListParams.builder().name("n2").page(page).build(), 1); - assertSearch(InstitutionListParams.builder().code("i2").name("n2").page(page).build(), 1); - assertSearch(InstitutionListParams.builder().code("i1").name("n2").page(page).build(), 0); + assertSearch(InstitutionListParams.builder().code(asList("i1")).page(page).build(), 1); + assertSearch(InstitutionListParams.builder().code(asList("I1")).page(page).build(), 1); + assertSearch(InstitutionListParams.builder().name(asList("n2")).page(page).build(), 1); assertSearch( - InstitutionListParams.builder().fuzzyName("nime of third institution").page(page).build(), + InstitutionListParams.builder().code(asList("i2")).name(asList("n2")).page(page).build(), + 1); + assertSearch( + InstitutionListParams.builder().code(asList("i1")).name(asList("n2")).page(page).build(), + 0); + assertSearch( + InstitutionListParams.builder() + .fuzzyName(asList("nime of third institution")) + .page(page) + .build(), 1); assertSearch( InstitutionListParams.builder().query("nime of third institution").page(page).build(), 0); @@ -250,17 +263,17 @@ public void listTest() { .page(page) .build(), 0); - assertSearch(InstitutionListParams.builder().city("Odense").page(page).build(), 1); + assertSearch(InstitutionListParams.builder().city(asList("Odense")).page(page).build(), 1); assertSearch( InstitutionListParams.builder() - .city("Copenhagen") + .city(asList("Copenhagen")) .countries(Collections.singletonList(Country.DENMARK)) .page(page) .build(), 1); assertSearch( InstitutionListParams.builder() - .city("CPH") + .city(asList("CPH")) .countries(Collections.singletonList(Country.DENMARK)) .page(page) .build(), @@ -268,71 +281,80 @@ public void listTest() { // machine tags assertSearch( - InstitutionListParams.builder().machineTagNamespace("dummy").page(page).build(), 0); + InstitutionListParams.builder().machineTagNamespace(asList("dummy")).page(page).build(), 0); assertSearch( - InstitutionListParams.builder().machineTagName(mt.getName()).page(page).build(), + InstitutionListParams.builder().machineTagName(asList(mt.getName())).page(page).build(), 1, inst1.getKey()); assertSearch( - InstitutionListParams.builder().machineTagName(mt.getName()).page(page).build(), + InstitutionListParams.builder().machineTagName(asList(mt.getName())).page(page).build(), 1, inst1.getKey()); assertSearch( - InstitutionListParams.builder().machineTagValue(mt.getValue()).page(page).build(), + InstitutionListParams.builder().machineTagValue(asList(mt.getValue())).page(page).build(), 1, inst1.getKey()); assertSearch( InstitutionListParams.builder() - .machineTagName(mt.getName()) - .machineTagName(mt.getName()) - .machineTagValue(mt.getValue()) + .machineTagName(asList(mt.getName())) + .machineTagName(asList(mt.getName())) + .machineTagValue(asList(mt.getValue())) .page(page) .build(), 1, inst1.getKey()); // identifiers - assertSearch(InstitutionListParams.builder().identifier("dummy").page(page).build(), 0); + assertSearch(InstitutionListParams.builder().identifier(asList("dummy")).page(page).build(), 0); assertSearch( InstitutionListParams.builder() - .machineTagName(mt.getName()) - .machineTagName(mt.getName()) - .machineTagValue(mt.getValue()) + .machineTagName(asList(mt.getName())) + .machineTagName(asList(mt.getName())) + .machineTagValue(asList(mt.getValue())) .page(page) .build(), 1, inst1.getKey()); assertSearch( - InstitutionListParams.builder().identifierType(identifier.getType()).page(page).build(), + InstitutionListParams.builder() + .identifierType(asList(identifier.getType())) + .page(page) + .build(), 1, inst2.getKey()); assertSearch( - InstitutionListParams.builder().identifier(identifier.getIdentifier()).page(page).build(), + InstitutionListParams.builder() + .identifier(asList(identifier.getIdentifier())) + .page(page) + .build(), 1, inst2.getKey()); assertSearch( InstitutionListParams.builder() - .identifierType(identifier.getType()) - .identifier(identifier.getIdentifier()) + .identifierType(asList(identifier.getType())) + .identifier(asList(identifier.getIdentifier())) .page(page) .build(), 1, inst2.getKey()); assertSearch( - InstitutionListParams.builder().masterSourceType(MasterSourceType.IH).page(page).build(), + InstitutionListParams.builder() + .masterSourceType(asList(MasterSourceType.IH)) + .page(page) + .build(), 1, inst1.getKey()); assertSearch( InstitutionListParams.builder() - .masterSourceType(MasterSourceType.GBIF_REGISTRY) + .masterSourceType(asList(MasterSourceType.GBIF_REGISTRY)) .page(page) .build(), 1, @@ -340,7 +362,7 @@ public void listTest() { assertSearch( InstitutionListParams.builder() - .masterSourceType(MasterSourceType.GRSCICOLL) + .masterSourceType(asList(MasterSourceType.GRSCICOLL)) .page(page) .build(), 1); @@ -434,8 +456,7 @@ public void alternativeCodesTest() { assertSearch( InstitutionListParams.builder().query("i1 n1").page(page).build(), 1, inst1.getKey()); - InstitutionListParams params = - InstitutionListParams.builder().query("i1").page(page).build(); + InstitutionListParams params = InstitutionListParams.builder().query("i1").page(page).build(); List institutions = institutionMapper.list(params); long count = institutionMapper.count(params); assertEquals(1, institutions.size()); @@ -454,7 +475,7 @@ public void alternativeCodesTest() { assertEquals(2, count); assertSearch( - InstitutionListParams.builder().alternativeCode("i1").page(page).page(page).build(), + InstitutionListParams.builder().alternativeCode(asList("i1")).page(page).page(page).build(), 1, inst2.getKey()); } diff --git a/registry-integration-tests/src/test/resources/logback-test.xml b/registry-integration-tests/src/test/resources/logback-test.xml index bffcae77e..ac353eea7 100644 --- a/registry-integration-tests/src/test/resources/logback-test.xml +++ b/registry-integration-tests/src/test/resources/logback-test.xml @@ -7,9 +7,10 @@ - + + diff --git a/registry-persistence/src/main/java/org/gbif/registry/persistence/config/MyBatisConfiguration.java b/registry-persistence/src/main/java/org/gbif/registry/persistence/config/MyBatisConfiguration.java index cf59472be..fd8966420 100644 --- a/registry-persistence/src/main/java/org/gbif/registry/persistence/config/MyBatisConfiguration.java +++ b/registry-persistence/src/main/java/org/gbif/registry/persistence/config/MyBatisConfiguration.java @@ -130,6 +130,7 @@ ConfigurationCustomizer mybatisConfigCustomizer() { configuration .getTypeAliasRegistry() .registerAlias("CollectionSearchDto", CollectionSearchDto.class); + configuration.getTypeAliasRegistry().registerAlias("FacetDto", FacetDto.class); configuration .getTypeAliasRegistry() .registerAlias("InstitutionMatchedDto", InstitutionMatchedDto.class); diff --git a/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/CollectionsSearchMapper.java b/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/CollectionsSearchMapper.java index 34b11a0f0..36070c9db 100644 --- a/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/CollectionsSearchMapper.java +++ b/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/CollectionsSearchMapper.java @@ -17,9 +17,10 @@ import javax.annotation.Nullable; import org.apache.ibatis.annotations.Param; import org.gbif.registry.persistence.mapper.collections.dto.CollectionSearchDto; +import org.gbif.registry.persistence.mapper.collections.dto.FacetDto; import org.gbif.registry.persistence.mapper.collections.dto.InstitutionSearchDto; import org.gbif.registry.persistence.mapper.collections.dto.SearchDto; -import org.gbif.registry.persistence.mapper.collections.params.DescriptorsParams; +import org.gbif.registry.persistence.mapper.collections.params.DescriptorsListParams; import org.gbif.registry.persistence.mapper.collections.params.FullTextSearchParams; import org.gbif.registry.persistence.mapper.collections.params.InstitutionListParams; @@ -32,7 +33,15 @@ List searchInstitutions( long countInstitutions(@Nullable @Param("params") InstitutionListParams listParams); - List searchCollections(@Nullable @Param("params") DescriptorsParams params); + List searchCollections(@Nullable @Param("params") DescriptorsListParams params); - long countCollections(@Nullable @Param("params") DescriptorsParams listParams); + long countCollections(@Nullable @Param("params") DescriptorsListParams listParams); + + List collectionFacet(@Nullable @Param("params") DescriptorsListParams params); + + long collectionFacetCardinality(@Nullable @Param("params") DescriptorsListParams params); + + List institutionFacet(@Nullable @Param("params") InstitutionListParams params); + + long institutionFacetCardinality(@Nullable @Param("params") InstitutionListParams params); } diff --git a/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/dto/DescriptorDto.java b/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/dto/DescriptorDto.java index 009a11552..1fb9b617d 100644 --- a/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/dto/DescriptorDto.java +++ b/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/dto/DescriptorDto.java @@ -28,4 +28,18 @@ public class DescriptorDto { private Rank usageRank; private List taxonClassification; private Set taxonKeys; + private Integer kingdomKey; + private String kingdomName; + private Integer phylumKey; + private String phylumName; + private Integer classKey; + private String className; + private Integer orderKey; + private String orderName; + private Integer familyKey; + private String familyName; + private Integer genusKey; + private String genusName; + private Integer speciesKey; + private String speciesName; } diff --git a/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/dto/FacetDto.java b/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/dto/FacetDto.java new file mode 100644 index 000000000..3859b3afb --- /dev/null +++ b/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/dto/FacetDto.java @@ -0,0 +1,10 @@ +package org.gbif.registry.persistence.mapper.collections.dto; + +import lombok.Data; + +@Data +public class FacetDto { + + private String facet; + private long count; +} diff --git a/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/CollectionListParams.java b/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/CollectionListParams.java index c0860be29..f3b43ab2a 100644 --- a/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/CollectionListParams.java +++ b/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/CollectionListParams.java @@ -13,12 +13,8 @@ */ package org.gbif.registry.persistence.mapper.collections.params; -import org.gbif.api.vocabulary.collections.Source; - import java.util.List; - import javax.annotation.Nullable; - import lombok.Getter; import lombok.experimental.SuperBuilder; @@ -29,5 +25,5 @@ public class CollectionListParams extends ListParams { @Nullable List contentTypes; @Nullable List preservationTypes; @Nullable List accessionStatus; - @Nullable Boolean personalCollection; + @Nullable List personalCollection; } diff --git a/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/DescriptorsParams.java b/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/DescriptorsListParams.java similarity index 68% rename from registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/DescriptorsParams.java rename to registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/DescriptorsListParams.java index 3468351ec..6a73ef97c 100644 --- a/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/DescriptorsParams.java +++ b/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/DescriptorsListParams.java @@ -7,16 +7,17 @@ import lombok.experimental.SuperBuilder; import org.gbif.api.vocabulary.Country; import org.gbif.api.vocabulary.Rank; +import org.gbif.api.vocabulary.collections.CollectionFacetParameter; @Getter @SuperBuilder -public class DescriptorsParams extends CollectionListParams { +public class DescriptorsListParams extends CollectionListParams { // descriptors fields - List usageName; - List usageKey; - List usageRank; - List taxonKey; + @Nullable List usageName; + @Nullable List usageKey; + @Nullable List usageRank; + @Nullable List taxonKey; @Nullable List descriptorCountry; @Nullable RangeParam individualCount; @Nullable List identifiedBy; @@ -28,6 +29,20 @@ public class DescriptorsParams extends CollectionListParams { @Nullable List objectClassification; @Nullable List issues; + // facets + @Nullable CollectionFacetParameter facet; + + public boolean descriptorFacet() { + return facet == CollectionFacetParameter.DESCRIPTOR_COUNTRY + || facet == CollectionFacetParameter.KINGDOM_KEY + || facet == CollectionFacetParameter.PHYLUM_KEY + || facet == CollectionFacetParameter.CLASS_KEY + || facet == CollectionFacetParameter.ORDER_KEY + || facet == CollectionFacetParameter.FAMILY_KEY + || facet == CollectionFacetParameter.GENUS_KEY + || facet == CollectionFacetParameter.SPECIES_KEY; + } + public boolean descriptorSearch() { return query != null || usageName != null diff --git a/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/FullTextSearchParams.java b/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/FullTextSearchParams.java index 2a363d7a7..55ff5f9f4 100644 --- a/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/FullTextSearchParams.java +++ b/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/FullTextSearchParams.java @@ -1,5 +1,6 @@ package org.gbif.registry.persistence.mapper.collections.params; +import java.util.List; import javax.annotation.Nullable; import lombok.Builder; import lombok.Data; @@ -12,7 +13,7 @@ public class FullTextSearchParams { @Nullable String query; boolean highlight; @Nullable String type; - @Nullable Boolean displayOnNHCPortal; - @Nullable Country country; + @Nullable List displayOnNHCPortal; + @Nullable List country; int limit; } diff --git a/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/InstitutionListParams.java b/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/InstitutionListParams.java index bb8674cc8..621dd6ba1 100644 --- a/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/InstitutionListParams.java +++ b/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/InstitutionListParams.java @@ -13,20 +13,22 @@ */ package org.gbif.registry.persistence.mapper.collections.params; -import org.gbif.api.vocabulary.collections.Source; - import java.util.List; - import javax.annotation.Nullable; - import lombok.Getter; import lombok.experimental.SuperBuilder; +import org.gbif.api.vocabulary.collections.InstitutionFacetParameter; @Getter @SuperBuilder public class InstitutionListParams extends ListParams { @Nullable List types; + @Nullable List institutionalGovernances; + @Nullable List disciplines; + + // facets + @Nullable InstitutionFacetParameter facet; } diff --git a/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/ListParams.java b/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/ListParams.java index fbd818612..f8d8cd7dd 100644 --- a/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/ListParams.java +++ b/registry-persistence/src/main/java/org/gbif/registry/persistence/mapper/collections/params/ListParams.java @@ -19,10 +19,10 @@ import lombok.Getter; import lombok.experimental.SuperBuilder; import org.gbif.api.model.common.paging.Pageable; -import org.gbif.api.vocabulary.CollectionsSortField; import org.gbif.api.vocabulary.Country; import org.gbif.api.vocabulary.IdentifierType; import org.gbif.api.vocabulary.SortOrder; +import org.gbif.api.vocabulary.collections.CollectionsSortField; import org.gbif.api.vocabulary.collections.MasterSourceType; import org.gbif.api.vocabulary.collections.Source; @@ -32,30 +32,32 @@ public abstract class ListParams { @Nullable Boolean highlight; @Nullable String query; - @Nullable String code; - @Nullable String name; - @Nullable String alternativeCode; - @Nullable String machineTagNamespace; - @Nullable String machineTagName; - @Nullable String machineTagValue; - @Nullable IdentifierType identifierType; - @Nullable String identifier; + @Nullable List code; + @Nullable List name; + @Nullable List alternativeCode; + @Nullable List machineTagNamespace; + @Nullable List machineTagName; + @Nullable List machineTagValue; + @Nullable List identifierType; + @Nullable List identifier; @Nullable List countries; @Nullable List regionCountries; - @Nullable String city; - @Nullable String fuzzyName; - @Nullable Boolean active; - @Nullable MasterSourceType masterSourceType; + @Nullable List city; + @Nullable List fuzzyName; + @Nullable List active; + @Nullable List masterSourceType; @Nullable RangeParam numberSpecimens; - @Nullable Boolean displayOnNHCPortal; + @Nullable List displayOnNHCPortal; @Nullable RangeParam occurrenceCount; @Nullable RangeParam typeSpecimenCount; @Nullable List institutionKeys; - @Nullable Source source; - @Nullable String sourceId; + @Nullable List source; + @Nullable List sourceId; @Nullable CollectionsSortField sortBy; @Nullable SortOrder sortOrder; - @Nullable private Boolean deleted; - @Nullable private UUID replacedBy; - @Nullable private Pageable page; + @Nullable Boolean deleted; + @Nullable List replacedBy; + @Nullable Pageable page; + @Nullable Pageable facetPage; + @Nullable Integer facetMinCount; } diff --git a/registry-persistence/src/main/resources/liquibase/148-taxon-fields-collection-descriptors.xml b/registry-persistence/src/main/resources/liquibase/148-taxon-fields-collection-descriptors.xml new file mode 100644 index 000000000..08d0c4e46 --- /dev/null +++ b/registry-persistence/src/main/resources/liquibase/148-taxon-fields-collection-descriptors.xml @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/registry-persistence/src/main/resources/liquibase/master.xml b/registry-persistence/src/main/resources/liquibase/master.xml index 84c218960..7f759c02a 100644 --- a/registry-persistence/src/main/resources/liquibase/master.xml +++ b/registry-persistence/src/main/resources/liquibase/master.xml @@ -153,4 +153,5 @@ + diff --git a/registry-persistence/src/main/resources/org/gbif/registry/persistence/mapper/collections/CollectionMapper.xml b/registry-persistence/src/main/resources/org/gbif/registry/persistence/mapper/collections/CollectionMapper.xml index bf01b60ee..6ddf1fc23 100644 --- a/registry-persistence/src/main/resources/org/gbif/registry/persistence/mapper/collections/CollectionMapper.xml +++ b/registry-persistence/src/main/resources/org/gbif/registry/persistence/mapper/collections/CollectionMapper.xml @@ -238,158 +238,29 @@ INNER JOIN master_sync_metadata m ON c.master_sync_metadata_key = m.key - - - c.deleted IS NOT NULL - - - c.deleted IS NULL - - - - AND lower(c.code) = lower(#{params.code,jdbcType=VARCHAR}) - - - AND c.alternative_codes ?? #{params.alternativeCode,jdbcType=VARCHAR} - - - AND c.name = #{params.name,jdbcType=VARCHAR} - - - AND mt.namespace = #{params.machineTagNamespace,jdbcType=VARCHAR} - - - AND mt.name = #{params.machineTagName,jdbcType=VARCHAR} - - - AND mt.value = #{params.machineTagValue,jdbcType=VARCHAR} - - - AND id.type = #{params.identifierType,jdbcType=OTHER} - - - AND id.identifier = #{params.identifier,jdbcType=VARCHAR} - - - AND (addr.country IN - - #{item,jdbcType=VARCHAR} - - OR mail_addr.country IN - - #{item,jdbcType=VARCHAR} - - ) - - - AND (addr.country IN - - #{item,jdbcType=VARCHAR} - - OR mail_addr.country IN - - #{item,jdbcType=VARCHAR} - - ) - - - AND (normalize_name(addr.city) = normalize_name(#{params.city,jdbcType=VARCHAR}) - OR - normalize_name(mail_addr.city) = normalize_name(#{params.city,jdbcType=VARCHAR})) - - - AND similar_name(c.name, #{params.fuzzyName,jdbcType=VARCHAR}) - - - AND c.content_type && ARRAY - - #{item,jdbcType=VARCHAR}::text - - - - AND c.preservation_type && ARRAY - - #{item,jdbcType=VARCHAR}::text - - - - AND c.accession_status IN - - #{item,jdbcType=VARCHAR}::text - - - - AND c.active = #{params.active,jdbcType=BOOLEAN} - - - AND c.personal_collection = #{params.personalCollection,jdbcType=BOOLEAN} - - - AND c.master_source = #{params.masterSourceType,jdbcType=OTHER} - - - - - AND c.number_specimens = #{params.numberSpecimens.exactValue,jdbcType=INTEGER} - - - - AND c.number_specimens >= #{params.numberSpecimens.lowerBound,jdbcType=INTEGER} - - - AND c.number_specimens <= #{params.numberSpecimens.higherBound,jdbcType=INTEGER} - - - - - - - - AND c.occurrence_count = #{params.occurrenceCount.exactValue,jdbcType=INTEGER} - - - - AND c.occurrence_count >= #{params.occurrenceCount.lowerBound,jdbcType=INTEGER} - - - AND c.occurrence_count <= #{params.occurrenceCount.higherBound,jdbcType=INTEGER} - - - - - - - - AND c.type_specimen_count = #{params.typeSpecimenCount.exactValue,jdbcType=INTEGER} - - - - AND c.type_specimen_count >= #{params.typeSpecimenCount.lowerBound,jdbcType=INTEGER} - - - AND c.type_specimen_count <= #{params.typeSpecimenCount.higherBound,jdbcType=INTEGER} - - - - - - AND c.display_on_NHCPortal = #{params.displayOnNHCPortal,jdbcType=BOOLEAN} - - - AND c.replaced_by = #{params.replacedBy,jdbcType=OTHER} - - - AND c.institution_key IN - - #{item,jdbcType=VARCHAR} - - - - AND m.source = #{params.source,jdbcType=OTHER} - - - AND m.source_id = #{params.sourceId,jdbcType=VARCHAR} - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/registry-persistence/src/main/resources/org/gbif/registry/persistence/mapper/collections/CollectionsSearchMapper.xml b/registry-persistence/src/main/resources/org/gbif/registry/persistence/mapper/collections/CollectionsSearchMapper.xml index a876547da..607a50f50 100644 --- a/registry-persistence/src/main/resources/org/gbif/registry/persistence/mapper/collections/CollectionsSearchMapper.xml +++ b/registry-persistence/src/main/resources/org/gbif/registry/persistence/mapper/collections/CollectionsSearchMapper.xml @@ -4,6 +4,7 @@ + i.key, i.code, i.name, i.description, i.alternative_codes, @@ -49,9 +50,10 @@ AS query ON query @@ fulltext_search WHERE i.deleted IS NULL - - AND i.display_on_NHCPortal = #{params.displayOnNHCPortal,jdbcType=BOOLEAN} - + + + + ) UNION ALL @@ -65,9 +67,10 @@ FROM institution i WHERE i.deleted IS NULL AND i.code = trim(#{params.query}) - - AND i.display_on_NHCPortal = #{params.displayOnNHCPortal,jdbcType=BOOLEAN} - + + + + ) @@ -82,9 +85,10 @@ FROM institution i WHERE i.deleted IS NULL AND similarity(i.name, #{params.query}) >= 0.65 - - AND i.display_on_NHCPortal = #{params.displayOnNHCPortal,jdbcType=BOOLEAN} - + + + + ) @@ -102,9 +106,10 @@ LEFT JOIN institution i ON i.key = c.institution_key WHERE c.deleted IS NULL - - AND c.display_on_NHCPortal = #{params.displayOnNHCPortal,jdbcType=BOOLEAN} - + + + + ) UNION ALL @@ -119,9 +124,10 @@ FROM collection c LEFT JOIN institution i ON i.key = c.institution_key WHERE c.deleted IS NULL AND c.code = trim(#{params.query}) - - AND c.display_on_NHCPortal = #{params.displayOnNHCPortal,jdbcType=BOOLEAN} - + + + + ) @@ -137,9 +143,10 @@ FROM collection c LEFT JOIN institution i ON i.key = c.institution_key WHERE c.deleted IS NULL AND similarity(c.name, #{params.query}) >= 0.65 - - AND c.display_on_NHCPortal = #{params.displayOnNHCPortal,jdbcType=BOOLEAN} - + + + + ) @@ -148,8 +155,16 @@ LEFT JOIN address ad ON ad.key = matches.address_key LEFT JOIN address mail ON mail.key = matches.mailing_address_key - - ad.country = #{params.country,jdbcType=OTHER} OR mail.country = #{params.country,jdbcType=OTHER} + + AND (ad.country IN + + #{item,jdbcType=VARCHAR} + + OR mail.country IN + + #{item,jdbcType=VARCHAR} + + ) @@ -444,4 +459,216 @@ OR @@ ds.fulltext_search + + + + + + + + CASE WHEN a.country is not null THEN a.country ELSE ma.country END + + + unnest(i.type) + + + unnest(i.discipline) + + + + + + + + COUNT(DISTINCT CASE WHEN a.country is not null THEN a.country ELSE ma.country END) + FROM institution i + + + COUNT(DISTINCT facet) + FROM institution i + CROSS JOIN unnest( + + + i.type + + + i.discipline + + + ) AS facet + + + + + + + + + + + + CASE WHEN a.country is not null THEN a.country ELSE ma.country END + + + unnest(c.preservation_type) + + + unnest(c.content_type) + + + c.institution_key + + + unnest(cd.type_status) + + + c.accession_status + + + cd.country + + + cd.kingdom_key + + + cd.phylum_key + + + cd.class_key + + + cd.order_key + + + cd.family_key + + + cd.genus_key + + + cd.species_key + + + + + + + + COUNT(DISTINCT CASE WHEN a.country is not null THEN a.country ELSE ma.country END) + FROM collection c + + + COUNT(DISTINCT facet) + FROM collection c + CROSS JOIN unnest( + + + c.preservation_type + + + c.content_type + + + c.type_status + + + ) AS facet + + + COUNT(DISTINCT + + + c.institution_key + + + c.accession_status + + + cd.country + + + cd.kingdom_key + + + cd.phylum_key + + + cd.class_key + + + cd.order_key + + + cd.family_key + + + cd.genus_key + + + cd.species_key + + + ) FROM collection c + + + + diff --git a/registry-persistence/src/main/resources/org/gbif/registry/persistence/mapper/collections/Common.xml b/registry-persistence/src/main/resources/org/gbif/registry/persistence/mapper/collections/Common.xml index f6160f87a..d7a51e8a4 100644 --- a/registry-persistence/src/main/resources/org/gbif/registry/persistence/mapper/collections/Common.xml +++ b/registry-persistence/src/main/resources/org/gbif/registry/persistence/mapper/collections/Common.xml @@ -31,4 +31,197 @@ + + + AND ${field} IN + + #{item,jdbcType=VARCHAR} + + + + + + + AND ${field} IN + + #{item,jdbcType=OTHER} + + + + + + + AND ${field} IN + + #{item,jdbcType=BOOLEAN} + + + + + + + AND ${field} && ARRAY + + #{item,jdbcType=VARCHAR}::text + + + + + + + + ${alias}deleted IS NOT NULL + + + ${alias}deleted IS NULL + + + + AND lower(${alias}code) IN + + lower(#{item,jdbcType=VARCHAR}) + + + + AND ${alias}alternative_codes ??| + + #{item,jdbcType=VARCHAR} + + + + + + + + + + + + + + + + + + + + + + + + + + + + AND (addr.country IN + + #{item,jdbcType=VARCHAR} + + OR mail_addr.country IN + + #{item,jdbcType=VARCHAR} + + ) + + + AND (addr.country IN + + #{item,jdbcType=VARCHAR} + + OR mail_addr.country IN + + #{item,jdbcType=VARCHAR} + + ) + + + AND ( + + + normalize_name(addr.city) = normalize_name(#{item,jdbcType=VARCHAR}) OR + normalize_name(mail_addr.city) = normalize_name(#{item,jdbcType=VARCHAR}) OR + + + ) + + + AND ( + + + similar_name(${alias}name, #{item,jdbcType=VARCHAR}) OR + + + ) + + + + + + + + + + + + + AND ${alias}number_specimens = #{params.numberSpecimens.exactValue,jdbcType=INTEGER} + + + + AND ${alias}number_specimens >= #{params.numberSpecimens.lowerBound,jdbcType=INTEGER} + + + AND ${alias}number_specimens <= #{params.numberSpecimens.higherBound,jdbcType=INTEGER} + + + + + + + + AND ${alias}occurrence_count = #{params.occurrenceCount.exactValue,jdbcType=INTEGER} + + + + AND ${alias}occurrence_count >= #{params.occurrenceCount.lowerBound,jdbcType=INTEGER} + + + AND ${alias}occurrence_count <= #{params.occurrenceCount.higherBound,jdbcType=INTEGER} + + + + + + + + AND ${alias}type_specimen_count = #{params.typeSpecimenCount.exactValue,jdbcType=INTEGER} + + + + AND ${alias}type_specimen_count >= #{params.typeSpecimenCount.lowerBound,jdbcType=INTEGER} + + + AND ${alias}type_specimen_count <= #{params.typeSpecimenCount.higherBound,jdbcType=INTEGER} + + + + + + + + + + + + + + + + + + + + + + diff --git a/registry-persistence/src/main/resources/org/gbif/registry/persistence/mapper/collections/DescriptorsMapper.xml b/registry-persistence/src/main/resources/org/gbif/registry/persistence/mapper/collections/DescriptorsMapper.xml index e906e625b..0610d448e 100644 --- a/registry-persistence/src/main/resources/org/gbif/registry/persistence/mapper/collections/DescriptorsMapper.xml +++ b/registry-persistence/src/main/resources/org/gbif/registry/persistence/mapper/collections/DescriptorsMapper.xml @@ -122,13 +122,16 @@ collection_descriptor_group_key, usage_key, usage_name, usage_rank, taxon_classification, taxon_keys, country, individual_count, identified_by, date_identified, type_status, recorded_by, discipline, object_classification_name, - issues + issues, kingdom_key, kingdom_name, phylum_key, phylum_name, class_key, class_name, order_key, order_name, family_key, + family_name, genus_key, genus_name, species_key, species_name d.key, d.collection_descriptor_group_key, d.usage_key, d.usage_name, d.usage_rank, d.taxon_classification, d.country, d.individual_count, d.identified_by, d.date_identified, d.type_status, d.recorded_by, d.discipline, - d.object_classification_name, d.issues + d.object_classification_name, d.issues, d.kingdom_key, d.kingdom_name, d.phylum_key, d.phylum_name, + d.class_key, d.class_name, d.order_key, d.order_name, d.family_key, d.family_name, d.genus_key, d.genus_name, + d.species_key, d.species_name @@ -146,7 +149,21 @@ #{recordedBy,jdbcType=ARRAY,typeHandler=StringArrayTypeHandler}, #{discipline,jdbcType=VARCHAR}, #{objectClassificationName,jdbcType=VARCHAR}, - #{issues,jdbcType=ARRAY,typeHandler=StringArrayTypeHandler} + #{issues,jdbcType=ARRAY,typeHandler=StringArrayTypeHandler}, + #{kingdomKey,jdbcType=INTEGER}, + #{kingdomName,jdbcType=VARCHAR}, + #{phylumKey,jdbcType=INTEGER}, + #{phylumName,jdbcType=VARCHAR}, + #{classKey,jdbcType=INTEGER}, + #{className,jdbcType=VARCHAR}, + #{orderKey,jdbcType=INTEGER}, + #{orderName,jdbcType=VARCHAR}, + #{familyKey,jdbcType=INTEGER}, + #{familyName,jdbcType=VARCHAR}, + #{genusKey,jdbcType=INTEGER}, + #{genusName,jdbcType=VARCHAR}, + #{speciesKey,jdbcType=INTEGER}, + #{speciesName,jdbcType=VARCHAR} @@ -163,7 +180,21 @@ recorded_by = #{recordedBy,jdbcType=ARRAY,typeHandler=StringArrayTypeHandler}, discipline = #{discipline,jdbcType=VARCHAR}, object_classification_name = #{objectClassificationName,jdbcType=VARCHAR}, - issues = #{issues,jdbcType=ARRAY,typeHandler=StringArrayTypeHandler} + issues = #{issues,jdbcType=ARRAY,typeHandler=StringArrayTypeHandler}, + kingdom_key = #{kingdomKey,jdbcType=INTEGER}, + kingdom_name = #{kingdomName,jdbcType=VARCHAR}, + phylum_key = #{phylumKey,jdbcType=INTEGER}, + phylum_name = #{phylumName,jdbcType=VARCHAR}, + class_key = #{classKey,jdbcType=INTEGER}, + class_name = #{className,jdbcType=VARCHAR}, + order_key = #{orderKey,jdbcType=INTEGER}, + order_name = #{orderName,jdbcType=VARCHAR}, + family_key = #{familyKey,jdbcType=INTEGER}, + family_name = #{familyName,jdbcType=VARCHAR}, + genus_key = #{genusKey,jdbcType=INTEGER}, + genus_name = #{genusName,jdbcType=VARCHAR}, + species_key = #{speciesKey,jdbcType=INTEGER}, + species_name = #{speciesName,jdbcType=VARCHAR} diff --git a/registry-persistence/src/main/resources/org/gbif/registry/persistence/mapper/collections/InstitutionMapper.xml b/registry-persistence/src/main/resources/org/gbif/registry/persistence/mapper/collections/InstitutionMapper.xml index 0bee61489..11e46e0b1 100644 --- a/registry-persistence/src/main/resources/org/gbif/registry/persistence/mapper/collections/InstitutionMapper.xml +++ b/registry-persistence/src/main/resources/org/gbif/registry/persistence/mapper/collections/InstitutionMapper.xml @@ -263,155 +263,25 @@ INNER JOIN master_sync_metadata m ON i.master_sync_metadata_key = m.key - - - i.deleted IS NOT NULL - - - i.deleted IS NULL - - - - AND lower(i.code) = lower(#{params.code,jdbcType=VARCHAR}) - - - AND i.alternative_codes ?? #{params.alternativeCode,jdbcType=VARCHAR} - - - AND i.name = #{params.name,jdbcType=VARCHAR} - - - AND mt.namespace = #{params.machineTagNamespace,jdbcType=VARCHAR} - - - AND mt.name = #{params.machineTagName,jdbcType=VARCHAR} - - - AND mt.value = #{params.machineTagValue,jdbcType=VARCHAR} - - - AND id.type = #{params.identifierType,jdbcType=OTHER} - - - AND id.identifier = #{params.identifier,jdbcType=VARCHAR} - - - AND (addr.country IN - - #{item,jdbcType=VARCHAR} - - OR mail_addr.country IN - - #{item,jdbcType=VARCHAR} - - ) - - - AND (addr.country IN - - #{item,jdbcType=VARCHAR} - - OR mail_addr.country IN - - #{item,jdbcType=VARCHAR} - - ) - - - AND (normalize_name(addr.city) = normalize_name(#{params.city,jdbcType=VARCHAR}) - OR - normalize_name(mail_addr.city) = normalize_name(#{params.city,jdbcType=VARCHAR})) - - - AND similar_name(i.name, #{params.fuzzyName,jdbcType=VARCHAR}) - - - AND i.type && ARRAY - - #{item,jdbcType=VARCHAR}::text - - - - AND i.institutional_governance && ARRAY - - #{item,jdbcType=VARCHAR}::text - - - - AND i.discipline && ARRAY - - #{item,jdbcType=VARCHAR}::text - - - - AND i.active = #{params.active,jdbcType=OTHER} - - - AND i.master_source = #{params.masterSourceType,jdbcType=OTHER} - - - - - AND i.number_specimens = #{params.numberSpecimens.exactValue,jdbcType=INTEGER} - - - - AND i.number_specimens >= #{params.numberSpecimens.lowerBound,jdbcType=INTEGER} - - - AND i.number_specimens <= #{params.numberSpecimens.higherBound,jdbcType=INTEGER} - - - - - - - - AND i.occurrence_count = #{params.occurrenceCount.exactValue,jdbcType=INTEGER} - - - - AND i.occurrence_count >= #{params.occurrenceCount.lowerBound,jdbcType=INTEGER} - - - AND i.occurrence_count <= #{params.occurrenceCount.higherBound,jdbcType=INTEGER} - - - - - - - - AND i.type_specimen_count = #{params.typeSpecimenCount.exactValue,jdbcType=INTEGER} - - - - AND i.type_specimen_count >= #{params.typeSpecimenCount.lowerBound,jdbcType=INTEGER} - - - AND i.type_specimen_count <= #{params.typeSpecimenCount.higherBound,jdbcType=INTEGER} - - - - - - AND i.display_on_NHCPortal = #{params.displayOnNHCPortal,jdbcType=BOOLEAN} - - - AND i.replaced_by = #{params.replacedBy,jdbcType=OTHER} - - - AND i.key IN - - #{item,jdbcType=VARCHAR} - - - - AND m.source = #{params.source,jdbcType=OTHER} - - - AND m.source_id = #{params.sourceId,jdbcType=VARCHAR} - + + + + + + + + + + + + + + + + + + + diff --git a/registry-service/src/main/java/org/gbif/registry/service/collections/CollectionsSearchService.java b/registry-service/src/main/java/org/gbif/registry/service/collections/CollectionsSearchService.java index 4735f63bb..f45ec8575 100644 --- a/registry-service/src/main/java/org/gbif/registry/service/collections/CollectionsSearchService.java +++ b/registry-service/src/main/java/org/gbif/registry/service/collections/CollectionsSearchService.java @@ -32,25 +32,31 @@ import org.gbif.api.model.collections.request.InstitutionSearchRequest; import org.gbif.api.model.collections.request.SearchRequest; import org.gbif.api.model.collections.search.BaseSearchResponse; +import org.gbif.api.model.collections.search.CollectionFacet; import org.gbif.api.model.collections.search.CollectionSearchResponse; import org.gbif.api.model.collections.search.CollectionsFullSearchResponse; import org.gbif.api.model.collections.search.DescriptorMatch; +import org.gbif.api.model.collections.search.FacetedSearchResponse; import org.gbif.api.model.collections.search.Highlight; import org.gbif.api.model.collections.search.InstitutionSearchResponse; import org.gbif.api.model.common.paging.Pageable; import org.gbif.api.model.common.paging.PagingRequest; -import org.gbif.api.model.common.paging.PagingResponse; import org.gbif.api.vocabulary.Country; +import org.gbif.api.vocabulary.collections.CollectionFacetParameter; +import org.gbif.api.vocabulary.collections.CollectionsFacetParameter; +import org.gbif.api.vocabulary.collections.InstitutionFacetParameter; import org.gbif.registry.domain.collections.TypeParam; import org.gbif.registry.persistence.mapper.collections.CollectionsSearchMapper; import org.gbif.registry.persistence.mapper.collections.dto.BaseSearchDto; import org.gbif.registry.persistence.mapper.collections.dto.CollectionSearchDto; +import org.gbif.registry.persistence.mapper.collections.dto.FacetDto; import org.gbif.registry.persistence.mapper.collections.dto.InstitutionSearchDto; import org.gbif.registry.persistence.mapper.collections.dto.SearchDto; -import org.gbif.registry.persistence.mapper.collections.params.DescriptorsParams; +import org.gbif.registry.persistence.mapper.collections.params.DescriptorsListParams; import org.gbif.registry.persistence.mapper.collections.params.FullTextSearchParams; import org.gbif.registry.persistence.mapper.collections.params.InstitutionListParams; import org.gbif.registry.persistence.mapper.collections.params.ListParams; +import org.gbif.registry.service.collections.utils.SearchUtils; import org.gbif.registry.service.collections.utils.Vocabularies; import org.gbif.vocabulary.client.ConceptClient; import org.springframework.beans.factory.annotation.Autowired; @@ -76,8 +82,8 @@ public List search( String query, boolean highlight, TypeParam type, - Boolean displayOnNHCPortal, - Country country, + List displayOnNHCPortal, + List country, int limit) { List dtos = searchMapper.search( @@ -136,8 +142,8 @@ public List search( return responses; } - public PagingResponse searchInstitutions( - InstitutionSearchRequest searchRequest) { + public FacetedSearchResponse + searchInstitutions(InstitutionSearchRequest searchRequest) { Pageable page = searchRequest.getPage() == null ? new PagingRequest() : searchRequest.getPage(); @@ -177,17 +183,45 @@ public PagingResponse searchInstitutions( }) .collect(Collectors.toList()); - return new PagingResponse<>(page, searchMapper.countInstitutions(listParams), results); + List> facets = new ArrayList<>(); + if (searchRequest.getFacets() != null && !searchRequest.getFacets().isEmpty()) { + searchRequest + .getFacets() + .forEach( + f -> { + InstitutionListParams.InstitutionListParamsBuilder facetParamsBuilder = + searchRequest.isMultiSelectFacets() + ? InstitutionListParams.builder() + : listParamsBuilder; + + InstitutionListParams facetParams = + (InstitutionListParams) + facetParamsBuilder + .facet(f) + .facetMinCount(searchRequest.getFacetMinCount()) + .facetPage(extractFacetPage(searchRequest, f)) + .build(); + + facets.add( + createFacet( + f, + searchMapper.institutionFacet(facetParams), + searchMapper.institutionFacetCardinality(facetParams))); + }); + } + + return new FacetedSearchResponse<>( + page, searchMapper.countInstitutions(listParams), results, facets); } - public PagingResponse searchCollections( - CollectionDescriptorsSearchRequest searchRequest) { + public FacetedSearchResponse + searchCollections(CollectionDescriptorsSearchRequest searchRequest) { Pageable page = searchRequest.getPage() == null ? new PagingRequest() : searchRequest.getPage(); Set institutionKeys = new HashSet<>(); if (searchRequest.getInstitution() != null) { - institutionKeys.add(searchRequest.getInstitution()); + institutionKeys.addAll(searchRequest.getInstitution()); } if (searchRequest.getInstitutionKeys() != null) { institutionKeys.addAll(searchRequest.getInstitutionKeys()); @@ -195,8 +229,8 @@ public PagingResponse searchCollections( Vocabularies.addChildrenConcepts(searchRequest, conceptClient); - DescriptorsParams.DescriptorsParamsBuilder listParamsBuilder = - DescriptorsParams.builder() + DescriptorsListParams.DescriptorsListParamsBuilder listParamsBuilder = + DescriptorsListParams.builder() .contentTypes(searchRequest.getContentTypes()) .preservationTypes(searchRequest.getPreservationTypes()) .accessionStatus(searchRequest.getAccessionStatus()) @@ -223,60 +257,121 @@ public PagingResponse searchCollections( .objectClassification(searchRequest.getObjectClassification()) .issues(searchRequest.getIssue()); buildCommonParams(listParamsBuilder, searchRequest); - DescriptorsParams listParams = listParamsBuilder.build(); + DescriptorsListParams listParams = listParamsBuilder.build(); List dtos = searchMapper.searchCollections(listParams); Map responsesMap = new HashMap<>(); List results = new ArrayList<>(); - dtos.stream() - .forEach( - dto -> { - if (responsesMap.containsKey(dto.getKey())) { - CollectionSearchResponse existing = responsesMap.get(dto.getKey()); - if (Boolean.TRUE.equals(listParams.getHighlight())) { - addHighlights(existing, dto); - } - if (isCollectionDescriptorResult(dto, listParams)) { - existing.getDescriptorMatches().add(addDescriptorMatch(dto)); - } - return; - } - - CollectionSearchResponse response = new CollectionSearchResponse(); - responsesMap.put(dto.getKey(), response); - results.add(response); - - createCommonResponse(dto, response); - response.setContentTypes(dto.getContentTypes()); - response.setPersonalCollection(dto.isPersonalCollection()); - response.setPreservationTypes(dto.getPreservationTypes()); - response.setAccessionStatus(dto.getAccessionStatus()); - response.setInstitutionKey(dto.getInstitutionKey()); - response.setInstitutionName(dto.getInstitutionName()); - response.setInstitutionCode(dto.getInstitutionCode()); - response.setNumberSpecimens(dto.getNumberSpecimens()); - response.setTaxonomicCoverage(dto.getTaxonomicCoverage()); - response.setGeographicCoverage(dto.getGeographicCoverage()); - response.setDepartment(dto.getDepartment()); - response.setDivision(dto.getDivision()); - response.setDisplayOnNHCPortal(dto.isDisplayOnNHCPortal()); - response.setOccurrenceCount(dto.getOccurrenceCount()); - response.setTypeSpecimenCount(dto.getTypeSpecimenCount()); - - if (isCollectionDescriptorResult(dto, listParams)) { - response.getDescriptorMatches().add(addDescriptorMatch(dto)); - } - - if (Boolean.TRUE.equals(searchRequest.getHl())) { - addHighlights(response, dto); - } - }); - - return new PagingResponse<>(page, searchMapper.countCollections(listParams), results); + dtos.forEach( + dto -> { + if (responsesMap.containsKey(dto.getKey())) { + CollectionSearchResponse existing = responsesMap.get(dto.getKey()); + if (Boolean.TRUE.equals(listParams.getHighlight())) { + addHighlights(existing, dto); + } + if (isCollectionDescriptorResult(dto, listParams)) { + existing.getDescriptorMatches().add(addDescriptorMatch(dto)); + } + return; + } + + CollectionSearchResponse response = new CollectionSearchResponse(); + responsesMap.put(dto.getKey(), response); + results.add(response); + + createCommonResponse(dto, response); + response.setContentTypes(dto.getContentTypes()); + response.setPersonalCollection(dto.isPersonalCollection()); + response.setPreservationTypes(dto.getPreservationTypes()); + response.setAccessionStatus(dto.getAccessionStatus()); + response.setInstitutionKey(dto.getInstitutionKey()); + response.setInstitutionName(dto.getInstitutionName()); + response.setInstitutionCode(dto.getInstitutionCode()); + response.setNumberSpecimens(dto.getNumberSpecimens()); + response.setTaxonomicCoverage(dto.getTaxonomicCoverage()); + response.setGeographicCoverage(dto.getGeographicCoverage()); + response.setDepartment(dto.getDepartment()); + response.setDivision(dto.getDivision()); + response.setDisplayOnNHCPortal(dto.isDisplayOnNHCPortal()); + response.setOccurrenceCount(dto.getOccurrenceCount()); + response.setTypeSpecimenCount(dto.getTypeSpecimenCount()); + + if (isCollectionDescriptorResult(dto, listParams)) { + response.getDescriptorMatches().add(addDescriptorMatch(dto)); + } + + if (Boolean.TRUE.equals(searchRequest.getHl())) { + addHighlights(response, dto); + } + }); + + List> facets = new ArrayList<>(); + if (searchRequest.getFacets() != null && !searchRequest.getFacets().isEmpty()) { + searchRequest + .getFacets() + .forEach( + f -> { + DescriptorsListParams.DescriptorsListParamsBuilder facetParamsBuilder = + searchRequest.isMultiSelectFacets() + ? DescriptorsListParams.builder() + : listParamsBuilder; + + DescriptorsListParams facetParams = + (DescriptorsListParams) + facetParamsBuilder + .facet(f) + .facetMinCount(searchRequest.getFacetMinCount()) + .facetPage(extractFacetPage(searchRequest, f)) + .build(); + + facets.add( + createFacet( + f, + searchMapper.collectionFacet(facetParams), + searchMapper.collectionFacetCardinality(facetParams))); + }); + } + + return new FacetedSearchResponse<>( + page, searchMapper.countCollections(listParams), results, facets); + } + + private CollectionFacet createFacet( + F f, List facetDtos, long cardinality) { + List facetCounts = + facetDtos.stream() + .filter( + dto -> + !Strings.isNullOrEmpty(dto.getFacet()) + && !"null".equalsIgnoreCase(dto.getFacet())) + .map(dto -> new CollectionFacet.Count(dto.getFacet(), dto.getCount())) + .collect(Collectors.toList()); + + CollectionFacet collectionFacet = new CollectionFacet<>(); + collectionFacet.setField(f); + collectionFacet.setCounts(facetCounts); + collectionFacet.setCardinality(cardinality); + return collectionFacet; + } + + private static Pageable extractFacetPage( + SearchRequest searchRequest, F facetParameter) { + if (searchRequest.getFacetPages() != null + && searchRequest.getFacetPages().get(facetParameter) != null) { + return searchRequest.getFacetPages().get(facetParameter); + } + + int limit = + searchRequest.getFacetLimit() != null + ? searchRequest.getFacetLimit() + : SearchUtils.DEFAULT_FACET_LIMIT; + + long offset = searchRequest.getFacetOffset() != null ? searchRequest.getFacetOffset() : 0; + return new PagingRequest(offset, limit); } private static boolean isCollectionDescriptorResult( - CollectionSearchDto dto, DescriptorsParams params) { + CollectionSearchDto dto, DescriptorsListParams params) { return dto.getDescriptorKey() != null && (dto.getQueryDescriptorRank() != null && dto.getQueryDescriptorRank() > 0 || params.descriptorSearchWithoutQuery()); diff --git a/registry-service/src/main/java/org/gbif/registry/service/collections/DefaultCollectionService.java b/registry-service/src/main/java/org/gbif/registry/service/collections/DefaultCollectionService.java index de9f522da..158789b9a 100644 --- a/registry-service/src/main/java/org/gbif/registry/service/collections/DefaultCollectionService.java +++ b/registry-service/src/main/java/org/gbif/registry/service/collections/DefaultCollectionService.java @@ -155,7 +155,7 @@ private PagingResponse listInternal( Set institutionKeys = new HashSet<>(); if (searchRequest.getInstitution() != null) { - institutionKeys.add(searchRequest.getInstitution()); + institutionKeys.addAll(searchRequest.getInstitution()); } if (searchRequest.getInstitutionKeys() != null) { institutionKeys.addAll(searchRequest.getInstitutionKeys()); diff --git a/registry-service/src/main/java/org/gbif/registry/service/collections/batch/CollectionBatchHandler.java b/registry-service/src/main/java/org/gbif/registry/service/collections/batch/CollectionBatchHandler.java index 7c69603fd..269b92f98 100644 --- a/registry-service/src/main/java/org/gbif/registry/service/collections/batch/CollectionBatchHandler.java +++ b/registry-service/src/main/java/org/gbif/registry/service/collections/batch/CollectionBatchHandler.java @@ -13,6 +13,13 @@ */ package org.gbif.registry.service.collections.batch; +import com.google.common.base.Strings; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; import org.gbif.api.model.collections.Collection; import org.gbif.api.model.collections.CollectionEntityType; import org.gbif.api.model.collections.request.CollectionSearchRequest; @@ -22,20 +29,11 @@ import org.gbif.registry.persistence.mapper.collections.BatchMapper; import org.gbif.registry.security.grscicoll.GrSciCollAuthorizationService; import org.gbif.registry.service.collections.batch.model.ParsedData; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.stream.Collectors; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; -import com.google.common.base.Strings; - @Service public class CollectionBatchHandler extends BaseBatchHandler { @@ -86,12 +84,17 @@ List findEntity(String code, List identifiers) { List collectionsFound = new ArrayList<>(); if (!Strings.isNullOrEmpty(code)) { collectionsFound = - collectionService.list(CollectionSearchRequest.builder().code(code).build()).getResults(); + collectionService + .list(CollectionSearchRequest.builder().code(Collections.singletonList(code)).build()) + .getResults(); if (collectionsFound.isEmpty()) { collectionsFound = collectionService - .list(CollectionSearchRequest.builder().alternativeCode(code).build()) + .list( + CollectionSearchRequest.builder() + .alternativeCode(Collections.singletonList(code)) + .build()) .getResults(); } } @@ -104,8 +107,8 @@ List findEntity(String code, List identifiers) { collectionService .list( CollectionSearchRequest.builder() - .identifier(identifier.getIdentifier()) - .identifierType(identifier.getType()) + .identifier(Collections.singletonList(identifier.getIdentifier())) + .identifierType(Collections.singletonList(identifier.getType())) .build()) .getResults(); i++; diff --git a/registry-service/src/main/java/org/gbif/registry/service/collections/batch/InstitutionBatchHandler.java b/registry-service/src/main/java/org/gbif/registry/service/collections/batch/InstitutionBatchHandler.java index c98b471f5..f71f0af85 100644 --- a/registry-service/src/main/java/org/gbif/registry/service/collections/batch/InstitutionBatchHandler.java +++ b/registry-service/src/main/java/org/gbif/registry/service/collections/batch/InstitutionBatchHandler.java @@ -13,6 +13,13 @@ */ package org.gbif.registry.service.collections.batch; +import com.google.common.base.Strings; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; import org.gbif.api.model.collections.CollectionEntityType; import org.gbif.api.model.collections.Institution; import org.gbif.api.model.collections.request.InstitutionSearchRequest; @@ -22,20 +29,11 @@ import org.gbif.registry.security.grscicoll.GrSciCollAuthorizationService; import org.gbif.registry.service.collections.batch.FileFields.InstitutionFields; import org.gbif.registry.service.collections.batch.model.ParsedData; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.stream.Collectors; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; -import com.google.common.base.Strings; - @Service public class InstitutionBatchHandler extends BaseBatchHandler { private final InstitutionService institutionService; @@ -86,13 +84,17 @@ List findEntity(String code, List identifiers) { if (!Strings.isNullOrEmpty(code)) { institutionsFound = institutionService - .list(InstitutionSearchRequest.builder().code(code).build()) + .list( + InstitutionSearchRequest.builder().code(Collections.singletonList(code)).build()) .getResults(); if (institutionsFound.isEmpty()) { institutionsFound = institutionService - .list(InstitutionSearchRequest.builder().alternativeCode(code).build()) + .list( + InstitutionSearchRequest.builder() + .alternativeCode(Collections.singletonList(code)) + .build()) .getResults(); } } @@ -105,8 +107,8 @@ List findEntity(String code, List identifiers) { institutionService .list( InstitutionSearchRequest.builder() - .identifier(identifier.getIdentifier()) - .identifierType(identifier.getType()) + .identifier(Collections.singletonList(identifier.getIdentifier())) + .identifierType(Collections.singletonList(identifier.getType())) .build()) .getResults(); i++; diff --git a/registry-service/src/main/java/org/gbif/registry/service/collections/descriptors/DefaultDescriptorService.java b/registry-service/src/main/java/org/gbif/registry/service/collections/descriptors/DefaultDescriptorService.java index 26bb27307..141bbffe8 100644 --- a/registry-service/src/main/java/org/gbif/registry/service/collections/descriptors/DefaultDescriptorService.java +++ b/registry-service/src/main/java/org/gbif/registry/service/collections/descriptors/DefaultDescriptorService.java @@ -35,6 +35,7 @@ import org.gbif.api.service.collections.CollectionService; import org.gbif.api.service.collections.DescriptorsService; import org.gbif.api.vocabulary.Country; +import org.gbif.api.vocabulary.Rank; import org.gbif.api.vocabulary.collections.MasterSourceType; import org.gbif.checklistbank.ws.client.NubResourceClient; import org.gbif.dwc.terms.DwcTerm; @@ -440,6 +441,34 @@ private void interpretDescriptor(Map valuesMap, DescriptorDto de descriptorDto.setUsageName(taxonomyResult.getResult().getUsageName()); descriptorDto.setTaxonKeys(taxonomyResult.getResult().getTaxonKeys()); descriptorDto.setTaxonClassification(taxonomyResult.getResult().getTaxonClassification()); + taxonomyResult + .getResult() + .getTaxonClassification() + .forEach( + r -> { + if (r.getRank() == Rank.KINGDOM) { + descriptorDto.setKingdomKey(r.getKey()); + descriptorDto.setKingdomName(r.getName()); + } else if (r.getRank() == Rank.PHYLUM) { + descriptorDto.setPhylumKey(r.getKey()); + descriptorDto.setPhylumName(r.getName()); + } else if (r.getRank() == Rank.CLASS) { + descriptorDto.setClassKey(r.getKey()); + descriptorDto.setClassName(r.getName()); + } else if (r.getRank() == Rank.ORDER) { + descriptorDto.setOrderKey(r.getKey()); + descriptorDto.setOrderName(r.getName()); + } else if (r.getRank() == Rank.FAMILY) { + descriptorDto.setFamilyKey(r.getKey()); + descriptorDto.setFamilyName(r.getName()); + } else if (r.getRank() == Rank.GENUS) { + descriptorDto.setGenusKey(r.getKey()); + descriptorDto.setGenusName(r.getName()); + } else if (r.getRank() == Rank.SPECIES) { + descriptorDto.setSpeciesKey(r.getKey()); + descriptorDto.setSpeciesName(r.getName()); + } + }); } addIssues(descriptorDto, taxonomyResult); diff --git a/registry-service/src/main/java/org/gbif/registry/service/collections/merge/InstitutionMergeService.java b/registry-service/src/main/java/org/gbif/registry/service/collections/merge/InstitutionMergeService.java index 6f96db2d7..ee30b0fac 100644 --- a/registry-service/src/main/java/org/gbif/registry/service/collections/merge/InstitutionMergeService.java +++ b/registry-service/src/main/java/org/gbif/registry/service/collections/merge/InstitutionMergeService.java @@ -13,6 +13,14 @@ */ package org.gbif.registry.service.collections.merge; +import static com.google.common.base.Preconditions.checkArgument; +import static org.gbif.registry.security.UserRoles.GRSCICOLL_ADMIN_ROLE; +import static org.gbif.registry.security.UserRoles.GRSCICOLL_MEDIATOR_ROLE; + +import com.google.common.base.Strings; +import java.util.Collections; +import java.util.UUID; +import javax.annotation.Nullable; import org.gbif.api.model.collections.Address; import org.gbif.api.model.collections.AlternativeCode; import org.gbif.api.model.collections.Collection; @@ -25,23 +33,12 @@ import org.gbif.api.model.registry.MachineTag; import org.gbif.api.service.collections.CollectionService; import org.gbif.api.service.collections.InstitutionService; - -import java.util.UUID; - -import javax.annotation.Nullable; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.annotation.Secured; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; -import com.google.common.base.Strings; - -import static com.google.common.base.Preconditions.checkArgument; -import static org.gbif.registry.security.UserRoles.GRSCICOLL_ADMIN_ROLE; -import static org.gbif.registry.security.UserRoles.GRSCICOLL_MEDIATOR_ROLE; - /** Service to merge duplicated {@link Institution}. */ @Service public class InstitutionMergeService extends BaseMergeService { @@ -206,7 +203,9 @@ private void moveCollectionsToAnotherInstitution( // move the collections to the entity to keep PagingResponse collections = collectionService.list( - CollectionSearchRequest.builder().institution(sourceInstitutionKey).build()); + CollectionSearchRequest.builder() + .institution(Collections.singletonList(sourceInstitutionKey)) + .build()); collections .getResults() .forEach( diff --git a/registry-service/src/main/java/org/gbif/registry/service/collections/utils/SearchUtils.java b/registry-service/src/main/java/org/gbif/registry/service/collections/utils/SearchUtils.java index 8faf5c76d..ca96f201c 100644 --- a/registry-service/src/main/java/org/gbif/registry/service/collections/utils/SearchUtils.java +++ b/registry-service/src/main/java/org/gbif/registry/service/collections/utils/SearchUtils.java @@ -20,4 +20,5 @@ public class SearchUtils { public static final Pattern INTEGER_RANGE = Pattern.compile("^(\\d+|\\*)\\s*,\\s*(\\d+|\\*)$"); public static final String WILDCARD_SEARCH = "*"; + public static final int DEFAULT_FACET_LIMIT = 10; } diff --git a/registry-ws-client/src/main/java/org/gbif/registry/ws/client/collections/CollectionsSearchClient.java b/registry-ws-client/src/main/java/org/gbif/registry/ws/client/collections/CollectionsSearchClient.java index 3aaffc437..27dbec417 100644 --- a/registry-ws-client/src/main/java/org/gbif/registry/ws/client/collections/CollectionsSearchClient.java +++ b/registry-ws-client/src/main/java/org/gbif/registry/ws/client/collections/CollectionsSearchClient.java @@ -14,14 +14,17 @@ package org.gbif.registry.ws.client.collections; import java.util.List; +import java.util.Set; import lombok.AllArgsConstructor; import org.gbif.api.model.collections.request.CollectionDescriptorsSearchRequest; import org.gbif.api.model.collections.request.InstitutionSearchRequest; import org.gbif.api.model.collections.search.CollectionSearchResponse; import org.gbif.api.model.collections.search.CollectionsFullSearchResponse; +import org.gbif.api.model.collections.search.FacetedSearchResponse; import org.gbif.api.model.collections.search.InstitutionSearchResponse; -import org.gbif.api.model.common.paging.PagingResponse; import org.gbif.api.vocabulary.Country; +import org.gbif.api.vocabulary.collections.CollectionFacetParameter; +import org.gbif.api.vocabulary.collections.InstitutionFacetParameter; import org.gbif.registry.domain.collections.TypeParam; import org.springframework.cloud.openfeign.SpringQueryMap; import org.springframework.http.MediaType; @@ -43,22 +46,35 @@ List searchCrossEntities( value = "institution/search", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) - PagingResponse searchInstitutions( - @SpringQueryMap InstitutionSearchRequest searchRequest); + FacetedSearchResponse searchInstitutions( + @SpringQueryMap InstitutionSearchRequest searchRequest, + @RequestParam(value = "facet", required = false) Set facets); + + default FacetedSearchResponse + searchInstitutions(InstitutionSearchRequest searchRequest) { + return searchInstitutions(searchRequest, searchRequest.getFacets()); + } @RequestMapping( value = "collection/search", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) - PagingResponse searchCollections( - @SpringQueryMap CollectionDescriptorsSearchRequest searchRequest); + FacetedSearchResponse searchCollections( + @SpringQueryMap CollectionDescriptorsSearchRequest searchRequest, + @RequestParam(value = "facet", required = false) Set facets); + + default FacetedSearchResponse + searchCollections(@SpringQueryMap CollectionDescriptorsSearchRequest searchRequest) { + return searchCollections(searchRequest, searchRequest.getFacets()); + } default List searchCrossEntities( @RequestParam(value = "q", required = false) String query, @RequestParam(value = "hl", defaultValue = "false") boolean highlight, @RequestParam(value = "entityType", required = false) TypeParam type, - @RequestParam(value = "displayOnNHCPortal", required = false) Boolean displayOnNHCPortal, - @SpringQueryMap Country country, + @RequestParam(value = "displayOnNHCPortal", required = false) + List displayOnNHCPortal, + @SpringQueryMap List country, @RequestParam(value = "limit", defaultValue = "20") int limit) { return searchCrossEntities( SearchRequest.of(query, highlight, type, displayOnNHCPortal, country, limit)); @@ -69,8 +85,8 @@ class SearchRequest { String q; boolean hl; TypeParam entityType; - Boolean displayOnNHCPortal; - Country country; + List displayOnNHCPortal; + List country; int limit; } } diff --git a/registry-ws/src/main/java/org/gbif/registry/ws/config/WebMvcConfig.java b/registry-ws/src/main/java/org/gbif/registry/ws/config/WebMvcConfig.java index fb65b8691..2489b6889 100644 --- a/registry-ws/src/main/java/org/gbif/registry/ws/config/WebMvcConfig.java +++ b/registry-ws/src/main/java/org/gbif/registry/ws/config/WebMvcConfig.java @@ -26,6 +26,7 @@ import org.gbif.registry.ws.converter.UuidTextMessageConverter; import org.gbif.registry.ws.provider.CollectionDescriptorsSearchRequestHandlerMethodArgumentResolver; import org.gbif.registry.ws.provider.CollectionSearchRequestHandlerMethodArgumentResolver; +import org.gbif.registry.ws.provider.CountryListHandlerMethodArgumentResolver; import org.gbif.registry.ws.provider.InstitutionSearchRequestHandlerMethodArgumentResolver; import org.gbif.registry.ws.provider.PartialDateHandlerMethodArgumentResolver; import org.gbif.registry.ws.provider.networkEntitiesList.*; @@ -64,6 +65,7 @@ public void addInterceptors(InterceptorRegistry registry) { public void addArgumentResolvers(List argumentResolvers) { argumentResolvers.add(new PageableHandlerMethodArgumentResolver()); argumentResolvers.add(new CountryHandlerMethodArgumentResolver()); + argumentResolvers.add(new CountryListHandlerMethodArgumentResolver()); argumentResolvers.add(new PartialDateHandlerMethodArgumentResolver()); argumentResolvers.add(new DatasetSearchRequestHandlerMethodArgumentResolver()); argumentResolvers.add(new DatasetSuggestRequestHandlerMethodArgumentResolver()); diff --git a/registry-ws/src/main/java/org/gbif/registry/ws/provider/BaseGrSciCollSearchRequestHandlerMethodArgumentResolver.java b/registry-ws/src/main/java/org/gbif/registry/ws/provider/BaseGrSciCollSearchRequestHandlerMethodArgumentResolver.java index f26ea1035..83183bbf8 100644 --- a/registry-ws/src/main/java/org/gbif/registry/ws/provider/BaseGrSciCollSearchRequestHandlerMethodArgumentResolver.java +++ b/registry-ws/src/main/java/org/gbif/registry/ws/provider/BaseGrSciCollSearchRequestHandlerMethodArgumentResolver.java @@ -13,13 +13,33 @@ */ package org.gbif.registry.ws.provider; +import static org.gbif.registry.service.collections.utils.SearchUtils.DEFAULT_FACET_LIMIT; +import static org.gbif.ws.util.CommonWsUtils.*; +import static org.gbif.ws.util.WebserviceParameter.PARAM_FACET; +import static org.gbif.ws.util.WebserviceParameter.PARAM_FACET_LIMIT; +import static org.gbif.ws.util.WebserviceParameter.PARAM_FACET_MINCOUNT; +import static org.gbif.ws.util.WebserviceParameter.PARAM_FACET_MULTISELECT; +import static org.gbif.ws.util.WebserviceParameter.PARAM_FACET_OFFSET; + import com.google.common.base.Strings; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; import org.gbif.api.model.collections.request.SearchRequest; import org.gbif.api.model.common.paging.Pageable; +import org.gbif.api.model.common.paging.PagingRequest; import org.gbif.api.util.VocabularyUtils; import org.gbif.api.vocabulary.*; +import org.gbif.api.vocabulary.collections.CollectionsFacetParameter; +import org.gbif.api.vocabulary.collections.CollectionsSortField; import org.gbif.api.vocabulary.collections.MasterSourceType; import org.gbif.api.vocabulary.collections.Source; import org.gbif.registry.service.collections.utils.SearchUtils; @@ -27,12 +47,13 @@ import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; -public abstract class BaseGrSciCollSearchRequestHandlerMethodArgumentResolver +public abstract class BaseGrSciCollSearchRequestHandlerMethodArgumentResolver< + F extends CollectionsFacetParameter> implements HandlerMethodArgumentResolver { public static final int MAX_PAGE_SIZE = 1000; - protected void fillSearchRequestParams( + protected > void fillSearchRequestParams( T request, NativeWebRequest webRequest) { // page Pageable page = PageableProvider.getPagingRequest(webRequest, MAX_PAGE_SIZE); @@ -48,38 +69,28 @@ protected void fillSearchRequestParams( } } - request.setAlternativeCode(webRequest.getParameter("alternativeCode")); - request.setCode(webRequest.getParameter("code")); - request.setName(webRequest.getParameter("name")); + extractMultivalueParam(webRequest, "alternativeCode").ifPresent(request::setAlternativeCode); + extractMultivalueParam(webRequest, "code").ifPresent(request::setCode); + extractMultivalueParam(webRequest, "name").ifPresent(request::setName); + extractMultivalueParam(webRequest, "contact", UUID::fromString).ifPresent(request::setContact); + extractMultivalueParam(webRequest, "identifier").ifPresent(request::setIdentifier); + extractMultivalueParam( + webRequest, + "identifierType", + v -> VocabularyUtils.lookup(v, IdentifierType.class).orElse(null)) + .ifPresent(request::setIdentifierType); - String contact = webRequest.getParameter("contact"); - if (!Strings.isNullOrEmpty(contact)) { - try { - request.setContact(UUID.fromString(contact)); - } catch (Exception e) { - throw new IllegalArgumentException("Invalid UUID for contact: " + contact); - } - } + extractMultivalueParam(webRequest, "machineTagName").ifPresent(request::setMachineTagName); + extractMultivalueParam(webRequest, "machineTagNamespace") + .ifPresent(request::setMachineTagNamespace); + extractMultivalueParam(webRequest, "machineTagValue").ifPresent(request::setMachineTagValue); + extractMultivalueParam(webRequest, "city").ifPresent(request::setCity); + extractMultivalueParam(webRequest, "fuzzyName").ifPresent(request::setFuzzyName); - request.setIdentifier(webRequest.getParameter("identifier")); - request.setIdentifierType( - VocabularyUtils.lookupEnum( - webRequest.getParameter("identifierType"), IdentifierType.class)); - request.setMachineTagName(webRequest.getParameter("machineTagName")); - request.setMachineTagNamespace(webRequest.getParameter("machineTagNamespace")); - request.setMachineTagValue(webRequest.getParameter("machineTagValue")); request.setQ(webRequest.getParameter("q")); - request.setCity(webRequest.getParameter("city")); - request.setFuzzyName(webRequest.getParameter("fuzzyName")); - String active = webRequest.getParameter("active"); - if (!Strings.isNullOrEmpty(active)) { - try { - request.setActive(Boolean.parseBoolean(active)); - } catch (Exception e) { - throw new IllegalArgumentException("Invalid boolean for active: " + active); - } - } + extractMultivalueParam(webRequest, "active", Boolean::parseBoolean) + .ifPresent(request::setActive); String[] countryParams = webRequest.getParameterValues("country"); if (countryParams != null && countryParams.length > 0) { @@ -110,14 +121,8 @@ protected void fillSearchRequestParams( } } - String masterSourceTypeParam = webRequest.getParameter("masterSourceType"); - if (!Strings.isNullOrEmpty(masterSourceTypeParam)) { - try { - request.setMasterSourceType(MasterSourceType.valueOf(masterSourceTypeParam)); - } catch (Exception e) { - throw new IllegalArgumentException("Invalid master source type: " + masterSourceTypeParam); - } - } + extractMultivalueParam(webRequest, "masterSourceType", MasterSourceType::valueOf) + .ifPresent(request::setMasterSourceType); String numberSpecimensParam = webRequest.getParameter("numberSpecimens"); if (!Strings.isNullOrEmpty(numberSpecimensParam)) { @@ -125,15 +130,8 @@ protected void fillSearchRequestParams( request.setNumberSpecimens(numberSpecimensParam); } - String displayOnNHCPortal = webRequest.getParameter("displayOnNHCPortal"); - if (!Strings.isNullOrEmpty(displayOnNHCPortal)) { - try { - request.setDisplayOnNHCPortal(Boolean.parseBoolean(displayOnNHCPortal)); - } catch (Exception e) { - throw new IllegalArgumentException( - "Invalid boolean for displayOnNHCPortal: " + displayOnNHCPortal); - } - } + extractMultivalueParam(webRequest, "displayOnNHCPortal", Boolean::parseBoolean) + .ifPresent(request::setDisplayOnNHCPortal); String sortByParam = webRequest.getParameter("sortBy"); if (!Strings.isNullOrEmpty(sortByParam)) { @@ -165,30 +163,113 @@ protected void fillSearchRequestParams( request.setTypeSpecimenCount(typeSpecimenCountParam); } - String[] institutionKeysParams = webRequest.getParameterValues("institutionKey"); - if (institutionKeysParams != null && institutionKeysParams.length > 0) { - request.setInstitutionKeys(new ArrayList<>()); - for (String keyParam : institutionKeysParams) { + extractMultivalueParam(webRequest, "institutionKey", UUID::fromString) + .ifPresent(request::setInstitutionKeys); + extractMultivalueParam(webRequest, "source", Source::valueOf).ifPresent(request::setSource); + extractMultivalueParam(webRequest, "sourceId").ifPresent(request::setSourceId); + + fillFacetParams(request, webRequest); + } + + protected Optional> extractMultivalueParam( + NativeWebRequest webRequest, String paramName) { + String[] listParams = webRequest.getParameterValues(paramName); + if (listParams != null && listParams.length > 0) { + return Optional.of(Arrays.asList(listParams)); + } + return Optional.empty(); + } + + protected Optional> extractMultivalueParam( + NativeWebRequest webRequest, String paramName, Function mapper) { + String[] listParams = webRequest.getParameterValues(paramName); + + if (listParams != null) { + List result = new ArrayList<>(); + for (String param : listParams) { try { - request.getInstitutionKeys().add(UUID.fromString(keyParam)); + result.add(mapper.apply(param)); } catch (Exception ex) { throw new IllegalArgumentException( - "Invalid UUID for institution key parameter: " + keyParam); + "Invalid value " + param + " for parameter " + paramName); } } + return Optional.of(result); } + return Optional.empty(); + } + + protected > void fillFacetParams( + T searchRequest, NativeWebRequest webRequest) { + final Map params = + webRequest.getParameterMap().entrySet().stream() + .collect(Collectors.toMap(e -> e.getKey().toLowerCase(), Map.Entry::getValue)); - String sourceParam = webRequest.getParameter("source"); - if (!Strings.isNullOrEmpty(sourceParam)) { - request.setSource(Source.valueOf(sourceParam)); + final String facetMultiSelectValue = getFirstIgnoreCase(params, PARAM_FACET_MULTISELECT); + if (facetMultiSelectValue != null) { + searchRequest.setMultiSelectFacets(Boolean.parseBoolean(facetMultiSelectValue)); } - String sourceIdParam = webRequest.getParameter("sourceId"); - if (!Strings.isNullOrEmpty(sourceIdParam)) { - request.setSourceId(sourceIdParam); + final String facetMinCountValue = getFirstIgnoreCase(params, PARAM_FACET_MINCOUNT); + if (facetMinCountValue != null) { + searchRequest.setFacetMinCount(Integer.parseInt(facetMinCountValue)); } + + final String facetLimit = getFirstIgnoreCase(params, PARAM_FACET_LIMIT); + if (facetLimit != null) { + searchRequest.setFacetLimit(Integer.parseInt(facetLimit)); + } + + final String facetOffset = getFirstIgnoreCase(params, PARAM_FACET_OFFSET); + if (facetOffset != null) { + searchRequest.setFacetOffset(Integer.parseInt(facetOffset)); + } + + final List facetParams = + params.get(PARAM_FACET) != null + ? Arrays.asList(params.get(PARAM_FACET)) + : Collections.emptyList(); + if (!facetParams.isEmpty()) { + Set parsedFacets = new HashSet<>(); + for (String f : facetParams) { + if (f.isEmpty()) { + continue; + } + F facet = findFacetParam(f); + if (facet != null) { + parsedFacets.add(facet); + String pFacetOffset = getFirstIgnoreCase(params, f + '.' + PARAM_FACET_OFFSET); + String pFacetLimit = getFirstIgnoreCase(params, f + '.' + PARAM_FACET_LIMIT); + if (pFacetLimit != null) { + if (pFacetOffset != null) { + searchRequest + .getFacetPages() + .put( + facet, + new PagingRequest( + Integer.parseInt(pFacetOffset), Integer.parseInt(pFacetLimit))); + } else { + searchRequest + .getFacetPages() + .put(facet, new PagingRequest(0, Integer.parseInt(pFacetLimit))); + } + } else if (pFacetOffset != null) { + searchRequest + .getFacetPages() + .put(facet, new PagingRequest(Integer.parseInt(pFacetOffset), DEFAULT_FACET_LIMIT)); + } + } + } + searchRequest.setFacets(parsedFacets); + } + } + + private static String getFirstIgnoreCase(Map params, String param) { + return getFirst(params, param.toLowerCase()); } + protected abstract F findFacetParam(String facetParam); + protected static void validateIntegerRange(String param, String paramName) { boolean rangeMatch = SearchUtils.INTEGER_RANGE.matcher(param).find(); boolean numberMatch = true; diff --git a/registry-ws/src/main/java/org/gbif/registry/ws/provider/CollectionDescriptorsSearchRequestHandlerMethodArgumentResolver.java b/registry-ws/src/main/java/org/gbif/registry/ws/provider/CollectionDescriptorsSearchRequestHandlerMethodArgumentResolver.java index 8112161e3..0036e92f8 100644 --- a/registry-ws/src/main/java/org/gbif/registry/ws/provider/CollectionDescriptorsSearchRequestHandlerMethodArgumentResolver.java +++ b/registry-ws/src/main/java/org/gbif/registry/ws/provider/CollectionDescriptorsSearchRequestHandlerMethodArgumentResolver.java @@ -15,14 +15,12 @@ import com.google.common.base.Strings; import java.util.ArrayList; -import java.util.Arrays; import java.util.Optional; import org.gbif.api.model.collections.request.CollectionDescriptorsSearchRequest; import org.gbif.api.util.SearchTypeValidator; import org.gbif.api.util.VocabularyUtils; import org.gbif.api.vocabulary.Country; import org.gbif.api.vocabulary.Rank; -import org.gbif.api.vocabulary.TypeStatus; import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; @@ -48,48 +46,20 @@ public Object resolveArgument( CollectionDescriptorsSearchRequest.builder().build(); fillCollectionSearchRequest(searchRequest, webRequest); - String[] usageKeys = webRequest.getParameterValues("usageKey"); - if (usageKeys != null && usageKeys.length > 0) { - searchRequest.setUsageKey(new ArrayList<>()); - for (String keyParam : usageKeys) { - try { - searchRequest.getUsageKey().add(Integer.parseInt(keyParam)); - } catch (Exception ex) { - throw new IllegalArgumentException( - "Invalid integer for usage key parameter: " + keyParam); - } - } - } - - String[] usageNames = webRequest.getParameterValues("usageName"); - if (usageNames != null && usageNames.length > 0) { - searchRequest.setUsageName(Arrays.asList(usageNames)); - } - - String[] usageRanks = webRequest.getParameterValues("usageRank"); - if (usageRanks != null && usageRanks.length > 0) { - searchRequest.setUsageRank(new ArrayList<>()); - for (String param : usageRanks) { - try { - searchRequest.getUsageRank().add(Rank.valueOf(param)); - } catch (Exception ex) { - throw new IllegalArgumentException("Invalid rank for usage rank parameter: " + param); - } - } - } - - String[] taxonKeys = webRequest.getParameterValues("taxonKey"); - if (taxonKeys != null && taxonKeys.length > 0) { - searchRequest.setTaxonKey(new ArrayList<>()); - for (String keyParam : taxonKeys) { - try { - searchRequest.getTaxonKey().add(Integer.parseInt(keyParam)); - } catch (Exception ex) { - throw new IllegalArgumentException( - "Invalid integer for taxon key parameter: " + keyParam); - } - } - } + extractMultivalueParam(webRequest, "usageKey", Integer::parseInt) + .ifPresent(searchRequest::setUsageKey); + extractMultivalueParam(webRequest, "usageName").ifPresent(searchRequest::setUsageName); + extractMultivalueParam(webRequest, "usageRank", Rank::valueOf) + .ifPresent(searchRequest::setUsageRank); + extractMultivalueParam(webRequest, "taxonKey", Integer::parseInt) + .ifPresent(searchRequest::setTaxonKey); + extractMultivalueParam(webRequest, "identifiedBy").ifPresent(searchRequest::setIdentifiedBy); + extractMultivalueParam(webRequest, "typeStatus").ifPresent(searchRequest::setTypeStatus); + extractMultivalueParam(webRequest, "recordedBy").ifPresent(searchRequest::setRecordedBy); + extractMultivalueParam(webRequest, "discipline").ifPresent(searchRequest::setDiscipline); + extractMultivalueParam(webRequest, "objectClassification") + .ifPresent(searchRequest::setObjectClassification); + extractMultivalueParam(webRequest, "issue").ifPresent(searchRequest::setIssue); String[] descriptorCountries = webRequest.getParameterValues("descriptorCountry"); if (descriptorCountries != null && descriptorCountries.length > 0) { @@ -113,46 +83,9 @@ public Object resolveArgument( searchRequest.setIndividualCount(individualCountParam); } - String[] identifiedByParam = webRequest.getParameterValues("identifiedBy"); - if (identifiedByParam != null && identifiedByParam.length > 0) { - searchRequest.setIdentifiedBy(Arrays.asList(identifiedByParam)); - } - Optional.ofNullable(webRequest.getParameter("dateIdentified")) .ifPresent(v -> searchRequest.setDateIdentified(SearchTypeValidator.parseDateRange(v))); - String[] typeStatusParam = webRequest.getParameterValues("typeStatus"); - if (typeStatusParam != null && typeStatusParam.length > 0) { - searchRequest.setTypeStatus(new ArrayList<>()); - for (String param : typeStatusParam) { - try { - searchRequest.getTypeStatus().add(TypeStatus.valueOf(param).name()); - } catch (Exception ex) { - throw new IllegalArgumentException("Invalid type status parameter: " + param); - } - } - } - - String[] recordedByParam = webRequest.getParameterValues("recordedBy"); - if (recordedByParam != null && recordedByParam.length > 0) { - searchRequest.setRecordedBy(Arrays.asList(recordedByParam)); - } - - String[] disciplineParam = webRequest.getParameterValues("discipline"); - if (disciplineParam != null && disciplineParam.length > 0) { - searchRequest.setDiscipline(Arrays.asList(disciplineParam)); - } - - String[] objectClassificationParam = webRequest.getParameterValues("objectClassification"); - if (objectClassificationParam != null && objectClassificationParam.length > 0) { - searchRequest.setObjectClassification(Arrays.asList(objectClassificationParam)); - } - - String[] issueParam = webRequest.getParameterValues("issue"); - if (issueParam != null && issueParam.length > 0) { - searchRequest.setIssue(Arrays.asList(issueParam)); - } - return searchRequest; } } diff --git a/registry-ws/src/main/java/org/gbif/registry/ws/provider/CollectionSearchRequestHandlerMethodArgumentResolver.java b/registry-ws/src/main/java/org/gbif/registry/ws/provider/CollectionSearchRequestHandlerMethodArgumentResolver.java index 2af4800a6..a6def9d97 100644 --- a/registry-ws/src/main/java/org/gbif/registry/ws/provider/CollectionSearchRequestHandlerMethodArgumentResolver.java +++ b/registry-ws/src/main/java/org/gbif/registry/ws/provider/CollectionSearchRequestHandlerMethodArgumentResolver.java @@ -13,10 +13,9 @@ */ package org.gbif.registry.ws.provider; -import com.google.common.base.Strings; -import java.util.Arrays; import java.util.UUID; import org.gbif.api.model.collections.request.CollectionSearchRequest; +import org.gbif.api.vocabulary.collections.CollectionFacetParameter; import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; @@ -24,7 +23,7 @@ @SuppressWarnings("NullableProblems") public class CollectionSearchRequestHandlerMethodArgumentResolver - extends BaseGrSciCollSearchRequestHandlerMethodArgumentResolver { + extends BaseGrSciCollSearchRequestHandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { @@ -48,38 +47,20 @@ protected void fillCollectionSearchRequest( CollectionSearchRequest searchRequest, NativeWebRequest webRequest) { fillSearchRequestParams(searchRequest, webRequest); - String institution = webRequest.getParameter("institution"); - if (!Strings.isNullOrEmpty(institution)) { - try { - searchRequest.setInstitution(UUID.fromString(institution)); - } catch (Exception e) { - throw new IllegalArgumentException("Invalid UUID for institution: " + institution); - } - } - - String[] contentTypes = webRequest.getParameterValues("contentType"); - if (contentTypes != null && contentTypes.length > 0) { - searchRequest.setContentTypes(Arrays.asList(contentTypes)); - } - - String[] preservationTypes = webRequest.getParameterValues("preservationType"); - if (preservationTypes != null && preservationTypes.length > 0) { - searchRequest.setPreservationTypes(Arrays.asList(preservationTypes)); - } - - String[] accessionStatuses = webRequest.getParameterValues("accessionStatus"); - if (accessionStatuses != null && accessionStatuses.length > 0) { - searchRequest.setAccessionStatus(Arrays.asList(accessionStatuses)); - } + extractMultivalueParam(webRequest, "institution", UUID::fromString) + .ifPresent(searchRequest::setInstitution); + extractMultivalueParam(webRequest, "contentType").ifPresent(searchRequest::setContentTypes); + extractMultivalueParam(webRequest, "preservationType") + .ifPresent(searchRequest::setPreservationTypes); + extractMultivalueParam(webRequest, "accessionStatus") + .ifPresent(searchRequest::setAccessionStatus); + extractMultivalueParam(webRequest, "contentType").ifPresent(searchRequest::setContentTypes); + extractMultivalueParam(webRequest, "personalCollection", Boolean::parseBoolean) + .ifPresent(searchRequest::setPersonalCollection); + } - String personalCollection = webRequest.getParameter("personalCollection"); - if (!Strings.isNullOrEmpty(personalCollection)) { - try { - searchRequest.setPersonalCollection(Boolean.parseBoolean(personalCollection)); - } catch (Exception e) { - throw new IllegalArgumentException( - "Invalid boolean for personalCollection: " + personalCollection); - } - } + @Override + protected CollectionFacetParameter findFacetParam(String facetParam) { + return CollectionFacetParameter.valueOf(facetParam); } } diff --git a/registry-ws/src/main/java/org/gbif/registry/ws/provider/CountryListHandlerMethodArgumentResolver.java b/registry-ws/src/main/java/org/gbif/registry/ws/provider/CountryListHandlerMethodArgumentResolver.java new file mode 100644 index 000000000..248fc1615 --- /dev/null +++ b/registry-ws/src/main/java/org/gbif/registry/ws/provider/CountryListHandlerMethodArgumentResolver.java @@ -0,0 +1,55 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.gbif.registry.ws.provider; + +import java.util.Arrays; +import org.gbif.api.util.VocabularyUtils; +import org.gbif.api.vocabulary.Country; +import org.springframework.core.MethodParameter; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +public class CountryListHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return "Country[]".equals(parameter.getParameterType().getSimpleName()); + } + + @Override + public Object resolveArgument( + MethodParameter parameter, + ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, + WebDataBinderFactory binderFactory) { + final String paramName = parameter.getParameterName(); + final String[] countryCodes = + paramName != null ? webRequest.getParameterMap().get(paramName) : null; + return countryCodes != null + ? Arrays.stream(countryCodes).map(this::parseCountry).toArray(Country[]::new) + : null; + } + + private Country parseCountry(String param) { + Country parsed = Country.fromIsoCode(param); + + if (parsed == null) { + // if nothing found also try by enum name + parsed = VocabularyUtils.lookupEnum(param, Country.class); + } + return parsed; + } +} diff --git a/registry-ws/src/main/java/org/gbif/registry/ws/provider/InstitutionSearchRequestHandlerMethodArgumentResolver.java b/registry-ws/src/main/java/org/gbif/registry/ws/provider/InstitutionSearchRequestHandlerMethodArgumentResolver.java index 1b976ff03..ace6fac15 100644 --- a/registry-ws/src/main/java/org/gbif/registry/ws/provider/InstitutionSearchRequestHandlerMethodArgumentResolver.java +++ b/registry-ws/src/main/java/org/gbif/registry/ws/provider/InstitutionSearchRequestHandlerMethodArgumentResolver.java @@ -14,16 +14,15 @@ package org.gbif.registry.ws.provider; import org.gbif.api.model.collections.request.InstitutionSearchRequest; +import org.gbif.api.vocabulary.collections.InstitutionFacetParameter; import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.ModelAndViewContainer; -import java.util.Arrays; - @SuppressWarnings("NullableProblems") public class InstitutionSearchRequestHandlerMethodArgumentResolver - extends BaseGrSciCollSearchRequestHandlerMethodArgumentResolver { + extends BaseGrSciCollSearchRequestHandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { @@ -40,21 +39,16 @@ public Object resolveArgument( InstitutionSearchRequest searchRequest = InstitutionSearchRequest.builder().build(); fillSearchRequestParams(searchRequest, webRequest); - String[] types = webRequest.getParameterValues("type"); - if (types != null && types.length > 0) { - searchRequest.setType(Arrays.asList(types)); - } - - String[] governances = webRequest.getParameterValues("institutionalGovernance"); - if (governances != null && governances.length > 0) { - searchRequest.setInstitutionalGovernance(Arrays.asList(governances)); - } - - String[] disciplines = webRequest.getParameterValues("discipline"); - if (disciplines != null && disciplines.length > 0) { - searchRequest.setDisciplines(Arrays.asList(disciplines)); - } + extractMultivalueParam(webRequest, "type").ifPresent(searchRequest::setType); + extractMultivalueParam(webRequest, "institutionalGovernance") + .ifPresent(searchRequest::setInstitutionalGovernance); + extractMultivalueParam(webRequest, "discipline").ifPresent(searchRequest::setDisciplines); return searchRequest; } + + @Override + protected InstitutionFacetParameter findFacetParam(String facetParam) { + return InstitutionFacetParameter.valueOf(facetParam); + } } diff --git a/registry-ws/src/main/java/org/gbif/registry/ws/resources/collections/BaseCollectionEntityResource.java b/registry-ws/src/main/java/org/gbif/registry/ws/resources/collections/BaseCollectionEntityResource.java index 463fde2df..daf61149a 100644 --- a/registry-ws/src/main/java/org/gbif/registry/ws/resources/collections/BaseCollectionEntityResource.java +++ b/registry-ws/src/main/java/org/gbif/registry/ws/resources/collections/BaseCollectionEntityResource.java @@ -15,6 +15,35 @@ import java.util.Objects; +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.base.Preconditions; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.Explode; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.extensions.Extension; +import io.swagger.v3.oas.annotations.extensions.ExtensionProperty; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import java.io.IOException; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import javax.servlet.http.HttpServletRequest; +import lombok.SneakyThrows; import org.gbif.api.annotation.EmptyToNull; import org.gbif.api.annotation.NullToNotFound; import org.gbif.api.annotation.Trim; @@ -37,27 +66,12 @@ import org.gbif.api.service.collections.ChangeSuggestionService; import org.gbif.api.service.collections.CollectionEntityService; import org.gbif.api.vocabulary.*; +import org.gbif.api.vocabulary.collections.CollectionsSortField; import org.gbif.api.vocabulary.collections.MasterSourceType; import org.gbif.registry.persistence.mapper.collections.params.DuplicatesSearchParams; import org.gbif.registry.service.collections.duplicates.DuplicatesService; import org.gbif.registry.service.collections.merge.MergeService; import org.gbif.registry.ws.resources.Docs; - -import java.io.IOException; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; -import java.util.UUID; - -import javax.annotation.Nullable; -import javax.servlet.http.HttpServletRequest; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.ByteArrayResource; @@ -70,22 +84,6 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -import com.google.common.base.Preconditions; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Parameters; -import io.swagger.v3.oas.annotations.enums.Explode; -import io.swagger.v3.oas.annotations.enums.ParameterIn; -import io.swagger.v3.oas.annotations.extensions.Extension; -import io.swagger.v3.oas.annotations.extensions.ExtensionProperty; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import lombok.SneakyThrows; - -import static com.google.common.base.Preconditions.checkArgument; - /** Base class to implement the CRUD methods of a {@link CollectionEntity}. */ @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) public abstract class BaseCollectionEntityResource< @@ -982,4 +980,12 @@ public ResponseEntity getBatchResultFile(@PathVariable("key") int batc private String getNormalizedApiBaseUrl() { return apiBaseUrl.endsWith("/") ? apiBaseUrl.substring(0, apiBaseUrl.length() - 1) : apiBaseUrl; } + + protected String join(List values, Function mapper) { + return values != null ? values.stream().map(mapper).collect(Collectors.joining("-")) : null; + } + + protected String join(List values) { + return values != null ? String.join("-", values) : null; + } } diff --git a/registry-ws/src/main/java/org/gbif/registry/ws/resources/collections/CollectionResource.java b/registry-ws/src/main/java/org/gbif/registry/ws/resources/collections/CollectionResource.java index 5a7d96be9..89f04acd5 100644 --- a/registry-ws/src/main/java/org/gbif/registry/ws/resources/collections/CollectionResource.java +++ b/registry-ws/src/main/java/org/gbif/registry/ws/resources/collections/CollectionResource.java @@ -45,7 +45,6 @@ import java.util.List; import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import javax.servlet.http.HttpServletResponse; @@ -73,6 +72,7 @@ import org.gbif.api.util.iterables.Iterables; import org.gbif.api.vocabulary.Country; import org.gbif.api.vocabulary.GbifRegion; +import org.gbif.api.vocabulary.IdentifierType; import org.gbif.api.vocabulary.Rank; import org.gbif.api.vocabulary.collections.AccessionStatus; import org.gbif.api.vocabulary.collections.CollectionContentType; @@ -365,34 +365,22 @@ private String getExportFileHeader(CollectionSearchRequest searchRequest, Export String preFileName = CsvWriter.notNullJoiner( "-", - searchRequest.getGbifRegion() != null - ? searchRequest.getGbifRegion().stream() - .map(GbifRegion::name) - .collect(Collectors.joining("-")) - : null, - searchRequest.getCountry() != null - ? searchRequest.getCountry().stream() - .map(Country::getIso2LetterCode) - .collect(Collectors.joining("-")) - : null, - searchRequest.getCity(), - searchRequest.getInstitution() != null - ? searchRequest.getInstitution().toString() - : null, - searchRequest.getAlternativeCode(), - searchRequest.getCode(), - searchRequest.getName(), - searchRequest.getContact() != null ? searchRequest.getContact().toString() : null, - searchRequest.getIdentifierType() != null - ? searchRequest.getIdentifierType().name() - : null, - searchRequest.getIdentifier(), - searchRequest.getMachineTagNamespace(), - searchRequest.getMachineTagName(), - searchRequest.getMachineTagValue(), - searchRequest.getFuzzyName(), + join(searchRequest.getGbifRegion(), GbifRegion::name), + join(searchRequest.getCountry(), Country::getIso2LetterCode), + join(searchRequest.getCity()), + join(searchRequest.getInstitution(), UUID::toString), + join(searchRequest.getAlternativeCode()), + join(searchRequest.getCode()), + join(searchRequest.getName()), + join(searchRequest.getContact(), UUID::toString), + join(searchRequest.getIdentifierType(), IdentifierType::name), + join(searchRequest.getIdentifier()), + join(searchRequest.getMachineTagNamespace()), + join(searchRequest.getMachineTagName()), + join(searchRequest.getMachineTagValue()), + join(searchRequest.getFuzzyName()), searchRequest.getQ()); - if (preFileName.length() > 0) { + if (!preFileName.isEmpty()) { preFileName += "-"; } return ContentDisposition.builder("attachment") diff --git a/registry-ws/src/main/java/org/gbif/registry/ws/resources/collections/CollectionsSearchResource.java b/registry-ws/src/main/java/org/gbif/registry/ws/resources/collections/CollectionsSearchResource.java index 7966c2c80..6ec8dacd1 100644 --- a/registry-ws/src/main/java/org/gbif/registry/ws/resources/collections/CollectionsSearchResource.java +++ b/registry-ws/src/main/java/org/gbif/registry/ws/resources/collections/CollectionsSearchResource.java @@ -22,16 +22,19 @@ import io.swagger.v3.oas.annotations.extensions.ExtensionProperty; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; +import java.util.Arrays; import java.util.List; import org.gbif.api.documentation.CommonParameters; import org.gbif.api.model.collections.request.CollectionDescriptorsSearchRequest; import org.gbif.api.model.collections.request.InstitutionSearchRequest; import org.gbif.api.model.collections.search.CollectionSearchResponse; import org.gbif.api.model.collections.search.CollectionsFullSearchResponse; +import org.gbif.api.model.collections.search.FacetedSearchResponse; import org.gbif.api.model.collections.search.InstitutionSearchResponse; import org.gbif.api.model.common.paging.Pageable; import org.gbif.api.model.common.paging.PagingResponse; import org.gbif.api.vocabulary.Country; +import org.gbif.api.vocabulary.collections.CollectionFacetParameter; import org.gbif.registry.domain.collections.TypeParam; import org.gbif.registry.service.collections.CollectionsSearchService; import org.springframework.http.MediaType; @@ -91,11 +94,12 @@ public List searchCrossEntities( @RequestParam(value = "q", required = false) String query, @RequestParam(value = "hl", defaultValue = "false") boolean highlight, @RequestParam(value = "entityType", required = false) TypeParam type, - @RequestParam(value = "displayOnNHCPortal", required = false) Boolean displayOnNHCPortal, - Country country, + @RequestParam(value = "displayOnNHCPortal", required = false) + List displayOnNHCPortal, + Country[] country, @RequestParam(value = "limit", defaultValue = "20") int limit) { return collectionsSearchService.search( - query, highlight, type, displayOnNHCPortal, country, limit); + query, highlight, type, displayOnNHCPortal, Arrays.asList(country), limit); } @Operation( @@ -129,8 +133,8 @@ public PagingResponse searchInstitutions( @ApiResponse(responseCode = "200", description = "Search successful") @ApiResponse(responseCode = "400", description = "Invalid search query provided") @GetMapping("collection/search") - public PagingResponse searchCollections( - CollectionDescriptorsSearchRequest searchRequest) { + public FacetedSearchResponse + searchCollections(CollectionDescriptorsSearchRequest searchRequest) { return collectionsSearchService.searchCollections(searchRequest); } } diff --git a/registry-ws/src/main/java/org/gbif/registry/ws/resources/collections/InstitutionResource.java b/registry-ws/src/main/java/org/gbif/registry/ws/resources/collections/InstitutionResource.java index 2bceef117..1114801fb 100644 --- a/registry-ws/src/main/java/org/gbif/registry/ws/resources/collections/InstitutionResource.java +++ b/registry-ws/src/main/java/org/gbif/registry/ws/resources/collections/InstitutionResource.java @@ -23,6 +23,19 @@ import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.List; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; +import javax.servlet.http.HttpServletResponse; import org.gbif.api.annotation.NullToNotFound; import org.gbif.api.annotation.Trim; import org.gbif.api.documentation.CommonParameters; @@ -40,6 +53,7 @@ import org.gbif.api.util.iterables.Iterables; import org.gbif.api.vocabulary.Country; import org.gbif.api.vocabulary.GbifRegion; +import org.gbif.api.vocabulary.IdentifierType; import org.gbif.api.vocabulary.collections.Discipline; import org.gbif.api.vocabulary.collections.InstitutionGovernance; import org.gbif.api.vocabulary.collections.InstitutionType; @@ -58,19 +72,6 @@ import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; -import javax.servlet.http.HttpServletResponse; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.Writer; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.List; -import java.util.UUID; -import java.util.stream.Collectors; - /** * Class that acts both as the WS endpoint for {@link Institution} entities and also provides an * * implementation of {@link InstitutionService}. @@ -139,15 +140,15 @@ public InstitutionResource( schema = @Schema(implementation = Discipline.class), in = ParameterIn.QUERY), @Parameter( - name = "sourceId", - description = "sourceId of MasterSourceMetadata", - schema = @Schema(implementation = String.class), - in = ParameterIn.QUERY), + name = "sourceId", + description = "sourceId of MasterSourceMetadata", + schema = @Schema(implementation = String.class), + in = ParameterIn.QUERY), @Parameter( - name = "source", - description = "Source attribute of MasterSourceMetadata", - schema = @Schema(implementation = Source.class), - in = ParameterIn.QUERY) + name = "source", + description = "Source attribute of MasterSourceMetadata", + schema = @Schema(implementation = Source.class), + in = ParameterIn.QUERY) }) @SearchRequestParameters @interface InstitutionSearchParameters {} @@ -334,31 +335,21 @@ private String getExportFileHeader(InstitutionSearchRequest searchRequest, Expor String preFileName = CsvWriter.notNullJoiner( "-", - searchRequest.getGbifRegion() != null - ? searchRequest.getGbifRegion().stream() - .map(GbifRegion::name) - .collect(Collectors.joining("-")) - : null, - searchRequest.getCountry() != null - ? searchRequest.getCountry().stream() - .map(Country::getIso2LetterCode) - .collect(Collectors.joining("-")) - : null, - searchRequest.getCity(), - searchRequest.getAlternativeCode(), - searchRequest.getCode(), - searchRequest.getName(), - searchRequest.getContact() != null ? searchRequest.getContact().toString() : null, - searchRequest.getIdentifierType() != null - ? searchRequest.getIdentifierType().name() - : null, - searchRequest.getIdentifier(), - searchRequest.getMachineTagNamespace(), - searchRequest.getMachineTagName(), - searchRequest.getMachineTagValue(), - searchRequest.getFuzzyName(), + join(searchRequest.getGbifRegion(), GbifRegion::name), + join(searchRequest.getCountry(), Country::getIso2LetterCode), + join(searchRequest.getCity()), + join(searchRequest.getAlternativeCode()), + join(searchRequest.getCode()), + join(searchRequest.getName()), + join(searchRequest.getContact(), UUID::toString), + join(searchRequest.getIdentifierType(), IdentifierType::name), + join(searchRequest.getIdentifier()), + join(searchRequest.getMachineTagNamespace()), + join(searchRequest.getMachineTagName()), + join(searchRequest.getMachineTagValue()), + join(searchRequest.getFuzzyName()), searchRequest.getQ()); - if (preFileName.length() > 0) { + if (!preFileName.isEmpty()) { preFileName += "-"; }