package org.molgenis.data.support;
import com.google.common.collect.Iterators;
import com.google.common.collect.Sets;
import org.molgenis.data.*;
import org.molgenis.data.QueryRule.Operator;
import org.molgenis.data.aggregation.AggregateQuery;
import org.molgenis.data.aggregation.AggregateResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static com.google.common.base.Predicates.notNull;
import static com.google.common.collect.Iterables.*;
import static com.google.common.collect.Maps.uniqueIndex;
/**
* Base class for repositories. Subclasses can override supported methods
*/
public abstract class AbstractRepository implements Repository<Entity>
{
private static final int FIND_ALL_BATCH_SIZE = 1000;
private final Logger LOG = LoggerFactory.getLogger(getClass());
private String name;
@Override
public String getName()
{
if (name == null) name = getEntityType().getName();
return name;
}
@Override
public Set<Operator> getQueryOperators()
{
if (!getCapabilities().contains(RepositoryCapability.QUERYABLE))
{
return Collections.emptySet();
}
else
{
return EnumSet.allOf(Operator.class);
}
}
public Query<Entity> query()
{
return new QueryImpl<>(this);
}
@Override
public long count()
{
return count(new QueryImpl<>());
}
@Override
public void close() throws IOException
{
}
@Override
public long count(Query<Entity> q)
{
throw new UnsupportedOperationException();
}
@Override
public Stream<Entity> findAll(Query<Entity> q)
{
throw new UnsupportedOperationException();
}
@Override
public Entity findOne(Query<Entity> q)
{
throw new UnsupportedOperationException();
}
@Override
public Entity findOneById(Object id)
{
return findOneById(id, null);
}
@Override
public Entity findOneById(Object id, Fetch fetch)
{
throw new UnsupportedOperationException();
}
@Override
public Stream<Entity> findAll(Stream<Object> ids)
{
return findAll(ids, null);
}
@Override
public Stream<Entity> findAll(Stream<Object> ids, Fetch fetch)
{
Iterator<List<Object>> batches = Iterators.partition(ids.iterator(), FIND_ALL_BATCH_SIZE);
Iterable<List<Object>> iterable = () -> batches;
return StreamSupport.stream(iterable.spliterator(), false)
.flatMap(batch -> StreamSupport.stream(findAllBatched(batch, fetch).spliterator(), false));
}
private Iterable<Entity> findAllBatched(List<Object> ids, Fetch fetch)
{
String fieldIdAttributeName = getEntityType().getIdAttribute().getName();
if (fetch != null) fetch.field(fieldIdAttributeName);
Query<Entity> inQuery = new QueryImpl<>().in(fieldIdAttributeName, Sets.newHashSet(ids)).fetch(fetch);
Map<Object, Entity> indexedEntities = uniqueIndex(findAll(inQuery).iterator(), Entity::getIdValue);
return filter(transform(ids, id -> lookup(indexedEntities, id)), notNull());
}
private Entity lookup(Map<Object, Entity> index, Object id)
{
Entity result = index.get(id);
if (result == null)
{
LOG.debug("Lookup: Couldn't find {} for id {}.", getName(), id);
}
return result;
}
@Override
public AggregateResult aggregate(AggregateQuery aggregateQuery)
{
throw new UnsupportedOperationException();
}
@Override
public void update(Entity entity)
{
throw new UnsupportedOperationException();
}
@Override
public void update(Stream<Entity> entities)
{
throw new UnsupportedOperationException();
}
@Override
public void delete(Entity entity)
{
throw new UnsupportedOperationException();
}
@Override
public void delete(Stream<Entity> entities)
{
throw new UnsupportedOperationException();
}
@Override
public void deleteById(Object id)
{
throw new UnsupportedOperationException();
}
@Override
public void deleteAll(Stream<Object> ids)
{
throw new UnsupportedOperationException();
}
@Override
public void deleteAll()
{
throw new UnsupportedOperationException();
}
@Override
public void add(Entity entity)
{
throw new UnsupportedOperationException();
}
@Override
public Integer add(Stream<Entity> entities)
{
throw new UnsupportedOperationException();
}
@Override
public void forEachBatched(Fetch fetch, Consumer<List<Entity>> consumer, int batchSize)
{
// by default: ignore fetch
for (List<Entity> entities : partition(this, batchSize))
{
consumer.accept(entities);
}
}
}