com.eventsourcing.queries.IsLatestEntity Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eventsourcing-queries Show documentation
Show all versions of eventsourcing-queries Show documentation
Event capture and querying framework for Java
/**
* Copyright (c) 2016, All Contributors (see CONTRIBUTORS file)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.eventsourcing.queries;
import com.eventsourcing.EntityHandle;
import com.eventsourcing.hlc.HybridTimestamp;
import com.eventsourcing.index.EntityIndex;
import com.googlecode.cqengine.IndexedCollection;
import com.googlecode.cqengine.attribute.Attribute;
import com.googlecode.cqengine.attribute.SimpleAttribute;
import com.googlecode.cqengine.query.Query;
import com.googlecode.cqengine.query.logical.Or;
import com.googlecode.cqengine.query.option.QueryOptions;
import com.googlecode.cqengine.query.simple.SimpleQuery;
import com.googlecode.cqengine.resultset.ResultSet;
import lombok.Value;
import lombok.experimental.Accessors;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import static com.googlecode.cqengine.query.QueryFactory.and;
import static com.googlecode.cqengine.query.QueryFactory.greaterThan;
/**
* IsLatestEntity filters for entities that are the most recent against a queryFunction.
*
* For example, to query the latest NameChanged across all entities:
*
*
* isLatestEntity(repository.getIndexEngine().getIndexedCollection(NameChanged.class),
* (h) -> equal(NameChanged.REFERENCE_ID, h.get().reference()),
* NameChanged.TIMESTAMP)
*
*
* @see QueryFactory#isLatestEntity(IndexedCollection, Function, EntityIndex)
* @see QueryFactory#isLatestEntity(IndexedCollection, Query, EntityIndex)
*
* @param
*/
public class IsLatestEntity extends SimpleQuery {
private final IndexedCollection collection;
private final Attribute timestampAttribute;
private Function> queryFunction;
private Query query;
/**
* @param collection collection to query against
* @param queryFunction query returning function
* @param timestampAttribute timestamp attribute.
*/
public IsLatestEntity(IndexedCollection collection,
Function> queryFunction,
Attribute timestampAttribute) {
super(timestampAttribute);
this.collection = collection;
this.queryFunction = queryFunction;
this.timestampAttribute = timestampAttribute;
}
/**
* @param collection collection to query against
* @param query query
* @param timestampAttribute timestamp attribute.
*/
public IsLatestEntity(IndexedCollection collection,
Query query,
Attribute timestampAttribute) {
super(timestampAttribute);
this.collection = collection;
this.query = query;
this.timestampAttribute = timestampAttribute;
}
@Value
@Accessors(fluent = true)
private static class TerminatedRecords {
private Map, UUID> queries = new HashMap<>();
}
private Optional terminatedQuery(O object, Query query, QueryOptions queryOptions) {
if (queryOptions.get(TerminatedRecords.class) == null) {
queryOptions.put(TerminatedRecords.class, new TerminatedRecords<>());
}
TerminatedRecords terminatedRecords = queryOptions.get(TerminatedRecords.class);
UUID record = (UUID) terminatedRecords.queries().get(query);
if (record == null) {
return Optional.empty();
}
return Optional.of(record == object.uuid());
}
private boolean matches(ResultSet resultSet, Query query, O object, QueryOptions queryOptions) {
boolean matches = resultSet.size() == 0;
if (matches) {
@SuppressWarnings("unchecked")
TerminatedRecords terminatedRecords = queryOptions.get(TerminatedRecords.class);
terminatedRecords.queries().put(query, object.uuid());
}
return matches;
}
@Override
protected boolean matchesSimpleAttribute(SimpleAttribute attribute, O object, QueryOptions
queryOptions) {
Query actualQuery = query == null ? queryFunction.apply(object) : query;
Optional terminatedQuery = terminatedQuery(object, actualQuery, queryOptions);
if (terminatedQuery.isPresent()) {
return terminatedQuery.get();
}
HybridTimestamp value = attribute.getValue(object, queryOptions);
try (ResultSet resultSet = collection.retrieve(and(
actualQuery,
greaterThan(timestampAttribute, value)))) {
return matches(resultSet, actualQuery, object, queryOptions);
}
}
@Override
protected boolean matchesNonSimpleAttribute(Attribute attribute, O object, QueryOptions
queryOptions) {
Query actualQuery = query == null ? queryFunction.apply(object) : query;
Optional terminatedQuery = terminatedQuery(object, actualQuery, queryOptions);
if (terminatedQuery.isPresent()) {
return terminatedQuery.get();
}
Iterable values = attribute.getValues(object, queryOptions);
List> conditions = StreamSupport.stream(values.spliterator(), false)
.map(v -> greaterThan(timestampAttribute, v))
.collect(Collectors.toList());
Query timestampQuery = conditions.size() == 1 ? conditions.get(0) : new Or<>(conditions);
try (ResultSet resultSet = collection.retrieve(and(
actualQuery,
timestampQuery))) {
return matches(resultSet, actualQuery, object, queryOptions);
}
}
@Override protected int calcHashCode() {
int result = collection.hashCode();
result = 31 * result + (query == null ? queryFunction : query).hashCode();
result = 31 * result + timestampAttribute.hashCode();
return result;
}
@Override
public String toString() {
return "isLatestEntity(" +
"IndexedCollection<" + timestampAttribute.getObjectType().getSimpleName() + ">" +
", query=" + query == null ? queryFunction.toString() : query +
", timestamp=" + asLiteral(timestampAttribute.getAttributeName()) +
")";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof IsLatestEntity)) return false;
IsLatestEntity latestReference = (IsLatestEntity) o;
if (!collection.equals(latestReference.collection)) return false;
if (query != null && !query.equals(query)) return false;
if (queryFunction != null && !queryFunction.equals(latestReference.queryFunction)) return false;
if (!timestampAttribute.equals(latestReference.timestampAttribute)) return false;
return true;
}
}