diff --git a/dbrepo-metadata-db/setup-schema.sql b/dbrepo-metadata-db/setup-schema.sql index d69d524079f6357f05a47913f99c923ff1967255..b6561d250327203abf02b3ffc26f0d41b949a2a6 100644 --- a/dbrepo-metadata-db/setup-schema.sql +++ b/dbrepo-metadata-db/setup-schema.sql @@ -145,6 +145,7 @@ CREATE TABLE IF NOT EXISTS `fda`.`mdb_columns` dfID bigint, cName VARCHAR(100), internal_name VARCHAR(100) NOT NULL, + alias VARCHAR(100), Datatype ENUM ('CHAR','VARCHAR','BINARY','VARBINARY','TINYBLOB','TINYTEXT','TEXT','BLOB','MEDIUMTEXT','MEDIUMBLOB','LONGTEXT','LONGBLOB','ENUM','SET','BIT','TINYINT','BOOL','SMALLINT','MEDIUMINT','INT','BIGINT','FLOAT','DOUBLE','DECIMAL','DATE','DATETIME','TIMESTAMP','TIME','YEAR'), length INT NULL, ordinal_position INTEGER NOT NULL, @@ -349,13 +350,13 @@ CREATE TABLE IF NOT EXISTS `fda`.`mdb_banner_messages` CREATE TABLE IF NOT EXISTS `fda`.`mdb_ontologies` ( - id bigint NOT NULL AUTO_INCREMENT, - prefix VARCHAR(8) NOT NULL, - uri TEXT NOT NULL, + id bigint NOT NULL AUTO_INCREMENT, + prefix VARCHAR(8) NOT NULL, + uri TEXT NOT NULL, uri_pattern TEXT, - sparql_endpoint TEXT NULL, + sparql_endpoint TEXT NULL, last_modified timestamp, - created timestamp NOT NULL DEFAULT NOW(), + created timestamp NOT NULL DEFAULT NOW(), UNIQUE (prefix), UNIQUE (uri(200)), PRIMARY KEY (id) diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnBriefDto.java index 4745442c0a7c59dd0f1e8ce8dcd7d8c7276ef610..e811991912c8188ae0506d8bc344eb53f208e90d 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnBriefDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnBriefDto.java @@ -37,6 +37,9 @@ public class ColumnBriefDto { @Schema(example = "mdb_date") private String internalName; + @Schema + private String alias; + @NotNull @JsonProperty("column_type") @Schema(example = "date") diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnDto.java index 48b0745c6cd715cadb4005bf54e3d60b26f67c91..eb748f1ec48dbceeff66923a056bfb125eee2c3a 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnDto.java @@ -53,6 +53,10 @@ public class ColumnDto { @Schema(example = "mdb_date") private String internalName; + @Field(name = "alias", type = FieldType.Keyword) + @Schema + private String alias; + @JsonProperty("date_format") @Field(name = "date_format", includeInParent = true, type = FieldType.Nested) private ImageDateDto dateFormat; diff --git a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumn.java b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumn.java index 364b74b02ac61f7ce8fe7b24149220c33aada07d..98c5d5d8895fb4839f539db0331fd23f9bedf7fd 100644 --- a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumn.java +++ b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/table/columns/TableColumn.java @@ -66,10 +66,8 @@ public class TableColumn implements Comparable<TableColumn> { @Column private Integer indexLength; - @Transient - @ToString.Exclude - @org.springframework.data.annotation.Transient - private transient String alias; + @Column + private String alias; @Column(name = "datatype", nullable = false) @Enumerated(EnumType.STRING) diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/QueryMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/QueryMapper.java index 2f9d906f7a965cb359f68be1cb0244fa6cea53a1..d25717e5034620624ad16eb8486d6ee739afcae6 100644 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/QueryMapper.java +++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/QueryMapper.java @@ -85,7 +85,13 @@ public interface QueryMapper { int[] idx = new int[]{1}; final Map<String, Object> map = new HashMap<>(); for (final TableColumn column : columns) { - final String columnOrAlias = column.getAlias() != null ? column.getAlias() : column.getInternalName(); + final String columnOrAlias; + if (column.getAlias() != null) { + log.debug("column {} has alias {}", column.getInternalName(), column.getAlias()); + columnOrAlias = column.getAlias(); + } else { + columnOrAlias = column.getInternalName(); + } if (List.of(TableColumnType.BLOB, TableColumnType.TINYBLOB, TableColumnType.MEDIUMBLOB, TableColumnType.LONGBLOB).contains(column.getColumnType())) { log.debug("column {} is of type blob", columnOrAlias); final Blob blob = result.getBlob(idx[0]++); @@ -692,14 +698,9 @@ public interface QueryMapper { return columnsToRawFindAllQuery(table.getInternalName(), table.getColumns(), timestamp, size, page); } - default String viewToRawFindAllQuery(View view, Long size, Long page) - throws ImageNotSupportedException { + default String viewToRawFindAllQuery(View view, Long size, Long page) { log.trace("mapping view to find all query, view={}, size={}, page={}", view, size, page); /* param check */ - if (!view.getDatabase().getContainer().getImage().getName().equals("mariadb")) { - log.error("Currently only MariaDB is supported"); - throw new ImageNotSupportedException("Currently only MariaDB is supported"); - } return columnsToRawFindAllQuery(view.getInternalName(), view.getColumns(), null, size, page); } @@ -795,36 +796,36 @@ public interface QueryMapper { .toInstant(); } case BINARY, VARBINARY, BIT -> { - log.trace("mapping {} to binary", data); + log.trace("mapping {} -> binary", data); return Long.parseLong(String.valueOf(data), 2); } case TEXT, CHAR, VARCHAR, TINYTEXT, MEDIUMTEXT, LONGTEXT, ENUM, SET -> { - log.trace("mapping {} to character array", data); + log.trace("mapping {} -> string", data); return String.valueOf(data); } case BIGINT -> { - log.trace("mapping {} to bigint number", data); + log.trace("mapping {} -> biginteger", data); return new BigInteger(String.valueOf(data)); } case INT, TINYINT, SMALLINT, MEDIUMINT -> { - log.trace("mapping {} to int number", data); + log.trace("mapping {} -> integer", data); return Integer.parseInt(String.valueOf(data)); } case DECIMAL, FLOAT, DOUBLE -> { - log.trace("mapping {} to decimal number", data); + log.trace("mapping {} -> double", data); return Double.valueOf(String.valueOf(data)); } case BOOL -> { - log.trace("mapping {} to boolean", data); + log.trace("mapping {} -> boolean", data); return Boolean.valueOf(String.valueOf(data)); } case TIME -> { - log.trace("mapping {} to time", data); + log.trace("mapping {} -> time", data); return String.valueOf(data); } case YEAR -> { final String tmp = String.valueOf(data); - log.trace("mapping {} to year", tmp); + log.trace("mapping {} -> year", tmp); return tmp.substring(0, tmp.indexOf('-')); } } diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/repository/ViewIdxRepositoryIntegrationTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/repository/ViewIdxRepositoryIntegrationTest.java index ac7360e38d2551dc3d61ad9de7d3489b896b75de..26a9add0bb8c93678979d19eef8a9a840eff1b7d 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/repository/ViewIdxRepositoryIntegrationTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/repository/ViewIdxRepositoryIntegrationTest.java @@ -77,7 +77,7 @@ public class ViewIdxRepositoryIntegrationTest extends BaseUnitTest { private static final OpensearchContainer opensearchContainer = new OpensearchContainer(DockerImageName.parse("opensearchproject/opensearch:2.8.0")); @DynamicPropertySource - static void elasticsearchProperties(DynamicPropertyRegistry registry) { + static void openSearchProperties(DynamicPropertyRegistry registry) { final int idx = opensearchContainer.getHttpHostAddress().lastIndexOf(':'); registry.add("spring.opensearch.host", () -> "127.0.0.1"); registry.add("spring.opensearch.port", () -> opensearchContainer.getHttpHostAddress().substring(idx + 1)); diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceIntegrationTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceIntegrationTest.java index 580a9d454526e0c66ed2a39e3c187efc06c59e6a..f31408c4865ee20159d10e556be72d0023ac15b0 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceIntegrationTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceIntegrationTest.java @@ -87,7 +87,7 @@ public class IdentifierServiceIntegrationTest extends BaseUnitTest { private static final OpensearchContainer opensearchContainer = new OpensearchContainer(DockerImageName.parse("opensearchproject/opensearch:2.8.0")); @DynamicPropertySource - static void elasticsearchProperties(DynamicPropertyRegistry registry) { + static void openSearchProperties(DynamicPropertyRegistry registry) { final int idx = opensearchContainer.getHttpHostAddress().lastIndexOf(':'); registry.add("spring.opensearch.host", () -> "127.0.0.1"); registry.add("spring.opensearch.port", () -> opensearchContainer.getHttpHostAddress().substring(idx + 1)); diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/QueryServiceIntegrationTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/QueryServiceIntegrationTest.java index 07fa1e92b65492df0e267077430297d6f82f77ab..47085ed2da077fb0d8da36d7b0d6a204daee6bfe 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/QueryServiceIntegrationTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/QueryServiceIntegrationTest.java @@ -459,6 +459,29 @@ public class QueryServiceIntegrationTest extends BaseUnitTest { assertEquals("Vienna", result.get(8).get("location")); } + @Test + public void viewFindAll_succeeds() throws DatabaseConnectionException, TableMalformedException, + DatabaseNotFoundException, ImageNotSupportedException, QueryMalformedException, UserNotFoundException, + InterruptedException, ViewMalformedException, PaginationException, ViewNotFoundException { + + /* mock */ + Thread.sleep(1000) /* wait for test container some more */; + + /* test */ + final QueryResultDto response = queryService.viewFindAll(DATABASE_1_ID, VIEW_2, 0L, 10L, USER_1_PRINCIPAL); + assertNotNull(response.getResult()); + final List<Map<String, Object>> result = response.getResult(); + assertEquals(0.6, result.get(0).get("rainfall")); + assertEquals("Albury", result.get(0).get("loc")); + assertEquals(13.4, result.get(0).get("mintemp")); + assertEquals(0.0, result.get(1).get("rainfall")); + assertEquals("Albury", result.get(1).get("loc")); + assertEquals(7.4, result.get(1).get("mintemp")); + assertEquals(0.0, result.get(2).get("rainfall")); + assertEquals("Albury", result.get(2).get("loc")); + assertEquals(12.9, result.get(2).get("mintemp")); + } + @Test public void count_emptySet_succeeds() throws DatabaseConnectionException, TableMalformedException, DatabaseNotFoundException, ImageNotSupportedException, QueryMalformedException, UserNotFoundException, diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java index 3ce4672df421ea673deadd3b7a398f62ee04099f..a7568af28b5517aa1e978c0972a0a1809ad1d208 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java @@ -6,6 +6,7 @@ import at.tuwien.api.database.ViewCreateDto; import at.tuwien.config.MariaDbConfig; import at.tuwien.config.MariaDbContainerConfig; import at.tuwien.entities.database.View; +import at.tuwien.entities.database.table.columns.TableColumn; import at.tuwien.exception.*; import at.tuwien.repository.mdb.*; import lombok.extern.log4j.Log4j2; @@ -39,7 +40,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; @Log4j2 @Testcontainers @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) -@EnableAutoConfiguration(exclude= RabbitAutoConfiguration.class) +@EnableAutoConfiguration(exclude = RabbitAutoConfiguration.class) @SpringBootTest @ExtendWith(SpringExtension.class) @MockAmqp @@ -79,7 +80,7 @@ public class ViewServiceIntegrationTest extends BaseUnitTest { private static final OpensearchContainer opensearchContainer = new OpensearchContainer(DockerImageName.parse("opensearchproject/opensearch:2.8.0")); @DynamicPropertySource - static void elasticsearchProperties(DynamicPropertyRegistry registry) { + static void openSearchProperties(DynamicPropertyRegistry registry) { final int idx = opensearchContainer.getHttpHostAddress().lastIndexOf(':'); registry.add("spring.opensearch.host", () -> "127.0.0.1"); registry.add("spring.opensearch.port", () -> opensearchContainer.getHttpHostAddress().substring(idx + 1)); @@ -173,4 +174,34 @@ public class ViewServiceIntegrationTest extends BaseUnitTest { assertNull(row2.get("lng")); } + @Test + public void create_withAlias_succeeds() throws DatabaseNotFoundException, UserNotFoundException, + DatabaseConnectionException, ViewMalformedException, QueryMalformedException { + final ViewCreateDto request = ViewCreateDto.builder() + .name(VIEW_2_NAME + "_with_alias") + .query(VIEW_2_QUERY) + .isPublic(VIEW_2_PUBLIC) + .build(); + + /* test */ + final View response = viewService.create(DATABASE_1_ID, request, USER_1_PRINCIPAL); + assertEquals(VIEW_2_NAME + "_with_alias", response.getName()); + assertEquals(VIEW_2_INTERNAL_NAME + "_with_alias", response.getInternalName()); + assertEquals(VIEW_2_QUERY, response.getQuery()); + final List<TableColumn> columns = response.getColumns(); + assertEquals(4, columns.size()); + final TableColumn column0 = columns.get(0); + assertEquals("date", column0.getInternalName()); + assertNull(column0.getAlias()); + final TableColumn column1 = columns.get(1); + assertEquals("location", column1.getInternalName()); + assertEquals("loc", column1.getAlias()); + final TableColumn column2 = columns.get(2); + assertEquals("mintemp", column2.getInternalName()); + assertNull(column2.getAlias()); + final TableColumn column3 = columns.get(3); + assertEquals("rainfall", column3.getInternalName()); + assertNull(column3.getAlias()); + } + } diff --git a/dbrepo-metadata-service/rest-service/src/test/resources/application.properties b/dbrepo-metadata-service/rest-service/src/test/resources/application.properties index a004db1bceab10545f026b3752c6db633ddc80b2..9002d61ea23c22e68787be490ab9a15df260e67e 100644 --- a/dbrepo-metadata-service/rest-service/src/test/resources/application.properties +++ b/dbrepo-metadata-service/rest-service/src/test/resources/application.properties @@ -16,7 +16,8 @@ spring.jpa.hibernate.ddl-auto=create # logging logging.level.root=error -logging.level.at.tuwien.=${LOG_LEVEL:-debug} +logging.level.at.tuwien.=debug +logging.level.at.tuwien.service.impl.=trace # rabbitmq fda.broker.endpoint=http://localhost:15672 diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java index abdfec2c8199d5c72089ef0403715ab869d62614..fcde64dccef014d9a6ec4c42a8a0795e3e457804 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java @@ -148,8 +148,8 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService database.getContainer(), database); try { final Connection connection = dataSource.getConnection(); + log.trace("preparing statement {}", statement); final PreparedStatement preparedStatement = prepareStatement(connection, statement); - log.trace("prepared statement {}", statement); final ResultSet resultSet = preparedStatement.executeQuery(); return queryMapper.resultListToQueryResultDto(columns, resultSet); } catch (SQLException e) { @@ -197,8 +197,7 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService public QueryResultDto viewFindAll(Long databaseId, View view, Long page, Long size, Principal principal) throws DatabaseNotFoundException, QueryMalformedException, TableMalformedException { /* run query */ - String statement = queryMapper.queryToRawTimestampedQuery(view.getQuery(), Instant.now(), true, page, size); - return executeNonPersistent(databaseId, statement, view.getColumns()); + return executeNonPersistent(databaseId, view.getQuery(), view.getColumns()); } @Override diff --git a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java index 8e7fa5ab4bcb31efa6fa973f5ed601bb165b09fc..39d7f78e6d17340c4989f4bcb30309cec98ef198 100644 --- a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java +++ b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java @@ -5078,23 +5078,10 @@ public abstract class BaseTest { public final static Long VIEW_2_CONTAINER_ID = CONTAINER_1_ID; public final static Long VIEW_2_DATABASE_ID = DATABASE_1_ID; public final static Boolean VIEW_2_PUBLIC = true; - public final static String VIEW_2_QUERY = "select `date`, `location`, `mintemp`, `rainfall` from `weather_aus` where `location` = 'Albury'"; + public final static String VIEW_2_QUERY = "select `date`, `location` as loc, `mintemp`, `rainfall` from `weather_aus` where `location` = 'Albury'"; public final static String VIEW_2_QUERY_HASH = "c76efcbab7e117ed286fd6c0f766178727debfeb5544633f8fbfaf2230340d17"; public final static List<TableColumn> VIEW_2_COLUMNS = List.of(TableColumn.builder() - .id(COLUMN_1_1_ID) - .ordinalPosition(COLUMN_1_1_ORDINALPOS) - .table(TABLE_1) - .name(COLUMN_1_1_NAME) - .internalName(COLUMN_1_1_INTERNAL_NAME) - .columnType(COLUMN_1_1_TYPE) - .isNullAllowed(COLUMN_1_1_NULL) - .autoGenerated(COLUMN_1_1_AUTO_GENERATED) - .isPrimaryKey(COLUMN_1_1_PRIMARY) - .enums(COLUMN_1_1_ENUM_VALUES) - .sets(COLUMN_1_1_SET_VALUES) - .build(), - TableColumn.builder() .id(COLUMN_1_2_ID) .ordinalPosition(COLUMN_1_2_ORDINALPOS) .table(TABLE_1) @@ -5113,6 +5100,7 @@ public abstract class BaseTest { .ordinalPosition(COLUMN_1_3_ORDINALPOS) .table(TABLE_1) .name(COLUMN_1_3_NAME) + .alias("loc") .internalName(COLUMN_1_3_INTERNAL_NAME) .columnType(COLUMN_1_3_TYPE) .isNullAllowed(COLUMN_1_3_NULL) diff --git a/dbrepo-ui/components/query/Results.vue b/dbrepo-ui/components/query/Results.vue index 94a61b3eb0a030810c893be8b7634ee4f2e7466d..60f6eaabed62610bc31f4366412f0a540b6da9ec 100644 --- a/dbrepo-ui/components/query/Results.vue +++ b/dbrepo-ui/components/query/Results.vue @@ -47,8 +47,8 @@ export default { if (this.type === 'view' && this.view && this.view.columns) { return this.view.columns.map((c) => { return { - text: c.internal_name, - value: c.internal_name, + text: c.alias ? c.alias : c.internal_name, + value: c.alias ? c.alias : c.internal_name, sortable: false } })