Update indexes
All checks were successful
Maven build / build (push) Successful in 2m19s

This commit is contained in:
Guillaume Dugas
2025-11-26 11:27:48 +01:00
parent 03b1e0851b
commit def2e4140f
13 changed files with 89 additions and 112 deletions

View File

@@ -5,16 +5,13 @@ import fr.codeanddata.semrack.core.SemdocStorage;
import fr.codeanddata.semrack.core.models.*; import fr.codeanddata.semrack.core.models.*;
import fr.codeanddata.semrack.core.services.SemrackLookupService; import fr.codeanddata.semrack.core.services.SemrackLookupService;
import fr.codeanddata.semrack.core.utils.Traverser; import fr.codeanddata.semrack.core.utils.Traverser;
import fr.codeanddata.semrack.index.postgres.entities.IndexDocumentEntity; import fr.codeanddata.semrack.index.postgres.dtos.IndexEntry;
import fr.codeanddata.semrack.index.postgres.entities.IndexKeyEntity; import fr.codeanddata.semrack.index.postgres.entities.SemrackIndexEntity;
import fr.codeanddata.semrack.index.postgres.repositories.IndexDocumentRepository; import fr.codeanddata.semrack.index.postgres.repositories.SemrackIndexRepository;
import fr.codeanddata.semrack.index.postgres.repositories.IndexKeyRepository;
import io.quarkus.hibernate.reactive.panache.PanacheRepository; import io.quarkus.hibernate.reactive.panache.PanacheRepository;
import io.quarkus.hibernate.reactive.panache.common.WithSession; import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.quarkus.hibernate.reactive.panache.common.WithTransaction; import io.quarkus.hibernate.reactive.panache.common.WithTransaction;
import io.quarkus.vertx.ConsumeEvent;
import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.Uni;
import io.vertx.core.eventbus.EventBus;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import org.hibernate.query.Page; import org.hibernate.query.Page;
@@ -22,9 +19,7 @@ import org.hibernate.query.Page;
import java.util.*; import java.util.*;
@ApplicationScoped @ApplicationScoped
public class SemdocJpaIndex implements SemdocIndex, PanacheRepository<IndexKeyEntity> { public class SemdocJpaIndex implements SemdocIndex, PanacheRepository<SemrackIndexEntity> {
public static final String INDEX_CONSUMER = "semdoc-jpa-index";
@Inject @Inject
Traverser traverser; Traverser traverser;
@@ -36,18 +31,12 @@ public class SemdocJpaIndex implements SemdocIndex, PanacheRepository<IndexKeyEn
SemdocStorage storage; SemdocStorage storage;
@Inject @Inject
IndexDocumentRepository indexDocumentRepository; SemrackIndexRepository indexRepository;
@Inject
IndexKeyRepository indexKeyRepository;
@Inject
EventBus bus;
@Override @Override
@WithSession @WithSession
public Uni<Long> count(SearchRequest request) { public Uni<Long> 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 StringBuilder query = new StringBuilder("SELECT DISTINCT count(uid) FROM semrack_index");
final String lookup = lookupService.lookup(request.getFilter()); final String lookup = lookupService.lookup(request.getFilter());
final String whereClause = lookup.isEmpty() ? "" : " WHERE " + lookup; final String whereClause = lookup.isEmpty() ? "" : " WHERE " + lookup;
@@ -67,29 +56,23 @@ public class SemdocJpaIndex implements SemdocIndex, PanacheRepository<IndexKeyEn
public Uni<Void> index(String documentId) { public Uni<Void> index(String documentId) {
return storage.get(documentId) return storage.get(documentId)
.call(document -> clear(documentId)) .call(document -> clear(documentId))
.call(document -> indexDocumentRepository.persist(IndexDocumentEntity.builder() .call(document -> {
.uid(documentId) final Map<String, IndexEntry> indexes = new HashMap<>();
.build()) final List<TraverserPath> annotationPaths = traverser.apply(document.getAnnotations()).stream().peek(x -> x.setFullPath("annotations" + x.getFullPath())).toList();
.call(index -> { final List<TraverserPath> metadataPaths = traverser.apply(document.getMetadata()).stream().peek(x -> x.setFullPath("metadata" + x.getFullPath())).toList();
final List<TraverserPath> annotationPaths = traverser.apply(document.getAnnotations()).stream().peek(x -> x.setFullPath("annotations" + x.getFullPath())).toList(); final List<TraverserPath> allPaths = new ArrayList<>();
final List<TraverserPath> metadataPaths = traverser.apply(document.getMetadata()).stream().peek(x -> x.setFullPath("metadata" + x.getFullPath())).toList(); allPaths.addAll(annotationPaths);
allPaths.addAll(metadataPaths);
allPaths.forEach(x -> indexes.put(x.getFullPath(), IndexEntry.builder()
.type(x.getType())
.value(x.getValue())
.build()));
final List<TraverserPath> allPaths = new ArrayList<>(); return indexRepository.persist(SemrackIndexEntity.builder()
allPaths.addAll(annotationPaths); .uid(document.getUid())
allPaths.addAll(metadataPaths); .indexes(indexes)
return indexKeyRepository.persist(allPaths .build());
.stream() })
.map(path -> {
final Map<String, Object> values = new HashMap<>();
values.put("v", path.getValue());
return IndexKeyEntity.builder()
.index(index)
.type(path.getType())
.fullPath(path.getFullPath())
.value(values)
.build();
}));
}))
.replaceWithVoid(); .replaceWithVoid();
} }
@@ -97,12 +80,11 @@ public class SemdocJpaIndex implements SemdocIndex, PanacheRepository<IndexKeyEn
@WithSession @WithSession
public Uni<IndexSearchResult> search(SearchRequest request) { public Uni<IndexSearchResult> search(SearchRequest request) {
final IndexSearchResult result = new IndexSearchResult(); 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 StringBuilder query = new StringBuilder("SELECT DISTINCT uid FROM semrack_index");
final String lookup = lookupService.lookup(request.getFilter()); final String lookup = lookupService.lookup(request.getFilter());
final String whereClause = lookup.isEmpty() ? "" : " WHERE " + lookup; final String whereClause = lookup.isEmpty() ? "" : " WHERE " + lookup;
query.append(whereClause); query.append(whereClause);
query.append(" GROUP BY d.uid");
return getSession() return getSession()
.chain(s -> queryPaginationInfo(request) .chain(s -> queryPaginationInfo(request)
@@ -119,8 +101,7 @@ public class SemdocJpaIndex implements SemdocIndex, PanacheRepository<IndexKeyEn
@WithTransaction @WithTransaction
public Uni<Void> clear(String documentId) { public Uni<Void> clear(String documentId) {
return Uni.createFrom().nullItem() return Uni.createFrom().nullItem()
.call(() -> IndexKeyEntity.delete("index.uid = ?1", documentId)) .call(() -> SemrackIndexEntity.delete("uid = ?1", documentId))
.call(() -> IndexDocumentEntity.delete("uid = ?1", documentId))
.replaceWithVoid(); .replaceWithVoid();
} }

View File

@@ -0,0 +1,14 @@
package fr.codeanddata.semrack.index.postgres.dtos;
import fr.codeanddata.semrack.core.utils.Traverser;
import lombok.*;
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class IndexEntry {
Traverser.PathTypes type;
Object value;
}

View File

@@ -1,20 +0,0 @@
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;
}

View File

@@ -1,33 +0,0 @@
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<String, Object> value;
}

View File

@@ -0,0 +1,26 @@
package fr.codeanddata.semrack.index.postgres.entities;
import fr.codeanddata.semrack.index.postgres.dtos.IndexEntry;
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 = "SemrackIndex")
@Table(name = "semrack_index")
public class SemrackIndexEntity extends PanacheEntity {
@Column(nullable = false, unique = true, columnDefinition = "varchar(256)")
String uid;
@JdbcTypeCode(SqlTypes.JSON)
Map<String, IndexEntry> indexes;
}

View File

@@ -18,6 +18,6 @@ public class AndLookup implements SemrackJpaLookupExpression<List<Map<String, Ob
@Override @Override
public String apply(Object expressions) { public String apply(Object expressions) {
final List<Map<String, Object>> typedExpressions = convert(expressions); final List<Map<String, Object>> typedExpressions = convert(expressions);
return String.join(" AND ", typedExpressions.stream().map(lookupService::lookup).toList()); return "(" + String.join(" AND ", typedExpressions.stream().map(lookupService::lookup).toList()) + ")";
} }
} }

View File

@@ -24,7 +24,7 @@ public class EqualLookup implements SemrackJpaLookupExpression<EqualLookupParams
} }
try { try {
return "(idx.fullpath = '"+ equalLookupParams.getField() + "' AND idx.value->'v' = '"+ objectMapper.writeValueAsString(equalLookupParams.getValue())+"'::jsonb)"; return "(indexes->'"+ equalLookupParams.getField() + "'->'value' = '"+ objectMapper.writeValueAsString(equalLookupParams.getValue())+"'::jsonb)";
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@@ -31,7 +31,7 @@ public class InLookup implements SemrackJpaLookupExpression<InLookupParams> {
for (Object value : inLookupParams.getValues()) { for (Object value : inLookupParams.getValues()) {
inValues.add("'" + objectMapper.writeValueAsString(value) + "'::jsonb"); inValues.add("'" + objectMapper.writeValueAsString(value) + "'::jsonb");
} }
return "(idx.fullpath = '" + inLookupParams.getField() + "' AND idx.value->'v' in (" + String.join(",", inValues) + "))"; return "(indexes->'" + inLookupParams.getField() + "'->'value' in (" + String.join(",", inValues) + "))";
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@@ -18,6 +18,6 @@ public class OrLookup implements SemrackJpaLookupExpression<List<Map<String, Obj
@Override @Override
public String apply(Object expressions) { public String apply(Object expressions) {
final List<Map<String, Object>> typedExpressions = convert(expressions); final List<Map<String, Object>> typedExpressions = convert(expressions);
return String.join(" OR ", typedExpressions.stream().map(lookupService::lookup).toList()); return "(" + String.join(" OR ", typedExpressions.stream().map(lookupService::lookup).toList()) + ")";
} }
} }

View File

@@ -1,9 +0,0 @@
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<IndexDocumentEntity> {
}

View File

@@ -1,9 +1,9 @@
package fr.codeanddata.semrack.index.postgres.repositories; package fr.codeanddata.semrack.index.postgres.repositories;
import fr.codeanddata.semrack.index.postgres.entities.IndexKeyEntity; import fr.codeanddata.semrack.index.postgres.entities.SemrackIndexEntity;
import io.quarkus.hibernate.reactive.panache.PanacheRepository; import io.quarkus.hibernate.reactive.panache.PanacheRepository;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped @ApplicationScoped
public class IndexKeyRepository implements PanacheRepository<IndexKeyEntity> { public class SemrackIndexRepository implements PanacheRepository<SemrackIndexEntity> {
} }

View File

@@ -0,0 +1,18 @@
drop sequence if exists index_document_SEQ;
drop sequence if exists index_key_SEQ;
drop table if exists index_key;
drop table if exists index_document;
create table semrack_index
(
id bigint NOT NULL,
uid varchar(256) NOT NULL,
indexes jsonb,
constraint pk_semrack_index primary key (id)
);
alter table semrack_index
add constraint uc_semrack_index_uid unique (uid);
create sequence semrack_index_SEQ start with 1 increment by 50;

View File

@@ -32,7 +32,7 @@
<lombok.version>1.18.38</lombok.version> <lombok.version>1.18.38</lombok.version>
<org.mapstruct.version>1.6.3</org.mapstruct.version> <org.mapstruct.version>1.6.3</org.mapstruct.version>
<semrack.version>1.0-SNAPSHOT</semrack.version> <semrack.version>1.0-SNAPSHOT</semrack.version>
<quarkus.version>3.27.0</quarkus.version> <quarkus.version>3.29.4</quarkus.version>
<jsonpath.version>2.9.0</jsonpath.version> <jsonpath.version>2.9.0</jsonpath.version>
</properties> </properties>