Skip to content

Commit

Permalink
HUEditorViewBuffer_HighVolume: support for streaming DocumentIdsSelec…
Browse files Browse the repository at this point in the history
…tion.ALL

also: changed all methods which are returning rows lists to streaming.
we have to do this because it might be that we will handle very large
amount of data.

#668
  • Loading branch information
teosarca committed Nov 11, 2017
1 parent b90bfd1 commit d1b5f53
Show file tree
Hide file tree
Showing 19 changed files with 355 additions and 233 deletions.
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
package de.metas.ui.web.handlingunits;

import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;

import org.adempiere.util.Services;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;

import de.metas.handlingunits.IHUQueryBuilder;
import de.metas.handlingunits.IHandlingUnitsDAO;
import de.metas.handlingunits.model.I_M_HU;
import de.metas.ui.web.handlingunits.HUEditorRowFilter.Select;
import de.metas.ui.web.process.adprocess.ViewBasedProcessTemplate;
import de.metas.ui.web.window.datatypes.DocumentIdsSelection;
import de.metas.util.stream.StreamUtils;
import lombok.NonNull;

/*
Expand All @@ -27,12 +24,12 @@
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 2 of the
* License, or (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-2.0.html>.
Expand All @@ -41,7 +38,7 @@

/**
* A {@link ViewBasedProcessTemplate} implementation template which add convenient functionalities around {@link HUEditorView}.
*
*
* @author metas-dev <dev@metasfresh.com>
*
*/
Expand All @@ -59,57 +56,46 @@ protected final HUEditorRow getSingleSelectedRow()
return HUEditorRow.cast(super.getSingleSelectedRow());
}

protected final List<HUEditorRow> getSelectedRows(@NonNull final HUEditorRowFilter filter)
protected final Stream<HUEditorRow> streamSelectedRows(@NonNull final HUEditorRowFilter filter)
{
final DocumentIdsSelection selectedDocumentIds = getSelectedDocumentIds();
final List<HUEditorRow> allRows = getView().getByIds(selectedDocumentIds);

return allRows.stream()
.filter(toPredicate(filter))
.collect(ImmutableList.toImmutableList());

return getView()
.streamByIds(selectedDocumentIds)
.filter(row -> matches(row, filter));
}

/**
* Calls {@link #getSelectedRows(Select)} and returns the {@link HUEditorRow}s' {@code M_HU_ID}s.
*
* @param select
*/
protected final Set<Integer> getSelectedHUIds(@NonNull final Select select)
protected final Stream<Integer> streamSelectedHUIds(@NonNull final Select select)
{
return getSelectedHUIds(HUEditorRowFilter.select(select));
return streamSelectedHUIds(HUEditorRowFilter.select(select));
}

protected final Set<Integer> getSelectedHUIds(@NonNull final HUEditorRowFilter filter)
protected final Stream<Integer> streamSelectedHUIds(@NonNull final HUEditorRowFilter filter)
{
return getSelectedRows(filter)
.stream()
return streamSelectedRows(filter)
.map(HUEditorRow::getM_HU_ID)
.filter(huId -> huId > 0)
.collect(ImmutableSet.toImmutableSet());
.filter(huId -> huId > 0);
}

/**
* Gets <b>all</b> selected {@link HUEditorRow}s and loads the top level-HUs from those.
* I.e. this method does not rely on {@link HUEditorRow#isTopLevel()}, but checks the underlying HU.
*
*
* @param select
* @return
*/
protected final List<I_M_HU> getSelectedHUs(@NonNull final Select select)
protected final Stream<I_M_HU> streamSelectedHUs(@NonNull final Select select)
{
return getSelectedHUs(HUEditorRowFilter.select(select));
return streamSelectedHUs(HUEditorRowFilter.select(select));
}

