diff --git a/CHANGELOG.md b/CHANGELOG.md index a582f53bf..9b19d9888 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## Unreleased +### Added + +- New endpoint `/v6/digipress` for faster retrieval of the newspaper list (manifestations): + supports both filtering and sorting + ## [9.2.1](https://github.com/dbmdz/metadata-service/releases/tag/9.2.1) - 2024-06-11 ### Fixed diff --git a/metasvc-client/pom.xml b/metasvc-client/pom.xml index 42dd26b1d..c1d7c63f2 100644 --- a/metasvc-client/pom.xml +++ b/metasvc-client/pom.xml @@ -6,7 +6,7 @@ io.github.dbmdz.metadata metasvc - 9.2.2-SNAPSHOT + 9.3.0-SNAPSHOT Metadata-Service: Repository Client diff --git a/metasvc-client/src/main/java/de/digitalcollections/client/BaseRestClient.java b/metasvc-client/src/main/java/de/digitalcollections/client/BaseRestClient.java index 07afc55b8..5bd63e4e6 100644 --- a/metasvc-client/src/main/java/de/digitalcollections/client/BaseRestClient.java +++ b/metasvc-client/src/main/java/de/digitalcollections/client/BaseRestClient.java @@ -1,11 +1,13 @@ package de.digitalcollections.client; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import de.digitalcollections.model.exception.TechnicalException; import de.digitalcollections.model.exception.http.HttpErrorDecoder; import de.digitalcollections.model.list.ListRequest; +import de.digitalcollections.model.list.ListResponse; import de.digitalcollections.model.list.filtering.FilterCriteria; import de.digitalcollections.model.list.filtering.FilterCriterion; import de.digitalcollections.model.list.filtering.FilterLogicalOperator; @@ -202,11 +204,41 @@ protected String doDeleteRequestForString(String requestUrl) throws TechnicalExc } } + protected ListResponse doGetRequestForObjectListResponse( + String requestUrl, Class targetType, ListRequest listRequest) throws TechnicalException { + if (listRequest != null) { + if (listRequest.hasFiltering()) + requestUrl += + (requestUrl.contains("?") ? "&" : "?") + + getFilterParamsAsString(listRequest.getFiltering()); + if (listRequest.hasSorting()) + requestUrl += (requestUrl.contains("?") ? "&" : "?") + getSortParams(listRequest); + } + HttpRequest req = createGetRequest(requestUrl); + try { + HttpResponse response = http.send(req, HttpResponse.BodyHandlers.ofByteArray()); + Integer statusCode = response.statusCode(); + if (statusCode >= 400) { + throw HttpErrorDecoder.decode("GET " + requestUrl, statusCode, response); + } + // This is the most performant approach for Jackson + final byte[] body = response.body(); + if (body == null || body.length == 0) { + return null; + } + ListResponse result = + mapper.readerFor(new TypeReference>() {}).readValue(body); + return result; + } catch (IOException | InterruptedException e) { + throw new TechnicalException("Failed to retrieve response due to connection error", e); + } + } + protected T doGetRequestForObject(String requestUrl) throws TechnicalException { - return (T) doGetRequestForObject(requestUrl, targetType); + return doGetRequestForObject(requestUrl, targetType); } - protected Object doGetRequestForObject(String requestUrl, Class targetType) + protected U doGetRequestForObject(String requestUrl, Class targetType) throws TechnicalException { HttpRequest req = createGetRequest(requestUrl); try { @@ -220,7 +252,7 @@ protected Object doGetRequestForObject(String requestUrl, Class targetType) if (body == null || body.length == 0) { return null; } - T result = mapper.readerFor(targetType).readValue(body); + U result = mapper.readerFor(targetType).readValue(body); return result; } catch (IOException | InterruptedException e) { throw new TechnicalException("Failed to retrieve response due to connection error", e); @@ -228,18 +260,28 @@ protected Object doGetRequestForObject(String requestUrl, Class targetType) } protected List doGetRequestForObjectList(String requestUrl) throws TechnicalException { - return doGetRequestForObjectList(requestUrl, targetType, null); + return doGetRequestForObjectList(requestUrl, targetType, (ListRequest) null); } - protected List doGetRequestForObjectList(String requestUrl, Class targetType) + protected List doGetRequestForObjectList(String requestUrl, Class targetType) throws TechnicalException { - return doGetRequestForObjectList(requestUrl, targetType, null); + return doGetRequestForObjectList(requestUrl, targetType, (ListRequest) null); } - protected List doGetRequestForObjectList( - String requestUrl, Class targetType, Filtering filtering) throws TechnicalException { - if (filtering != null) { - requestUrl += (requestUrl.contains("?") ? "&" : "?") + getFilterParamsAsString(filtering); + protected List doGetRequestForObjectList( + String requestUrl, Class targetType, Filtering filtering) throws TechnicalException { + return doGetRequestForObjectList(requestUrl, targetType, new ListRequest(filtering)); + } + + protected List doGetRequestForObjectList( + String requestUrl, Class targetType, ListRequest listRequest) throws TechnicalException { + if (listRequest != null) { + if (listRequest.hasFiltering()) + requestUrl += + (requestUrl.contains("?") ? "&" : "?") + + getFilterParamsAsString(listRequest.getFiltering()); + if (listRequest.hasSorting()) + requestUrl += (requestUrl.contains("?") ? "&" : "?") + getSortParams(listRequest); } HttpRequest req = createGetRequest(requestUrl); // TODO add creation of a request id if needed @@ -255,7 +297,7 @@ protected List doGetRequestForObjectList( if (body == null || body.length == 0) { return null; } - List result = mapper.readerForListOf(targetType).readValue(body); + List result = mapper.readerForListOf(targetType).readValue(body); return result; } catch (IOException | InterruptedException e) { throw new TechnicalException("Failed to retrieve response due to connection error", e); @@ -295,6 +337,7 @@ protected PageResponse doGetRequestForPagedObjectList( } } + // FIXME: targetType is ignored. protected PageResponse doGetRequestForPagedObjectList( String requestUrl, PageRequest pageRequest, Class targetType) throws TechnicalException { return doGetRequestForPagedObjectList(requestUrl, pageRequest); @@ -524,7 +567,7 @@ protected T doPutRequestForObject(String requestUrl, T object) throws TechnicalE } } - protected Object doPutRequestForObject(String requestUrl, Object bodyObject, Class targetType) + protected U doPutRequestForObject(String requestUrl, Object bodyObject, Class targetType) throws TechnicalException { try { HttpRequest req = createPutRequest(requestUrl, bodyObject); @@ -538,15 +581,15 @@ protected Object doPutRequestForObject(String requestUrl, Object bodyObject, Cla if (body == null) { return null; } - Object result = mapper.readerFor(targetType).readValue(body); + U result = mapper.readerFor(targetType).readValue(body); return result; } catch (IOException | InterruptedException e) { throw new TechnicalException("Failed to retrieve response due to error", e); } } - protected List> doPutRequestForObjectList( - String requestUrl, List> list, Class targetType) throws TechnicalException { + protected List doPutRequestForObjectList( + String requestUrl, List list, Class targetType) throws TechnicalException { try { HttpRequest req = createPutRequest(requestUrl, list); HttpResponse response = http.send(req, HttpResponse.BodyHandlers.ofByteArray()); @@ -559,7 +602,7 @@ protected List> doPutRequestForObjectList( if (body == null || body.length == 0) { return null; } - List> result = mapper.readerForListOf(targetType).readValue(body); + List result = mapper.readerForListOf(targetType).readValue(body); return result; } catch (IOException | InterruptedException e) { throw new TechnicalException("Failed to retrieve response due to error", e); diff --git a/metasvc-client/src/main/java/de/digitalcollections/cudami/client/identifiable/entity/work/CudamiManifestationsClient.java b/metasvc-client/src/main/java/de/digitalcollections/cudami/client/identifiable/entity/work/CudamiManifestationsClient.java index 97a985837..712d18433 100644 --- a/metasvc-client/src/main/java/de/digitalcollections/cudami/client/identifiable/entity/work/CudamiManifestationsClient.java +++ b/metasvc-client/src/main/java/de/digitalcollections/cudami/client/identifiable/entity/work/CudamiManifestationsClient.java @@ -5,8 +5,11 @@ import de.digitalcollections.model.exception.TechnicalException; import de.digitalcollections.model.identifiable.entity.item.Item; import de.digitalcollections.model.identifiable.entity.manifestation.Manifestation; +import de.digitalcollections.model.list.ListRequest; +import de.digitalcollections.model.list.ListResponse; import de.digitalcollections.model.list.paging.PageRequest; import de.digitalcollections.model.list.paging.PageResponse; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.net.http.HttpClient; import java.util.List; import java.util.Locale; @@ -14,6 +17,9 @@ public class CudamiManifestationsClient extends CudamiEntitiesClient { + @SuppressFBWarnings(value = "SS_SHOULD_BE_STATIC", justification = "non-static is fine, though") + private final String digiPressBaseEndpoint = API_VERSION_PREFIX + "/digipress"; + public CudamiManifestationsClient(HttpClient http, String serverUrl, ObjectMapper mapper) { super(http, serverUrl, Manifestation.class, mapper, API_VERSION_PREFIX + "/manifestations"); } @@ -34,4 +40,13 @@ public List getLanguagesOfItems(UUID uuid) throws TechnicalException { return doGetRequestForObjectList( String.format("%s/%s/items/languages", baseEndpoint, uuid), Locale.class); } + + public ListResponse findNewspapers(ListRequest listRequest) + throws TechnicalException { + return doGetRequestForObjectListResponse(digiPressBaseEndpoint, targetType, listRequest); + } + + public void refreshNewspapers() throws TechnicalException { + doPutRequestForObject(digiPressBaseEndpoint + "/refresh", null); + } } diff --git a/metasvc-client/src/main/java/de/digitalcollections/cudami/client/identifiable/entity/work/CudamiWorksClient.java b/metasvc-client/src/main/java/de/digitalcollections/cudami/client/identifiable/entity/work/CudamiWorksClient.java index 0d4f6a52f..0b4c634f3 100644 --- a/metasvc-client/src/main/java/de/digitalcollections/cudami/client/identifiable/entity/work/CudamiWorksClient.java +++ b/metasvc-client/src/main/java/de/digitalcollections/cudami/client/identifiable/entity/work/CudamiWorksClient.java @@ -4,7 +4,6 @@ import de.digitalcollections.cudami.client.identifiable.entity.CudamiEntitiesClient; import de.digitalcollections.model.exception.TechnicalException; import de.digitalcollections.model.identifiable.entity.agent.Agent; -import de.digitalcollections.model.identifiable.entity.digitalobject.DigitalObject; import de.digitalcollections.model.identifiable.entity.manifestation.Manifestation; import de.digitalcollections.model.identifiable.entity.work.Work; import de.digitalcollections.model.list.paging.PageRequest; @@ -23,8 +22,7 @@ public CudamiWorksClient(HttpClient http, String serverUrl, ObjectMapper mapper) public Set getCreators(UUID uuid) throws TechnicalException { return (Set) - doGetRequestForObjectList( - String.format("%s/%s/creators", baseEndpoint, uuid), DigitalObject.class); + doGetRequestForObjectList(String.format("%s/%s/creators", baseEndpoint, uuid), Agent.class); } public PageResponse findChildren(UUID uuid, PageRequest pageRequest) diff --git a/metasvc-lobid-client/pom.xml b/metasvc-lobid-client/pom.xml index 26e1b92dc..ffde81c0c 100644 --- a/metasvc-lobid-client/pom.xml +++ b/metasvc-lobid-client/pom.xml @@ -7,7 +7,7 @@ io.github.dbmdz.metadata metasvc - 9.2.2-SNAPSHOT + 9.3.0-SNAPSHOT Metadata-Service: lobid.org Client diff --git a/metasvc-model/pom.xml b/metasvc-model/pom.xml index 85d3d598a..2d5207889 100644 --- a/metasvc-model/pom.xml +++ b/metasvc-model/pom.xml @@ -6,7 +6,7 @@ io.github.dbmdz.metadata metasvc - 9.2.2-SNAPSHOT + 9.3.0-SNAPSHOT Metadata-Service: Model diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/list/ListRequest.java b/metasvc-model/src/main/java/de/digitalcollections/model/list/ListRequest.java index 3d3ed157d..31a590c85 100644 --- a/metasvc-model/src/main/java/de/digitalcollections/model/list/ListRequest.java +++ b/metasvc-model/src/main/java/de/digitalcollections/model/list/ListRequest.java @@ -40,6 +40,10 @@ public ListRequest(Sorting sorting) { this(sorting, null, null); } + public ListRequest(Filtering filtering) { + this(null, filtering, null); + } + /** * Creates a new {@link ListRequest} with sorting parameters applied. * diff --git a/metasvc-server/backend-api/pom.xml b/metasvc-server/backend-api/pom.xml index f147867c5..5711c9cd4 100644 --- a/metasvc-server/backend-api/pom.xml +++ b/metasvc-server/backend-api/pom.xml @@ -6,7 +6,7 @@ io.github.dbmdz.metadata metasvc-server - 9.2.2-SNAPSHOT + 9.3.0-SNAPSHOT Metadata-Service: Repository Server (Backend API) diff --git a/metasvc-server/backend-api/src/main/java/io/github/dbmdz/metadata/server/backend/api/repository/identifiable/entity/work/DigipressManifestationRepository.java b/metasvc-server/backend-api/src/main/java/io/github/dbmdz/metadata/server/backend/api/repository/identifiable/entity/work/DigipressManifestationRepository.java new file mode 100644 index 000000000..dce21e89d --- /dev/null +++ b/metasvc-server/backend-api/src/main/java/io/github/dbmdz/metadata/server/backend/api/repository/identifiable/entity/work/DigipressManifestationRepository.java @@ -0,0 +1,14 @@ +package io.github.dbmdz.metadata.server.backend.api.repository.identifiable.entity.work; + +import de.digitalcollections.model.identifiable.entity.manifestation.Manifestation; +import de.digitalcollections.model.list.ListRequest; +import de.digitalcollections.model.list.ListResponse; +import io.github.dbmdz.metadata.server.backend.api.repository.exceptions.RepositoryException; + +public interface DigipressManifestationRepository { + + void refreshTable(); + + ListResponse getNewspapers(ListRequest listRequest) + throws RepositoryException; +} diff --git a/metasvc-server/backend-file/pom.xml b/metasvc-server/backend-file/pom.xml index 5d866c378..7d4a42bd4 100644 --- a/metasvc-server/backend-file/pom.xml +++ b/metasvc-server/backend-file/pom.xml @@ -5,7 +5,7 @@ io.github.dbmdz.metadata metasvc-server - 9.2.2-SNAPSHOT + 9.3.0-SNAPSHOT Metadata-Service: Repository Server (Backend IMPL File) diff --git a/metasvc-server/backend-inmemory/pom.xml b/metasvc-server/backend-inmemory/pom.xml index 043ee1ca5..7e9ad2bfd 100644 --- a/metasvc-server/backend-inmemory/pom.xml +++ b/metasvc-server/backend-inmemory/pom.xml @@ -6,7 +6,7 @@ io.github.dbmdz.metadata metasvc-server - 9.2.2-SNAPSHOT + 9.3.0-SNAPSHOT Metadata-Service: Repository Server (Backend IMPL InMemory) diff --git a/metasvc-server/backend-jdbi/pom.xml b/metasvc-server/backend-jdbi/pom.xml index a4e325c5b..842b7dd59 100644 --- a/metasvc-server/backend-jdbi/pom.xml +++ b/metasvc-server/backend-jdbi/pom.xml @@ -8,7 +8,7 @@ io.github.dbmdz.metadata metasvc-server - 9.2.2-SNAPSHOT + 9.3.0-SNAPSHOT Metadata-Service: Repository Server (Backend IMPL JDBI PostgreSql) diff --git a/metasvc-server/backend-jdbi/src/main/java/io/github/dbmdz/metadata/server/backend/impl/database/AbstractPagingSortingFilteringRepositoryImpl.java b/metasvc-server/backend-jdbi/src/main/java/io/github/dbmdz/metadata/server/backend/impl/database/AbstractPagingSortingFilteringRepositoryImpl.java index 5a958f141..7d7b9c535 100644 --- a/metasvc-server/backend-jdbi/src/main/java/io/github/dbmdz/metadata/server/backend/impl/database/AbstractPagingSortingFilteringRepositoryImpl.java +++ b/metasvc-server/backend-jdbi/src/main/java/io/github/dbmdz/metadata/server/backend/impl/database/AbstractPagingSortingFilteringRepositoryImpl.java @@ -46,20 +46,20 @@ private void addOffset(PageRequest pageRequest, StringBuilder sqlQuery) { } } - protected void addOrderBy(PageRequest pageRequest, StringBuilder sqlQuery) { - if (pageRequest != null) { - // Sorting - Sorting sorting = pageRequest.getSorting(); - String orderBy = getOrderBy(sorting); - if (StringUtils.hasText(orderBy)) { - if (!sqlQuery.toString().matches("(?i).* order by .*")) { - sqlQuery.append(" ORDER BY "); - } else { - sqlQuery.append(", "); - } - sqlQuery.append(orderBy); - } + protected void addOrderBy(Sorting sorting, StringBuilder sqlQuery) { + Optional orderBy = + Optional.ofNullable(sorting).map(this::getOrderBy).filter(StringUtils::hasText); + if (orderBy.isEmpty()) return; + if (!sqlQuery.toString().matches("(?i).* order by .*")) { + sqlQuery.append(" ORDER BY "); + } else { + sqlQuery.append(", "); } + sqlQuery.append(orderBy.get()); + } + + protected void addOrderBy(PageRequest pageRequest, StringBuilder sqlQuery) { + if (pageRequest != null) addOrderBy(pageRequest.getSorting(), sqlQuery); } /* diff --git a/metasvc-server/backend-jdbi/src/main/java/io/github/dbmdz/metadata/server/backend/impl/jdbi/identifiable/entity/work/DigipressManifestationRepositoryImpl.java b/metasvc-server/backend-jdbi/src/main/java/io/github/dbmdz/metadata/server/backend/impl/jdbi/identifiable/entity/work/DigipressManifestationRepositoryImpl.java new file mode 100644 index 000000000..ac41960f6 --- /dev/null +++ b/metasvc-server/backend-jdbi/src/main/java/io/github/dbmdz/metadata/server/backend/impl/jdbi/identifiable/entity/work/DigipressManifestationRepositoryImpl.java @@ -0,0 +1,104 @@ +package io.github.dbmdz.metadata.server.backend.impl.jdbi.identifiable.entity.work; + +import de.digitalcollections.cudami.model.config.CudamiConfig; +import de.digitalcollections.model.identifiable.entity.Entity; +import de.digitalcollections.model.identifiable.entity.agent.Agent; +import de.digitalcollections.model.identifiable.entity.manifestation.Manifestation; +import de.digitalcollections.model.list.ListRequest; +import de.digitalcollections.model.list.ListResponse; +import io.github.dbmdz.metadata.server.backend.api.repository.exceptions.RepositoryException; +import io.github.dbmdz.metadata.server.backend.api.repository.identifiable.IdentifierRepository; +import io.github.dbmdz.metadata.server.backend.api.repository.identifiable.alias.UrlAliasRepository; +import io.github.dbmdz.metadata.server.backend.api.repository.identifiable.entity.work.DigipressManifestationRepository; +import io.github.dbmdz.metadata.server.backend.impl.jdbi.identifiable.entity.EntityRepositoryImpl; +import io.github.dbmdz.metadata.server.backend.impl.jdbi.identifiable.entity.agent.AgentRepositoryImpl; +import io.github.dbmdz.metadata.server.backend.impl.jdbi.identifiable.entity.geo.location.HumanSettlementRepositoryImpl; +import io.github.dbmdz.metadata.server.backend.impl.jdbi.type.LocalDateRangeMapper; +import io.github.dbmdz.metadata.server.backend.impl.jdbi.type.MainSubTypeMapper.ExpressionTypeMapper; +import io.github.dbmdz.metadata.server.backend.impl.jdbi.type.TitleMapper; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.jdbi.v3.core.Jdbi; +import org.jdbi.v3.core.JdbiException; +import org.jdbi.v3.core.statement.StatementException; +import org.springframework.stereotype.Repository; + +@Repository +public class DigipressManifestationRepositoryImpl extends ManifestationRepositoryImpl + implements DigipressManifestationRepository { + + public DigipressManifestationRepositoryImpl( + Jdbi jdbi, + CudamiConfig cudamiConfig, + IdentifierRepository identifierRepository, + UrlAliasRepository urlAliasRepository, + ExpressionTypeMapper expressionTypeMapper, + LocalDateRangeMapper dateRangeMapper, + TitleMapper titleMapper, + EntityRepositoryImpl entityRepository, + AgentRepositoryImpl agentRepository, + HumanSettlementRepositoryImpl humanSettlementRepository) { + super( + jdbi, + cudamiConfig, + identifierRepository, + urlAliasRepository, + expressionTypeMapper, + dateRangeMapper, + titleMapper, + entityRepository, + agentRepository, + humanSettlementRepository); + } + + @Override + public void refreshTable() { + dbi.useHandle(h -> h.execute("refresh materialized view digipress;")); + } + + @Override + public String getColumnName(String modelProperty) { + return switch (modelProperty) { + case "lastModified" -> "mf_lastModified"; + case "identifiableObjectType" -> "mf_identifiableObjectType"; + case "nameLocalesOfOriginalScripts" -> "mf_nameLocalesOfOriginalScripts"; + case "identifiers.id" -> "id_id"; + case "identifiers.namespace" -> "id_namespace"; + case "expressionTypes", + "manifestationType", + "manufacturingType", + "mediaTypes", + "otherLanguages" -> + "mf_%s".formatted(modelProperty); + default -> super.getColumnName(modelProperty).replace('.', '_'); + }; + } + + @Override + public ListResponse getNewspapers(ListRequest listRequest) + throws RepositoryException { + StringBuilder sqlQuery = new StringBuilder("select * from digipress "); + Map mappings = new HashMap<>(); + if (listRequest.hasFiltering()) addFiltering(listRequest.getFiltering(), sqlQuery, mappings); + if (listRequest.hasSorting()) addOrderBy(listRequest.getSorting(), sqlQuery); + + try { + List results = + dbi.withHandle( + h -> + h.createQuery(sqlQuery.toString()) + .bindMap(mappings) + .reduceRows(this::basicReduceRowsBiConsumer) + .collect(Collectors.toList())); + return new ListResponse<>(results, listRequest); + } catch (StatementException e) { + String detailMessage = e.getCause() != null ? e.getCause().getMessage() : e.getMessage(); + throw new RepositoryException( + String.format("The SQL statement is defective: %s", detailMessage), e); + } catch (JdbiException e) { + throw new RepositoryException(e); + } + } +} diff --git a/metasvc-server/backend-jdbi/src/main/java/io/github/dbmdz/metadata/server/backend/impl/jdbi/identifiable/entity/work/ManifestationRepositoryImpl.java b/metasvc-server/backend-jdbi/src/main/java/io/github/dbmdz/metadata/server/backend/impl/jdbi/identifiable/entity/work/ManifestationRepositoryImpl.java index 1783df1b1..fc75cc3b5 100644 --- a/metasvc-server/backend-jdbi/src/main/java/io/github/dbmdz/metadata/server/backend/impl/jdbi/identifiable/entity/work/ManifestationRepositoryImpl.java +++ b/metasvc-server/backend-jdbi/src/main/java/io/github/dbmdz/metadata/server/backend/impl/jdbi/identifiable/entity/work/ManifestationRepositoryImpl.java @@ -59,12 +59,14 @@ import org.jdbi.v3.core.result.RowView; import org.jdbi.v3.core.statement.PreparedBatch; import org.jdbi.v3.core.statement.StatementException; +import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Repository; @SuppressFBWarnings( value = "VA_FORMAT_STRING_USES_NEWLINE", justification = "Newline is OK in multiline strings") @Repository +@Primary public class ManifestationRepositoryImpl extends EntityRepositoryImpl implements ManifestationRepository { @@ -255,13 +257,13 @@ public String getColumnName(String modelProperty) { case "titles": case "version": case "work": - return modelProperty; + return tableAlias + "." + modelProperty; case "expressionTypes": case "manifestationType": case "manufacturingType": case "mediaTypes": case "otherLanguages": - return modelProperty.toLowerCase(); + return tableAlias + "." + modelProperty.toLowerCase(); default: return super.getColumnName(modelProperty); } diff --git a/metasvc-server/backend-jdbi/src/main/resources/io/github/dbmdz/metadata/server/backend/impl/database/migration/R__materialized_view_digipress.sql b/metasvc-server/backend-jdbi/src/main/resources/io/github/dbmdz/metadata/server/backend/impl/database/migration/R__materialized_view_digipress.sql new file mode 100644 index 000000000..7085f0f8f --- /dev/null +++ b/metasvc-server/backend-jdbi/src/main/resources/io/github/dbmdz/metadata/server/backend/impl/database/migration/R__materialized_view_digipress.sql @@ -0,0 +1,158 @@ +DROP MATERIALIZED VIEW IF EXISTS digipress; + +CREATE MATERIALIZED VIEW digipress AS +SELECT + mf.uuid mf_uuid, + mf.created mf_created, + mf.last_modified mf_lastModified, + mf.description mf_description, + mf.identifiable_objecttype mf_identifiableObjectType, + mf.identifiable_type mf_type, + mf.label mf_label, + mf.preview_hints mf_previewImageRenderingHints, + mf.custom_attrs mf_customAttributes, + mf.navdate mf_navDate, + mf.refid mf_refId, + mf.notes mf_notes, + mf.expressiontypes mf_expressionTypes, + mf.language mf_language, + mf.manifestationtype mf_manifestationType, + mf.manufacturingtype mf_manufacturingType, + mf.mediatypes mf_mediaTypes, + mf.titles mf_titles, + -- work + w.uuid wo_uuid, + get_identifiers (w.uuid) wo_identifiers, + w.label wo_label, + w.titles wo_titles, + mms.title parent_title, + mms.sortKey parent_sortKey, + parent.uuid parent_uuid, + parent.label parent_label, + parent.titles parent_titles, + parent.manifestationtype parent_manifestationType, + parent.refid parent_refId, + parent.notes parent_notes, + parent.created parent_created, + parent.last_modified parent_lastModified, + parent.identifiable_objecttype parent_identifiableObjectType, + get_identifiers (parent.uuid) parent_identifiers, + -- parent's work + parentwork.uuid parentwork_uuid, + get_identifiers (parentwork.uuid) parentwork_identifiers, + parentwork.label parentwork_label, + parentwork.titles parentwork_titles, + rel.predicate rel_predicate, + rel.sortindex rel_sortindex, + rel.additional_predicates rel_additionalPredicates, + max(rel.sortindex) OVER ( + PARTITION BY + mf.uuid + ) relation_max_sortindex, + get_identifiers (e.uuid) e_identifiers, + -- entity's name, if any + ename.name entity_name, + ename.name_locales_original_scripts entity_nameLocalesOfOriginalScripts, + e.uuid e_uuid, + e.created e_created, + e.last_modified e_lastModified, + e.description e_description, + e.identifiable_objecttype e_identifiableObjectType, + e.identifiable_type e_type, + e.label e_label, + e.preview_hints e_previewImageRenderingHints, + e.custom_attrs e_customAttributes, + e.navdate e_navDate, + e.refid e_refId, + e.notes e_notes, + mf.publication_info mf_publicationInfo, + mf.production_info mf_productionInfo, + mf.distribution_info mf_distributionInfo, + -- publisher + ag.uuid ag_uuid, + ag.created ag_created, + ag.last_modified ag_lastModified, + ag.description ag_description, + ag.identifiable_objecttype ag_identifiableObjectType, + ag.identifiable_type ag_type, + ag.label ag_label, + ag.preview_hints ag_previewImageRenderingHints, + ag.custom_attrs ag_customAttributes, + ag.navdate ag_navDate, + ag.refid ag_refId, + ag.notes ag_notes, + ag.name ag_name, + ag.name_locales_original_scripts ag_nameLocalesOfOriginalScripts, + h.uuid hs_uuid, + h.created hs_created, + h.last_modified hs_lastModified, + h.description hs_description, + h.identifiable_objecttype hs_identifiableObjectType, + h.identifiable_type hs_type, + h.label hs_label, + h.preview_hints hs_previewImageRenderingHints, + h.custom_attrs hs_customAttributes, + h.navdate hs_navDate, + h.refid hs_refId, + h.notes hs_notes, + h.name hs_name, + h.name_locales_original_scripts hs_nameLocalesOfOriginalScripts, + h.geolocation_type hs_geoLocationType, + h.settlement_type hs_humanSettlementType, + get_identifiers (ag.uuid) ag_identifiers, + get_identifiers (h.uuid) hs_identifiers, + id.uuid id_uuid, + id.created id_created, + id.last_modified id_lastModified, + id.identifiable id_identifiable, + id.namespace id_namespace, + id.identifier id_id, + file.uuid pi_uuid, + file.label pi_label, + file.filename pi_filename, + file.mimetype pi_mimeType, + file.uri pi_uri, + file.http_base_url pi_httpBaseUrl, + ua.uuid ua_uuid, + ua.created ua_created, + ua.last_modified ua_lastModified, + ua.last_published ua_lastPublished, + ua.primary ua_primary, + ua.slug ua_slug, + ua.target_language ua_targetLanguage, + ua.target_identifiable_objecttype uaidf_identifiableObjectType, + ua.target_identifiable_type uaidf_identifiableType, + ua.target_uuid uaidf_uuid, + uawebs.uuid uawebs_uuid, + uawebs.label uawebs_label, + uawebs.url uawebs_url +FROM + ( + SELECT + mf.* + FROM + manifestations AS mf + LEFT JOIN identifiers id ON id.identifiable = mf.uuid + WHERE + manifestationtype IN ('NEWSPAPER', 'JOURNAL') + AND (id.namespace = 'mdz-manifestation-periodical') + ) AS mf + LEFT JOIN ( + manifestation_manifestations mms + INNER JOIN manifestations parent ON parent.uuid = mms.subject_uuid + ) ON mms.object_uuid = mf.uuid + LEFT JOIN works parentwork ON parentwork.uuid = parent.work + LEFT JOIN ( + rel_entity_entities rel + INNER JOIN entities e ON rel.subject_uuid = e.uuid + ) ON rel.object_uuid = mf.uuid + -- select the entity's name, if any + LEFT JOIN named_entities ename ON ename.uuid = e.uuid + LEFT JOIN works w ON w.uuid = mf.work + LEFT JOIN agents ag ON ag.uuid = ANY (mf.publishing_info_agent_uuids) + LEFT JOIN humansettlements h ON h.uuid = ANY (mf.publishing_info_locations_uuids) + LEFT JOIN identifiers AS id ON mf.uuid = id.identifiable + LEFT JOIN fileresources_image AS file ON mf.previewfileresource = file.uuid + LEFT JOIN url_aliases AS ua ON mf.uuid = ua.target_uuid + LEFT JOIN websites AS uawebs ON uawebs.uuid = ua.website_uuid; + diff --git a/metasvc-server/backend-lobid/pom.xml b/metasvc-server/backend-lobid/pom.xml index 18119b963..d30030a23 100644 --- a/metasvc-server/backend-lobid/pom.xml +++ b/metasvc-server/backend-lobid/pom.xml @@ -6,7 +6,7 @@ io.github.dbmdz.metadata metasvc-server - 9.2.2-SNAPSHOT + 9.3.0-SNAPSHOT Metadata-Service: Repository Server (Backend IMPL External System lobid.org) diff --git a/metasvc-server/business/pom.xml b/metasvc-server/business/pom.xml index 359ec884d..cfa525f84 100644 --- a/metasvc-server/business/pom.xml +++ b/metasvc-server/business/pom.xml @@ -6,7 +6,7 @@ io.github.dbmdz.metadata metasvc-server - 9.2.2-SNAPSHOT + 9.3.0-SNAPSHOT Metadata-Service: Repository Server (Business) diff --git a/metasvc-server/business/src/main/java/io/github/dbmdz/metadata/server/business/api/service/identifiable/entity/work/DigipressService.java b/metasvc-server/business/src/main/java/io/github/dbmdz/metadata/server/business/api/service/identifiable/entity/work/DigipressService.java new file mode 100644 index 000000000..320d52e30 --- /dev/null +++ b/metasvc-server/business/src/main/java/io/github/dbmdz/metadata/server/business/api/service/identifiable/entity/work/DigipressService.java @@ -0,0 +1,14 @@ +package io.github.dbmdz.metadata.server.business.api.service.identifiable.entity.work; + +import de.digitalcollections.model.identifiable.entity.manifestation.Manifestation; +import de.digitalcollections.model.list.ListRequest; +import de.digitalcollections.model.list.ListResponse; +import io.github.dbmdz.metadata.server.backend.api.repository.exceptions.RepositoryException; + +public interface DigipressService { + + void refreshTable(); + + ListResponse getNewspapers(ListRequest listRequest) + throws RepositoryException; +} diff --git a/metasvc-server/business/src/main/java/io/github/dbmdz/metadata/server/business/impl/service/identifiable/entity/work/DigipressServiceImpl.java b/metasvc-server/business/src/main/java/io/github/dbmdz/metadata/server/business/impl/service/identifiable/entity/work/DigipressServiceImpl.java new file mode 100644 index 000000000..b8ad51064 --- /dev/null +++ b/metasvc-server/business/src/main/java/io/github/dbmdz/metadata/server/business/impl/service/identifiable/entity/work/DigipressServiceImpl.java @@ -0,0 +1,30 @@ +package io.github.dbmdz.metadata.server.business.impl.service.identifiable.entity.work; + +import de.digitalcollections.model.identifiable.entity.manifestation.Manifestation; +import de.digitalcollections.model.list.ListRequest; +import de.digitalcollections.model.list.ListResponse; +import io.github.dbmdz.metadata.server.backend.api.repository.exceptions.RepositoryException; +import io.github.dbmdz.metadata.server.backend.api.repository.identifiable.entity.work.DigipressManifestationRepository; +import io.github.dbmdz.metadata.server.business.api.service.identifiable.entity.work.DigipressService; +import org.springframework.stereotype.Service; + +@Service +public class DigipressServiceImpl implements DigipressService { + + private DigipressManifestationRepository repository; + + public DigipressServiceImpl(DigipressManifestationRepository repository) { + this.repository = repository; + } + + @Override + public void refreshTable() { + repository.refreshTable(); + } + + @Override + public ListResponse getNewspapers(ListRequest listRequest) + throws RepositoryException { + return repository.getNewspapers(listRequest); + } +} diff --git a/metasvc-server/pom.xml b/metasvc-server/pom.xml index 6aedc2f0a..97b79f6be 100644 --- a/metasvc-server/pom.xml +++ b/metasvc-server/pom.xml @@ -6,7 +6,7 @@ io.github.dbmdz.metadata metasvc - 9.2.2-SNAPSHOT + 9.3.0-SNAPSHOT Metadata-Service: Repository Server diff --git a/metasvc-server/webapp/pom.xml b/metasvc-server/webapp/pom.xml index ddf1ee0df..d5e7621b2 100644 --- a/metasvc-server/webapp/pom.xml +++ b/metasvc-server/webapp/pom.xml @@ -6,7 +6,7 @@ io.github.dbmdz.metadata metasvc-server - 9.2.2-SNAPSHOT + 9.3.0-SNAPSHOT Metadata-Service: Repository Server (Webapp) diff --git a/metasvc-server/webapp/src/main/java/io/github/dbmdz/metadata/server/controller/identifiable/entity/work/DigipressController.java b/metasvc-server/webapp/src/main/java/io/github/dbmdz/metadata/server/controller/identifiable/entity/work/DigipressController.java new file mode 100644 index 000000000..cfa7b1881 --- /dev/null +++ b/metasvc-server/webapp/src/main/java/io/github/dbmdz/metadata/server/controller/identifiable/entity/work/DigipressController.java @@ -0,0 +1,49 @@ +package io.github.dbmdz.metadata.server.controller.identifiable.entity.work; + +import de.digitalcollections.model.identifiable.entity.manifestation.Manifestation; +import de.digitalcollections.model.list.ListRequest; +import de.digitalcollections.model.list.ListResponse; +import de.digitalcollections.model.list.filtering.Filtering; +import de.digitalcollections.model.list.sorting.Order; +import de.digitalcollections.model.list.sorting.Sorting; +import io.github.dbmdz.metadata.server.backend.api.repository.exceptions.RepositoryException; +import io.github.dbmdz.metadata.server.business.api.service.identifiable.entity.work.DigipressService; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@Tag(name = "DigiPress optimised controller") +public class DigipressController { + + private DigipressService service; + + public DigipressController(DigipressService digipressService) { + service = digipressService; + } + + @PutMapping(value = "/v6/digipress/refresh") + @ResponseStatus(code = HttpStatus.NO_CONTENT) + public void refreshTable() { + service.refreshTable(); + } + + @GetMapping(path = "/v6/digipress", produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public ListResponse find( + @RequestParam(name = "filtering", required = false) Filtering filtering, + @RequestParam(name = "sortBy", required = false) List sortBy) + throws RepositoryException { + ListRequest listRequest = new ListRequest(); + listRequest.add(filtering); + if (sortBy != null && !sortBy.isEmpty()) listRequest.add(new Sorting(sortBy)); + return service.getNewspapers(listRequest); + } +} diff --git a/pom.xml b/pom.xml index c4223c2b2..e00748720 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ io.github.dbmdz.metadata metasvc - 9.2.2-SNAPSHOT + 9.3.0-SNAPSHOT pom Metadata-Service