|
|
@@ -14,65 +14,85 @@ import org.elasticsearch.core.Releasables;
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.Arrays;
|
|
|
-import java.util.BitSet;
|
|
|
+import java.util.HashSet;
|
|
|
import java.util.List;
|
|
|
+import java.util.Set;
|
|
|
|
|
|
public class ProjectOperator extends AbstractPageMappingOperator {
|
|
|
|
|
|
- private final BitSet bs;
|
|
|
+ private final Set<Integer> pagesUsed;
|
|
|
+ private final int[] projection;
|
|
|
private Block[] blocks;
|
|
|
|
|
|
- public record ProjectOperatorFactory(BitSet mask) implements OperatorFactory {
|
|
|
+ public record ProjectOperatorFactory(List<Integer> projection) implements OperatorFactory {
|
|
|
|
|
|
@Override
|
|
|
public Operator get(DriverContext driverContext) {
|
|
|
- return new ProjectOperator(mask);
|
|
|
+ return new ProjectOperator(projection);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public String describe() {
|
|
|
- return "ProjectOperator[mask = " + mask + "]";
|
|
|
+ return "ProjectOperator[projection = " + projection + "]";
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Creates a project that applies the given mask (as a bitset).
|
|
|
+ * Creates an operator that applies the given projection, encoded as an integer list where
|
|
|
+ * the ordinal indicates the output order and the value, the backing channel that to be used.
|
|
|
+ * Given the input {a,b,c,d}, project {a,d,a} is encoded as {0,3,0}.
|
|
|
*
|
|
|
- * @param mask bitset mask for enabling/disabling blocks / columns inside a Page
|
|
|
+ * @param projection list of blocks to keep and their order.
|
|
|
*/
|
|
|
- public ProjectOperator(BitSet mask) {
|
|
|
- this.bs = mask;
|
|
|
+ public ProjectOperator(List<Integer> projection) {
|
|
|
+ this.pagesUsed = new HashSet<>(projection);
|
|
|
+ this.projection = projection.stream().mapToInt(Integer::intValue).toArray();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
protected Page process(Page page) {
|
|
|
- if (page.getBlockCount() == 0) {
|
|
|
+ var blockCount = page.getBlockCount();
|
|
|
+ if (blockCount == 0) {
|
|
|
return page;
|
|
|
}
|
|
|
if (blocks == null) {
|
|
|
- blocks = new Block[bs.cardinality()];
|
|
|
+ blocks = new Block[projection.length];
|
|
|
}
|
|
|
|
|
|
Arrays.fill(blocks, null);
|
|
|
int b = 0;
|
|
|
- int positionCount = page.getPositionCount();
|
|
|
+ for (int source : projection) {
|
|
|
+ if (source >= blockCount) {
|
|
|
+ throw new IllegalArgumentException(
|
|
|
+ "Cannot project block with index [" + source + "] from a page with size [" + blockCount + "]"
|
|
|
+ );
|
|
|
+ }
|
|
|
+ var block = page.getBlock(source);
|
|
|
+ blocks[b++] = block;
|
|
|
+ }
|
|
|
+ // iterate the blocks to see which one isn't used
|
|
|
List<Releasable> blocksToRelease = new ArrayList<>();
|
|
|
- for (int i = 0; i < page.getBlockCount(); i++) {
|
|
|
- var block = page.getBlock(i);
|
|
|
- if (bs.get(i)) {
|
|
|
- assertNotReleasing(blocksToRelease, block);
|
|
|
- blocks[b++] = block;
|
|
|
- } else {
|
|
|
- blocksToRelease.add(block);
|
|
|
+
|
|
|
+ for (int i = 0; i < blockCount; i++) {
|
|
|
+ boolean used = false;
|
|
|
+ var current = page.getBlock(i);
|
|
|
+ for (int j = 0; j < blocks.length; j++) {
|
|
|
+ if (current == blocks[j]) {
|
|
|
+ used = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (used == false) {
|
|
|
+ blocksToRelease.add(current);
|
|
|
}
|
|
|
}
|
|
|
Releasables.close(blocksToRelease);
|
|
|
- return new Page(positionCount, blocks);
|
|
|
+ return new Page(page.getPositionCount(), blocks);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public String toString() {
|
|
|
- return "ProjectOperator[mask = " + bs + ']';
|
|
|
+ return "ProjectOperator[projection = " + Arrays.toString(projection) + ']';
|
|
|
}
|
|
|
|
|
|
static void assertNotReleasing(List<Releasable> toRelease, Block toKeep) {
|