From 03b1e0851b68f59bcd68158869032f9699c06d50 Mon Sep 17 00:00:00 2001 From: Guillaume Dugas Date: Wed, 19 Nov 2025 23:31:18 +0100 Subject: [PATCH] Add semrack index postgresql --- docker-compose.yml | 11 ++ integration/semrack-integration/pom.xml | 5 +- .../src/main/resources/application.properties | 3 +- modules/semrack-api-rest/deployment/pom.xml | 5 + .../semrack/api/rest/SemrackApi.java | 10 +- modules/semrack-core/deployment/pom.xml | 118 ++++++------ modules/semrack-core/pom.xml | 1 + modules/semrack-core/runtime/pom.xml | 4 + .../codeanddata/semrack/core/SemdocIndex.java | 13 ++ .../semrack/core/SemdocStorage.java | 10 +- .../AnnotateWriteInterceptor.java | 2 +- .../core/models/IndexSearchResult.java | 15 ++ .../semrack/core/models/TraverserPath.java | 15 ++ .../core/repositories/SemdocRepository.java | 10 +- .../core/services/SemrackIndexService.java | 35 ++++ .../core/storages/SemdocStorageProxy.java} | 34 ++-- .../semrack/core/utils/Traverser.java | 91 +++++++++ modules/semrack-core/testing/pom.xml | 47 +++++ .../semrack/core/MockProducer.java | 69 +++++++ .../src/main/resources/META-INF/beans.xml | 0 .../resources/META-INF/quarkus-extension.yaml | 9 + .../semrack/core/utils/TraverserTest.java | 54 ++++++ .../semrack-index-postgres/deployment/pom.xml | 81 ++++++++ .../SemrackIndexPostgresProcessor.java | 14 ++ .../test/SemrackIndexPostgresDevModeTest.java | 5 + .../test/SemrackIndexPostgresTest.java | 5 + .../integration-tests/pom.xml | 97 ++++++++++ .../it/SemrackIndexPostgresResource.java | 32 +++ .../src/main/resources/application.properties | 0 .../it/SemrackIndexPostgresResourceIT.java | 7 + .../it/SemrackIndexPostgresResourceTest.java | 21 ++ modules/semrack-index-postgres/pom.xml | 19 ++ .../semrack-index-postgres/runtime/pom.xml | 97 ++++++++++ .../index/postgres/EqualLookupParams.java | 13 ++ .../index/postgres}/InLookupParams.java | 4 +- .../index/postgres/SemdocJpaIndex.java | 136 +++++++++++++ .../entities/IndexDocumentEntity.java | 20 ++ .../postgres/entities/IndexKeyEntity.java | 33 ++++ .../index}/postgres/operators/AndLookup.java | 2 +- .../index/postgres/operators/EqualLookup.java | 32 +++ .../index/postgres/operators/InLookup.java | 39 ++++ .../index}/postgres/operators/OrLookup.java | 2 +- .../operators/SemrackJpaLookupExpression.java | 2 +- .../repositories/IndexDocumentRepository.java | 9 + .../repositories/IndexKeyRepository.java | 9 + .../src/main/resources/META-INF/beans.xml | 0 .../resources/META-INF/quarkus-extension.yaml | 9 + .../src/main/resources/application.properties | 9 + .../db/migration/V1.0.2__IndexPg.sql | 20 ++ .../main/resources/serialization-config.json | 3 + .../deployment/pom.xml | 6 +- .../src/main/resources/application.properties | 1 + .../semrack-storage-postgres/runtime/pom.xml | 5 + .../postgres/operators/ContainsLookup.java | 24 --- .../storage/postgres/operators/InLookup.java | 31 --- .../postgres/storage/SemdocJpaStorage.java | 182 ++++++++---------- .../src/main/resources/application.properties | 2 + ...V1.0.0__Base.sql => V1.0.1__StoragePg.sql} | 0 .../operators/ContainsLookupTest.java | 25 --- .../postgres/operators/InLookupTest.java | 43 ----- .../src/test/resources/application.properties | 1 + pom.xml | 18 +- 62 files changed, 1301 insertions(+), 318 deletions(-) create mode 100644 docker-compose.yml create mode 100644 modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemdocIndex.java create mode 100644 modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/IndexSearchResult.java create mode 100644 modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/TraverserPath.java create mode 100644 modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/services/SemrackIndexService.java rename modules/semrack-core/{deployment/src/test/java/fr/codeanddata/semrack/core/test/MockedStorageImpl.java => runtime/src/main/java/fr/codeanddata/semrack/core/storages/SemdocStorageProxy.java} (50%) create mode 100644 modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/utils/Traverser.java create mode 100644 modules/semrack-core/testing/pom.xml create mode 100644 modules/semrack-core/testing/src/main/java/fr/codeanddata/semrack/core/MockProducer.java create mode 100644 modules/semrack-core/testing/src/main/resources/META-INF/beans.xml create mode 100644 modules/semrack-core/testing/src/main/resources/META-INF/quarkus-extension.yaml create mode 100644 modules/semrack-core/testing/src/test/java/fr/codeanddata/semrack/core/utils/TraverserTest.java create mode 100644 modules/semrack-index-postgres/deployment/pom.xml create mode 100644 modules/semrack-index-postgres/deployment/src/main/java/fr/codeanddata/semrack/index/postgres/deployment/SemrackIndexPostgresProcessor.java create mode 100644 modules/semrack-index-postgres/deployment/src/test/java/fr/codeanddata/semrack/index/postgres/test/SemrackIndexPostgresDevModeTest.java create mode 100644 modules/semrack-index-postgres/deployment/src/test/java/fr/codeanddata/semrack/index/postgres/test/SemrackIndexPostgresTest.java create mode 100644 modules/semrack-index-postgres/integration-tests/pom.xml create mode 100644 modules/semrack-index-postgres/integration-tests/src/main/java/fr/codeanddata/semrack/index/postgres/it/SemrackIndexPostgresResource.java create mode 100644 modules/semrack-index-postgres/integration-tests/src/main/resources/application.properties create mode 100644 modules/semrack-index-postgres/integration-tests/src/test/java/fr/codeanddata/semrack/index/postgres/it/SemrackIndexPostgresResourceIT.java create mode 100644 modules/semrack-index-postgres/integration-tests/src/test/java/fr/codeanddata/semrack/index/postgres/it/SemrackIndexPostgresResourceTest.java create mode 100644 modules/semrack-index-postgres/pom.xml create mode 100644 modules/semrack-index-postgres/runtime/pom.xml create mode 100644 modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/EqualLookupParams.java rename modules/{semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/dtos => semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres}/InLookupParams.java (66%) create mode 100644 modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/SemdocJpaIndex.java create mode 100644 modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/entities/IndexDocumentEntity.java create mode 100644 modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/entities/IndexKeyEntity.java rename modules/{semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage => semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index}/postgres/operators/AndLookup.java (91%) create mode 100644 modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/operators/EqualLookup.java create mode 100644 modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/operators/InLookup.java rename modules/{semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage => semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index}/postgres/operators/OrLookup.java (91%) rename modules/{semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage => semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index}/postgres/operators/SemrackJpaLookupExpression.java (71%) create mode 100644 modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/repositories/IndexDocumentRepository.java create mode 100644 modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/repositories/IndexKeyRepository.java create mode 100644 modules/semrack-index-postgres/runtime/src/main/resources/META-INF/beans.xml create mode 100644 modules/semrack-index-postgres/runtime/src/main/resources/META-INF/quarkus-extension.yaml create mode 100644 modules/semrack-index-postgres/runtime/src/main/resources/application.properties create mode 100644 modules/semrack-index-postgres/runtime/src/main/resources/db/migration/V1.0.2__IndexPg.sql create mode 100644 modules/semrack-index-postgres/runtime/src/main/resources/serialization-config.json create mode 100644 modules/semrack-storage-postgres/deployment/src/main/resources/application.properties delete mode 100644 modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/ContainsLookup.java delete mode 100644 modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/InLookup.java rename modules/semrack-storage-postgres/runtime/src/main/resources/db/migration/{V1.0.0__Base.sql => V1.0.1__StoragePg.sql} (100%) delete mode 100644 modules/semrack-storage-postgres/runtime/src/test/java/fr/codeanddata/semrack/storage/postgres/operators/ContainsLookupTest.java delete mode 100644 modules/semrack-storage-postgres/runtime/src/test/java/fr/codeanddata/semrack/storage/postgres/operators/InLookupTest.java create mode 100644 modules/semrack-storage-postgres/runtime/src/test/resources/application.properties diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3543e17 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +services: + postgres: + image: postgres + environment: + POSTGRES_PASSWORD: postgres + ports: + - "18432:5432" + volumes: + - pgdata:/var/lib/postgres +volumes: + pgdata: diff --git a/integration/semrack-integration/pom.xml b/integration/semrack-integration/pom.xml index 0c8cf51..9385285 100644 --- a/integration/semrack-integration/pom.xml +++ b/integration/semrack-integration/pom.xml @@ -19,12 +19,15 @@ fr.codeanddata.semrack semrack-storage-postgres + + fr.codeanddata.semrack + semrack-index-postgres + fr.codeanddata.semrack semrack-api-rest - io.quarkus quarkus-junit5 diff --git a/integration/semrack-integration/src/main/resources/application.properties b/integration/semrack-integration/src/main/resources/application.properties index 5ce65a2..4b1b531 100644 --- a/integration/semrack-integration/src/main/resources/application.properties +++ b/integration/semrack-integration/src/main/resources/application.properties @@ -1,6 +1,6 @@ %dev.quarkus.log.min-level=TRACE -quarkus.hibernate-orm.mapping.format.global=ignore +#quarkus.hibernate-orm.mapping.format.global=ignore # Datasource quarkus.datasource.devservices.port=35432 @@ -9,6 +9,7 @@ quarkus.hibernate-orm.scripts.generation=none %prod.quarkus.datasource.reactive.url=postgresql://${semrack.db.host}:${semrack.db.port}/${semrack.db.name} %prod.quarkus.datasource.username=${semrack.db.user} %prod.quarkus.datasource.password=${semrack.db.password} +quarkus.hibernate-orm.log.sql=true ## Flyway quarkus.flyway.active=false diff --git a/modules/semrack-api-rest/deployment/pom.xml b/modules/semrack-api-rest/deployment/pom.xml index a0e0bff..ca65cc6 100644 --- a/modules/semrack-api-rest/deployment/pom.xml +++ b/modules/semrack-api-rest/deployment/pom.xml @@ -46,6 +46,11 @@ quarkus-junit5-internal test + + fr.codeanddata.semrack + semrack-core-testing + test + diff --git a/modules/semrack-api-rest/runtime/src/main/java/fr/codeanddata/semrack/api/rest/SemrackApi.java b/modules/semrack-api-rest/runtime/src/main/java/fr/codeanddata/semrack/api/rest/SemrackApi.java index 5b1aeff..f9ab2aa 100644 --- a/modules/semrack-api-rest/runtime/src/main/java/fr/codeanddata/semrack/api/rest/SemrackApi.java +++ b/modules/semrack-api-rest/runtime/src/main/java/fr/codeanddata/semrack/api/rest/SemrackApi.java @@ -1,10 +1,9 @@ package fr.codeanddata.semrack.api.rest; -import fr.codeanddata.semrack.core.SemdocStorage; -import fr.codeanddata.semrack.core.models.SemrackDocument; import fr.codeanddata.semrack.core.models.PushDocumentRequest; import fr.codeanddata.semrack.core.models.SearchRequest; import fr.codeanddata.semrack.core.models.SearchResult; +import fr.codeanddata.semrack.core.models.SemrackDocument; import fr.codeanddata.semrack.core.repositories.SemdocRepository; import io.smallrye.mutiny.Uni; import jakarta.inject.Inject; @@ -19,9 +18,6 @@ import org.jboss.resteasy.reactive.RestPath; //@Authenticated public class SemrackApi { - @Inject - SemdocStorage storage; - @Inject SemdocRepository repository; @@ -35,14 +31,14 @@ public class SemrackApi { @Path("/search") @Produces(MediaType.APPLICATION_JSON) public Uni searchDocument(SearchRequest query) { - return storage.searchDocument(query); + return repository.searchDocument(query); } @GET @Path("{uid}") @Produces(MediaType.APPLICATION_JSON) public Uni getDocument(@RestPath String uid) { - return storage.readDocument(uid); + return repository.get(uid); } @POST diff --git a/modules/semrack-core/deployment/pom.xml b/modules/semrack-core/deployment/pom.xml index e48bc1c..abd2514 100644 --- a/modules/semrack-core/deployment/pom.xml +++ b/modules/semrack-core/deployment/pom.xml @@ -1,59 +1,69 @@ - - 4.0.0 + + 4.0.0 - - fr.codeanddata.semrack - semrack-core-parent - 1.0-SNAPSHOT - - semrack-core-deployment - Semrack Core - Deployment + + fr.codeanddata.semrack + semrack-core-parent + 1.0-SNAPSHOT + + semrack-core-deployment + Semrack Core - Deployment - - - io.quarkus - quarkus-arc-deployment - - - io.quarkus - quarkus-security-deployment - - - io.quarkus - quarkus-jackson-deployment - - - fr.codeanddata.semrack - semrack-core - ${project.version} - - - io.quarkus - quarkus-junit5-internal - test - - + + + io.quarkus + quarkus-arc-deployment + + + io.quarkus + quarkus-vertx-deployment + + + io.quarkus + quarkus-security-deployment + + + io.quarkus + quarkus-jackson-deployment + + + fr.codeanddata.semrack + semrack-core + + + io.quarkus + quarkus-junit5-internal + test + + + fr.codeanddata.semrack + semrack-core-testing + ${project.version} + test + + - - - - maven-compiler-plugin - - - default-compile - - - - io.quarkus - quarkus-extension-processor - ${quarkus.version} - - - - - - - - + + + + maven-compiler-plugin + + + default-compile + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + + diff --git a/modules/semrack-core/pom.xml b/modules/semrack-core/pom.xml index 7305ecf..1064c39 100644 --- a/modules/semrack-core/pom.xml +++ b/modules/semrack-core/pom.xml @@ -15,6 +15,7 @@ deployment runtime + testing diff --git a/modules/semrack-core/runtime/pom.xml b/modules/semrack-core/runtime/pom.xml index e2cd7c9..4931182 100644 --- a/modules/semrack-core/runtime/pom.xml +++ b/modules/semrack-core/runtime/pom.xml @@ -16,6 +16,10 @@ io.quarkus quarkus-arc + + io.quarkus + quarkus-vertx + io.quarkus quarkus-security diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemdocIndex.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemdocIndex.java new file mode 100644 index 0000000..e815c16 --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemdocIndex.java @@ -0,0 +1,13 @@ +package fr.codeanddata.semrack.core; + +import fr.codeanddata.semrack.core.models.IndexSearchResult; +import fr.codeanddata.semrack.core.models.SearchRequest; +import io.smallrye.mutiny.Uni; + +public interface SemdocIndex { + Uni count(SearchRequest request); + Uni exist(SearchRequest query); + Uni index(String documentId); + Uni search(SearchRequest searchRequest); + Uni clear(String documentId); +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemdocStorage.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemdocStorage.java index 8bdce57..9ea7a41 100644 --- a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemdocStorage.java +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemdocStorage.java @@ -1,14 +1,12 @@ package fr.codeanddata.semrack.core; -import fr.codeanddata.semrack.core.models.SearchRequest; -import fr.codeanddata.semrack.core.models.SearchResult; import fr.codeanddata.semrack.core.models.SemrackDocument; import io.smallrye.mutiny.Uni; +import java.util.List; + public interface SemdocStorage { - Uni readDocument(String uid); - Uni searchDocument(SearchRequest query); - Uni countDocuments(SearchRequest request); - Uni documentsExist(SearchRequest query); + Uni get(String uid); + Uni> get(List uids); Uni storeDocument(SemrackDocument document); } diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/interceptors/AnnotateWriteInterceptor.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/interceptors/AnnotateWriteInterceptor.java index 77bce5c..a74c986 100644 --- a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/interceptors/AnnotateWriteInterceptor.java +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/interceptors/AnnotateWriteInterceptor.java @@ -24,7 +24,7 @@ public class AnnotateWriteInterceptor implements SemdocWriteInterceptor { final Map directives = context.getDirectives(); if (directives.containsKey(ANNOTATE_KEY)) { - final Map annotate = objectMapper.convertValue(directives.get(ANNOTATE_KEY), new TypeReference>() {}); + final Map annotate = objectMapper.convertValue(directives.get(ANNOTATE_KEY), new TypeReference<>() {}); annotate.keySet().forEach(key -> context.getNextDocument().getAnnotations().put(CUSTOM_PREFIX + key, annotate.get(key))); } diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/IndexSearchResult.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/IndexSearchResult.java new file mode 100644 index 0000000..1bd90c3 --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/IndexSearchResult.java @@ -0,0 +1,15 @@ +package fr.codeanddata.semrack.core.models; + +import lombok.*; + +import java.util.List; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class IndexSearchResult { + List uids; + PaginationInfo pagination; +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/TraverserPath.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/TraverserPath.java new file mode 100644 index 0000000..6ab0388 --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/TraverserPath.java @@ -0,0 +1,15 @@ +package fr.codeanddata.semrack.core.models; + +import fr.codeanddata.semrack.core.utils.Traverser; +import lombok.*; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class TraverserPath { + Traverser.PathTypes type; + String fullPath; + Object value; +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/repositories/SemdocRepository.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/repositories/SemdocRepository.java index 20126eb..3a7944e 100644 --- a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/repositories/SemdocRepository.java +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/repositories/SemdocRepository.java @@ -1,9 +1,9 @@ package fr.codeanddata.semrack.core.repositories; -import fr.codeanddata.semrack.core.SemdocStorage; import fr.codeanddata.semrack.core.SemdocWriteInterceptor; import fr.codeanddata.semrack.core.exceptions.SemrackRuntimeException; import fr.codeanddata.semrack.core.models.*; +import fr.codeanddata.semrack.core.storages.SemdocStorageProxy; import fr.codeanddata.semrack.core.utils.UIDGenerator; import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.ApplicationScoped; @@ -19,7 +19,7 @@ import java.util.concurrent.atomic.AtomicReference; public class SemdocRepository { @Inject - SemdocStorage semdocStorage; + SemdocStorageProxy semdocStorage; @Inject @Any @@ -28,10 +28,14 @@ public class SemdocRepository { @Inject UIDGenerator uidGenerator; - Uni searchDocument(SearchRequest query) { + public Uni searchDocument(SearchRequest query) { return semdocStorage.searchDocument(query); } + public Uni get(String documentId) { + return semdocStorage.readDocument(documentId); + } + public Uni pushDocument(PushDocumentRequest pushDocument) { return Optional.ofNullable(pushDocument.getUid()).map(semdocStorage::readDocument).orElse(Uni.createFrom().nullItem()) .chain(currentDocument -> { diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/services/SemrackIndexService.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/services/SemrackIndexService.java new file mode 100644 index 0000000..57d913f --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/services/SemrackIndexService.java @@ -0,0 +1,35 @@ +package fr.codeanddata.semrack.core.services; + +import fr.codeanddata.semrack.core.SemdocIndex; +import fr.codeanddata.semrack.core.models.IndexSearchResult; +import fr.codeanddata.semrack.core.models.SearchRequest; +import io.smallrye.mutiny.Uni; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +@ApplicationScoped +public class SemrackIndexService { + + @Inject + SemdocIndex semdocIndex; + + public Uni index(String documentId) { + return semdocIndex.index(documentId); + } + + public Uni search(SearchRequest searchRequest) { + return semdocIndex.search(searchRequest); + } + + public Uni count(SearchRequest request) { + return semdocIndex.count(request); + } + + public Uni exist(SearchRequest query) { + return semdocIndex.exist(query); + } + + public Uni clear(String documentId) { + return semdocIndex.clear(documentId); + } +} diff --git a/modules/semrack-core/deployment/src/test/java/fr/codeanddata/semrack/core/test/MockedStorageImpl.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/storages/SemdocStorageProxy.java similarity index 50% rename from modules/semrack-core/deployment/src/test/java/fr/codeanddata/semrack/core/test/MockedStorageImpl.java rename to modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/storages/SemdocStorageProxy.java index 3f85e40..1664e1c 100644 --- a/modules/semrack-core/deployment/src/test/java/fr/codeanddata/semrack/core/test/MockedStorageImpl.java +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/storages/SemdocStorageProxy.java @@ -1,36 +1,46 @@ -package fr.codeanddata.semrack.core.test; +package fr.codeanddata.semrack.core.storages; +import fr.codeanddata.semrack.core.SemdocIndex; import fr.codeanddata.semrack.core.SemdocStorage; import fr.codeanddata.semrack.core.models.SearchRequest; import fr.codeanddata.semrack.core.models.SearchResult; import fr.codeanddata.semrack.core.models.SemrackDocument; import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; @ApplicationScoped -public class MockedStorageImpl implements SemdocStorage { - @Override +public class SemdocStorageProxy { + + @Inject + SemdocStorage storage; + + @Inject + SemdocIndex index; + public Uni readDocument(String uid) { - return null; + return storage.get(uid); } - @Override public Uni searchDocument(SearchRequest query) { - return null; + return index.search(query) + .chain(searchResult -> storage.get(searchResult.getUids()) + .map(documents -> SearchResult.builder() + .documents(documents) + .pagination(searchResult.getPagination()) + .build())); } - @Override public Uni countDocuments(SearchRequest request) { - return null; + return index.count(request); } - @Override public Uni documentsExist(SearchRequest query) { - return null; + return index.exist(query); } - @Override public Uni storeDocument(SemrackDocument document) { - return null; + return storage.storeDocument(document) + .call(x -> index.index(x.getUid())); } } diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/utils/Traverser.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/utils/Traverser.java new file mode 100644 index 0000000..af78f3a --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/utils/Traverser.java @@ -0,0 +1,91 @@ +package fr.codeanddata.semrack.core.utils; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import fr.codeanddata.semrack.core.models.TraverserPath; +import io.vertx.core.json.Json; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.util.*; +import java.util.function.Function; + +@ApplicationScoped +public class Traverser implements Function> { + + @Inject + ObjectMapper mapper; + + @Override + public List apply(Object o) { + return parse("", o); + } + + public enum PathTypes { + UNKNOWN, + STRING, + NUMBER, + BOOLEAN, + LIST, + OBJECT + } + + List parse(String basePath, Object data) { + final List paths = new ArrayList<>(); + + PathTypes type = PathTypes.UNKNOWN; + if (data instanceof String) { + type = PathTypes.STRING; + } else if (data instanceof Boolean) { + type = PathTypes.BOOLEAN; + } else if (data instanceof Number) { + type = PathTypes.NUMBER; + } else if (data instanceof Collection) { + type = PathTypes.LIST; + } else if (data instanceof Map) { + type = PathTypes.OBJECT; + } + + switch (type) { + case PathTypes.LIST: + final Collection values = mapper.convertValue(data, Collection.class); + paths.add(TraverserPath.builder() + .fullPath(basePath) + .value(null) + .type(PathTypes.LIST) + .build()); + int i = 0; + for (Iterator it = values.iterator(); it.hasNext(); i++) { + final String fullPath = basePath + "[" + i + "]"; + final Object value = it.next(); + paths.addAll(parse(fullPath, value)); + } + break; + + case PathTypes.OBJECT: + paths.add(TraverserPath.builder() + .fullPath(basePath) + .value(null) + .type(PathTypes.OBJECT) + .build()); + final Map map = mapper.convertValue(data, new TypeReference<>() { + }); + for (Map.Entry entry : map.entrySet()) { + final String fullPath = basePath + "." + entry.getKey(); + final Object value = entry.getValue(); + paths.addAll(parse(fullPath, value)); + } + break; + + default: + paths.add(TraverserPath.builder() + .type(type) + .fullPath(basePath) + .value(data) + .build()); + break; + } + + return paths; + } +} diff --git a/modules/semrack-core/testing/pom.xml b/modules/semrack-core/testing/pom.xml new file mode 100644 index 0000000..fc22389 --- /dev/null +++ b/modules/semrack-core/testing/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + + + fr.codeanddata.semrack + semrack-core-parent + 1.0-SNAPSHOT + + semrack-core-testing + Semrack Core - Testing + + + + io.quarkus + quarkus-arc + + + fr.codeanddata.semrack + semrack-core + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + + + + maven-compiler-plugin + + + default-compile + + + + + + diff --git a/modules/semrack-core/testing/src/main/java/fr/codeanddata/semrack/core/MockProducer.java b/modules/semrack-core/testing/src/main/java/fr/codeanddata/semrack/core/MockProducer.java new file mode 100644 index 0000000..a892aa0 --- /dev/null +++ b/modules/semrack-core/testing/src/main/java/fr/codeanddata/semrack/core/MockProducer.java @@ -0,0 +1,69 @@ +package fr.codeanddata.semrack.core; + +import fr.codeanddata.semrack.core.models.IndexSearchResult; +import fr.codeanddata.semrack.core.models.SearchRequest; +import fr.codeanddata.semrack.core.models.SemrackDocument; +import io.quarkus.arc.DefaultBean; +import io.smallrye.mutiny.Uni; +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; + +import java.util.List; + +@Dependent +public class MockProducer { + @Produces + @DefaultBean + @Priority(100) + SemdocStorage produceStorage() { + return new SemdocStorage() { + @Override + public Uni get(String uid) { + return null; + } + + @Override + public Uni> get(List uids) { + return null; + } + + @Override + public Uni storeDocument(SemrackDocument document) { + return null; + } + }; + } + + @Produces + @DefaultBean + @Priority(100) + SemdocIndex produceIndex() { + return new SemdocIndex() { + @Override + public Uni count(SearchRequest request) { + return null; + } + + @Override + public Uni exist(SearchRequest query) { + return null; + } + + @Override + public Uni index(String documentId) { + return null; + } + + @Override + public Uni search(SearchRequest searchRequest) { + return null; + } + + @Override + public Uni clear(String documentId) { + return null; + } + }; + } +} diff --git a/modules/semrack-core/testing/src/main/resources/META-INF/beans.xml b/modules/semrack-core/testing/src/main/resources/META-INF/beans.xml new file mode 100644 index 0000000..e69de29 diff --git a/modules/semrack-core/testing/src/main/resources/META-INF/quarkus-extension.yaml b/modules/semrack-core/testing/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 0000000..1a89be6 --- /dev/null +++ b/modules/semrack-core/testing/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,9 @@ +name: Semrack Core Testing +#description: Do something useful. +metadata: +# keywords: +# - semrack-core +# guide: ... # To create and publish this guide, see https://github.com/quarkiverse/quarkiverse/wiki#documenting-your-extension +# categories: +# - "miscellaneous" +# status: "preview" diff --git a/modules/semrack-core/testing/src/test/java/fr/codeanddata/semrack/core/utils/TraverserTest.java b/modules/semrack-core/testing/src/test/java/fr/codeanddata/semrack/core/utils/TraverserTest.java new file mode 100644 index 0000000..c7d9974 --- /dev/null +++ b/modules/semrack-core/testing/src/test/java/fr/codeanddata/semrack/core/utils/TraverserTest.java @@ -0,0 +1,54 @@ +package fr.codeanddata.semrack.core.utils; + +import fr.codeanddata.semrack.core.models.TraverserPath; +import io.quarkus.test.junit.QuarkusTest; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +@QuarkusTest +public class TraverserTest { + + @Inject + Traverser traverser; + + @Test + void testUtil() { + Assertions.assertNotNull(traverser); + final Map user = Map.of( + "user", Map.of( + "name", "bob", + "age", 14, + "isAdult", false, + "family", List.of( + Map.of("name", "mom", "age", 38, "isAdult", true), + Map.of("name", "bro", "age", 6, "isAdult", false) + ))); + List paths = traverser.apply(user); + Assertions.assertNotNull(paths); + Assertions.assertEquals(14, paths.size()); + + List> expectations = List.of( + Arrays.asList(".user", null, Traverser.PathTypes.OBJECT), + Arrays.asList(".user.age", 14, Traverser.PathTypes.NUMBER), + Arrays.asList(".user.family[0].age", 38, Traverser.PathTypes.NUMBER), + Arrays.asList(".user.family[0].isAdult", true, Traverser.PathTypes.BOOLEAN), + Arrays.asList(".user.family[0].name", "mom", Traverser.PathTypes.STRING), + Arrays.asList(".user.family[1].age", 6, Traverser.PathTypes.NUMBER), + Arrays.asList(".user.family[1].isAdult", false, Traverser.PathTypes.BOOLEAN), + Arrays.asList(".user.family[1].name", "bro", Traverser.PathTypes.STRING), + Arrays.asList(".user.isAdult", false, Traverser.PathTypes.BOOLEAN), + Arrays.asList(".user.name", "bob", Traverser.PathTypes.STRING) + ); + + for (List expectation : expectations) { + final TraverserPath path = paths.stream().filter(x -> expectation.getFirst().equals(x.getFullPath())).findFirst().orElse(null); + Assertions.assertNotNull(path); + Assertions.assertEquals(expectation.get(1), path.getValue()); + } + } +} diff --git a/modules/semrack-index-postgres/deployment/pom.xml b/modules/semrack-index-postgres/deployment/pom.xml new file mode 100644 index 0000000..78a9c9d --- /dev/null +++ b/modules/semrack-index-postgres/deployment/pom.xml @@ -0,0 +1,81 @@ + + + 4.0.0 + + + fr.codeanddata.semrack + semrack-index-postgres-parent + 1.0-SNAPSHOT + ../pom.xml + + semrack-index-postgres-deployment + Semrack Index Postgres - Deployment + + + + fr.codeanddata.semrack + semrack-core-deployment + + + io.quarkus + quarkus-arc-deployment + + + io.quarkus + quarkus-hibernate-reactive-panache-deployment + + + io.quarkus + quarkus-reactive-pg-client-deployment + + + io.quarkus + quarkus-flyway-deployment + + + io.quarkus + quarkus-jdbc-postgresql-deployment + + + fr.codeanddata.semrack + semrack-index-postgres + + + io.quarkus + quarkus-junit5 + test + + + io.quarkus + quarkus-junit5-internal + test + + + fr.codeanddata.semrack + semrack-core-testing + + + + + + + maven-compiler-plugin + + + default-compile + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + + + diff --git a/modules/semrack-index-postgres/deployment/src/main/java/fr/codeanddata/semrack/index/postgres/deployment/SemrackIndexPostgresProcessor.java b/modules/semrack-index-postgres/deployment/src/main/java/fr/codeanddata/semrack/index/postgres/deployment/SemrackIndexPostgresProcessor.java new file mode 100644 index 0000000..d73dc86 --- /dev/null +++ b/modules/semrack-index-postgres/deployment/src/main/java/fr/codeanddata/semrack/index/postgres/deployment/SemrackIndexPostgresProcessor.java @@ -0,0 +1,14 @@ +package fr.codeanddata.semrack.index.postgres.deployment; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.FeatureBuildItem; + +class SemrackIndexPostgresProcessor { + + private static final String FEATURE = "semrack-index-postgres"; + + @BuildStep + FeatureBuildItem feature() { + return new FeatureBuildItem(FEATURE); + } +} diff --git a/modules/semrack-index-postgres/deployment/src/test/java/fr/codeanddata/semrack/index/postgres/test/SemrackIndexPostgresDevModeTest.java b/modules/semrack-index-postgres/deployment/src/test/java/fr/codeanddata/semrack/index/postgres/test/SemrackIndexPostgresDevModeTest.java new file mode 100644 index 0000000..cfe01e1 --- /dev/null +++ b/modules/semrack-index-postgres/deployment/src/test/java/fr/codeanddata/semrack/index/postgres/test/SemrackIndexPostgresDevModeTest.java @@ -0,0 +1,5 @@ +package fr.codeanddata.semrack.index.postgres.test; + +public class SemrackIndexPostgresDevModeTest { + +} diff --git a/modules/semrack-index-postgres/deployment/src/test/java/fr/codeanddata/semrack/index/postgres/test/SemrackIndexPostgresTest.java b/modules/semrack-index-postgres/deployment/src/test/java/fr/codeanddata/semrack/index/postgres/test/SemrackIndexPostgresTest.java new file mode 100644 index 0000000..5990905 --- /dev/null +++ b/modules/semrack-index-postgres/deployment/src/test/java/fr/codeanddata/semrack/index/postgres/test/SemrackIndexPostgresTest.java @@ -0,0 +1,5 @@ +package fr.codeanddata.semrack.index.postgres.test; + +public class SemrackIndexPostgresTest { + +} diff --git a/modules/semrack-index-postgres/integration-tests/pom.xml b/modules/semrack-index-postgres/integration-tests/pom.xml new file mode 100644 index 0000000..0716e22 --- /dev/null +++ b/modules/semrack-index-postgres/integration-tests/pom.xml @@ -0,0 +1,97 @@ + + + 4.0.0 + + + fr.codeanddata.semrack + semrack-index-postgres-parent + 1.0.0-SNAPSHOT + + semrack-index-postgres-integration-tests + Semrack Index Postgres - Integration Tests + + + true + + + + + io.quarkus + quarkus-rest + + + fr.codeanddata.semrack + semrack-index-postgres + ${project.version} + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + + + + + false + true + + + + diff --git a/modules/semrack-index-postgres/integration-tests/src/main/java/fr/codeanddata/semrack/index/postgres/it/SemrackIndexPostgresResource.java b/modules/semrack-index-postgres/integration-tests/src/main/java/fr/codeanddata/semrack/index/postgres/it/SemrackIndexPostgresResource.java new file mode 100644 index 0000000..94bb58d --- /dev/null +++ b/modules/semrack-index-postgres/integration-tests/src/main/java/fr/codeanddata/semrack/index/postgres/it/SemrackIndexPostgresResource.java @@ -0,0 +1,32 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package fr.codeanddata.semrack.index.postgres.it; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +@Path("/semrack-index-postgres") +@ApplicationScoped +public class SemrackIndexPostgresResource { + // add some rest methods here + + @GET + public String hello() { + return "Hello semrack-index-postgres"; + } +} diff --git a/modules/semrack-index-postgres/integration-tests/src/main/resources/application.properties b/modules/semrack-index-postgres/integration-tests/src/main/resources/application.properties new file mode 100644 index 0000000..e69de29 diff --git a/modules/semrack-index-postgres/integration-tests/src/test/java/fr/codeanddata/semrack/index/postgres/it/SemrackIndexPostgresResourceIT.java b/modules/semrack-index-postgres/integration-tests/src/test/java/fr/codeanddata/semrack/index/postgres/it/SemrackIndexPostgresResourceIT.java new file mode 100644 index 0000000..20c1f30 --- /dev/null +++ b/modules/semrack-index-postgres/integration-tests/src/test/java/fr/codeanddata/semrack/index/postgres/it/SemrackIndexPostgresResourceIT.java @@ -0,0 +1,7 @@ +package fr.codeanddata.semrack.index.postgres.it; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class SemrackIndexPostgresResourceIT extends SemrackIndexPostgresResourceTest { +} diff --git a/modules/semrack-index-postgres/integration-tests/src/test/java/fr/codeanddata/semrack/index/postgres/it/SemrackIndexPostgresResourceTest.java b/modules/semrack-index-postgres/integration-tests/src/test/java/fr/codeanddata/semrack/index/postgres/it/SemrackIndexPostgresResourceTest.java new file mode 100644 index 0000000..5341a1c --- /dev/null +++ b/modules/semrack-index-postgres/integration-tests/src/test/java/fr/codeanddata/semrack/index/postgres/it/SemrackIndexPostgresResourceTest.java @@ -0,0 +1,21 @@ +package fr.codeanddata.semrack.index.postgres.it; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.is; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class SemrackIndexPostgresResourceTest { + + @Test + public void testHelloEndpoint() { + given() + .when().get("/semrack-index-postgres") + .then() + .statusCode(200) + .body(is("Hello semrack-index-postgres")); + } +} diff --git a/modules/semrack-index-postgres/pom.xml b/modules/semrack-index-postgres/pom.xml new file mode 100644 index 0000000..fceb32a --- /dev/null +++ b/modules/semrack-index-postgres/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + fr.codeanddata.semrack + semrack-parent + 1.0-SNAPSHOT + ../../pom.xml + + semrack-index-postgres-parent + Semrack Index Postgres - Parent + pom + + + deployment + runtime + + diff --git a/modules/semrack-index-postgres/runtime/pom.xml b/modules/semrack-index-postgres/runtime/pom.xml new file mode 100644 index 0000000..f1d749b --- /dev/null +++ b/modules/semrack-index-postgres/runtime/pom.xml @@ -0,0 +1,97 @@ + + + 4.0.0 + + + fr.codeanddata.semrack + semrack-index-postgres-parent + 1.0-SNAPSHOT + ../pom.xml + + semrack-index-postgres + Semrack Index Postgres - Runtime + + + + fr.codeanddata.semrack + semrack-core + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-vertx + + + io.quarkus + quarkus-hibernate-reactive-panache + + + io.quarkus + quarkus-reactive-pg-client + + + io.quarkus + quarkus-flyway + + + io.quarkus + quarkus-jdbc-postgresql + + + org.flywaydb + flyway-database-postgresql + + + org.projectlombok + lombok + + + + + + + io.quarkus + quarkus-extension-maven-plugin + ${quarkus.version} + + + compile + + extension-descriptor + + + ${project.groupId}:${project.artifactId}-deployment:${project.version} + + + + + + maven-compiler-plugin + + + default-compile + + + + org.projectlombok + lombok + ${lombok.version} + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + true + + + + + + + diff --git a/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/EqualLookupParams.java b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/EqualLookupParams.java new file mode 100644 index 0000000..7bdf849 --- /dev/null +++ b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/EqualLookupParams.java @@ -0,0 +1,13 @@ +package fr.codeanddata.semrack.index.postgres; + +import lombok.*; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class EqualLookupParams { + String field; + Object value; +} diff --git a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/dtos/InLookupParams.java b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/InLookupParams.java similarity index 66% rename from modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/dtos/InLookupParams.java rename to modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/InLookupParams.java index 924aae3..6271bb1 100644 --- a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/dtos/InLookupParams.java +++ b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/InLookupParams.java @@ -1,4 +1,4 @@ -package fr.codeanddata.semrack.storage.postgres.dtos; +package fr.codeanddata.semrack.index.postgres; import lombok.*; @@ -11,5 +11,5 @@ import java.util.List; @NoArgsConstructor public class InLookupParams { String field; - List values; + List values; } diff --git a/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/SemdocJpaIndex.java b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/SemdocJpaIndex.java new file mode 100644 index 0000000..4eb3950 --- /dev/null +++ b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/SemdocJpaIndex.java @@ -0,0 +1,136 @@ +package fr.codeanddata.semrack.index.postgres; + +import fr.codeanddata.semrack.core.SemdocIndex; +import fr.codeanddata.semrack.core.SemdocStorage; +import fr.codeanddata.semrack.core.models.*; +import fr.codeanddata.semrack.core.services.SemrackLookupService; +import fr.codeanddata.semrack.core.utils.Traverser; +import fr.codeanddata.semrack.index.postgres.entities.IndexDocumentEntity; +import fr.codeanddata.semrack.index.postgres.entities.IndexKeyEntity; +import fr.codeanddata.semrack.index.postgres.repositories.IndexDocumentRepository; +import fr.codeanddata.semrack.index.postgres.repositories.IndexKeyRepository; +import io.quarkus.hibernate.reactive.panache.PanacheRepository; +import io.quarkus.hibernate.reactive.panache.common.WithSession; +import io.quarkus.hibernate.reactive.panache.common.WithTransaction; +import io.quarkus.vertx.ConsumeEvent; +import io.smallrye.mutiny.Uni; +import io.vertx.core.eventbus.EventBus; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.hibernate.query.Page; + +import java.util.*; + +@ApplicationScoped +public class SemdocJpaIndex implements SemdocIndex, PanacheRepository { + + public static final String INDEX_CONSUMER = "semdoc-jpa-index"; + + @Inject + Traverser traverser; + + @Inject + SemrackLookupService lookupService; + + @Inject + SemdocStorage storage; + + @Inject + IndexDocumentRepository indexDocumentRepository; + + @Inject + IndexKeyRepository indexKeyRepository; + + @Inject + EventBus bus; + + @Override + @WithSession + public Uni count(SearchRequest request) { + final StringBuilder query = new StringBuilder("SELECT DISTINCT count(d.id) FROM index_document d JOIN index_key idx on d.id = idx.index_document_id"); + + final String lookup = lookupService.lookup(request.getFilter()); + final String whereClause = lookup.isEmpty() ? "" : " WHERE " + lookup; + query.append(whereClause); + return getSession() + .chain(s -> s.createNativeQuery(query.toString(), Long.class).getSingleResultOrNull()) + .map(count -> count == null ? 0 : count); + } + + @Override + public Uni exist(SearchRequest query) { + return count(query).map(count -> count > 0); + } + + @Override + @WithTransaction + public Uni index(String documentId) { + return storage.get(documentId) + .call(document -> clear(documentId)) + .call(document -> indexDocumentRepository.persist(IndexDocumentEntity.builder() + .uid(documentId) + .build()) + .call(index -> { + final List annotationPaths = traverser.apply(document.getAnnotations()).stream().peek(x -> x.setFullPath("annotations" + x.getFullPath())).toList(); + final List metadataPaths = traverser.apply(document.getMetadata()).stream().peek(x -> x.setFullPath("metadata" + x.getFullPath())).toList(); + + final List allPaths = new ArrayList<>(); + allPaths.addAll(annotationPaths); + allPaths.addAll(metadataPaths); + return indexKeyRepository.persist(allPaths + .stream() + .map(path -> { + final Map values = new HashMap<>(); + values.put("v", path.getValue()); + return IndexKeyEntity.builder() + .index(index) + .type(path.getType()) + .fullPath(path.getFullPath()) + .value(values) + .build(); + })); + })) + .replaceWithVoid(); + } + + @Override + @WithSession + public Uni search(SearchRequest request) { + final IndexSearchResult result = new IndexSearchResult(); + final StringBuilder query = new StringBuilder("SELECT DISTINCT d.uid FROM index_document d JOIN index_key idx on d.id = idx.index_document_id"); + + final String lookup = lookupService.lookup(request.getFilter()); + final String whereClause = lookup.isEmpty() ? "" : " WHERE " + lookup; + query.append(whereClause); + query.append(" GROUP BY d.uid"); + + return getSession() + .chain(s -> queryPaginationInfo(request) + .invoke(result::setPagination) + .call(() -> s + .createNativeQuery(query.toString(), String.class) + .setPage(Page.page(Integer.parseInt(result.getPagination().getSize() + ""), Integer.parseInt(result.getPagination().getPage() + ""))) + .getResultList() + .invoke(result::setUids)) + .map(count -> result)); + } + + @Override + @WithTransaction + public Uni clear(String documentId) { + return Uni.createFrom().nullItem() + .call(() -> IndexKeyEntity.delete("index.uid = ?1", documentId)) + .call(() -> IndexDocumentEntity.delete("uid = ?1", documentId)) + .replaceWithVoid(); + } + + Uni queryPaginationInfo(SearchRequest query) { + return count(query) + .map(count -> PaginationInfo.builder() + .page(Optional.ofNullable(query.getPaginate()).map(SemrackPagination::getPage).orElse(0)) + .size(Optional.ofNullable(query.getPaginate()).map(SemrackPagination::getSize).orElse(10)) + .total(count) + .build()); + } + +} diff --git a/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/entities/IndexDocumentEntity.java b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/entities/IndexDocumentEntity.java new file mode 100644 index 0000000..e918b83 --- /dev/null +++ b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/entities/IndexDocumentEntity.java @@ -0,0 +1,20 @@ +package fr.codeanddata.semrack.index.postgres.entities; + +import io.quarkus.hibernate.reactive.panache.PanacheEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import lombok.*; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Entity(name = "IndexDocument") +@Table(name = "index_document") +public class IndexDocumentEntity extends PanacheEntity { + + @Column(nullable = false, unique = true, columnDefinition = "varchar(256)") + String uid; +} diff --git a/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/entities/IndexKeyEntity.java b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/entities/IndexKeyEntity.java new file mode 100644 index 0000000..452ed33 --- /dev/null +++ b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/entities/IndexKeyEntity.java @@ -0,0 +1,33 @@ +package fr.codeanddata.semrack.index.postgres.entities; + +import fr.codeanddata.semrack.core.utils.Traverser; +import io.quarkus.hibernate.reactive.panache.PanacheEntity; +import jakarta.persistence.*; +import lombok.*; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; + +import java.util.Map; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Entity(name = "IndexAnnotation") +@Table(name = "index_key") +public class IndexKeyEntity extends PanacheEntity { + + @ManyToOne + @JoinColumn(name = "index_document_id", nullable = false) + IndexDocumentEntity index; + + @Column(name = "fullpath") + String fullPath; + + @Enumerated(EnumType.STRING) + Traverser.PathTypes type; + + @JdbcTypeCode(SqlTypes.JSON) + Map value; +} diff --git a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/AndLookup.java b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/operators/AndLookup.java similarity index 91% rename from modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/AndLookup.java rename to modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/operators/AndLookup.java index c7022e3..e764661 100644 --- a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/AndLookup.java +++ b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/operators/AndLookup.java @@ -1,4 +1,4 @@ -package fr.codeanddata.semrack.storage.postgres.operators; +package fr.codeanddata.semrack.index.postgres.operators; import fr.codeanddata.semrack.core.services.SemrackLookupService; import io.smallrye.common.annotation.Identifier; diff --git a/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/operators/EqualLookup.java b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/operators/EqualLookup.java new file mode 100644 index 0000000..b15cd2d --- /dev/null +++ b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/operators/EqualLookup.java @@ -0,0 +1,32 @@ +package fr.codeanddata.semrack.index.postgres.operators; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import fr.codeanddata.semrack.core.exceptions.SemrackRuntimeException; +import fr.codeanddata.semrack.index.postgres.EqualLookupParams; +import io.smallrye.common.annotation.Identifier; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +@Identifier("equal") +@ApplicationScoped +public class EqualLookup implements SemrackJpaLookupExpression { + + @Inject + ObjectMapper objectMapper; + + @Override + public String apply(Object params) { + final EqualLookupParams equalLookupParams = convert(params, EqualLookupParams.class); + + if (equalLookupParams.getField() == null || equalLookupParams.getField().isEmpty()) { + throw new SemrackRuntimeException("Field expected"); + } + + try { + return "(idx.fullpath = '"+ equalLookupParams.getField() + "' AND idx.value->'v' = '"+ objectMapper.writeValueAsString(equalLookupParams.getValue())+"'::jsonb)"; + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } +} diff --git a/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/operators/InLookup.java b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/operators/InLookup.java new file mode 100644 index 0000000..8147fe2 --- /dev/null +++ b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/operators/InLookup.java @@ -0,0 +1,39 @@ +package fr.codeanddata.semrack.index.postgres.operators; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import fr.codeanddata.semrack.core.exceptions.SemrackRuntimeException; +import fr.codeanddata.semrack.index.postgres.InLookupParams; +import io.smallrye.common.annotation.Identifier; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.util.ArrayList; +import java.util.List; + +@Identifier("in") +@ApplicationScoped +public class InLookup implements SemrackJpaLookupExpression { + + @Inject + ObjectMapper objectMapper; + + @Override + public String apply(Object params) { + final InLookupParams inLookupParams = convert(params, InLookupParams.class); + + if (inLookupParams.getField() == null || inLookupParams.getField().isEmpty()) { + throw new SemrackRuntimeException("Field expected"); + } + + try { + final List inValues = new ArrayList<>(); + for (Object value : inLookupParams.getValues()) { + inValues.add("'" + objectMapper.writeValueAsString(value) + "'::jsonb"); + } + return "(idx.fullpath = '" + inLookupParams.getField() + "' AND idx.value->'v' in (" + String.join(",", inValues) + "))"; + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } +} diff --git a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/OrLookup.java b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/operators/OrLookup.java similarity index 91% rename from modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/OrLookup.java rename to modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/operators/OrLookup.java index 570bae7..c14f4e6 100644 --- a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/OrLookup.java +++ b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/operators/OrLookup.java @@ -1,4 +1,4 @@ -package fr.codeanddata.semrack.storage.postgres.operators; +package fr.codeanddata.semrack.index.postgres.operators; import fr.codeanddata.semrack.core.services.SemrackLookupService; import io.smallrye.common.annotation.Identifier; diff --git a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/SemrackJpaLookupExpression.java b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/operators/SemrackJpaLookupExpression.java similarity index 71% rename from modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/SemrackJpaLookupExpression.java rename to modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/operators/SemrackJpaLookupExpression.java index 71faeeb..6c4f7bc 100644 --- a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/SemrackJpaLookupExpression.java +++ b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/operators/SemrackJpaLookupExpression.java @@ -1,4 +1,4 @@ -package fr.codeanddata.semrack.storage.postgres.operators; +package fr.codeanddata.semrack.index.postgres.operators; import fr.codeanddata.semrack.core.SemrackLookupExpression; diff --git a/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/repositories/IndexDocumentRepository.java b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/repositories/IndexDocumentRepository.java new file mode 100644 index 0000000..0117490 --- /dev/null +++ b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/repositories/IndexDocumentRepository.java @@ -0,0 +1,9 @@ +package fr.codeanddata.semrack.index.postgres.repositories; + +import fr.codeanddata.semrack.index.postgres.entities.IndexDocumentEntity; +import io.quarkus.hibernate.reactive.panache.PanacheRepository; +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class IndexDocumentRepository implements PanacheRepository { +} diff --git a/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/repositories/IndexKeyRepository.java b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/repositories/IndexKeyRepository.java new file mode 100644 index 0000000..a0ad72c --- /dev/null +++ b/modules/semrack-index-postgres/runtime/src/main/java/fr/codeanddata/semrack/index/postgres/repositories/IndexKeyRepository.java @@ -0,0 +1,9 @@ +package fr.codeanddata.semrack.index.postgres.repositories; + +import fr.codeanddata.semrack.index.postgres.entities.IndexKeyEntity; +import io.quarkus.hibernate.reactive.panache.PanacheRepository; +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class IndexKeyRepository implements PanacheRepository { +} diff --git a/modules/semrack-index-postgres/runtime/src/main/resources/META-INF/beans.xml b/modules/semrack-index-postgres/runtime/src/main/resources/META-INF/beans.xml new file mode 100644 index 0000000..e69de29 diff --git a/modules/semrack-index-postgres/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/modules/semrack-index-postgres/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 0000000..9a95a5d --- /dev/null +++ b/modules/semrack-index-postgres/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,9 @@ +name: Semrack Index Postgres +#description: Do something useful. +metadata: +# keywords: +# - semrack-index-postgres +# guide: ... # To create and publish this guide, see https://github.com/quarkiverse/quarkiverse/wiki#documenting-your-extension +# categories: +# - "miscellaneous" +# status: "preview" diff --git a/modules/semrack-index-postgres/runtime/src/main/resources/application.properties b/modules/semrack-index-postgres/runtime/src/main/resources/application.properties new file mode 100644 index 0000000..d3ad2f6 --- /dev/null +++ b/modules/semrack-index-postgres/runtime/src/main/resources/application.properties @@ -0,0 +1,9 @@ +quarkus.datasource.db-kind=postgresql +quarkus.flyway.enabled=true +quarkus.flyway.active=false +quarkus.flyway.migrate-at-start=true +quarkus.flyway.out-of-order=true +quarkus.flyway.ignore-missing-migrations=true + +quarkus.native.resources.includes=src/main/resources/db/migration/** +quarkus.native.additional-build-args=-H:SerializationConfigurationResources=serialization-config.json diff --git a/modules/semrack-index-postgres/runtime/src/main/resources/db/migration/V1.0.2__IndexPg.sql b/modules/semrack-index-postgres/runtime/src/main/resources/db/migration/V1.0.2__IndexPg.sql new file mode 100644 index 0000000..fcdd924 --- /dev/null +++ b/modules/semrack-index-postgres/runtime/src/main/resources/db/migration/V1.0.2__IndexPg.sql @@ -0,0 +1,20 @@ +create table index_document +( + id bigint not null, + uid varchar(256) not null, + primary key (id) +); +create table index_key +( + id bigint not null, + fullpath varchar(255), + type varchar(255) check (type in ('UNKNOWN', 'STRING', 'NUMBER', 'BOOLEAN', 'LIST', 'OBJECT')), + value jsonb, + index_document_id bigint not null, + primary key (id) +); +alter table if exists index_document drop constraint if exists UKc9q9jn623dprjanh1v3wvh86; +alter table if exists index_document add constraint UKc9q9jn623dprjanh1v3wvh86 unique (uid); +create sequence index_document_SEQ start with 1 increment by 50; +create sequence index_key_SEQ start with 1 increment by 50; +alter table if exists index_key add constraint FKgy9d5xg0nf2gsvxu58hnw8fa2 foreign key (index_document_id) references index_document; diff --git a/modules/semrack-index-postgres/runtime/src/main/resources/serialization-config.json b/modules/semrack-index-postgres/runtime/src/main/resources/serialization-config.json new file mode 100644 index 0000000..e46f036 --- /dev/null +++ b/modules/semrack-index-postgres/runtime/src/main/resources/serialization-config.json @@ -0,0 +1,3 @@ +[ + {"name": "java.lang.String"} +] diff --git a/modules/semrack-storage-postgres/deployment/pom.xml b/modules/semrack-storage-postgres/deployment/pom.xml index c3432df..e4cf622 100644 --- a/modules/semrack-storage-postgres/deployment/pom.xml +++ b/modules/semrack-storage-postgres/deployment/pom.xml @@ -39,13 +39,17 @@ fr.codeanddata.semrack semrack-storage-postgres - ${project.version} io.quarkus quarkus-junit5-internal test + + fr.codeanddata.semrack + semrack-core-testing + test + diff --git a/modules/semrack-storage-postgres/deployment/src/main/resources/application.properties b/modules/semrack-storage-postgres/deployment/src/main/resources/application.properties new file mode 100644 index 0000000..aacfeff --- /dev/null +++ b/modules/semrack-storage-postgres/deployment/src/main/resources/application.properties @@ -0,0 +1 @@ +quarkus.hibernate-orm.mapping.format.global=ignore diff --git a/modules/semrack-storage-postgres/runtime/pom.xml b/modules/semrack-storage-postgres/runtime/pom.xml index ddfbc27..91dbdd8 100644 --- a/modules/semrack-storage-postgres/runtime/pom.xml +++ b/modules/semrack-storage-postgres/runtime/pom.xml @@ -44,6 +44,11 @@ org.projectlombok lombok + + fr.codeanddata.semrack + semrack-core-testing + test + io.quarkus quarkus-junit5 diff --git a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/ContainsLookup.java b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/ContainsLookup.java deleted file mode 100644 index 7b6d4c8..0000000 --- a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/ContainsLookup.java +++ /dev/null @@ -1,24 +0,0 @@ -package fr.codeanddata.semrack.storage.postgres.operators; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.smallrye.common.annotation.Identifier; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; - -@Identifier("contains") -@ApplicationScoped -public class ContainsLookup implements SemrackJpaLookupExpression { - - @Inject - ObjectMapper objectMapper; - - @Override - public String apply(Object params) { - try { - return "document @> '" + objectMapper.writeValueAsString(params) + "'"; - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } -} diff --git a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/InLookup.java b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/InLookup.java deleted file mode 100644 index c7e3e1a..0000000 --- a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/InLookup.java +++ /dev/null @@ -1,31 +0,0 @@ -package fr.codeanddata.semrack.storage.postgres.operators; - -import fr.codeanddata.semrack.core.exceptions.SemrackRuntimeException; -import fr.codeanddata.semrack.storage.postgres.dtos.InLookupParams; -import io.smallrye.common.annotation.Identifier; -import jakarta.enterprise.context.ApplicationScoped; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -@Identifier("in") -@ApplicationScoped -public class InLookup implements SemrackJpaLookupExpression { - - @Override - public String apply(Object params) { - final InLookupParams inLookupParams = convert(params, InLookupParams.class); - final List fields = new ArrayList<>(Arrays.asList(inLookupParams.getField().split("\\."))); - if (fields.isEmpty()) { - throw new SemrackRuntimeException("Field expected"); - } - - String query = "->> '" + fields.removeLast() + "'"; - if (!fields.isEmpty()) { - query = "-> '" + String.join("' -> '", fields) + "' " + query; - } - - return "document " + query + " in " + "('" + String.join("', '", inLookupParams.getValues()) + "')"; - } -} diff --git a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/storage/SemdocJpaStorage.java b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/storage/SemdocJpaStorage.java index b1e72c8..b702ab2 100644 --- a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/storage/SemdocJpaStorage.java +++ b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/storage/SemdocJpaStorage.java @@ -1,14 +1,10 @@ package fr.codeanddata.semrack.storage.postgres.storage; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; import fr.codeanddata.semrack.core.SemdocStorage; -import fr.codeanddata.semrack.core.enums.SemrackSortDirection; -import fr.codeanddata.semrack.core.models.*; +import fr.codeanddata.semrack.core.models.SemrackDocument; import fr.codeanddata.semrack.core.services.SemrackLookupService; import fr.codeanddata.semrack.core.utils.UIDGenerator; import fr.codeanddata.semrack.storage.postgres.entities.SemrackDocumentEntity; -import fr.codeanddata.semrack.storage.postgres.utils.PathMapper; import io.quarkus.hibernate.reactive.panache.PanacheRepository; import io.quarkus.hibernate.reactive.panache.common.WithSession; import io.quarkus.hibernate.reactive.panache.common.WithTransaction; @@ -16,7 +12,8 @@ import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import java.util.*; +import java.util.List; +import java.util.Optional; @ApplicationScoped public class SemdocJpaStorage implements SemdocStorage, PanacheRepository { @@ -24,42 +21,21 @@ public class SemdocJpaStorage implements SemdocStorage, PanacheRepository readDocument(String uid) { + public Uni get(String uid) { return find("uid = ?1", uid) .firstResult().map(d -> Optional.ofNullable(d).map(SemrackDocumentEntity::getDocument).orElse(null)); } @Override @WithSession - public Uni searchDocument(SearchRequest query) { - return search(query); - } - - @Override - public Uni countDocuments(SearchRequest request) { - final String lookup = lookupService.lookup(request.getFilter()); - final String whereClause = lookup.isEmpty() ? "" : " WHERE " + lookup; - - return getSession() - .chain(s -> s.createNativeQuery("SELECT count(id) FROM semrack_document" + whereClause, Long.class).getSingleResultOrNull()) - .map(count -> count == null ? 0 : count); - } - - @Override - public Uni documentsExist(SearchRequest query) { - return countDocuments(query) - .map(c -> c > 0); + public Uni> get(List uids) { + return find("uid in ?1", uids).list() + .map(d -> d.stream().map(SemrackDocumentEntity::getDocument).toList()); } @WithTransaction @@ -93,76 +69,76 @@ public class SemdocJpaStorage implements SemdocStorage, PanacheRepository document); } - Uni search(SearchRequest request) { - final String baseQuery = buildBaseQuery(request); - - final StringBuilder sorting = new StringBuilder(); - if (request.getSort() != null && !request.getSort().isEmpty()) { - sorting.append(" ORDER BY "); - sorting.append(String.join(", ", request.getSort().stream().map(sort -> sort.getField() + " " + Optional.ofNullable(sort.getDirection()).orElse(SemrackSortDirection.asc).name()).toList())); - } - - final StringBuilder paginate = new StringBuilder(); - if (request.getPaginate() != null) { - final SemrackPagination pagination = request.getPaginate(); - final Integer size = pagination.getSize() == null ? 200 : pagination.getSize(); - final Integer page = pagination.getPage() == null ? 0 : pagination.getPage(); - - paginate.append(" LIMIT ").append(size).append(" OFFSET ").append(size * page); - } - - return getSession() - .chain(s -> { - final SearchResult searchResult = SearchResult.builder().build(); - return countDocuments(request) - .invoke(count -> searchResult.setPagination(PaginationInfo.builder() - .page(Optional.ofNullable(request.getPaginate()).map(SemrackPagination::getPage).orElse(0)) - .size(Optional.ofNullable(request.getPaginate()).map(SemrackPagination::getSize).orElse(0)) - .total(count) - .build())) - .call(() -> s - .createNativeQuery(baseQuery + sorting + paginate, Object.class).getResultList() - .invoke(results -> searchResult.setDocuments(project(request.getFields(), results)))) - .map(count -> searchResult); - }); - } - - String buildBaseQuery(SearchRequest request) { - final String lookup = lookupService.lookup(request.getFilter()); - final String whereClause = lookup.isEmpty() ? "" : " WHERE " + lookup; - - if (request.getFields() == null || request.getFields().isEmpty()) { - return "SELECT document FROM semrack_document" + whereClause; - } else { - return "SELECT " + toJsonbPathExtract(request.getFields()) + " FROM semrack_document" + whereClause; - } - } - - String toJsonbPathExtract(List fields) { - return String.join(",", fields.stream().map(field -> { - final String serializedField = "'" + String.join("','", field.split("\\.")) + "'"; - return "jsonb_extract_path(document, " + serializedField + ")"; - }).toList()); - } - - List project(List fields, Object results) { - final List projectedResults = objectMapper.convertValue(results, List.class); - if (fields == null || fields.isEmpty()) { - return projectedResults.stream().map(result -> objectMapper.convertValue(result, SemrackDocument.class)).toList(); - } else { - return projectedResults.stream().map(result -> { - final List row = fields.size() == 1 ? List.of(result) : objectMapper.convertValue(result, new TypeReference<>() { - }); - final List rowMut = new ArrayList<>(row); - final List fieldCp = new ArrayList<>(fields); - final Map document = new HashMap<>(); - while (!fieldCp.isEmpty() && !row.isEmpty()) { - final String field = fieldCp.removeFirst(); - final Object value = rowMut.removeFirst(); - pathMapper.map(field, document, value); - } - return objectMapper.convertValue(document, SemrackDocument.class); - }).toList(); - } - } +// Uni search(SearchRequest request) { +// final String baseQuery = buildBaseQuery(request); +// +// final StringBuilder sorting = new StringBuilder(); +// if (request.getSort() != null && !request.getSort().isEmpty()) { +// sorting.append(" ORDER BY "); +// sorting.append(String.join(", ", request.getSort().stream().map(sort -> sort.getField() + " " + Optional.ofNullable(sort.getDirection()).orElse(SemrackSortDirection.asc).name()).toList())); +// } +// +// final StringBuilder paginate = new StringBuilder(); +// if (request.getPaginate() != null) { +// final SemrackPagination pagination = request.getPaginate(); +// final Integer size = pagination.getSize() == null ? 200 : pagination.getSize(); +// final Integer page = pagination.getPage() == null ? 0 : pagination.getPage(); +// +// paginate.append(" LIMIT ").append(size).append(" OFFSET ").append(size * page); +// } +// +// return getSession() +// .chain(s -> { +// final SearchResult searchResult = SearchResult.builder().build(); +// return countDocuments(request) +// .invoke(count -> searchResult.setPagination(PaginationInfo.builder() +// .page(Optional.ofNullable(request.getPaginate()).map(SemrackPagination::getPage).orElse(0)) +// .size(Optional.ofNullable(request.getPaginate()).map(SemrackPagination::getSize).orElse(0)) +// .total(count) +// .build())) +// .call(() -> s +// .createNativeQuery(baseQuery + sorting + paginate, Object.class).getResultList() +// .invoke(results -> searchResult.setDocuments(project(request.getFields(), results)))) +// .map(count -> searchResult); +// }); +// } +// +// String buildBaseQuery(SearchRequest request) { +// final String lookup = lookupService.lookup(request.getFilter()); +// final String whereClause = lookup.isEmpty() ? "" : " WHERE " + lookup; +// +// if (request.getFields() == null || request.getFields().isEmpty()) { +// return "SELECT document FROM semrack_document" + whereClause; +// } else { +// return "SELECT " + toJsonbPathExtract(request.getFields()) + " FROM semrack_document" + whereClause; +// } +// } +// +// String toJsonbPathExtract(List fields) { +// return String.join(",", fields.stream().map(field -> { +// final String serializedField = "'" + String.join("','", field.split("\\.")) + "'"; +// return "jsonb_extract_path(document, " + serializedField + ")"; +// }).toList()); +// } +// +// List project(List fields, Object results) { +// final List projectedResults = objectMapper.convertValue(results, List.class); +// if (fields == null || fields.isEmpty()) { +// return projectedResults.stream().map(result -> objectMapper.convertValue(result, SemrackDocument.class)).toList(); +// } else { +// return projectedResults.stream().map(result -> { +// final List row = fields.size() == 1 ? List.of(result) : objectMapper.convertValue(result, new TypeReference<>() { +// }); +// final List rowMut = new ArrayList<>(row); +// final List fieldCp = new ArrayList<>(fields); +// final Map document = new HashMap<>(); +// while (!fieldCp.isEmpty() && !row.isEmpty()) { +// final String field = fieldCp.removeFirst(); +// final Object value = rowMut.removeFirst(); +// pathMapper.map(field, document, value); +// } +// return objectMapper.convertValue(document, SemrackDocument.class); +// }).toList(); +// } +// } } diff --git a/modules/semrack-storage-postgres/runtime/src/main/resources/application.properties b/modules/semrack-storage-postgres/runtime/src/main/resources/application.properties index e2217b5..d3ad2f6 100644 --- a/modules/semrack-storage-postgres/runtime/src/main/resources/application.properties +++ b/modules/semrack-storage-postgres/runtime/src/main/resources/application.properties @@ -2,6 +2,8 @@ quarkus.datasource.db-kind=postgresql quarkus.flyway.enabled=true quarkus.flyway.active=false quarkus.flyway.migrate-at-start=true +quarkus.flyway.out-of-order=true +quarkus.flyway.ignore-missing-migrations=true quarkus.native.resources.includes=src/main/resources/db/migration/** quarkus.native.additional-build-args=-H:SerializationConfigurationResources=serialization-config.json diff --git a/modules/semrack-storage-postgres/runtime/src/main/resources/db/migration/V1.0.0__Base.sql b/modules/semrack-storage-postgres/runtime/src/main/resources/db/migration/V1.0.1__StoragePg.sql similarity index 100% rename from modules/semrack-storage-postgres/runtime/src/main/resources/db/migration/V1.0.0__Base.sql rename to modules/semrack-storage-postgres/runtime/src/main/resources/db/migration/V1.0.1__StoragePg.sql diff --git a/modules/semrack-storage-postgres/runtime/src/test/java/fr/codeanddata/semrack/storage/postgres/operators/ContainsLookupTest.java b/modules/semrack-storage-postgres/runtime/src/test/java/fr/codeanddata/semrack/storage/postgres/operators/ContainsLookupTest.java deleted file mode 100644 index b6c4283..0000000 --- a/modules/semrack-storage-postgres/runtime/src/test/java/fr/codeanddata/semrack/storage/postgres/operators/ContainsLookupTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package fr.codeanddata.semrack.storage.postgres.operators; - -import io.quarkus.test.junit.QuarkusTest; -import io.smallrye.common.annotation.Identifier; -import jakarta.inject.Inject; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import java.util.Map; - -@QuarkusTest -class ContainsLookupTest { - - @Inject - @Identifier("contains") - ContainsLookup subject; - - @Test - void testLookup() { - final String lookup = subject.apply(Map.of("product", Map.of("sku", "xxx"))); - final String expectedLookup = "document @> '{\"product\":{\"sku\":\"xxx\"}}'"; - Assertions.assertEquals(expectedLookup, lookup); - } - -} diff --git a/modules/semrack-storage-postgres/runtime/src/test/java/fr/codeanddata/semrack/storage/postgres/operators/InLookupTest.java b/modules/semrack-storage-postgres/runtime/src/test/java/fr/codeanddata/semrack/storage/postgres/operators/InLookupTest.java deleted file mode 100644 index 6132df7..0000000 --- a/modules/semrack-storage-postgres/runtime/src/test/java/fr/codeanddata/semrack/storage/postgres/operators/InLookupTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package fr.codeanddata.semrack.storage.postgres.operators; - -import fr.codeanddata.semrack.storage.postgres.dtos.InLookupParams; -import io.quarkus.test.junit.QuarkusTest; -import io.smallrye.common.annotation.Identifier; -import jakarta.inject.Inject; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import java.util.List; -import java.util.Map; - -@QuarkusTest -class InLookupTest { - - @Inject - @Identifier("in") - InLookup subject; - - @Test - void testConversion() { - final String field = "root.parent.child.field"; - final List values = List.of("un", "deux", "trois"); - final Map inParams = Map.of( - "field", field, - "values", values - ); - - final InLookupParams convertedParams = subject.convert(inParams, InLookupParams.class); - Assertions.assertEquals(field, convertedParams.getField()); - Assertions.assertEquals(values, convertedParams.getValues()); - } - - @Test - void testLookup() { - final String lookup = subject.apply(Map.of( - "field", "lookup.field", - "values", List.of("list", "of", "values"))); - final String expectedLookup = "document -> 'lookup' ->> 'field' in ('list', 'of', 'values')"; - Assertions.assertEquals(expectedLookup, lookup); - } - -} diff --git a/modules/semrack-storage-postgres/runtime/src/test/resources/application.properties b/modules/semrack-storage-postgres/runtime/src/test/resources/application.properties new file mode 100644 index 0000000..aacfeff --- /dev/null +++ b/modules/semrack-storage-postgres/runtime/src/test/resources/application.properties @@ -0,0 +1 @@ +quarkus.hibernate-orm.mapping.format.global=ignore diff --git a/pom.xml b/pom.xml index e8e50b4..d2e768e 100644 --- a/pom.xml +++ b/pom.xml @@ -9,6 +9,7 @@ modules/semrack-core + modules/semrack-index-postgres modules/semrack-storage-postgres modules/semrack-api-rest @@ -31,7 +32,7 @@ 1.18.38 1.6.3 1.0-SNAPSHOT - 3.25.3 + 3.27.0 2.9.0 @@ -67,11 +68,26 @@ semrack-core ${semrack.version} + + fr.codeanddata.semrack + semrack-core-testing + ${semrack.version} + fr.codeanddata.semrack semrack-core-deployment ${semrack.version} + + fr.codeanddata.semrack + semrack-index-postgres + ${semrack.version} + + + fr.codeanddata.semrack + semrack-index-postgres-deployment + ${semrack.version} + fr.codeanddata.semrack semrack-storage-postgres