From 4bbbde7a67a8c7170b8baf4e313eec542061ab0a Mon Sep 17 00:00:00 2001
From: Martin Weise <martin.weise@tuwien.ac.at>
Date: Tue, 27 Aug 2024 07:39:12 +0200
Subject: [PATCH] WIP

---
 dbrepo-metadata-db/1_setup-schema.sql         |  12 +-
 .../table/columns/ColumnCreateDto.java        |   6 +
 .../concepts/ColumnSemanticsUpdateDto.java    |   6 +
 .../java/at/tuwien/mapper/MetadataMapper.java |   6 +
 .../at/tuwien/endpoints/OntologyEndpoint.java |  71 +-------
 .../at/tuwien/endpoints/TableEndpoint.java    |  94 +---------
 .../endpoints/OntologyEndpointUnitTest.java   | 109 ------------
 .../endpoints/TableEndpointUnitTest.java      |  91 ----------
 .../tuwien/mvc/PrometheusEndpointMvcTest.java |  15 --
 .../tuwien/service/EntityServiceUnitTest.java | 163 -----------------
 .../java/at/tuwien/service/EntityService.java |  64 -------
 .../service/impl/EntityServiceImpl.java       | 167 ------------------
 .../tuwien/service/impl/TableServiceImpl.java |  53 ++++--
 dbrepo-ui/components/table/TableSchema.vue    |  14 +-
 dbrepo-ui/composables/table-service.ts        |  23 +--
 dbrepo-ui/dto/index.ts                        |  11 ++
 16 files changed, 88 insertions(+), 817 deletions(-)
 delete mode 100644 dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/EntityServiceUnitTest.java
 delete mode 100644 dbrepo-metadata-service/services/src/main/java/at/tuwien/service/EntityService.java
 delete mode 100644 dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/EntityServiceImpl.java