protected final List<I_M_HU> getSelectedHUs(@NonNull final HUEditorRowFilter filter)
protected final Stream<I_M_HU> streamSelectedHUs(@NonNull final HUEditorRowFilter filter)
{
// get all IDs, we we will enforce the filter using HUQueryBuilder
final Set<Integer> huIds = getSelectedHUIds(HUEditorRowFilter.ALL);

return toHUQueryBuilder(filter)
.addOnlyHUIds(huIds)
.createQuery()
.list(I_M_HU.class);
final Stream<Integer> huIds = streamSelectedHUIds(HUEditorRowFilter.ALL);
return StreamUtils.dice(huIds, 100)
.flatMap(huIdsChunk -> toHUQueryBuilder(filter)
.addOnlyHUIds(huIdsChunk)
.createQuery()
.stream(I_M_HU.class));
}

private static final IHUQueryBuilder toHUQueryBuilder(final HUEditorRowFilter filter)
Expand All @@ -120,11 +106,6 @@ private static final IHUQueryBuilder toHUQueryBuilder(final HUEditorRowFilter fi
.addHUStatusesToInclude(filter.getOnlyHUStatuses());
}

private static final Predicate<HUEditorRow> toPredicate(@NonNull final HUEditorRowFilter filter)
{
return row -> matches(row, filter);
}

private static final boolean matches(final HUEditorRow row, final HUEditorRowFilter filter)
{
final Select select = filter.getSelect();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package de.metas.ui.web.handlingunits;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.stream.Stream;

import org.adempiere.util.GuavaCollectors;
import org.adempiere.util.collections.IteratorUtils;
import org.compiere.util.CCache;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;

import de.metas.ui.web.window.datatypes.DocumentId;
import lombok.Builder;
import lombok.NonNull;

/*
* #%L
* metasfresh-webui-api
* %%
* Copyright (C) 2017 metas GmbH
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-2.0.html>.
* #L%
*/

class HUEditorRowsPagedLoadingIterator implements Iterator<HUEditorRow>
{
private final int DEFAULT_BUFFERSIZE = 100;

private final HUEditorViewRepository huEditorRepo;
private final CCache<DocumentId, HUEditorRow> cache;
private final int bufferSize;
private final Iterator<HUEditorRowId> rowIds;

private Iterator<HUEditorRow> currentPageIterator;
private boolean finished = false;

@Builder
private HUEditorRowsPagedLoadingIterator(
@NonNull final HUEditorViewRepository huEditorRepo,
@NonNull final CCache<DocumentId, HUEditorRow> cache,
final int bufferSize,
@NonNull final Iterator<HUEditorRowId> rowIds)
{
this.huEditorRepo = huEditorRepo;
this.cache = cache;
this.bufferSize = bufferSize > 0 ? bufferSize : DEFAULT_BUFFERSIZE;
this.rowIds = rowIds;
}

@Override
public boolean hasNext()
{
if (finished)
{
return false;
}

if (currentPageIterator == null || !currentPageIterator.hasNext())
{
currentPageIterator = getNextPageIterator();
if (!currentPageIterator.hasNext())
{
finished = true;
return false;
}
else
{
return true;
}
}
else
{
return currentPageIterator.hasNext();
}
}

@Override
public HUEditorRow next()
{
if (currentPageIterator == null)
{
throw new NoSuchElementException();
}

return currentPageIterator.next();
}

public Stream<HUEditorRow> stream()
{
return IteratorUtils.stream(this);
}

private Iterator<HUEditorRow> getNextPageIterator()
{
final HUEditorRow[] rows = new HUEditorRow[bufferSize];
final Map<HUEditorRowId, Integer> rowIdToLoad2index = new HashMap<>();

//
// Get from cache as much as possible
{
int idx = 0;
while (rowIds.hasNext() && idx < bufferSize)
{
final HUEditorRowId rowId = rowIds.next();

final HUEditorRowId topLevelRowId = rowId.toTopLevelRowId();
final HUEditorRow topLevelRow = cache.get(topLevelRowId.toDocumentId());

if (topLevelRow == null)
{
// to be loaded
rowIdToLoad2index.put(rowId, idx);
}
else
{
if (rowId.equals(topLevelRowId))
{
rows[idx] = topLevelRow;
}
else
{
rows[idx] = topLevelRow.getIncludedRowById(rowId.toDocumentId()).orElse(null);
}
}

idx++;
}
}

//
// Load missing rows (which were not found in cache)
if (!rowIdToLoad2index.isEmpty())
{
final ListMultimap<HUEditorRowId, HUEditorRowId> topLevelRowId2rowIds = rowIdToLoad2index.keySet()
.stream()
.map(rowId -> GuavaCollectors.entry(rowId.toTopLevelRowId(), rowId))
.collect(GuavaCollectors.toImmutableListMultimap());

final Set<Integer> topLevelHUIds = topLevelRowId2rowIds.keys().stream().map(HUEditorRowId::getTopLevelHUId).collect(ImmutableSet.toImmutableSet());

huEditorRepo.retrieveHUEditorRows(topLevelHUIds)
.forEach(topLevelRow -> {
final HUEditorRowId topLevelRowId = topLevelRow.getHURowId();
for (final HUEditorRowId includedRowId : topLevelRowId2rowIds.get(topLevelRowId))
{
final Integer idx = rowIdToLoad2index.remove(includedRowId);
if (idx == null)
{
// wtf?! shall not happen
continue;
}

if (topLevelRowId.equals(includedRowId))
{
rows[idx] = topLevelRow;
cache.put(topLevelRow.getId(), topLevelRow);
}
else
{
rows[idx] = topLevelRow.getIncludedRowById(includedRowId.toDocumentId()).orElse(null);
}
}
});
}

return Stream.of(rows)
.filter(row -> row != null) // IMPORTANT: just to make sure we won't stream some empty gaps (e.g. missing rows because HU was not a top level one)
.iterator();
}

}
6 changes: 0 additions & 6 deletions src/main/java/de/metas/ui/web/handlingunits/HUEditorView.java
Original file line number Diff line number Diff line change
Expand Up @@ -270,12 +270,6 @@ public HUEditorRow getById(final DocumentId rowId) throws EntityNotFoundExceptio
return rowsBuffer.getById(rowId);
}

