PredicateFilterMessageStoreSearcher.java

package net.morimekta.providence.storage;

import net.morimekta.providence.PMessage;

import javax.annotation.Nonnull;
import java.util.List;
import java.util.Map;
import java.util.function.BiPredicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Simple store searcher that finds all messages in a given store that matches
 * a simple java predicate. Note that this will always load all the data from
 * the store in order to do the search.
 *
 * @param <Q> The query type.
 * @param <K> The store's key type.
 * @param <M> The message type in the store.
 */
public class PredicateFilterMessageStoreSearcher<Q, K, M extends PMessage<M>>
        implements MessageSearcher<Q, M> {
    public interface PredicateFilter<Q, K, M extends PMessage<M>> {
        boolean test(K key, M message, Q query);
    }

    private final MessageReadOnlyStore<K, M> store;
    private final PredicateFilter<Q, K, M> predicate;

    public PredicateFilterMessageStoreSearcher(MessageReadOnlyStore<K, M> store,
                                               BiPredicate<M, Q> biPredicate) {
        this(store, (key, message, query) -> biPredicate.test(message, query));
    }

    public PredicateFilterMessageStoreSearcher(MessageReadOnlyStore<K, M> store,
                                               PredicateFilter<Q, K, M> predicate) {
        this.store = store;
        this.predicate = predicate;
    }

    @Nonnull
    @Override
    public List<M> search(@Nonnull Q query) {
        return stream(query).collect(Collectors.toList());
    }

    @Nonnull
    @Override
    public Stream<M> stream(@Nonnull Q query) {
        return store.getAll(store.keys())
                    .entrySet()
                    .stream()
                    .filter(entry -> predicate.test(entry.getKey(), entry.getValue(), query))
                    .map(Map.Entry::getValue);
    }
}