All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dido.operators.LeftStreamJoin Maven / Gradle / Ivy

The newest version!
package dido.operators;

import dido.data.Concatenator;
import dido.data.GenericData;
import dido.data.IndexedData;
import dido.data.SubData;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;

public class LeftStreamJoin implements StreamJoin {


    private final Consumer> primary = new PrimaryConsumer();

    private final Consumer> secondary = new SecondaryConsumer();

    private final Map, SingleJoin> data = new ConcurrentHashMap<>();

    private final Function, GenericData> primaryIndices;

    private final Function, GenericData> foreignIndices;

    private final Function, GenericData> secondaryIndices;

    private volatile Consumer> to;

    private final Concatenator.Factory concatenator = Concatenator.withSettings()
            .skipDuplicates(true).factory();

    private LeftStreamJoin(With with) {
        this.primaryIndices = with.primaryIndices();
        this.foreignIndices = with.foreignIndices();
        this.secondaryIndices = with.secondaryIndices();
    }

    public static class With {

        private int[] primaryIndices;

        private F[] primaryFields;

        private int[] foreignIndices;

        private F[] foreignFields;

        private int[] secondaryIndices;

        private F[] secondaryFields;

        public With primaryIndices(int... primaryIndices) {
            this.primaryIndices = primaryIndices;
            return this;
        }

        public With foreignIndices(int... foreignIndices) {
            this.foreignIndices = foreignIndices;
            return this;
        }

        public With secondaryIndices(int... secondaryIndices) {
            this.secondaryIndices = secondaryIndices;
            return this;
        }

        public With primaryFields(F... primaryFields) {
            this.primaryFields = primaryFields;
            return this;
        }

        public With foreignFields(F... foreignFields) {
            this.foreignFields = foreignFields;
            return this;
        }

        public With secondaryFields(F... secondaryFields) {
            this.secondaryFields = secondaryFields;
            return this;
        }

        private Function, GenericData> primaryIndices() {
            if (primaryIndices == null) {
                return SubData.ofFields(Objects.requireNonNull(primaryFields));
            }
            else {
                return SubData.ofIndices(primaryIndices);
            }
        }

        private Function, GenericData> foreignIndices() {
            if (foreignIndices == null) {
                return SubData.ofFields(Objects.requireNonNull(foreignFields));
            }
            else {
                return SubData.ofIndices(foreignIndices);
            }
        }

        private Function, GenericData> secondaryIndices() {
            if (secondaryIndices == null) {
                return SubData.ofFields(Objects.requireNonNull(secondaryFields));
            }
            else {
                return SubData.ofIndices(secondaryIndices);
            }
        }

        public StreamJoin make() {
            return new LeftStreamJoin<>(this);
        }
    }

    public static  With with() {
        return new With<>();
    }

    @Override
    public Consumer> getPrimary() {
        return primary;
    }

    @Override
    public Consumer> getSecondary() {
        return secondary;
    }

    @Override
    public void setTo(Consumer> to) {
        this.to = to;
    }

    class PrimaryConsumer implements Consumer> {

        @Override
        public void accept(IndexedData primaryData) {

            IndexedData keyOfPrimary = primaryIndices.apply(primaryData);

            IndexedData foreignKey = foreignIndices.apply(primaryData);

            SingleJoin singleJoin = data.computeIfAbsent(foreignKey, k -> new SingleJoin<>());

            singleJoin.primaries.mappedByKey.put(keyOfPrimary, primaryData);
            // ok so long as this is always called on the same thread.
            if (singleJoin.secondary != null) {
                to.accept(concatenator.concat(primaryData, singleJoin.secondary));
            }
        }
    }

    class SecondaryConsumer implements Consumer> {

        @Override
        public void accept(IndexedData secondaryData) {

            IndexedData keyOfSecondary = secondaryIndices.apply(secondaryData);

            SingleJoin singleJoin = data.computeIfAbsent(keyOfSecondary, k -> new SingleJoin<>());

            singleJoin.secondary = secondaryData;

            singleJoin.primaries.mappedByKey.values()
                    .forEach(primary -> to.accept(concatenator.concat(primary, secondaryData)));
        }
    }

    static class Primaries {

        private final Map, IndexedData> mappedByKey = new ConcurrentHashMap<>();
    }

    static class SingleJoin {

        private final Primaries primaries = new Primaries<>();

        private volatile IndexedData secondary;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy