com.eventsourcing.queries.ModelCollectionQuery 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.Model;
import com.eventsourcing.Repository;
import java.util.*;
import java.util.stream.BaseStream;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* A toolkit for composing model collections queries. An implementation of this interface
* can be passed to {@link #query(Repository, ModelCollectionQuery)} to retrieve a collection
* of matching model instances.
*
* Following logical operator functions can be used to compose a query:
*
*
* - {@link LogicalOperators#or(Collection)}
* - {@link LogicalOperators#and(Collection)}
*
*
* @param
*/
public interface ModelCollectionQuery {
Stream getCollectionStream(Repository repository);
static Collection query(Repository repository, ModelCollectionQuery query) {
try (Stream collectionStream = query.getCollectionStream(repository)) {
return collectionStream.collect(Collectors.toList());
}
}
final class LogicalOperators {
public static ModelCollectionQuery or(Collection> queries) {
return new Or<>(queries);
}
public static ModelCollectionQuery or(ModelCollectionQuery... queries) {
return new Or<>(queries);
}
public static class Or implements ModelCollectionQuery {
private final Collection> queries;
public Or(Collection> queries) {
this.queries = queries;
}
public Or(ModelCollectionQuery... queries) {
this.queries = Arrays.asList(queries);
}
@Override public Stream getCollectionStream(Repository repository) {
Stream stream = Stream.empty();
List> streams = new ArrayList<>();
Set seen = new HashSet<>();
for (ModelCollectionQuery query : queries) {
Stream queryStream = query.getCollectionStream(repository)
.filter(m -> !seen.contains(m.getId()))
.map(m -> {
seen.add(m.getId());
return m;
});
streams.add(queryStream);
stream = Stream.concat(stream, queryStream);
}
return stream.onClose(() -> {
streams.forEach(BaseStream::close);
});
}
}
public static ModelCollectionQuery and(Collection> queries) {
return new And<>(queries);
}
public static ModelCollectionQuery and(ModelCollectionQuery... queries) {
return new And<>(queries);
}
public static class And implements ModelCollectionQuery {
private final Collection> queries;
public And(Collection> queries) {
this.queries = queries;
}
public And(ModelCollectionQuery... queries) {
this.queries = Arrays.asList(queries);
}
@Override public Stream getCollectionStream(Repository repository) {
Iterator> iterator = queries.iterator();
if (iterator.hasNext()) {
ModelCollectionQuery first = iterator.next();
Stream firstStream = first.getCollectionStream(repository);
List realizedStream = firstStream.collect(Collectors.toList());
Set set = realizedStream.stream()
.map(Model::getId).collect(Collectors.toSet());
Set seen = new HashSet<>();
firstStream.close();
List> allQueries = new ArrayList<>();
iterator.forEachRemaining(allQueries::add);
Stream stream = Stream.empty();
List> streams = new ArrayList<>();
for (ModelCollectionQuery query : allQueries) {
Stream queryStream =
query.getCollectionStream(repository)
.filter(m -> set.contains(m.getId()) && !seen.contains(m.getId()))
.map(m -> {
seen.add(m.getId());
return m;
});
streams.add(queryStream);
stream = Stream.concat(stream, queryStream);
}
stream = Stream.concat(realizedStream.stream().filter(m -> seen.contains(m.getId())),
stream);
return stream.onClose(() -> streams.forEach(BaseStream::close));
} else {
return Stream.empty();
}
}
}
}
}