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

com.opencredo.concourse.cassandra.events.CassandraEventRetriever Maven / Gradle / Ivy

The newest version!
package com.opencredo.concourse.cassandra.events;

import com.datastax.driver.core.querybuilder.Clause;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.opencredo.concourse.domain.common.AggregateId;
import com.opencredo.concourse.domain.events.Event;
import com.opencredo.concourse.domain.events.sourcing.EventRetriever;
import com.opencredo.concourse.domain.events.matching.EventTypeMatcher;
import com.opencredo.concourse.domain.time.TimeRange;
import com.opencredo.concourse.domain.time.TimeRangeBound;
import org.springframework.data.cassandra.core.CassandraTemplate;

import java.lang.reflect.Type;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * An {@link EventRetriever} that retrieves events from Cassandra.
 */
public final class CassandraEventRetriever implements EventRetriever {

    /**
     * Construct an {@link EventRetriever} that retrieves events from Cassandra using the supplied
     * {@link CassandraTemplate} and {@link ObjectMapper}
     * @param cassandraTemplate The {@link CassandraTemplate} to use to execute Cassandra queries.
     * @param objectMapper The {@link ObjectMapper} to use to deserialise Event data.
     * @return The constructed {@link EventRetriever}.
     */
    public static EventRetriever create(CassandraTemplate cassandraTemplate, ObjectMapper objectMapper) {
        return create(cassandraTemplate, JsonDeserialiser.using(objectMapper));
    }

    /**
     * Construct an {@link EventRetriever} that retrieves events from Cassandra using the supplied
     * {@link CassandraTemplate} and deserialising {@link BiFunction}.
     * @param cassandraTemplate The {@link CassandraTemplate} to use to execute Cassandra queries.
     * @param deserialiser The deserialiser to use to deserialise Event data.
     * @return The constructed {@link EventRetriever}.
     */
    public static EventRetriever create(CassandraTemplate cassandraTemplate, BiFunction deserialiser) {
        return new CassandraEventRetriever(cassandraTemplate, deserialiser);
    }

    private final CassandraTemplate cassandraTemplate;
    private final BiFunction deserialiser;

    private CassandraEventRetriever(CassandraTemplate cassandraTemplate, BiFunction deserialiser) {
        this.cassandraTemplate = cassandraTemplate;
        this.deserialiser = deserialiser;
    }

    @Override
    public List getEvents(EventTypeMatcher matcher, AggregateId aggregateId, TimeRange timeRange) {
        final Select select = selectFromEvent();

        select.where(QueryBuilder.eq("aggregateType", aggregateId.getType()))
                .and(QueryBuilder.eq("aggregateId", aggregateId.getId()));

        constrainTimeRange(timeRange, select);

        List results = new LinkedList<>();

        runAndTranslate(matcher, select, results::add);

        return results;
    }

    @Override
    public Map> getEvents(EventTypeMatcher matcher, String aggregateType, Collection aggregateIds, TimeRange timeRange) {
        final Select select = selectFromEvent();

        select.where(QueryBuilder.eq("aggregateType", aggregateType))
                .and(QueryBuilder.in("aggregateId", aggregateIds.stream().collect(Collectors.toList())));

        constrainTimeRange(timeRange, select);

        Map> results = new HashMap<>();
        Consumer addToResults = event -> results.computeIfAbsent(event.getAggregateId(), id -> new LinkedList<>()).add(event);

        runAndTranslate(matcher, select, addToResults);

        return results;
    }

    private Consumer constrainUpperBound(Select select) {
        return constrainBound(select, QueryBuilder::lte, QueryBuilder::lt);
    }

    private Consumer constrainLowerBound(Select select) {
        return constrainBound(select, QueryBuilder::gte, QueryBuilder::gt);
    }

    private Consumer constrainBound(
            Select select,
            BiFunction inclusive,
            BiFunction exclusive) {
        return bound -> select.where((bound.isInclusive() ? inclusive : exclusive).apply("eventTimestamp", bound.getInstant().toEpochMilli()));
    }

    private void runAndTranslate(EventTypeMatcher matcher, Select select, Consumer addToResults) {
        EventTranslator eventTranslator = EventTranslator.using(matcher, deserialiser, addToResults);
        cassandraTemplate.query(select, eventTranslator);
    }

    private void constrainTimeRange(TimeRange timeRange, Select select) {
        timeRange.getUpperBound().ifPresent(constrainUpperBound(select));
        timeRange.getLowerBound().ifPresent(constrainLowerBound(select));
    }

    private Select selectFromEvent() {
        return QueryBuilder.select(
                "aggregateType", "aggregateId", "eventTimestamp", "streamId",
                "processingId", "name", "version", "parameters", "characteristics")
                .from("Event");
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy