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 it.cavallium.dbengine.utils.SimpleResource; 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 SimpleResource { private static final Hits EMPTY_HITS = new Hits<>(Flux.empty(), TotalHitsCount.of(0, true), null, false); private Flux results; private TotalHitsCount totalHitsCount; private Runnable onClose; public Hits(Flux results, TotalHitsCount totalHitsCount, Runnable onClose) { this(results, totalHitsCount, onClose, true); } private Hits(Flux results, TotalHitsCount totalHitsCount, Runnable onClose, boolean canClose) { super(canClose && onClose != null); this.results = results; this.totalHitsCount = totalHitsCount; this.onClose = onClose; } @SuppressWarnings("unchecked") public static Hits empty() { return (Hits) EMPTY_HITS; } 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() { ensureOpen(); return results; } public TotalHitsCount totalHitsCount() { ensureOpen(); return totalHitsCount; } @Override public String toString() { return "Hits[" + "results=" + results + ", " + "totalHitsCount=" + totalHitsCount + ']'; } @Override protected void onClose() { if (onClose != null) { onClose.run(); } } }