@Override
public List<HUEditorRow> getByIds(final DocumentIdsSelection rowIds)
{
return streamByIds(rowIds).collect(ImmutableList.toImmutableList());
}

@Override
public LookupValuesList getFilterParameterDropdown(final String filterId, final String filterParameterName, final Evaluatee ctx)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Stream;

import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;

import de.metas.printing.esb.base.util.Check;
Expand Down Expand Up @@ -68,35 +70,40 @@ interface HUEditorViewBuffer
/** @return top level rows and included rows recursive stream which are matching the given query */
default Stream<HUEditorRow> streamAllRecursive(@NonNull final HUEditorRowQuery query)
{
Stream<HUEditorRow> result = streamAllRecursive();

return streamAllRecursive().filter(toPredicate(query));
}

/* private */ static Predicate<HUEditorRow> toPredicate(@NonNull final HUEditorRowQuery query)
{
Predicate<HUEditorRow> predicate = Predicates.alwaysTrue();

// Filter by row type
final HUEditorRowType rowType = query.getRowType();
if (rowType != null)
{
result = result.filter(row -> Objects.equals(row.getType(), rowType));
predicate = predicate.and(row -> Objects.equals(row.getType(), rowType));
}

// Filter by string filter
final String stringFilter = query.getUserInputFilter();
if (!Check.isEmpty(stringFilter, true))
{
result = result.filter(row -> row.matchesStringFilter(stringFilter));
predicate = predicate.and(row -> row.matchesStringFilter(stringFilter));
}

// Exclude M_HU_IDs
final ImmutableSet<Integer> excludeHUIds = query.getExcludeHUIds();
if (!excludeHUIds.isEmpty())
{
result = result.filter(row -> !excludeHUIds.contains(row.getM_HU_ID()));
predicate = predicate.and(row -> !excludeHUIds.contains(row.getM_HU_ID()));
}

if (!query.getExcludeHUStatuses().isEmpty())
{
result = result.filter(row -> !query.getExcludeHUStatuses().contains(row.getHUStatusKey()));
predicate = predicate.and(row -> !query.getExcludeHUStatuses().contains(row.getHUStatusKey()));
}

return result;
return predicate;
}

/** @return true if there is any top level or included row which is matching given query */
Expand Down
Loading

0 comments on commit d1b5f53

Please sign in to comment.