This commit is contained in:
96
modules/semrack-storage-postgres/runtime/pom.xml
Normal file
96
modules/semrack-storage-postgres/runtime/pom.xml
Normal file
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>fr.codeanddata.semrack</groupId>
|
||||
<artifactId>semrack-storage-postgres-parent</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>semrack-storage-postgres</artifactId>
|
||||
<name>Semrack Storage Postgres - Runtime</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>fr.codeanddata.semrack</groupId>
|
||||
<artifactId>semrack-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-arc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-hibernate-reactive-panache</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-reactive-pg-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-flyway</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-jdbc-postgresql</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-database-postgresql</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-junit5</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-extension-maven-plugin</artifactId>
|
||||
<version>${quarkus.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>extension-descriptor</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<deployment>${project.groupId}:${project.artifactId}-deployment:${project.version}</deployment>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-compile</id>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-extension-processor</artifactId>
|
||||
<version>${quarkus.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,15 @@
|
||||
package fr.codeanddata.semrack.storage.postgres.dtos;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class InLookupParams {
|
||||
String field;
|
||||
List<String> values;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package fr.codeanddata.semrack.storage.postgres.entities;
|
||||
|
||||
import fr.codeanddata.semrack.core.models.SemrackDocument;
|
||||
import io.quarkus.hibernate.reactive.panache.PanacheEntity;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.JdbcTypeCode;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Entity(name = "SemrackDocument")
|
||||
@Table(name = "semrack_document")
|
||||
public class SemrackDocumentEntity extends PanacheEntity implements Serializable {
|
||||
@Column(unique = true, columnDefinition = "varchar(256)")
|
||||
String uid;
|
||||
|
||||
@JdbcTypeCode(SqlTypes.JSON)
|
||||
SemrackDocument document;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package fr.codeanddata.semrack.storage.postgres.operators;
|
||||
|
||||
import fr.codeanddata.semrack.core.services.SemrackLookupService;
|
||||
import io.smallrye.common.annotation.Identifier;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Identifier("and")
|
||||
@ApplicationScoped
|
||||
public class AndLookup implements SemrackJpaLookupExpression<List<Map<String, Object>>> {
|
||||
|
||||
@Inject
|
||||
SemrackLookupService lookupService;
|
||||
|
||||
@Override
|
||||
public String apply(Object expressions) {
|
||||
final List<Map<String, Object>> typedExpressions = convert(expressions);
|
||||
return String.join(" AND ", typedExpressions.stream().map(lookupService::lookup).toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
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<Object> {
|
||||
|
||||
@Inject
|
||||
ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
public String apply(Object params) {
|
||||
try {
|
||||
return "document @> '" + objectMapper.writeValueAsString(params) + "'";
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
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<InLookupParams> {
|
||||
|
||||
@Override
|
||||
public String apply(Object params) {
|
||||
final InLookupParams inLookupParams = convert(params, InLookupParams.class);
|
||||
final List<String> 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()) + "')";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package fr.codeanddata.semrack.storage.postgres.operators;
|
||||
|
||||
import fr.codeanddata.semrack.core.services.SemrackLookupService;
|
||||
import io.smallrye.common.annotation.Identifier;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Identifier("or")
|
||||
@ApplicationScoped
|
||||
public class OrLookup implements SemrackJpaLookupExpression<List<Map<String, Object>>> {
|
||||
|
||||
@Inject
|
||||
SemrackLookupService lookupService;
|
||||
|
||||
@Override
|
||||
public String apply(Object expressions) {
|
||||
final List<Map<String, Object>> typedExpressions = convert(expressions);
|
||||
return String.join(" OR ", typedExpressions.stream().map(lookupService::lookup).toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package fr.codeanddata.semrack.storage.postgres.operators;
|
||||
|
||||
import fr.codeanddata.semrack.core.SemrackLookupExpression;
|
||||
|
||||
public interface SemrackJpaLookupExpression<T> extends SemrackLookupExpression<T> {
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
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.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;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@ApplicationScoped
|
||||
public class SemdocJpaStorage implements SemdocStorage, PanacheRepository<SemrackDocumentEntity> {
|
||||
|
||||
@Inject
|
||||
UIDGenerator generator;
|
||||
|
||||
@Inject
|
||||
PathMapper pathMapper;
|
||||
|
||||
@Inject
|
||||
SemrackLookupService lookupService;
|
||||
|
||||
@Inject
|
||||
ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
@WithSession
|
||||
public Uni<SemrackDocument> readDocument(String uid) {
|
||||
return find("uid = ?1", uid)
|
||||
.firstResult().map(d -> Optional.ofNullable(d).map(SemrackDocumentEntity::getDocument).orElse(null));
|
||||
}
|
||||
|
||||
@Override
|
||||
@WithSession
|
||||
public Uni<SearchResult> searchDocument(SearchRequest query) {
|
||||
return search(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uni<Long> 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<Boolean> documentsExist(SearchRequest query) {
|
||||
return countDocuments(query)
|
||||
.map(c -> c > 0);
|
||||
}
|
||||
|
||||
@WithTransaction
|
||||
@Override
|
||||
public Uni<SemrackDocument> storeDocument(SemrackDocument document) {
|
||||
if (document.getUid() == null) {
|
||||
return createDocument(document);
|
||||
} else {
|
||||
return find("uid = ?1", document.getUid())
|
||||
.count()
|
||||
.chain(n -> n == 0 ? createDocument(document) : updateDocument(document));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO err : handle existing document
|
||||
@WithTransaction
|
||||
Uni<SemrackDocument> createDocument(SemrackDocument document) {
|
||||
final String uid = document.getUid() == null ? generator.apply(document) : document.getUid();
|
||||
document.setUid(uid);
|
||||
final SemrackDocumentEntity entity = SemrackDocumentEntity.builder()
|
||||
.uid(uid)
|
||||
.document(document)
|
||||
.build();
|
||||
return persist(entity).map(SemrackDocumentEntity::getDocument);
|
||||
}
|
||||
|
||||
// TODO err : document not exists
|
||||
@WithTransaction
|
||||
Uni<SemrackDocument> updateDocument(SemrackDocument document) {
|
||||
return update("uid = ?1, document = ?2 WHERE uid = ?1", document.getUid(), document)
|
||||
.map(x -> document);
|
||||
}
|
||||
|
||||
Uni<SearchResult> 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<String> fields) {
|
||||
return String.join(",", fields.stream().map(field -> {
|
||||
final String serializedField = "'" + String.join("','", field.split("\\.")) + "'";
|
||||
return "jsonb_extract_path(document, " + serializedField + ")";
|
||||
}).toList());
|
||||
}
|
||||
|
||||
List<SemrackDocument> project(List<String> 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<Object> row = fields.size() == 1 ? List.of(result) : objectMapper.convertValue(result, new TypeReference<>() {
|
||||
});
|
||||
final List<Object> rowMut = new ArrayList<>(row);
|
||||
final List<String> fieldCp = new ArrayList<>(fields);
|
||||
final Map<String, Object> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package fr.codeanddata.semrack.storage.postgres.utils;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@ApplicationScoped
|
||||
public class PathMapper {
|
||||
|
||||
@Inject
|
||||
ObjectMapper objectMapper;
|
||||
|
||||
public Map<String, Object> map(String path, Object value) {
|
||||
return map(path, new HashMap<>(), value);
|
||||
}
|
||||
|
||||
public Map<String, Object> map(String path, Map<String, Object> target, Object value) {
|
||||
setValue(new ArrayList<>(Stream.of(path.split("\\.")).toList()), target, value);
|
||||
return target;
|
||||
}
|
||||
|
||||
void setValue(List<String> paths, Map<String, Object> container, Object value) {
|
||||
if (!paths.isEmpty()) {
|
||||
final String key = paths.removeFirst();
|
||||
if (paths.isEmpty()) {
|
||||
container.put(key, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!container.containsKey(key) || !(container.get(key) instanceof Map)) {
|
||||
container.put(key, new HashMap<>());
|
||||
}
|
||||
|
||||
final Map<String, Object> target = objectMapper.convertValue(container.get(key), new TypeReference<>() {});
|
||||
container.put(key, target);
|
||||
setValue(paths, target, value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
name: Semrack Storage Postgres
|
||||
#description: Do something useful.
|
||||
metadata:
|
||||
# keywords:
|
||||
# - semrack-storage-postgres
|
||||
# guide: ... # To create and publish this guide, see https://github.com/quarkiverse/quarkiverse/wiki#documenting-your-extension
|
||||
# categories:
|
||||
# - "miscellaneous"
|
||||
# status: "preview"
|
||||
@@ -0,0 +1,7 @@
|
||||
quarkus.datasource.db-kind=postgresql
|
||||
quarkus.flyway.enabled=true
|
||||
quarkus.flyway.active=false
|
||||
quarkus.flyway.migrate-at-start=true
|
||||
|
||||
quarkus.native.resources.includes=src/main/resources/db/migration/**
|
||||
quarkus.native.additional-build-args=-H:SerializationConfigurationResources=serialization-config.json
|
||||
@@ -0,0 +1,10 @@
|
||||
create sequence semrack_document_SEQ start with 1 increment by 50;
|
||||
create table semrack_document
|
||||
(
|
||||
id bigint not null,
|
||||
uid varchar(256) unique,
|
||||
document jsonb,
|
||||
primary key (id)
|
||||
);
|
||||
alter table semrack_document
|
||||
add constraint UK307dbjrfh151l0r7bb36xdwew unique (uid);
|
||||
@@ -0,0 +1,3 @@
|
||||
[
|
||||
{"name": "java.lang.String"}
|
||||
]
|
||||
@@ -0,0 +1,25 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
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<String> values = List.of("un", "deux", "trois");
|
||||
final Map<String, Object> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package fr.codeanddata.semrack.storage.postgres.utils;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
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.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@QuarkusTest
|
||||
public class PathMapperTest {
|
||||
|
||||
@Inject
|
||||
PathMapper pathMapper;
|
||||
|
||||
@Inject
|
||||
ObjectMapper objectMapper;
|
||||
|
||||
@Test
|
||||
void testMapper() {
|
||||
final Map<String, Object> map = new HashMap<>();
|
||||
final Map<String, Object> root = new HashMap<>();
|
||||
map.put("root", root);
|
||||
root.put("name", "bob");
|
||||
|
||||
pathMapper.map("root.child.subchild1.value", map, 1);
|
||||
pathMapper.map("root.child.subchild2.value", map, 2);
|
||||
pathMapper.map("hello", map, "world");
|
||||
|
||||
Assertions.assertEquals("world", map.get("hello"));
|
||||
Assertions.assertEquals(1, conv(conv(conv(map, "root"), "child"), "subchild1").get("value"));
|
||||
Assertions.assertEquals(2, conv(conv(conv(map, "root"), "child"), "subchild2").get("value"));
|
||||
Assertions.assertEquals("bob", conv(map, "root").get( "name"));
|
||||
}
|
||||
|
||||
Map<String, Object> conv(Map<String, Object> container, String key) {
|
||||
return objectMapper.convertValue(container.get(key), new TypeReference<>() {});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user