package it.cavallium.dbengine.client; import io.netty5.buffer.api.Drop; import io.netty5.buffer.api.Owned; import io.netty5.buffer.api.Send; import it.cavallium.dbengine.client.query.current.data.TotalHitsCount; import io.netty5.buffer.api.internal.ResourceSupport; import it.cavallium.dbengine.database.collections.ValueGetter; import it.cavallium.dbengine.database.collections.ValueTransformer; import java.util.Map.Entry; import java.util.Optional; import java.util.function.Function; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.util.function.Tuples; public final class Hits extends ResourceSupport, Hits> { private static final Drop> DROP = new Drop<>() { @Override public void drop(Hits obj) { if (obj.onClose != null) { obj.onClose.run(); } } @Override public Drop> fork() { return this; } @Override public void attach(Hits obj) { } }; private Flux results; private TotalHitsCount totalHitsCount; private Runnable onClose; @SuppressWarnings({"unchecked", "rawtypes"}) public Hits(Flux results, TotalHitsCount totalHitsCount, Runnable onClose) { super((Drop>) (Drop) DROP); this.results = results; this.totalHitsCount = totalHitsCount; this.onClose = onClose; } public static Hits empty() { return new Hits<>(Flux.empty(), TotalHitsCount.of(0, true), null); } public static Hits> withValuesLazy(Hits> hits, ValueGetter valuesGetter) { var hitsEntry = hits.results().map(hitKey -> hitKey.withValue(valuesGetter::get)); return new Hits<>(hitsEntry, hits.totalHitsCount, hits::close); } public static Hits> withValues(Hits> hits, ValueGetter valuesGetter) { var hitsEntry = hits.results().flatMap(hitKey -> hitKey.withValue(valuesGetter::get)); return new Hits<>(hitsEntry, hits.totalHitsCount, hits::close); } public static Function>, Hits>> generateMapper( ValueGetter valueGetter) { return result -> { var hitsToTransform = result.results() .map(hit -> new LazyHitEntry<>(Mono.just(hit.key()), valueGetter.get(hit.key()), hit.score())); return new Hits<>(hitsToTransform, result.totalHitsCount(), result::close); }; } public static Function>, Hits>> generateMapper( ValueTransformer valueTransformer) { return result -> { try { var sharedHitsFlux = result.results().publish().refCount(3); var scoresFlux = sharedHitsFlux.map(HitKey::score); var keysFlux = sharedHitsFlux.map(HitKey::key); var valuesFlux = valueTransformer.transform(keysFlux); var transformedFlux = Flux.zip((Object[] data) -> { //noinspection unchecked var keyMono = Mono.just((T) data[0]); //noinspection unchecked var val = (Entry>) data[1]; var valMono = Mono.justOrEmpty(val.getValue()); var score = (Float) data[2]; return new LazyHitEntry<>(keyMono, valMono, score); }, keysFlux, valuesFlux, scoresFlux); return new Hits<>(transformedFlux, result.totalHitsCount(), result::close); } catch (Throwable t) { result.close(); throw t; } }; } public Flux results() { return results; } public TotalHitsCount totalHitsCount() { return totalHitsCount; } @Override public String toString() { return "Hits[" + "results=" + results + ", " + "totalHitsCount=" + totalHitsCount + ']'; } @Override protected RuntimeException createResourceClosedException() { return new IllegalStateException("Closed"); } @Override protected Owned> prepareSend() { var results = this.results; var totalHitsCount = this.totalHitsCount; var onClose = this.onClose; return drop -> { var instance = new Hits<>(results, totalHitsCount, onClose); drop.attach(instance); return instance; }; } protected void makeInaccessible() { this.results = null; this.totalHitsCount = null; this.onClose = null; } }