diff --git a/dbrepo-metadata-db/1_setup-schema.sql b/dbrepo-metadata-db/1_setup-schema.sql
index 62dc5c3095..9213b8ea88 100644
--- a/dbrepo-metadata-db/1_setup-schema.sql
+++ b/dbrepo-metadata-db/1_setup-schema.sql
@@ -543,7 +543,7 @@ VALUES (1, '%Y-%c-%d %H:%i:%S.%f', 'yyyy-MM-dd HH:mm:ss.SSSSSS', '2022-01-30 13:
        (1, '%d.%c.%Y', 'dd.MM.yyyy', '30.01.2022', false);
 
 INSERT INTO `mdb_ontologies` (prefix, uri, uri_pattern, sparql_endpoint, rdf_path)
-VALUES ('om', 'http://www.ontology-of-units-of-measure.org/resource/om-2/',
+VALUES ('om2', 'http://www.ontology-of-units-of-measure.org/resource/om-2/',
         'http://www.ontology-of-units-of-measure.org/resource/om-2/.*', null, 'rdf/om-2.0.rdf'),
        ('wd', 'http://www.wikidata.org/', 'http://www.wikidata.org/entity/.*', 'https://query.wikidata.org/sparql',
         null),
@@ -557,5 +557,13 @@ VALUES ('om', 'http://www.ontology-of-units-of-measure.org/resource/om-2/',
        ('rdfs', 'http://www.w3.org/2000/01/rdf-schema#', null, null, null),
        ('owl', 'http://www.w3.org/2002/07/owl#', null, null, null),
        ('prov', 'http://www.w3.org/ns/prov#', null, null, null),
-       ('db', 'http://dbpedia.org', 'http://dbpedia.org/ontology/.*', 'http://dbpedia.org/sparql', null);
+       ('db', 'http://dbpedia.org', 'http://dbpedia.org/ontology/.*', 'http://dbpedia.org/sparql', null),
+       ('aq', 'http://vocab.linkeddata.es/datosabiertos/def/medio-ambiente/calidad-aire', 'http://vocab.linkeddata.es/datosabiertos/def/medio-ambiente/calidad-aire/*', null, null),
+       ('building', 'https://www.auto.tuwien.ac.at/downloads/thinkhome/ontology/BuildingOntology.owl', 'https://www.auto.tuwien.ac.at/downloads/thinkhome/ontology/BuildingOntology.owl/*', null, null),
+       ('car', 'https://spec.edmcouncil.org/auto/ontology/VC/VehicleCore/', 'https://spec.edmcouncil.org/auto/ontology/VC/VehicleCore/*', null, null),
+       ('fff', 'http://sbmi.uth.edu/ontology/offf', 'http://sbmi.uth.edu/ontology/offf/*', null, null),
+       ('wq', 'http://purl.org/nemo/doce#/2022-02-23', 'http://purl.org/nemo/doce#/2022-02-23/*', null, null),
+       ('weather', 'https://www.auto.tuwien.ac.at/downloads/thinkhome/ontology/WeatherOntology.owl', 'https://www.auto.tuwien.ac.at/downloads/thinkhome/ontology/WeatherOntology.owl/*', null, null),
+       ('qudt', 'http://qudt.org/2.1/vocab/quantitykind', 'http://qudt.org/2.1/vocab/quantitykind/*', null, null),
+       ('ucum', 'http://elite.polito.it/ontologies/ucum-instances.owl', 'http://elite.polito.it/ontologies/ucum-instances.owl/*', null, null);
 COMMIT;
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnCreateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnCreateDto.java
index 37aa493020..03e224bc31 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnCreateDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnCreateDto.java
@@ -49,9 +49,15 @@ public class ColumnCreateDto {
     @JsonProperty("concept_uri")
     private String conceptUri;
 
+    @JsonProperty("concept_label")
+    private String conceptLabel;
+
     @JsonProperty("unit_uri")
     private String unitUri;
 
+    @JsonProperty("unit_label")
+    private String unitLabel;
+
     @Schema(description = "date format id")
     private Long dfid;
 
diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ColumnSemanticsUpdateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ColumnSemanticsUpdateDto.java
index 77a38f70b4..4d0533e11a 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ColumnSemanticsUpdateDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ColumnSemanticsUpdateDto.java
@@ -16,6 +16,12 @@ public class ColumnSemanticsUpdateDto {
     @JsonProperty("concept_uri")
     private String conceptUri;
 
+    @JsonProperty("concept_label")
+    private String conceptLabel;
+
     @JsonProperty("unit_uri")
     private String unitUri;
+
+    @JsonProperty("unit_label")
+    private String unitLabel;
 }
diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java
index 557d1e49ab..ff5b4f5361 100644
--- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java
+++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java
@@ -455,8 +455,14 @@ public interface MetadataMapper {
 
     TableColumnUnit unitSaveDtoToTableColumnUnit(UnitSaveDto data);
 
+    @Mappings({
+            @Mapping(target = "name", source = "label"),
+    })
     TableColumnUnit entityDtoToTableColumnUnit(EntityDto data);
 
+    @Mappings({
+            @Mapping(target = "name", source = "label"),
+    })
     TableColumnConcept entityDtoToTableColumnConcept(EntityDto data);
 
     TableColumnConcept conceptSaveDtoToTableColumnConcept(ConceptSaveDto data);
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java
index cd17c7ac0b..ac14066b41 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java
@@ -5,8 +5,6 @@ import at.tuwien.api.semantics.*;
 import at.tuwien.entities.semantics.Ontology;
 import at.tuwien.exception.*;
 import at.tuwien.mapper.MetadataMapper;
-import at.tuwien.mapper.SparqlMapper;
-import at.tuwien.service.EntityService;
 import at.tuwien.service.OntologyService;
 import io.micrometer.observation.annotation.Observed;
 import io.swagger.v3.oas.annotations.Operation;
@@ -34,14 +32,11 @@ import java.util.List;
 @RequestMapping(path = "/api/ontology")
 public class OntologyEndpoint {
 
-    private final EntityService entityService;
     private final MetadataMapper metadataMapper;
     private final OntologyService ontologyService;
 
     @Autowired
-    public OntologyEndpoint(EntityService entityService, MetadataMapper metadataMapper,
-                            OntologyService ontologyService) {
-        this.entityService = entityService;
+    public OntologyEndpoint(MetadataMapper metadataMapper, OntologyService ontologyService) {
         this.metadataMapper = metadataMapper;
         this.ontologyService = ontologyService;
     }
@@ -164,68 +159,4 @@ public class OntologyEndpoint {
                 .build();
     }
 
-    @GetMapping("/{ontologyId}/entity")
-    @PreAuthorize("hasAuthority('execute-semantic-query')")
-    @Observed(name = "dbrepo_ontologies_entities_find")
-    @Operation(summary = "Find entities",
-            description = "Finds semantic entities by label or uri in an ontology with id. Requires role `execute-semantic-query`.",
-            security = {@SecurityRequirement(name = "bearerAuth"), @SecurityRequirement(name = "basicAuth")})
-    @ApiResponses(value = {
-            @ApiResponse(responseCode = "200",
-                    description = "Found entities",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            array = @ArraySchema(schema = @Schema(implementation = EntityDto.class)))}),
-            @ApiResponse(responseCode = "400",
-                    description = "Filter params are invalid",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "404",
-                    description = "Could not find ontology",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "417",
-                    description = "Generated query or uri is malformed",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "422",
-                    description = "Ontology does not have rdf or sparql endpoint",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-    })
-    public ResponseEntity<List<EntityDto>> find(@NotNull @PathVariable("ontologyId") Long id,
-                                                @RequestParam(name = "label", required = false) String label,
-                                                @RequestParam(name = "uri", required = false) String uri)
-            throws OntologyNotFoundException, UriMalformedException, FilterBadRequestException, MalformedException {
-        log.debug("endpoint find entities by uri, id={}, label={}, uri={}", id, label, uri);
-        final Ontology ontology = ontologyService.find(id);
-        /* check */
-        if ((label != null && uri != null) || (label == null && uri == null)) {
-            log.error("Failed to find entities: either label or uri must be defined");
-            throw new FilterBadRequestException("Failed to find entities: either label or uri must be defined");
-        }
-        if (uri != null && !uri.startsWith(ontology.getUri())) {
-            log.error("Failed to find entities: uri {} does not start with expected ontology uri {}", uri, ontology.getUri());
-            throw new UriMalformedException("Failed to find entity: uri " + uri + " does not start with expected ontology uri " + ontology.getUri());
-        }
-        if (ontology.getSparqlEndpoint() == null) {
-            log.error("Failed to find SPARQL endpoint for ontology with id {}", ontology.getId());
-            throw new OntologyNotFoundException("Failed to find SPARQL endpoint for ontology with id " + ontology.getId());
-        }
-        /* get */
-        final List<EntityDto> dtos;
-        if (uri != null) {
-            dtos = entityService.findByUri(uri);
-            return ResponseEntity.ok()
-                    .body(dtos);
-        }
-        dtos = entityService.findByLabel(ontology, label);
-        return ResponseEntity.ok()
-                .body(dtos);
-    }
-
 }
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
index 4fb8240b1d..9b4fb753a3 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
@@ -8,8 +8,6 @@ import at.tuwien.api.database.table.columns.ColumnDto;
 import at.tuwien.api.database.table.columns.ColumnTypeDto;
 import at.tuwien.api.database.table.columns.concepts.ColumnSemanticsUpdateDto;
 import at.tuwien.api.error.ApiErrorDto;
-import at.tuwien.api.semantics.EntityDto;
-import at.tuwien.api.semantics.TableColumnEntityDto;
 import at.tuwien.entities.database.Database;
 import at.tuwien.entities.database.table.Table;
 import at.tuwien.entities.database.table.columns.TableColumn;
@@ -50,18 +48,15 @@ public class TableEndpoint {
 
     private final UserService userService;
     private final TableService tableService;
-    private final EntityService entityService;
     private final MetadataMapper metadataMapper;
     private final DatabaseService databaseService;
     private final EndpointValidator endpointValidator;
 
     @Autowired
-    public TableEndpoint(UserService userService, TableService tableService, EntityService entityService,
-                         MetadataMapper metadataMapper, DatabaseService databaseService,
-                         EndpointValidator endpointValidator) {
+    public TableEndpoint(UserService userService, TableService tableService, MetadataMapper metadataMapper,
+                         DatabaseService databaseService, EndpointValidator endpointValidator) {
         this.userService = userService;
         this.tableService = tableService;
-        this.entityService = entityService;
         this.metadataMapper = metadataMapper;
         this.databaseService = databaseService;
         this.endpointValidator = endpointValidator;
@@ -105,50 +100,6 @@ public class TableEndpoint {
         return ResponseEntity.ok(dto);
     }
 
-    @GetMapping("/{tableId}/suggest")
-    @Transactional(readOnly = true)
-    @PreAuthorize("hasAuthority('table-semantic-analyse')")
-    @Observed(name = "dbrepo_semantic_table_analyse")
-    @Operation(summary = "Suggest semantics",
-            description = "Suggests semantic concepts for a table. Requires role `table-semantic-analyse`.",
-            security = {@SecurityRequirement(name = "bearerAuth"), @SecurityRequirement(name = "basicAuth")})
-    @ApiResponses(value = {
-            @ApiResponse(responseCode = "200",
-                    description = "Suggested table semantics successfully",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            array = @ArraySchema(schema = @Schema(implementation = EntityDto.class)))}),
-            @ApiResponse(responseCode = "400",
-                    description = "Failed to parse statistic in search service",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "404",
-                    description = "Failed to find database/table in metadata database",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "417",
-                    description = "Generated query is malformed",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "422",
-                    description = "Ontology does not have rdf or sparql endpoint",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-    })
-    public ResponseEntity<List<EntityDto>> analyseTable(@NotNull @PathVariable("databaseId") Long databaseId,
-                                                        @NotNull @PathVariable("tableId") Long tableId)
-            throws MalformedException, TableNotFoundException, DatabaseNotFoundException {
-        log.debug("endpoint analyse table semantics, databaseId={}, tableId={}", databaseId, tableId);
-        final Table table = tableService.findById(databaseId, tableId);
-        final List<EntityDto> dtos = entityService.suggestByTable(table);
-        return ResponseEntity.ok()
-                .body(dtos);
-    }
-
     @PutMapping("/{tableId}")
     @Transactional
     @PreAuthorize("hasAuthority('update-table-statistic')")
@@ -254,47 +205,6 @@ public class TableEndpoint {
                 .body(columnDto);
     }
 
-    @GetMapping("/{tableId}/column/{columnId}/suggest")
-    @Transactional(readOnly = true)
-    @PreAuthorize("hasAuthority('table-semantic-analyse')")
-    @Observed(name = "dbrepo_semantic_column_analyse")
-    @Operation(summary = "Suggest semantics",
-            description = "Suggests column semantics. Requires role `table-semantic-analyse`.",
-            security = {@SecurityRequirement(name = "bearerAuth"), @SecurityRequirement(name = "basicAuth")})
-    @ApiResponses(value = {
-            @ApiResponse(responseCode = "200",
-                    description = "Suggested table column semantics successfully",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            array = @ArraySchema(schema = @Schema(implementation = TableColumnEntityDto.class)))}),
-            @ApiResponse(responseCode = "400",
-                    description = "Generated query is malformed",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "404",
-                    description = "Failed to find database/table in metadata database",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "422",
-                    description = "Ontology does not have rdf or sparql endpoint",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-    })
-    public ResponseEntity<List<TableColumnEntityDto>> analyseTableColumn(@NotNull @PathVariable("databaseId") Long databaseId,
-                                                                         @NotNull @PathVariable("tableId") Long tableId,
-                                                                         @NotNull @PathVariable("columnId") Long columnId)
-            throws MalformedException, TableNotFoundException, DatabaseNotFoundException {
-        log.debug("endpoint analyse table column semantics, databaseId={}, tableId={}, columnId={}", databaseId, tableId, columnId);
-        final Table table = tableService.findById(databaseId, tableId);
-        TableColumn column = tableService.findColumnById(table, columnId);
-        final List<TableColumnEntityDto> dtos = entityService.suggestByColumn(column);
-        return ResponseEntity.ok()
-                .body(dtos);
-    }
-
     @PostMapping
     @Transactional(rollbackFor = {Exception.class})
     @PreAuthorize("hasAuthority('create-table')")
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/OntologyEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/OntologyEndpointUnitTest.java
index fc20a0b9e3..297c21a386 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/OntologyEndpointUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/OntologyEndpointUnitTest.java
@@ -5,7 +5,6 @@ import at.tuwien.api.semantics.*;
 import at.tuwien.entities.semantics.Ontology;
 import at.tuwien.entities.user.User;
 import at.tuwien.exception.*;
-import at.tuwien.service.EntityService;
 import at.tuwien.service.OntologyService;
 import at.tuwien.service.UserService;
 import lombok.extern.log4j.Log4j2;
@@ -37,9 +36,6 @@ public class OntologyEndpointUnitTest extends AbstractUnitTest {
     @MockBean
     private OntologyService ontologyService;
 
-    @MockBean
-    private EntityService entityService;
-
     @MockBean
     private UserService userService;
 
@@ -177,78 +173,6 @@ public class OntologyEndpointUnitTest extends AbstractUnitTest {
         delete_generic(ONTOLOGY_1_ID, ONTOLOGY_1);
     }
 
-    @Test
-    @WithAnonymousUser
-    public void find_anonymous_fails() {
-
-        /* test */
-        assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
-            find_generic(ONTOLOGY_2_ID, "Apache Jena", null, ONTOLOGY_2, null);
-        });
-    }
-
-    @Test
-    @WithMockUser(username = USER_4_USERNAME, authorities = {})
-    public void find_noRole_fails() {
-
-        /* test */
-        assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
-            find_generic(ONTOLOGY_2_ID, "Apache Jena", null, ONTOLOGY_2, null);
-        });
-    }
-
-    @Test
-    @WithMockUser(username = USER_1_USERNAME, authorities = {"execute-semantic-query"})
-    public void find_hasRoleInvalidParams_succeeds() {
-
-        /* test */
-        assertThrows(FilterBadRequestException.class, () -> {
-            find_generic(ONTOLOGY_2_ID, "Apache Jena", "http://www.wikidata.org/entity/Q1686799", ONTOLOGY_2, null);
-        });
-    }
-
-    @Test
-    @WithMockUser(username = USER_1_USERNAME, authorities = {"execute-semantic-query"})
-    public void find_hasRoleNotOntologyUri_succeeds() {
-
-        /* test */
-        assertThrows(UriMalformedException.class, () -> {
-            find_generic(ONTOLOGY_2_ID, null, "https://wikidata.org/entity/Q1686799", ONTOLOGY_2, null);
-        });
-    }
-
-    @Test
-    @WithMockUser(username = USER_1_USERNAME, authorities = {"execute-semantic-query"})
-    public void find_hasRoleLabel_succeeds() throws MalformedException, UriMalformedException, OntologyNotFoundException,
-            FilterBadRequestException {
-        final EntityDto entityDto = EntityDto.builder()
-                .label("Apache Jena")
-                .uri("http://www.wikidata.org/entity/Q1686799")
-                .build();
-
-        /* test */
-        final List<EntityDto> response = find_generic(ONTOLOGY_2_ID, "Apache Jena", null, ONTOLOGY_2, entityDto);
-        final EntityDto entity0 = response.get(0);
-        assertEquals("Apache Jena", entity0.getLabel());
-        assertEquals("http://www.wikidata.org/entity/Q1686799", entity0.getUri());
-    }
-
-    @Test
-    @WithMockUser(username = USER_1_USERNAME, authorities = {"execute-semantic-query"})
-    public void find_hasRoleUri_succeeds() throws MalformedException, UriMalformedException, OntologyNotFoundException,
-            FilterBadRequestException {
-        final EntityDto entityDto = EntityDto.builder()
-                .label("Apache Jena")
-                .uri("http://www.wikidata.org/entity/Q1686799")
-                .build();
-
-        /* test */
-        final List<EntityDto> response = find_generic(ONTOLOGY_2_ID, null, "http://www.wikidata.org/entity/Q1686799", ONTOLOGY_2, entityDto);
-        final EntityDto entity0 = response.get(0);
-        assertEquals("Apache Jena", entity0.getLabel());
-        assertEquals("http://www.wikidata.org/entity/Q1686799", entity0.getUri());
-    }
-
     /* ################################################################################################### */
     /* ## GENERIC TEST CASES                                                                            ## */
     /* ################################################################################################### */
@@ -347,37 +271,4 @@ public class OntologyEndpointUnitTest extends AbstractUnitTest {
         final ResponseEntity<?> response = ontologyEndpoint.delete(ontologyId);
         assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
     }
-
-    public List<EntityDto> find_generic(Long ontologyId, String label, String uri, Ontology ontology,
-                                        EntityDto entityDto) throws MalformedException, UriMalformedException,
-            FilterBadRequestException, OntologyNotFoundException {
-
-        /* mock */
-        if (ontology != null) {
-            when(ontologyService.find(ontologyId))
-                    .thenReturn(ontology);
-        } else {
-            doThrow(OntologyNotFoundException.class)
-                    .when(ontologyService)
-                    .find(ontologyId);
-        }
-        if (entityDto != null) {
-            when(entityService.findByLabel(ontology, label))
-                    .thenReturn(List.of(entityDto));
-            when(entityService.findByUri(uri))
-                    .thenReturn(List.of(entityDto));
-        } else {
-            when(entityService.findByLabel(ontology, label))
-                    .thenReturn(List.of());
-            when(entityService.findByUri(uri))
-                    .thenReturn(List.of());
-        }
-
-        /* test */
-        final ResponseEntity<List<EntityDto>> response = ontologyEndpoint.find(ontologyId, label, uri);
-        assertEquals(HttpStatus.OK, response.getStatusCode());
-        final List<EntityDto> body = response.getBody();
-        assertNotNull(body);
-        return body;
-    }
 }
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java
index 154ebda86c..2a9b4ec382 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java
@@ -8,8 +8,6 @@ import at.tuwien.api.database.table.columns.ColumnCreateDto;
 import at.tuwien.api.database.table.columns.ColumnDto;
 import at.tuwien.api.database.table.columns.ColumnTypeDto;
 import at.tuwien.api.database.table.columns.concepts.ColumnSemanticsUpdateDto;
-import at.tuwien.api.semantics.EntityDto;
-import at.tuwien.api.semantics.TableColumnEntityDto;
 import at.tuwien.entities.database.Database;
 import at.tuwien.entities.database.DatabaseAccess;
 import at.tuwien.entities.database.table.Table;
@@ -58,9 +56,6 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
     @MockBean
     private UserService userService;
 
-    @MockBean
-    private EntityService entityService;
-
     @MockBean
     private BrokerService messageQueueService;
 
@@ -395,64 +390,6 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
         generic_findById(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, USER_1_PRINCIPAL, USER_1, null);
     }
 
-    @Test
-    @WithAnonymousUser
-    public void analyseTable_anonymous_fails() {
-
-        /* test */
-        assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
-            analyseTable_generic(DATABASE_1_ID, TABLE_1_ID, TABLE_1);
-        });
-    }
-
-    @Test
-    @WithMockUser(username = USER_4_USERNAME)
-    public void findAll_noRole_fails() {
-
-        /* test */
-        assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
-            analyseTable_generic(DATABASE_1_ID, TABLE_1_ID, TABLE_1);
-        });
-    }
-
-    @Test
-    @WithMockUser(username = USER_1_USERNAME, authorities = {"table-semantic-analyse"})
-    public void findAll_hasRole_succeeds() throws MalformedException, TableNotFoundException,
-            DatabaseNotFoundException {
-
-        /* test */
-        analyseTable_generic(DATABASE_1_ID, TABLE_1_ID, TABLE_1);
-    }
-
-    @Test
-    @WithAnonymousUser
-    public void analyseTableColumn_anonymous_fails() {
-
-        /* test */
-        assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
-            analyseTableColumn_generic(DATABASE_1_ID, TABLE_1_ID, TABLE_1_COLUMNS.get(0).getId(), TABLE_1_COLUMNS.get(0));
-        });
-    }
-
-    @Test
-    @WithMockUser(username = USER_4_USERNAME)
-    public void analyseTableColumn_noRole_fails() {
-
-        /* test */
-        assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
-            analyseTableColumn_generic(DATABASE_1_ID, TABLE_1_ID, TABLE_1_COLUMNS.get(0).getId(), TABLE_1_COLUMNS.get(0));
-        });
-    }
-
-    @Test
-    @WithMockUser(username = USER_1_USERNAME, authorities = {"table-semantic-analyse"})
-    public void analyseTableColumn_hasRole_succeeds() throws MalformedException, TableNotFoundException,
-            DatabaseNotFoundException {
-
-        /* test */
-        analyseTableColumn_generic(DATABASE_1_ID, TABLE_1_ID, TABLE_1_COLUMNS.get(0).getId(), TABLE_1_COLUMNS.get(0));
-    }
-
     @Test
     @WithAnonymousUser
     public void update_publicAnonymous_fails() {
@@ -837,34 +774,6 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
     /* ## GENERIC TEST CASES                                                                            ## */
     /* ################################################################################################### */
 
-    public void analyseTable_generic(Long databaseId, Long tableId, Table table) throws MalformedException,
-            TableNotFoundException, DatabaseNotFoundException {
-
-        /* mock */
-        when(entityService.suggestByTable(table))
-                .thenReturn(List.of());
-
-        /* test */
-        final ResponseEntity<List<EntityDto>> response = tableEndpoint.analyseTable(databaseId, tableId);
-        assertEquals(HttpStatus.OK, response.getStatusCode());
-        final List<EntityDto> body = response.getBody();
-        assertNotNull(body);
-    }
-
-    public void analyseTableColumn_generic(Long databaseId, Long tableId, Long columnId, TableColumn tableColumn)
-            throws MalformedException, TableNotFoundException, DatabaseNotFoundException {
-
-        /* mock */
-        when(entityService.suggestByColumn(tableColumn))
-                .thenReturn(List.of());
-
-        /* test */
-        final ResponseEntity<List<TableColumnEntityDto>> response = tableEndpoint.analyseTableColumn(databaseId, tableId, columnId);
-        assertEquals(HttpStatus.OK, response.getStatusCode());
-        final List<TableColumnEntityDto> body = response.getBody();
-        assertNotNull(body);
-    }
-
     protected ResponseEntity<List<TableBriefDto>> generic_list(Long databaseId, Database database, Principal principal,
                                                                User user, DatabaseAccess access)
             throws NotAllowedException, DatabaseNotFoundException, AccessNotFoundException, UserNotFoundException {
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java
index d3f75d7060..ce621be569 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java
@@ -446,11 +446,6 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest {
         } catch (Exception e) {
             /* ignore */
         }
-        try {
-            ontologyEndpoint.find(ONTOLOGY_1_ID, "thing", null);
-        } catch (Exception e) {
-            /* ignore */
-        }
 
         /* test */
         for (String metric : List.of("dbrepo_ontologies_findall", "dbrepo_ontologies_find", "dbrepo_ontologies_create", "dbrepo_ontologies_update", "dbrepo_ontologies_delete", "dbrepo_ontologies_entities_find")) {
@@ -529,21 +524,11 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest {
         } catch (Exception e) {
             /* ignore */
         }
-        try {
-            tableEndpoint.analyseTable(DATABASE_1_ID, TABLE_1_ID);
-        } catch (Exception e) {
-            /* ignore */
-        }
         try {
             tableEndpoint.updateStatistic(DATABASE_1_ID, TABLE_1_ID);
         } catch (Exception e) {
             /* ignore */
         }
-        try {
-            tableEndpoint.analyseTableColumn(DATABASE_1_ID, TABLE_1_ID, TABLE_1_COLUMNS.get(0).getId());
-        } catch (Exception e) {
-            /* ignore */
-        }
         try {
             tableEndpoint.update(DATABASE_1_ID, TABLE_1_ID, TABLE_1_COLUMNS.get(3).getId(), request, USER_1_PRINCIPAL);
         } catch (Exception e) {
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/EntityServiceUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/EntityServiceUnitTest.java
deleted file mode 100644
index fd6ac82762..0000000000
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/EntityServiceUnitTest.java
+++ /dev/null
@@ -1,163 +0,0 @@
-package at.tuwien.service;
-
-import at.tuwien.test.AbstractUnitTest;
-import at.tuwien.api.semantics.EntityDto;
-import at.tuwien.api.semantics.TableColumnEntityDto;
-import at.tuwien.exception.*;
-import lombok.extern.log4j.Log4j2;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.test.context.junit.jupiter.SpringExtension;
-
-import java.util.List;
-
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.when;
-
-@Log4j2
-@SpringBootTest
-@ExtendWith(SpringExtension.class)
-public class EntityServiceUnitTest extends AbstractUnitTest {
-
-    @MockBean
-    private OntologyService ontologyService;
-
-    @Autowired
-    private EntityService entityService;
-
-    @BeforeEach
-    public void beforeEach() {
-        genesis();
-    }
-
-    @Test
-    public void findByLabel_wikidataSparql_succeeds() throws MalformedException {
-
-        /* mock */
-        when(ontologyService.findAll())
-                .thenReturn(List.of(ONTOLOGY_1, ONTOLOGY_2, ONTOLOGY_3, ONTOLOGY_4));
-
-        /* test */
-        final List<EntityDto> response = entityService.findByLabel(ONTOLOGY_2, "temperature");
-        assertFalse(response.isEmpty());
-        final EntityDto entity0 = response.get(0);
-        assertNotNull(entity0.getUri());
-        log.trace("found concept {}", entity0);
-    }
-
-    @Test
-    public void findByUri_wikidataSparql_succeeds() throws MalformedException, OntologyNotFoundException {
-
-        /* mock */
-        when(ontologyService.find(CONCEPT_1_URI))
-                .thenReturn(ONTOLOGY_1);
-        when(ontologyService.findAll())
-                .thenReturn(List.of(ONTOLOGY_1, ONTOLOGY_2, ONTOLOGY_3, ONTOLOGY_4));
-
-        /* test */
-        final List<EntityDto> response = entityService.findByUri(CONCEPT_1_URI);
-        assertEquals(1, response.size());
-        final EntityDto entity0 = response.get(0);
-        assertNotNull(entity0.getUri());
-        log.trace("found concept {}", entity0);
-    }
-
-    @Test
-    public void findOneByUri_wikidataSparql_succeeds() throws MalformedException, SemanticEntityNotFoundException,
-            OntologyNotFoundException {
-
-        /* mock */
-        when(ontologyService.find(CONCEPT_1_URI))
-                .thenReturn(ONTOLOGY_1);
-        when(ontologyService.findAll())
-                .thenReturn(List.of(ONTOLOGY_1, ONTOLOGY_2, ONTOLOGY_3, ONTOLOGY_4));
-
-        /* test */
-        final EntityDto response = entityService.findOneByUri(CONCEPT_1_URI);
-        assertNotNull(response.getUri());
-        log.trace("found concept {}", response);
-    }
-
-    @Test
-    public void findByLabel_om2Rdf_succeeds() throws MalformedException {
-
-        /* mock */
-        when(ontologyService.findAll())
-                .thenReturn(List.of(ONTOLOGY_1, ONTOLOGY_2, ONTOLOGY_3, ONTOLOGY_4));
-
-        /* test */
-        final List<EntityDto> response = entityService.findByLabel(ONTOLOGY_1, "millimetre");
-        assertFalse(response.isEmpty());
-        final EntityDto entity0 = response.get(0);
-        assertNotNull(entity0.getUri());
-        log.trace("found unit {}", entity0);
-    }
-
-    @Test
-    public void findByUri_om2Rdf_succeeds() throws MalformedException, OntologyNotFoundException {
-
-        /* mock */
-        when(ontologyService.find(UNIT_1_URI))
-                .thenReturn(ONTOLOGY_1);
-        when(ontologyService.findAll())
-                .thenReturn(List.of(ONTOLOGY_1, ONTOLOGY_2, ONTOLOGY_3, ONTOLOGY_4, ONTOLOGY_5));
-
-        /* test */
-        final List<EntityDto> response = entityService.findByUri(UNIT_1_URI);
-        assertEquals(1, response.size());
-        final EntityDto entity0 = response.get(0);
-        assertNotNull(entity0.getUri());
-        log.trace("found unit {}", entity0);
-    }
-
-    @Test
-    @Disabled("integration")
-    public void suggestByTable_succeeds() throws MalformedException {
-
-        /* mock */
-        when(ontologyService.findAll())
-                .thenReturn(List.of(ONTOLOGY_1, ONTOLOGY_2, ONTOLOGY_3, ONTOLOGY_4, ONTOLOGY_5));
-        when(ontologyService.findAllProcessable())
-                .thenReturn(List.of(ONTOLOGY_2, ONTOLOGY_5));
-
-        /* test */
-        final List<EntityDto> response = entityService.suggestByTable(TABLE_2);
-        assertEquals(1, response.size());
-    }
-
-    @Test
-    public void suggestTableColumnSemantics_succeeds() throws MalformedException {
-
-        /* mock */
-        when(ontologyService.findAll())
-                .thenReturn(List.of(ONTOLOGY_1, ONTOLOGY_2, ONTOLOGY_3, ONTOLOGY_4, ONTOLOGY_5));
-        when(ontologyService.findAllProcessable())
-                .thenReturn(List.of(ONTOLOGY_2, ONTOLOGY_5));
-
-        /* test */
-        final List<TableColumnEntityDto> response = entityService.suggestByColumn(TABLE_1_COLUMNS.get(0));
-        assertFalse(response.isEmpty());
-    }
-
-    @Test
-    public void findByUri_noRdfNoSparql_fails() throws OntologyNotFoundException {
-
-        /* mock */
-        doThrow(OntologyNotFoundException.class)
-                .when(ontologyService)
-                .find(anyString());
-
-        /* test */
-        assertThrows(OntologyNotFoundException.class, () -> {
-            entityService.findByUri("http://schema.org/MedicalCondition");
-        });
-    }
-
-}
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/EntityService.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/EntityService.java
deleted file mode 100644
index 69a801cf5c..0000000000
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/EntityService.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package at.tuwien.service;
-
-import at.tuwien.api.semantics.EntityDto;
-import at.tuwien.api.semantics.TableColumnEntityDto;
-import at.tuwien.entities.database.table.Table;
-import at.tuwien.entities.database.table.columns.TableColumn;
-import at.tuwien.entities.semantics.Ontology;
-import at.tuwien.exception.*;
-
-import java.util.List;
-
-public interface EntityService {
-
-    /**
-     * Finds entities in the ontology whose label match the given label.
-     *
-     * @param ontology The ontology.
-     * @param label    The label.
-     * @return The list of entities that match.
-     */
-    List<EntityDto> findByLabel(Ontology ontology, String label) throws MalformedException;
-
-    /**
-     * Finds entities in the ontology whose label match the given label with maximum number of entities.
-     *
-     * @param ontology The ontology.
-     * @param label    The label.
-     * @param limit    The maximum number of entities to return.
-     * @return The list of entities that match.
-     */
-    List<EntityDto> findByLabel(Ontology ontology, String label, Integer limit) throws MalformedException;
-
-    /**
-     * Finds entities in the ontology whose uri match the given uri.
-     *
-     * @param uri      The uri.
-     * @return The list of entities that match.
-     */
-    List<EntityDto> findByUri(String uri) throws MalformedException, OntologyNotFoundException;
-
-    /**
-     * Finds an entity in the ontology whose uri match the given uri.
-     *
-     * @param uri      The uri.
-     * @return The entity, if successful.
-     */
-    EntityDto findOneByUri(String uri) throws MalformedException, SemanticEntityNotFoundException, OntologyNotFoundException;
-
-    /**
-     * Attempts to suggest table semantics for a table with given id in database with given id.
-     *
-     * @param table    The table.
-     * @return The list of entities that were suggested.
-     */
-    List<EntityDto> suggestByTable(Table table) throws MalformedException;
-
-    /**
-     * Attempts to suggest table column semantics for a table column in table with given id in database with given id.
-     *
-     * @param column   The table column.
-     * @return The list of entities that were suggested.
-     */
-    List<TableColumnEntityDto> suggestByColumn(TableColumn column) throws MalformedException;
-}
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/EntityServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/EntityServiceImpl.java
deleted file mode 100644
index dba30481f5..0000000000
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/EntityServiceImpl.java
+++ /dev/null
@@ -1,167 +0,0 @@
-package at.tuwien.service.impl;
-
-import at.tuwien.api.semantics.EntityDto;
-import at.tuwien.api.semantics.TableColumnEntityDto;
-import at.tuwien.config.JenaConfig;
-import at.tuwien.entities.database.table.Table;
-import at.tuwien.entities.database.table.columns.TableColumn;
-import at.tuwien.entities.semantics.Ontology;
-import at.tuwien.exception.*;
-import at.tuwien.mapper.SparqlMapper;
-import at.tuwien.service.EntityService;
-import at.tuwien.service.OntologyService;
-import lombok.extern.log4j.Log4j2;
-import org.apache.jena.query.*;
-import org.apache.jena.rdf.model.RDFNode;
-import org.apache.jena.riot.RDFDataMgr;
-import org.apache.jena.riot.RiotException;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-@Log4j2
-@Service
-public class EntityServiceImpl implements EntityService {
-
-    private final Dataset dataset;
-    private final JenaConfig jenaConfig;
-    private final SparqlMapper ontologyMapper;
-    private final OntologyService ontologyService;
-
-    @Autowired
-    public EntityServiceImpl(Dataset dataset, JenaConfig jenaConfig, SparqlMapper ontologyMapper,
-                             OntologyService ontologyService) {
-        this.dataset = dataset;
-        this.jenaConfig = jenaConfig;
-        this.ontologyMapper = ontologyMapper;
-        this.ontologyService = ontologyService;
-    }
-
-    public void validateOntology(Ontology ontology) throws MalformedException {
-        if (ontology.getRdfPath() == null && ontology.getSparqlEndpoint() == null) {
-            log.error("Ontology with uri {} is invalid: no RDF file present and no SPARQL endpoint found", ontology.getUri());
-            throw new MalformedException("Ontology with uri " + ontology.getUri() + " is invalid: no RDF file present and no SPARQL endpoint found");
-        }
-    }
-
-    @Override
-    public List<EntityDto> findByLabel(Ontology ontology, String label) throws MalformedException {
-        return findByLabel(ontology, label, 10);
-    }
-
-    @Override
-    public List<EntityDto> findByLabel(Ontology ontology, String label, Integer limit) throws MalformedException {
-        /* check */
-        validateOntology(ontology);
-        /* find */
-        final List<Ontology> ontologies = ontologyService.findAll();
-        final String statement = ontologyMapper.ontologyToFindByLabelQuery(ontologies, ontology, label, limit);
-        log.trace("execute sparql query:\n{}", statement);
-        final List<EntityDto> results = new LinkedList<>();
-        if (ontology.getSparqlEndpoint() == null && ontology.getRdfPath() != null) {
-            log.debug("load rdf model from path {}", ontology.getRdfPath());
-            this.dataset.setDefaultModel(RDFDataMgr.loadModel(ontology.getRdfPath()));
-        }
-        try (QueryExecution execution = QueryExecutionDatasetBuilder.create()
-                .model(this.dataset.getDefaultModel())
-                .query(statement)
-                .timeout(jenaConfig.getConnectionTimeout(), TimeUnit.MILLISECONDS)
-                .build()) {
-            final Iterator<QuerySolution> resultSet = execution.execSelect();
-            while (resultSet.hasNext()) {
-                final QuerySolution solution = resultSet.next();
-                final RDFNode description = solution.get("description");
-                final EntityDto entity = EntityDto.builder()
-                        .uri(solution.get("o").toString())
-                        .label(label)
-                        .description(description != null ? description.asLiteral().getLexicalForm() : null)
-                        .build();
-                results.add(entity);
-            }
-        } catch (QueryParseException | IllegalArgumentException | RiotException e) {
-            log.error("Failed to parse query: {}", e.getMessage());
-            throw new MalformedException("Failed to parse query: " + e.getMessage(), e);
-        }
-        return results;
-    }
-
-    @Override
-    public List<EntityDto> findByUri(String uri) throws MalformedException, OntologyNotFoundException {
-        /* find */
-        final Ontology ontology = ontologyService.find(uri);
-        final List<Ontology> ontologies = ontologyService.findAll();
-        final String statement = ontologyMapper.ontologyToFindByUriQuery(ontologies, ontology, uri);
-        log.trace("execute sparql query:\n{}", statement);
-        try (QueryExecution execution = QueryExecutionDatasetBuilder.create()
-                .model(this.dataset.getDefaultModel())
-                .query(statement)
-                .timeout(jenaConfig.getConnectionTimeout(), TimeUnit.MILLISECONDS)
-                .build()) {
-            final Iterator<QuerySolution> resultSet = execution.execSelect();
-            final List<EntityDto> results = new LinkedList<>();
-            while (resultSet.hasNext()) {
-                final QuerySolution solution = resultSet.next();
-                final RDFNode label = solution.get("label");
-                final RDFNode description = solution.get("description");
-                final EntityDto entity = EntityDto.builder()
-                        .uri(uri)
-                        .label(label != null ? label.asLiteral().getLexicalForm() : null)
-                        .description(description != null ? description.asLiteral().getLexicalForm() : null)
-                        .build();
-                results.add(entity);
-            }
-            return results;
-        } catch (QueryParseException | IllegalArgumentException | RiotException e) {
-            log.error("Failed to parse query: {}", e.getMessage());
-            throw new MalformedException("Failed to parse query: " + e.getMessage(), e);
-        }
-    }
-
-    @Override
-    public EntityDto findOneByUri(String uri) throws MalformedException, SemanticEntityNotFoundException,
-            OntologyNotFoundException {
-        /* find */
-        final List<EntityDto> results = findByUri(uri);
-        if (results.size() != 1) {
-            log.error("None or multiple entities found for uri {}", uri);
-            throw new SemanticEntityNotFoundException("None or multiple entities found for uri " + uri);
-        }
-        return results.get(0);
-    }
-
-    @Override
-    @Transactional(readOnly = true)
-    public List<EntityDto> suggestByTable(Table table) throws MalformedException {
-        final List<EntityDto> suggestions = new LinkedList<>();
-        for (Ontology ontology : ontologyService.findAllProcessable()) {
-            suggestions.addAll(findByLabel(ontology, table.getName(), 3));
-        }
-        return suggestions;
-    }
-
-    @Override
-    @Transactional(readOnly = true)
-    public List<TableColumnEntityDto> suggestByColumn(TableColumn tableColumn) throws MalformedException {
-        final List<TableColumnEntityDto> suggestions = new LinkedList<>();
-        for (Ontology ontology : ontologyService.findAllProcessable()) {
-            suggestions.addAll(findByLabel(ontology, tableColumn.getName(), 3)
-                    .stream()
-                    .map(e -> TableColumnEntityDto.builder()
-                            .databaseId(tableColumn.getTable().getDatabase().getId())
-                            .tableId(tableColumn.getTable().getId())
-                            .columnId(tableColumn.getId())
-                            .label(e.getLabel())
-                            .uri(e.getUri())
-                            .description(e.getDescription())
-                            .build())
-                    .toList());
-        }
-        return suggestions;
-    }
-
-}
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java
index 7ca855974e..8572372516 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java
@@ -6,6 +6,7 @@ import at.tuwien.api.database.table.columns.ColumnCreateDto;
 import at.tuwien.api.database.table.columns.ColumnStatisticDto;
 import at.tuwien.api.database.table.columns.ColumnTypeDto;
 import at.tuwien.api.database.table.columns.concepts.ColumnSemanticsUpdateDto;
+import at.tuwien.api.semantics.EntityDto;
 import at.tuwien.config.RabbitConfig;
 import at.tuwien.entities.container.image.ContainerImageDate;
 import at.tuwien.entities.database.Database;
@@ -36,7 +37,6 @@ public class TableServiceImpl implements TableService {
     private final UserService userService;
     private final UnitService unitService;
     private final RabbitConfig rabbitConfig;
-    private final EntityService entityService;
     private final ConceptService conceptService;
     private final MetadataMapper metadataMapper;
     private final DatabaseService databaseService;
@@ -46,13 +46,12 @@ public class TableServiceImpl implements TableService {
 
     @Autowired
     public TableServiceImpl(UserService userService, UnitService unitService, RabbitConfig rabbitConfig,
-                            EntityService entityService, ConceptService conceptService, MetadataMapper metadataMapper,
+                            ConceptService conceptService, MetadataMapper metadataMapper,
                             DatabaseService databaseService, DataServiceGateway dataServiceGateway,
                             DatabaseRepository databaseRepository, SearchServiceGateway searchServiceGateway) {
         this.userService = userService;
         this.unitService = unitService;
         this.rabbitConfig = rabbitConfig;
-        this.entityService = entityService;
         this.conceptService = conceptService;
         this.metadataMapper = metadataMapper;
         this.databaseService = databaseService;
@@ -128,23 +127,31 @@ public class TableServiceImpl implements TableService {
                 column.setTable(table);
                 if (c.getUnitUri() != null) {
                     log.trace("column {} has assigned unit uri: {}", column.getInternalName(), c.getUnitUri());
-                    TableColumnUnit unit;
                     try {
-                        unit = unitService.find(c.getUnitUri());
+                        column.setUnit(unitService.find(c.getUnitUri()));
+                        log.debug("found unit by uri: {}", c.getUnitUri());
                     } catch (UnitNotFoundException e) {
-                        unit = unitService.create(metadataMapper.entityDtoToTableColumnUnit(entityService.findOneByUri(c.getUnitUri())));
+                        final TableColumnUnit unit = metadataMapper.entityDtoToTableColumnUnit(EntityDto.builder()
+                                .uri(c.getUnitUri())
+                                .label(c.getUnitLabel())
+                                .build());
+                        column.setUnit(unitService.create(unit));
+                        log.trace("created unit in metadata database: {}", unit);
                     }
-                    column.setUnit(unit);
                 }
                 if (c.getConceptUri() != null) {
                     log.trace("column {} has assigned concept uri: {}", column.getInternalName(), c.getConceptUri());
-                    TableColumnConcept concept;
                     try {
-                        concept = conceptService.find(c.getConceptUri());
+                        column.setConcept(conceptService.find(c.getConceptUri()));
+                        log.debug("found concept by uri: {}", c.getConceptUri());
                     } catch (ConceptNotFoundException e) {
-                        concept = conceptService.create(metadataMapper.entityDtoToTableColumnConcept(entityService.findOneByUri(c.getConceptUri())));
+                        final TableColumnConcept concept = metadataMapper.entityDtoToTableColumnConcept(EntityDto.builder()
+                                .uri(c.getConceptUri())
+                                .label(c.getConceptLabel())
+                                .build());
+                        column.setConcept(conceptService.create(concept));
+                        log.trace("created concept in metadata database: {}", concept);
                     }
-                    column.setConcept(concept);
                 }
                 if (List.of(TableColumnType.TIME, TableColumnType.TIMESTAMP, TableColumnType.DATE, TableColumnType.DATETIME).contains(column.getColumnType())) {
                     final Optional<ContainerImageDate> optional = database.getContainer()
@@ -218,24 +225,32 @@ public class TableServiceImpl implements TableService {
             SemanticEntityNotFoundException {
         /* assign */
         if (data.getUnitUri() != null) {
-            TableColumnUnit unit;
             try {
-                unit = unitService.find(data.getUnitUri());
+                column.setUnit(unitService.find(data.getUnitUri()));
+                log.debug("found unit by uri: {}", data.getUnitUri());
             } catch (UnitNotFoundException e) {
-                unit = metadataMapper.entityDtoToTableColumnUnit(entityService.findOneByUri(data.getUnitUri()));
+                final TableColumnUnit unit = metadataMapper.entityDtoToTableColumnUnit(EntityDto.builder()
+                        .uri(data.getUnitUri())
+                        .label(data.getUnitLabel())
+                        .build());
+                log.trace("create unit in metadata database: {}", unit);
+                column.setUnit(unit);
             }
-            column.setUnit(unit);
         } else {
             column.setUnit(null);
         }
         if (data.getConceptUri() != null) {
-            TableColumnConcept concept;
             try {
-                concept = conceptService.find(data.getConceptUri());
+                conceptService.find(data.getConceptUri());
+                log.debug("found concept by uri: {}", data.getConceptUri());
             } catch (ConceptNotFoundException e) {
-                concept = metadataMapper.entityDtoToTableColumnConcept(entityService.findOneByUri(data.getConceptUri()));
+                final TableColumnConcept concept = metadataMapper.entityDtoToTableColumnConcept(EntityDto.builder()
+                        .uri(data.getConceptUri())
+                        .label(data.getConceptLabel())
+                        .build());
+                log.trace("create concept in metadata database: {}", concept);
+                column.setConcept(concept);
             }
-            column.setConcept(concept);
         } else {
             column.setConcept(null);
         }
diff --git a/dbrepo-ui/components/table/TableSchema.vue b/dbrepo-ui/components/table/TableSchema.vue
index 845039b136..8539962de5 100644
--- a/dbrepo-ui/components/table/TableSchema.vue
+++ b/dbrepo-ui/components/table/TableSchema.vue
@@ -149,11 +149,12 @@
           cols="1">
           <v-select
             v-if="c.concepts"
-            v-model="c.concept_uri"
+            v-model="c.concept"
             persistent-hint
+            :items="c.concepts"
             :item-title="item => `${item.name} (${item.ontology})`"
             item-value="uri"
-            :items="c.concepts"
+            return-object
             :label="$t('pages.table.subpages.schema.concept.label')"
             :hint="$t('pages.table.subpages.schema.concept.hint')"
             :id="'object' + c.name"
@@ -169,11 +170,12 @@
           cols="1">
           <v-select
             v-if="c.units"
-            v-model="c.unit_uri"
+            v-model="c.unit"
             persistent-hint
+            :items="c.units"
             :item-title="item => `${item.name} (${item.ontology})`"
             item-value="uri"
-            :items="c.units"
+            return-object
             :label="$t('pages.table.subpages.schema.unit.label')"
             :hint="$t('pages.table.subpages.schema.unit.hint')"
             :id="'unit' + c.name"
@@ -423,9 +425,9 @@ export default {
         name,
         type,
         concepts: [],
-        concept_uri: null,
+        concept: null,
         units: [],
-        unit_uri: null,
+        unit: null,
         null_allowed,
         primary_key,
         dfid: null,
diff --git a/dbrepo-ui/composables/table-service.ts b/dbrepo-ui/composables/table-service.ts
index d26fbce900..52959d33c0 100644
--- a/dbrepo-ui/composables/table-service.ts
+++ b/dbrepo-ui/composables/table-service.ts
@@ -187,22 +187,6 @@ export const useTableService = (): any => {
     });
   }
 
-  async function suggest(databaseId: number, tableId: number, columnId: number): Promise<TableColumnEntityDto[]> {
-    const axios = useAxiosInstance()
-    console.debug('suggest semantic entities for table column with id', columnId, 'of table with id', tableId, 'of database with id', databaseId)
-    return new Promise<TableColumnEntityDto[]>((resolve, reject) => {
-      axios.get<TableColumnEntityDto[]>(`/api/database/${databaseId}/table/${tableId}/column/${columnId}/suggest`)
-        .then((response) => {
-          console.info('Suggested semantic entities for table column with id', columnId, 'of table with id', tableId, 'of database with id', databaseId)
-          resolve(response.data)
-        })
-        .catch((error) => {
-          console.error('Failed to suggest semantic entities', error)
-          reject(axiosErrorToApiError(error))
-        })
-    })
-  }
-
   function prepareColumns(columns: InternalColumnDto[]): ColumnCreateDto[] {
     return columns.map((c: InternalColumnDto) => {
       const column: ColumnCreateDto = {
@@ -215,8 +199,10 @@ export const useTableService = (): any => {
         sets: c.sets_values ? c.sets_values.split(',') : [],
         index_length: c.index_length,
         null_allowed: c.null_allowed,
-        concept_uri: c.concept_uri ? c.concept_uri : null,
-        unit_uri: c.unit_uri ? c.unit_uri : null
+        concept_uri: c.concept?.uri ? c.concept.uri : null,
+        concept_label: c.concept?.name ? c.concept.name : null,
+        unit_uri: c.unit?.uri ? c.unit.uri : null,
+        unit_label: c.unit?.name ? c.unit.name : null
       }
       return column
     })
@@ -271,7 +257,6 @@ export const useTableService = (): any => {
     remove,
     removeTuple,
     history,
-    suggest,
     prepareColumns,
     prepareConstraints,
     isOwner,
diff --git a/dbrepo-ui/dto/index.ts b/dbrepo-ui/dto/index.ts
index 4aff532930..e6cb7ffbe3 100644
--- a/dbrepo-ui/dto/index.ts
+++ b/dbrepo-ui/dto/index.ts
@@ -639,11 +639,20 @@ interface ColumnCreateDto {
   enums: string[];
   sets: string[];
   unit_uri: string | null;
+  unit_label: string | null;
   concept_uri: string | null;
+  concept_label: string | null;
   index_length: number;
   null_allowed: boolean;
 }
 
+interface EntityDto {
+  name: string | null;
+  score: number;
+  uri: string;
+  ontology: string;
+}
+
 interface InternalColumnDto {
   name: string;
   type: string;
@@ -652,6 +661,8 @@ interface InternalColumnDto {
   dfid: number;
   enums: string[];
   sets: string[];
+  unit: EntityDto | null;
+  concept: EntityDto | null;
   primary_key: boolean;
   index_length: number;
   null_allowed: boolean;
-- 
GitLab