diff --git a/src/main/java/jp/co/yahoo/yosegi/binary/ColumnBinaryMakerConfig.java b/src/main/java/jp/co/yahoo/yosegi/binary/ColumnBinaryMakerConfig.java index 6553d6ee..f9647c9f 100644 --- a/src/main/java/jp/co/yahoo/yosegi/binary/ColumnBinaryMakerConfig.java +++ b/src/main/java/jp/co/yahoo/yosegi/binary/ColumnBinaryMakerConfig.java @@ -20,6 +20,7 @@ import jp.co.yahoo.yosegi.binary.maker.DumpSpreadColumnBinaryMaker; import jp.co.yahoo.yosegi.binary.maker.DumpUnionColumnBinaryMaker; +import jp.co.yahoo.yosegi.binary.maker.FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker; import jp.co.yahoo.yosegi.binary.maker.IColumnBinaryMaker; import jp.co.yahoo.yosegi.binary.maker.MaxLengthBasedArrayColumnBinaryMaker; import jp.co.yahoo.yosegi.binary.maker.OptimizedNullArrayDumpBooleanColumnBinaryMaker; @@ -89,7 +90,7 @@ public ColumnBinaryMakerConfig() throws IOException { spreadMakerClass = FindColumnBinaryMaker.get( DumpSpreadColumnBinaryMaker.class.getName() ); booleanMakerClass = FindColumnBinaryMaker.get( - OptimizedNullArrayDumpBooleanColumnBinaryMaker.class.getName() ); + FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker.class.getName() ); byteMakerClass = FindColumnBinaryMaker.get( OptimizedNullArrayDumpLongColumnBinaryMaker.class.getName() ); doubleMakerClass = FindColumnBinaryMaker.get( diff --git a/src/main/java/jp/co/yahoo/yosegi/binary/ColumnBinaryMakerNameShortCut.java b/src/main/java/jp/co/yahoo/yosegi/binary/ColumnBinaryMakerNameShortCut.java index 18abeda6..40aeacf6 100644 --- a/src/main/java/jp/co/yahoo/yosegi/binary/ColumnBinaryMakerNameShortCut.java +++ b/src/main/java/jp/co/yahoo/yosegi/binary/ColumnBinaryMakerNameShortCut.java @@ -104,6 +104,9 @@ public final class ColumnBinaryMakerNameShortCut { "jp.co.yahoo.yosegi.binary.maker.OptimizedNullArrayDumpBooleanColumnBinaryMaker" , "ND5" ); CLASS_NAME_PAIR.set( "jp.co.yahoo.yosegi.binary.maker.OptimizedNullArrayDumpBytesColumnBinaryMaker" , "ND6" ); + CLASS_NAME_PAIR.set( + "jp.co.yahoo.yosegi.binary.maker.FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker", + "ND7"); CLASS_NAME_PAIR.set( "jp.co.yahoo.yosegi.binary.maker.OptimizedNullArrayFloatColumnBinaryMaker" , "N1" ); diff --git a/src/main/java/jp/co/yahoo/yosegi/binary/maker/FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker.java b/src/main/java/jp/co/yahoo/yosegi/binary/maker/FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker.java new file mode 100644 index 00000000..cff660d7 --- /dev/null +++ b/src/main/java/jp/co/yahoo/yosegi/binary/maker/FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker.java @@ -0,0 +1,306 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package jp.co.yahoo.yosegi.binary.maker; + +import jp.co.yahoo.yosegi.binary.ColumnBinary; +import jp.co.yahoo.yosegi.binary.ColumnBinaryMakerConfig; +import jp.co.yahoo.yosegi.binary.ColumnBinaryMakerCustomConfigNode; +import jp.co.yahoo.yosegi.binary.CompressResultNode; +import jp.co.yahoo.yosegi.binary.maker.index.FlagBooleanIndex; +import jp.co.yahoo.yosegi.blockindex.BlockIndexNode; +import jp.co.yahoo.yosegi.blockindex.BooleanBlockIndex; +import jp.co.yahoo.yosegi.compressor.CompressResult; +import jp.co.yahoo.yosegi.compressor.FindCompressor; +import jp.co.yahoo.yosegi.compressor.ICompressor; +import jp.co.yahoo.yosegi.inmemory.IMemoryAllocator; +import jp.co.yahoo.yosegi.message.objects.BooleanObj; +import jp.co.yahoo.yosegi.message.objects.PrimitiveObject; +import jp.co.yahoo.yosegi.spread.analyzer.IColumnAnalizeResult; +import jp.co.yahoo.yosegi.spread.column.ColumnType; +import jp.co.yahoo.yosegi.spread.column.ICell; +import jp.co.yahoo.yosegi.spread.column.IColumn; +import jp.co.yahoo.yosegi.spread.column.PrimitiveCell; +import jp.co.yahoo.yosegi.spread.column.PrimitiveColumn; +import jp.co.yahoo.yosegi.util.io.nullencoder.NullBinaryEncoder; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +public class FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker + implements IColumnBinaryMaker { + + private static final BooleanObj TRUE = new BooleanObj(true); + private static final BooleanObj FALSE = new BooleanObj(false); + + // Metadata layout + // ColumnStart, nullLength + private static final int META_LENGTH = Integer.BYTES * 2; + + private static byte[] decompressBinary(final ColumnBinary columnBinary) throws IOException { + int start = columnBinary.binaryStart + BooleanBlockIndex.BitFlags.LENGTH; + int length = columnBinary.binaryLength - BooleanBlockIndex.BitFlags.LENGTH; + ICompressor compressor = FindCompressor.get(columnBinary.compressorClassName); + return compressor.decompress(columnBinary.binary, start, length); + } + + private BooleanBlockIndex.BitFlags getBitFlags(final ColumnBinary columnBinary) { + ByteBuffer wrapBuffer = + ByteBuffer.wrap( + columnBinary.binary, columnBinary.binaryStart, BooleanBlockIndex.BitFlags.LENGTH); + // bitFlags: 001:hasTrue, 010:hasFalse, 100:hasNull + byte flags = wrapBuffer.get(); + return new BooleanBlockIndex.BitFlags(flags); + } + + @Override + public ColumnBinary toBinary( + final ColumnBinaryMakerConfig commonConfig, + final ColumnBinaryMakerCustomConfigNode currentConfigNode, + final CompressResultNode compressResultNode, + final IColumn column) + throws IOException { + ColumnBinaryMakerConfig currentConfig = commonConfig; + if (currentConfigNode != null) { + currentConfig = currentConfigNode.getCurrentConfig(); + } + + boolean[] isTrueArray = new boolean[column.size()]; + int trueCount = 0; + int trueMax = 0; + int falseMax = 0; + boolean[] isNullArray = new boolean[column.size()]; + int rowCount = 0; + int nullCount = 0; + int nullMaxIndex = 0; + int notNullMaxIndex = 0; + + int startIndex = 0; + for (; startIndex < column.size(); startIndex++) { + ICell cell = column.get(startIndex); + if (cell.getType() != ColumnType.NULL) { + break; + } + } + + for (int i = startIndex, nullIndex = 0; i < column.size(); i++, nullIndex++) { + ICell cell = column.get(i); + if (cell.getType() == ColumnType.NULL) { + nullCount++; + nullMaxIndex = nullIndex; + isNullArray[nullIndex] = true; + continue; + } + isTrueArray[rowCount] = ((PrimitiveCell) cell).getRow().getBoolean(); + if (isTrueArray[rowCount]) { + trueCount++; + trueMax = rowCount; + } else { + falseMax = rowCount; + } + + notNullMaxIndex = nullIndex; + rowCount++; + } + + int nullLength = + NullBinaryEncoder.getBinarySize(nullCount, rowCount, nullMaxIndex, notNullMaxIndex); + int isTrueLength = + NullBinaryEncoder.getBinarySize(trueCount, rowCount - trueCount, trueMax, falseMax); + + int binaryLength = META_LENGTH + nullLength + isTrueLength; + byte[] binaryRaw = new byte[binaryLength]; + ByteBuffer wrapBuffer = ByteBuffer.wrap(binaryRaw, 0, binaryRaw.length); + wrapBuffer.putInt(startIndex); + wrapBuffer.putInt(nullLength); + + NullBinaryEncoder.toBinary( + binaryRaw, + META_LENGTH, + nullLength, + isNullArray, + nullCount, + rowCount, + nullMaxIndex, + notNullMaxIndex); + + NullBinaryEncoder.toBinary( + binaryRaw, + META_LENGTH + nullLength, + isTrueLength, + isTrueArray, + trueCount, + rowCount - trueCount, + trueMax, + falseMax); + + CompressResult compressResult = + compressResultNode.getCompressResult( + this.getClass().getName(), + "c0", + currentConfig.compressionPolicy, + currentConfig.allowedRatio); + byte[] compressBinary = + currentConfig.compressorClass.compress(binaryRaw, 0, binaryRaw.length, compressResult); + + byte[] binary = new byte[BooleanBlockIndex.BitFlags.LENGTH + compressBinary.length]; + wrapBuffer = ByteBuffer.wrap(binary, 0, binary.length); + + BooleanBlockIndex.BitFlags bitFlags = + new BooleanBlockIndex.BitFlags( + trueCount > 0, (rowCount - trueCount) > 0, startIndex > 0 || nullCount > 0); + wrapBuffer.put(bitFlags.getBitFlags()); + wrapBuffer.put(compressBinary); + + return new ColumnBinary( + this.getClass().getName(), + currentConfig.compressorClass.getClass().getName(), + column.getColumnName(), + ColumnType.BOOLEAN, + column.size(), + binaryRaw.length, + Byte.BYTES * rowCount, + -1, + binary, + 0, + binary.length, + null); + } + + @Override + public int calcBinarySize(final IColumnAnalizeResult analizeResult) { + return analizeResult.getColumnSize(); + } + + @Override + public IColumn toColumn(final ColumnBinary columnBinary) throws IOException { + BooleanBlockIndex.BitFlags bitFlags = getBitFlags(columnBinary); + + return new HeaderIndexLazyColumn( + columnBinary.columnName, + columnBinary.columnType, + new BooleanColumnManager(columnBinary), + new FlagBooleanIndex(bitFlags.hasTrue(), bitFlags.hasFalse(), bitFlags.hasNull())); + } + + @Override + public void loadInMemoryStorage(final ColumnBinary columnBinary, final IMemoryAllocator allocator) + throws IOException { + byte[] binary = + FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker.decompressBinary(columnBinary); + ByteBuffer wrapBuffer = ByteBuffer.wrap(binary, 0, binary.length); + int startIndex = wrapBuffer.getInt(); + int nullLength = wrapBuffer.getInt(); + int isTrueLength = binary.length - META_LENGTH - nullLength; + + boolean[] isNullArray = NullBinaryEncoder.toIsNullArray(binary, META_LENGTH, nullLength); + + allocator.setValueCount(startIndex + isNullArray.length); + + boolean[] isTrueArray = + NullBinaryEncoder.toIsNullArray(binary, META_LENGTH + nullLength, isTrueLength); + + for (int i = 0; i < startIndex; i++) { + allocator.setNull(i); + } + int isTrueIndex = 0; + for (int i = 0; i < isNullArray.length; i++) { + if (isNullArray[i]) { + allocator.setNull(i + startIndex); + } else { + allocator.setBoolean(i + startIndex, isTrueArray[isTrueIndex]); + isTrueIndex++; + } + } + } + + @Override + public void setBlockIndexNode( + final BlockIndexNode parentNode, final ColumnBinary columnBinary, final int spreadIndex) + throws IOException { + BooleanBlockIndex.BitFlags bitFlags = getBitFlags(columnBinary); + + BlockIndexNode currentNode = parentNode.getChildNode(columnBinary.columnName); + currentNode.setBlockIndex( + new BooleanBlockIndex(bitFlags.hasTrue(), bitFlags.hasFalse(), bitFlags.hasNull())); + } + + public class BooleanColumnManager implements IColumnManager { + + private final ColumnBinary columnBinary; + private PrimitiveColumn column; + private boolean isCreate; + + public BooleanColumnManager(final ColumnBinary columnBinary) throws IOException { + this.columnBinary = columnBinary; + } + + private void create() throws IOException { + if (isCreate) { + return; + } + byte[] binary = + FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker.decompressBinary(columnBinary); + ByteBuffer wrapBuffer = ByteBuffer.wrap(binary, 0, binary.length); + int startIndex = wrapBuffer.getInt(); + int nullLength = wrapBuffer.getInt(); + int isTrueLength = binary.length - META_LENGTH - nullLength; + + boolean[] isNullArray = NullBinaryEncoder.toIsNullArray(binary, META_LENGTH, nullLength); + + boolean[] isTrueArray = + NullBinaryEncoder.toIsNullArray(binary, META_LENGTH + nullLength, isTrueLength); + + PrimitiveObject[] valueArray = new PrimitiveObject[isNullArray.length]; + + int isTrueIndex = 0; + for (int i = 0; i < isNullArray.length; i++) { + if (!isNullArray[i]) { + valueArray[i] = (isTrueArray[isTrueIndex]) ? TRUE : FALSE; + isTrueIndex++; + } + } + + column = new PrimitiveColumn(columnBinary.columnType, columnBinary.columnName); + column.setCellManager( + new OptimizedNullArrayCellManager(columnBinary.columnType, startIndex, valueArray)); + + isCreate = true; + } + + @Override + public IColumn get() { + try { + create(); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + return column; + } + + @Override + public List getColumnKeys() { + return new ArrayList(); + } + + @Override + public int getColumnSize() { + return 0; + } + } +} diff --git a/src/main/java/jp/co/yahoo/yosegi/binary/maker/index/FlagBooleanIndex.java b/src/main/java/jp/co/yahoo/yosegi/binary/maker/index/FlagBooleanIndex.java new file mode 100644 index 00000000..8ca14774 --- /dev/null +++ b/src/main/java/jp/co/yahoo/yosegi/binary/maker/index/FlagBooleanIndex.java @@ -0,0 +1,61 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package jp.co.yahoo.yosegi.binary.maker.index; + +import jp.co.yahoo.yosegi.spread.column.filter.BooleanFilter; +import jp.co.yahoo.yosegi.spread.column.filter.FilterType; +import jp.co.yahoo.yosegi.spread.column.filter.IFilter; +import jp.co.yahoo.yosegi.spread.column.index.ICellIndex; + +import java.io.IOException; + +public class FlagBooleanIndex implements ICellIndex { + + private final boolean hasTrue; + private final boolean hasFalse; + private final boolean hasNull; + + /** + * FlagBooleanIndex constructor. + * @param hasTrue true when true values exist. + * @param hasFalse true when false values exist. + * @param hasNull true when null values exist. + */ + public FlagBooleanIndex(final boolean hasTrue, final boolean hasFalse, final boolean hasNull) { + this.hasTrue = hasTrue; + this.hasFalse = hasFalse; + this.hasNull = hasNull; + } + + @Override + public boolean[] filter(final IFilter filter, final boolean[] filterArray) throws IOException { + if (filter.getFilterType() == FilterType.BOOLEAN) { + BooleanFilter booleanFilter = (BooleanFilter) filter; + // NOTE: return filterArray filled with false when there is no target value + if (booleanFilter.getFlag()) { + if (!hasTrue) { + return filterArray; + } + } else { + if (!hasFalse) { + return filterArray; + } + } + } + // NOTE: return null when there are some target values + return null; + } +} diff --git a/src/main/java/jp/co/yahoo/yosegi/blockindex/RangeBlockIndexNameShortCut.java b/src/main/java/jp/co/yahoo/yosegi/blockindex/BlockIndexNameShortCut.java similarity index 92% rename from src/main/java/jp/co/yahoo/yosegi/blockindex/RangeBlockIndexNameShortCut.java rename to src/main/java/jp/co/yahoo/yosegi/blockindex/BlockIndexNameShortCut.java index cfd903c8..77507ad5 100644 --- a/src/main/java/jp/co/yahoo/yosegi/blockindex/RangeBlockIndexNameShortCut.java +++ b/src/main/java/jp/co/yahoo/yosegi/blockindex/BlockIndexNameShortCut.java @@ -20,7 +20,7 @@ import jp.co.yahoo.yosegi.util.Pair; -public final class RangeBlockIndexNameShortCut { +public final class BlockIndexNameShortCut { private static final Pair CLASS_NAME_PAIR = new Pair(); @@ -34,9 +34,11 @@ public final class RangeBlockIndexNameShortCut { CLASS_NAME_PAIR.set( "jp.co.yahoo.yosegi.blockindex.StringRangeBlockIndex" , "R6" ); CLASS_NAME_PAIR.set( "jp.co.yahoo.yosegi.blockindex.FullRangeBlockIndex" , "FR0" ); + + CLASS_NAME_PAIR.set( "jp.co.yahoo.yosegi.blockindex.BooleanBlockIndex" , "BI0" ); } - private RangeBlockIndexNameShortCut() {} + private BlockIndexNameShortCut() {} /** * Get the shortcut name from the class name. diff --git a/src/main/java/jp/co/yahoo/yosegi/blockindex/BlockIndexNode.java b/src/main/java/jp/co/yahoo/yosegi/blockindex/BlockIndexNode.java index 0fae5be4..b8efb24d 100644 --- a/src/main/java/jp/co/yahoo/yosegi/blockindex/BlockIndexNode.java +++ b/src/main/java/jp/co/yahoo/yosegi/blockindex/BlockIndexNode.java @@ -120,7 +120,7 @@ public int getBinarySize() throws IOException { length += Integer.BYTES; if ( blockIndex != null ) { length += Integer.BYTES; - length += RangeBlockIndexNameShortCut.getShortCutName( + length += BlockIndexNameShortCut.getShortCutName( blockIndex.getClass().getName() ).getBytes( "UTF-8" ).length; length += Integer.BYTES; length += blockIndex.getBinarySize(); @@ -154,13 +154,13 @@ public int toBinary( final byte[] buffer , final int start ) throws IOException } else { wrapBuffer.putInt( offset , 1 ); offset += Integer.BYTES; - byte[] rangeClassNameBytes = RangeBlockIndexNameShortCut.getShortCutName( + byte[] blockIndexClassNameBytes = BlockIndexNameShortCut.getShortCutName( blockIndex.getClass().getName() ).getBytes( "UTF-8" ); - wrapBuffer.putInt( offset , rangeClassNameBytes.length ); + wrapBuffer.putInt( offset , blockIndexClassNameBytes.length ); offset += Integer.BYTES; wrapBuffer.position( offset ); - wrapBuffer.put( rangeClassNameBytes ); - offset += rangeClassNameBytes.length; + wrapBuffer.put( blockIndexClassNameBytes ); + offset += blockIndexClassNameBytes.length; byte[] indexBinary = blockIndex.toBinary(); wrapBuffer.putInt( offset , indexBinary.length ); offset += Integer.BYTES; @@ -228,7 +228,7 @@ public static BlockIndexNode createFromBinary( wrapBuffer.get( indexBinary , 0 , indexBinaryLength ); offset += indexBinaryLength; IBlockIndex blockIndex = FindBlockIndex.get( - RangeBlockIndexNameShortCut.getClassName( new String( classNameBytes , "UTF-8" ) ) ); + BlockIndexNameShortCut.getClassName( new String( classNameBytes , "UTF-8" ) ) ); blockIndex.setFromBinary( indexBinary , 0 , indexBinary.length ); result.setBlockIndex( blockIndex ); } diff --git a/src/main/java/jp/co/yahoo/yosegi/blockindex/BlockIndexType.java b/src/main/java/jp/co/yahoo/yosegi/blockindex/BlockIndexType.java index a6e38b00..4626546c 100644 --- a/src/main/java/jp/co/yahoo/yosegi/blockindex/BlockIndexType.java +++ b/src/main/java/jp/co/yahoo/yosegi/blockindex/BlockIndexType.java @@ -30,5 +30,6 @@ public enum BlockIndexType { RANGE_LONG, RANGE_FLOAT, RANGE_DOUBLE, + BOOLEAN, } diff --git a/src/main/java/jp/co/yahoo/yosegi/blockindex/BooleanBlockIndex.java b/src/main/java/jp/co/yahoo/yosegi/blockindex/BooleanBlockIndex.java new file mode 100644 index 00000000..3c46b690 --- /dev/null +++ b/src/main/java/jp/co/yahoo/yosegi/blockindex/BooleanBlockIndex.java @@ -0,0 +1,203 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package jp.co.yahoo.yosegi.blockindex; + +import jp.co.yahoo.yosegi.spread.column.filter.BooleanFilter; +import jp.co.yahoo.yosegi.spread.column.filter.FilterType; +import jp.co.yahoo.yosegi.spread.column.filter.IFilter; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +public class BooleanBlockIndex implements IBlockIndex { + + private boolean hasTrue; + private boolean hasFalse; + private boolean hasNull; + + /** + * BooleanBlockIndex constructor. + */ + public BooleanBlockIndex() { + hasTrue = false; + hasFalse = false; + hasNull = false; + } + + /** + * BooleanBlockIndex constructor. + * @param hasTrue true when true values exist. + * @param hasFalse true when false values exist. + * @param hasNull true when null values exist. + */ + public BooleanBlockIndex(final boolean hasTrue, final boolean hasFalse, final boolean hasNull) { + this.hasTrue = hasTrue; + this.hasFalse = hasFalse; + this.hasNull = hasNull; + } + + @Override + public IBlockIndex clone() { + return new BooleanBlockIndex(hasTrue, hasFalse, hasNull); + } + + @Override + public BlockIndexType getBlockIndexType() { + return BlockIndexType.BOOLEAN; + } + + @Override + public boolean merge(final IBlockIndex blockIndex) { + if (!(blockIndex instanceof BooleanBlockIndex)) { + return false; + } + BooleanBlockIndex booleanBlockIndex = (BooleanBlockIndex) blockIndex; + if (booleanBlockIndex.hasTrue()) { + hasTrue = true; + } + if (booleanBlockIndex.hasFalse()) { + hasFalse = true; + } + if (booleanBlockIndex.hasNull()) { + hasNull = true; + } + return true; + } + + @Override + public int getBinarySize() { + return BitFlags.LENGTH; + } + + @Override + public byte[] toBinary() { + byte[] result = new byte[getBinarySize()]; + ByteBuffer wrapBuffer = ByteBuffer.wrap(result); + BitFlags bitFlags = new BitFlags(hasTrue, hasFalse, hasNull); + wrapBuffer.put(bitFlags.getBitFlags()); + return result; + } + + @Override + public void setFromBinary(final byte[] buffer, final int start, final int length) { + ByteBuffer wrapBuffer = ByteBuffer.wrap(buffer, start, length); + byte flags = wrapBuffer.get(); + BitFlags bitFlags = new BitFlags(flags); + hasTrue = bitFlags.hasTrue(); + hasFalse = bitFlags.hasFalse(); + hasNull = bitFlags.hasNull(); + } + + @Override + public List getBlockSpreadIndex(final IFilter filter) { + if (filter.getFilterType() == FilterType.BOOLEAN) { + BooleanFilter booleanFilter = (BooleanFilter) filter; + if (booleanFilter.getFlag()) { + if (!hasTrue) { + return new ArrayList(); + } + } else { + if (!hasFalse) { + return new ArrayList(); + } + } + } + return null; + } + + @Override + public IBlockIndex getNewInstance() { + return new BooleanBlockIndex(); + } + + public boolean hasTrue() { + return hasTrue; + } + + public boolean hasFalse() { + return hasFalse; + } + + public boolean hasNull() { + return hasNull; + } + + @Override + public String toString() { + return String.format( + "%s hasTrue=%b hasFalse=%b hasNull=%b", + this.getClass().getName(), hasTrue, hasFalse, hasNull); + } + + public static class BitFlags { + + public static final int LENGTH = Byte.BYTES; + + private static final byte HAS_TRUE = 1; + private static final byte HAS_FALSE = 1 << 1; + private static final byte HAS_NULL = 1 << 2; + + private boolean hasTrue; + private boolean hasFalse; + private boolean hasNull; + + // bitFlags: 001:hasTrue, 010:hasFalse, 100:hasNull + private byte bitFlags; + + /** + * BitFlags constructor. + * @param bitFlags 001:hasTrue, 010:hasFalse, 100:hasNull. + */ + public BitFlags(final byte bitFlags) { + this.bitFlags = bitFlags; + hasTrue = (bitFlags & HAS_TRUE) != 0; + hasFalse = (bitFlags & HAS_FALSE) != 0; + hasNull = (bitFlags & HAS_NULL) != 0; + } + + /** + * BitFlags constructor. + * @param hasTrue true when true values exist. + * @param hasFalse true when false values exist. + * @param hasNull true when null values exist. + */ + public BitFlags(final boolean hasTrue, final boolean hasFalse, final boolean hasNull) { + this.hasTrue = hasTrue; + this.hasFalse = hasFalse; + this.hasNull = hasNull; + bitFlags = hasTrue ? HAS_TRUE : 0; + bitFlags |= hasFalse ? HAS_FALSE : 0; + bitFlags |= hasNull ? HAS_NULL : 0; + } + + public byte getBitFlags() { + return bitFlags; + } + + public boolean hasTrue() { + return hasTrue; + } + + public boolean hasFalse() { + return hasFalse; + } + + public boolean hasNull() { + return hasNull; + } + } +} diff --git a/src/main/java/jp/co/yahoo/yosegi/blockindex/EncryptionSupportedBlockIndexNode.java b/src/main/java/jp/co/yahoo/yosegi/blockindex/EncryptionSupportedBlockIndexNode.java index caa32333..d36b1088 100644 --- a/src/main/java/jp/co/yahoo/yosegi/blockindex/EncryptionSupportedBlockIndexNode.java +++ b/src/main/java/jp/co/yahoo/yosegi/blockindex/EncryptionSupportedBlockIndexNode.java @@ -20,7 +20,6 @@ import jp.co.yahoo.yosegi.encryptor.AdditionalAuthenticationData; import jp.co.yahoo.yosegi.encryptor.EncryptionSettingNode; -import jp.co.yahoo.yosegi.encryptor.EncryptorFactoryNameShortCut; import jp.co.yahoo.yosegi.encryptor.IEncryptor; import jp.co.yahoo.yosegi.encryptor.IEncryptorFactory; import jp.co.yahoo.yosegi.encryptor.Module; @@ -94,12 +93,12 @@ public int getBinarySize( length += Integer.BYTES; } else { byte[] nodeNameBytes = nodeName.getBytes( "UTF-8" ); - byte[] rangeClassNameBytes = RangeBlockIndexNameShortCut.getShortCutName( + byte[] blockIndexClassNameBytes = BlockIndexNameShortCut.getShortCutName( blockIndex.getClass().getName() ).getBytes( "UTF-8" ); byte[] indexBinary = blockIndex.toBinary(); int newIndexBinarySize = Integer.BYTES * 3 + nodeNameBytes.length - + rangeClassNameBytes.length + + blockIndexClassNameBytes.length + indexBinary.length; length += Integer.BYTES; @@ -170,19 +169,19 @@ public int toBinary( wrapBuffer.putInt( 0 ); } else { byte[] nodeNameBinary = nodeName.getBytes( "UTF-8" ); - byte[] rangeClassNameBytes = RangeBlockIndexNameShortCut.getShortCutName( + byte[] blockIndexClassNameBytes = BlockIndexNameShortCut.getShortCutName( blockIndex.getClass().getName() ).getBytes( "UTF-8" ); byte[] indexBinary = blockIndex.toBinary(); byte[] newIndexBinary = new byte[ Integer.BYTES * 3 + nodeNameBinary.length - + rangeClassNameBytes.length + + blockIndexClassNameBytes.length + indexBinary.length ]; ByteBuffer newIndexBuffer = ByteBuffer.wrap( newIndexBinary ); newIndexBuffer.putInt( nodeNameBinary.length ); newIndexBuffer.put( nodeNameBinary ); - newIndexBuffer.putInt( rangeClassNameBytes.length ); - newIndexBuffer.put( rangeClassNameBytes ); + newIndexBuffer.putInt( blockIndexClassNameBytes.length ); + newIndexBuffer.put( blockIndexClassNameBytes ); newIndexBuffer.putInt( indexBinary.length ); newIndexBuffer.put( indexBinary ); @@ -244,7 +243,7 @@ private static IBlockIndex createBlockIndexFromByteBuffer( "UTF-8" ); decriptBuffer.position( decriptBuffer.position() + blockIndexClassNameBinaryLength ); IBlockIndex blockIndex = FindBlockIndex.get( - RangeBlockIndexNameShortCut.getClassName( blockIndexClassName ) ); + BlockIndexNameShortCut.getClassName( blockIndexClassName ) ); int indexBinaryLength = decriptBuffer.getInt(); blockIndex.setFromBinary( decriptBuffer.array() , decriptBuffer.position() , indexBinaryLength ); diff --git a/src/test/java/jp/co/yahoo/yosegi/binary/TestColumnBinaryMakerConfig.java b/src/test/java/jp/co/yahoo/yosegi/binary/TestColumnBinaryMakerConfig.java index 94db3efd..0ccdf3fa 100644 --- a/src/test/java/jp/co/yahoo/yosegi/binary/TestColumnBinaryMakerConfig.java +++ b/src/test/java/jp/co/yahoo/yosegi/binary/TestColumnBinaryMakerConfig.java @@ -39,7 +39,7 @@ private static Stream parametersForT_getColumnMaker_1(){ arguments( ColumnType.UNION , DumpUnionColumnBinaryMaker.class.getName() ), arguments( ColumnType.ARRAY , MaxLengthBasedArrayColumnBinaryMaker.class.getName() ), arguments( ColumnType.SPREAD , DumpSpreadColumnBinaryMaker.class.getName() ), - arguments( ColumnType.BOOLEAN , OptimizedNullArrayDumpBooleanColumnBinaryMaker.class.getName() ), + arguments( ColumnType.BOOLEAN , FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker.class.getName() ), arguments( ColumnType.BYTE , OptimizedNullArrayDumpLongColumnBinaryMaker.class.getName() ), arguments( ColumnType.BYTES , OptimizedNullArrayDumpBytesColumnBinaryMaker.class.getName() ), arguments( ColumnType.DOUBLE , OptimizedNullArrayDumpDoubleColumnBinaryMaker.class.getName() ), diff --git a/src/test/java/jp/co/yahoo/yosegi/binary/maker/TestFlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker.java b/src/test/java/jp/co/yahoo/yosegi/binary/maker/TestFlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker.java new file mode 100644 index 00000000..ec23190b --- /dev/null +++ b/src/test/java/jp/co/yahoo/yosegi/binary/maker/TestFlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker.java @@ -0,0 +1,269 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package jp.co.yahoo.yosegi.binary.maker; + +import jp.co.yahoo.yosegi.binary.ColumnBinary; +import jp.co.yahoo.yosegi.binary.ColumnBinaryMakerConfig; +import jp.co.yahoo.yosegi.binary.CompressResultNode; +import jp.co.yahoo.yosegi.blockindex.BlockIndexNode; +import jp.co.yahoo.yosegi.blockindex.BooleanBlockIndex; +import jp.co.yahoo.yosegi.inmemory.IMemoryAllocator; +import jp.co.yahoo.yosegi.message.objects.BooleanObj; +import jp.co.yahoo.yosegi.message.objects.PrimitiveObject; +import jp.co.yahoo.yosegi.spread.analyzer.BooleanColumnAnalizeResult; +import jp.co.yahoo.yosegi.spread.analyzer.BooleanColumnAnalizer; +import jp.co.yahoo.yosegi.spread.analyzer.IColumnAnalizeResult; +import jp.co.yahoo.yosegi.spread.analyzer.IColumnAnalizer; +import jp.co.yahoo.yosegi.spread.column.ColumnType; +import jp.co.yahoo.yosegi.spread.column.IColumn; +import jp.co.yahoo.yosegi.spread.column.PrimitiveColumn; +import jp.co.yahoo.yosegi.spread.column.filter.BooleanFilter; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +class TestFlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker { + + private IColumn makeColumn(final String columnName, Boolean[] values) throws IOException { + IColumn column = new PrimitiveColumn(ColumnType.BOOLEAN, columnName); + for (int i = 0; i < values.length; i++) { + if (values[i] != null) { + column.add(ColumnType.BOOLEAN, new BooleanObj(values[i]), i); + } + } + return column; + } + + private ColumnBinary makeColumnBinary(final String columnName, Boolean[] values) + throws IOException { + IColumn column = makeColumn(columnName, values); + FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker maker = + new FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker(); + ColumnBinaryMakerConfig defaultConfig = new ColumnBinaryMakerConfig(); + ColumnBinary columnBinary = + maker.toBinary(defaultConfig, null, new CompressResultNode(), column); + return columnBinary; + } + + private class TestMemorayAllocator implements IMemoryAllocator { + public final Map map; + + public TestMemorayAllocator() { + map = new HashMap<>(); + } + + @Override + public void setNull(final int index) { + map.put(index, null); + } + + @Override + public void setBoolean(final int index, final boolean value) { + map.put(index, value); + } + } + + public static Stream D_toBinary_1() { + return Stream.of( + // columnName, columnValues + arguments("TEST1", new Boolean[] {true}), + arguments("TEST1", new Boolean[] {false}), + arguments("TEST1", new Boolean[] {null, true}), + arguments("TEST1", new Boolean[] {null, false}), + arguments("TEST1", new Boolean[] {true, false}), + arguments("TEST1", new Boolean[] {false, true}), + arguments("TEST1", new Boolean[] {true, null, false})); + } + + @ParameterizedTest + @MethodSource("D_toBinary_1") + public void T_toBinary_1(final String columnName, final Boolean[] columnValues) + throws IOException { + ColumnBinary columnBinary = makeColumnBinary(columnName, columnValues); + assertEquals(columnName, columnBinary.columnName); + assertEquals(ColumnType.BOOLEAN, columnBinary.columnType); + assertEquals(columnValues.length, columnBinary.rowCount); + assertEquals( + FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker.class.getName(), + columnBinary.makerClassName); + } + + public static Stream D_calcBinarySize_1() { + return Stream.of( + // columnName, columnValues + arguments("TEST1", new Boolean[] {true}, 1), + arguments("TEST1", new Boolean[] {false}, 1), + arguments("TEST1", new Boolean[] {null, true}, 2), + arguments("TEST1", new Boolean[] {null, false}, 2), + arguments("TEST1", new Boolean[] {true, false}, 2), + arguments("TEST1", new Boolean[] {false, true}, 2), + arguments("TEST1", new Boolean[] {true, null, false}, 3)); + } + + @ParameterizedTest + @MethodSource("D_calcBinarySize_1") + public void T_calcBinarySize_1( + final String columnName, final Boolean[] columnValues, final int expected) + throws IOException { + IColumn column = makeColumn(columnName, columnValues); + IColumnAnalizer analizer = new BooleanColumnAnalizer(column); + IColumnAnalizeResult analizeResult = analizer.analize(); + FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker maker = + new FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker(); + assertEquals(expected, maker.calcBinarySize(analizeResult)); + } + + public static Stream D_toColumn_1() { + return Stream.of( + // columnName, columnValues + arguments("TEST1", new Boolean[] {true}), + arguments("TEST1", new Boolean[] {false}), + arguments("TEST1", new Boolean[] {null, true}), + arguments("TEST1", new Boolean[] {null, false}), + arguments("TEST1", new Boolean[] {true, false}), + arguments("TEST1", new Boolean[] {false, true}), + arguments("TEST1", new Boolean[] {true, null, false})); + } + + @ParameterizedTest + @MethodSource("D_toColumn_1") + public void T_toClumn_1(final String columnName, final Boolean[] columnValues) + throws IOException { + ColumnBinary columnBinary = makeColumnBinary(columnName, columnValues); + FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker maker = + new FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker(); + IColumn newColumn = maker.toColumn(columnBinary); + assertEquals(ColumnType.BOOLEAN, newColumn.getColumnType()); + assertEquals(columnName, newColumn.getColumnName()); + assertEquals(columnValues.length, newColumn.size()); + for (int i = 0; i < columnValues.length; i++) { + if (newColumn.get(i).getRow() == null) { + assertNull(newColumn.get(i).getRow()); + } else { + assertEquals(columnValues[i], ((PrimitiveObject) newColumn.get(i).getRow()).getBoolean()); + } + } + } + + public static Stream D_columnFilter_1() { + return Stream.of( + // columnName, columnValues, filterFlag, expectedNull + arguments("TEST1", new Boolean[] {true}, true, true), + arguments("TEST1", new Boolean[] {true}, false, false), + arguments("TEST1", new Boolean[] {false}, true, false), + arguments("TEST1", new Boolean[] {false}, false, true), + arguments("TEST1", new Boolean[] {null, true}, true, true), + arguments("TEST1", new Boolean[] {null, true}, false, false), + arguments("TEST1", new Boolean[] {null, false}, true, false), + arguments("TEST1", new Boolean[] {null, false}, false, true), + arguments("TEST1", new Boolean[] {true, false}, true, true), + arguments("TEST1", new Boolean[] {true, false}, false, true), + arguments("TEST1", new Boolean[] {false, true}, true, true), + arguments("TEST1", new Boolean[] {false, true}, false, true), + arguments("TEST1", new Boolean[] {true, null, false}, true, true), + arguments("TEST1", new Boolean[] {true, null, false}, false, true)); + } + + @ParameterizedTest + @MethodSource("D_columnFilter_1") + public void T_columnFilter_1( + final String columnName, + final Boolean[] columnValues, + final boolean filterFlag, + final boolean expectedNull) + throws IOException { + ColumnBinary columnBinary = makeColumnBinary(columnName, columnValues); + FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker maker = + new FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker(); + IColumn newColumn = maker.toColumn(columnBinary); + boolean[] bArray = new boolean[newColumn.size()]; + if (expectedNull) { + assertNull(newColumn.filter(new BooleanFilter(filterFlag), bArray)); + } else { + boolean[] expected = new boolean[newColumn.size()]; + assertArrayEquals(expected, newColumn.filter(new BooleanFilter(filterFlag), bArray)); + } + } + + public static Stream D_loadInMemoryStorage_1() { + return Stream.of( + // columnName, columnValues + arguments("TEST1", new Boolean[] {true}), + arguments("TEST1", new Boolean[] {false}), + arguments("TEST1", new Boolean[] {null, true}), + arguments("TEST1", new Boolean[] {null, false}), + arguments("TEST1", new Boolean[] {true, false}), + arguments("TEST1", new Boolean[] {false, true}), + arguments("TEST1", new Boolean[] {true, null, false})); + } + + @ParameterizedTest + @MethodSource("D_loadInMemoryStorage_1") + public void T_loadInMemoryStorage_1(final String columnName, final Boolean[] columnValues) + throws IOException { + ColumnBinary columnBinary = makeColumnBinary(columnName, columnValues); + FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker maker = + new FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker(); + TestMemorayAllocator allocator = new TestMemorayAllocator(); + maker.loadInMemoryStorage(columnBinary, allocator); + for (int i = 0; i < columnValues.length; i++) { + assertEquals(columnValues[i], allocator.map.get(i)); + } + } + + public static Stream D_setBlockIndexNode_1() { + return Stream.of( + // columnName, columnValues, expectedHasTrue, expectedHasFalse, expectedHasNull + arguments("TEST1", new Boolean[] {true}, true, false, false), + arguments("TEST1", new Boolean[] {false}, false, true, false), + arguments("TEST1", new Boolean[] {null, true}, true, false, true), + arguments("TEST1", new Boolean[] {null, false}, false, true, true), + arguments("TEST1", new Boolean[] {true, false}, true, true, false), + arguments("TEST1", new Boolean[] {false, true}, true, true, false), + arguments("TEST1", new Boolean[] {true, null, false}, true, true, true)); + } + + @ParameterizedTest + @MethodSource("D_setBlockIndexNode_1") + public void T_setBlockIndexNode_1( + final String columnName, + final Boolean[] columnValues, + final boolean expectedHasTrue, + final boolean expectedHasFalse, + final boolean expectedHasNull) + throws IOException { + ColumnBinary columnBinary = makeColumnBinary(columnName, columnValues); + FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker maker = + new FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker(); + BlockIndexNode parentNode = new BlockIndexNode(); + maker.setBlockIndexNode(parentNode, columnBinary, 0); + BlockIndexNode currentNode = parentNode.getChildNode(columnBinary.columnName); + BooleanBlockIndex blockIndex = (BooleanBlockIndex) currentNode.getBlockIndex(); + assertEquals(expectedHasTrue, blockIndex.hasTrue()); + assertEquals(expectedHasFalse, blockIndex.hasFalse()); + assertEquals(expectedHasNull, blockIndex.hasNull()); + } +} diff --git a/src/test/java/jp/co/yahoo/yosegi/binary/maker/index/TestFlagBooleanIndex.java b/src/test/java/jp/co/yahoo/yosegi/binary/maker/index/TestFlagBooleanIndex.java new file mode 100644 index 00000000..42222e9d --- /dev/null +++ b/src/test/java/jp/co/yahoo/yosegi/binary/maker/index/TestFlagBooleanIndex.java @@ -0,0 +1,66 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package jp.co.yahoo.yosegi.binary.maker.index; + +import jp.co.yahoo.yosegi.spread.column.filter.BooleanFilter; +import jp.co.yahoo.yosegi.spread.column.filter.IFilter; +import jp.co.yahoo.yosegi.spread.column.index.ICellIndex; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.IOException; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +class TestFlagBooleanIndex { + + private static final boolean[] dummy = new boolean[0]; + + public static Stream D_filter_1() { + return Stream.of( + arguments(new FlagBooleanIndex(true, false, false), new BooleanFilter(true), null), + arguments(new FlagBooleanIndex(true, false, false), new BooleanFilter(false), dummy), + arguments(new FlagBooleanIndex(true, true, false), new BooleanFilter(true), null), + arguments(new FlagBooleanIndex(true, true, false), new BooleanFilter(false), null), + arguments(new FlagBooleanIndex(true, false, true), new BooleanFilter(true), null), + arguments(new FlagBooleanIndex(true, false, true), new BooleanFilter(false), dummy), + arguments(new FlagBooleanIndex(true, true, true), new BooleanFilter(true), null), + arguments(new FlagBooleanIndex(true, true, true), new BooleanFilter(false), null), + arguments(new FlagBooleanIndex(false, true, false), new BooleanFilter(true), dummy), + arguments(new FlagBooleanIndex(false, true, false), new BooleanFilter(false), null), + arguments(new FlagBooleanIndex(false, false, true), new BooleanFilter(true), dummy), + arguments(new FlagBooleanIndex(false, false, true), new BooleanFilter(false), dummy), + arguments(new FlagBooleanIndex(false, true, true), new BooleanFilter(true), dummy), + arguments(new FlagBooleanIndex(false, true, true), new BooleanFilter(false), null), + arguments(new FlagBooleanIndex(false, false, false), new BooleanFilter(true), dummy), + arguments(new FlagBooleanIndex(false, false, false), new BooleanFilter(false), dummy)); + } + + @ParameterizedTest + @MethodSource("D_filter_1") + public void T_filter_1(final ICellIndex cellIndex, final IFilter filter, final boolean[] result) + throws IOException { + boolean[] r = cellIndex.filter(filter, new boolean[0]); + if (result == null) { + assertNull(r); + } else { + assertEquals(result.length, r.length); + } + } +} diff --git a/src/test/java/jp/co/yahoo/yosegi/blackbox/TestBooleanBlockIndex.java b/src/test/java/jp/co/yahoo/yosegi/blackbox/TestBooleanBlockIndex.java new file mode 100644 index 00000000..89d7e6e1 --- /dev/null +++ b/src/test/java/jp/co/yahoo/yosegi/blackbox/TestBooleanBlockIndex.java @@ -0,0 +1,636 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package jp.co.yahoo.yosegi.blackbox; + +import javafx.util.Pair; +import jp.co.yahoo.yosegi.config.Configuration; +import jp.co.yahoo.yosegi.message.objects.PrimitiveObject; +import jp.co.yahoo.yosegi.message.parser.IParser; +import jp.co.yahoo.yosegi.message.parser.json.JacksonMessageReader; +import jp.co.yahoo.yosegi.reader.YosegiReader; +import jp.co.yahoo.yosegi.spread.Spread; +import jp.co.yahoo.yosegi.spread.column.ColumnType; +import jp.co.yahoo.yosegi.spread.column.IColumn; +import jp.co.yahoo.yosegi.spread.column.filter.BooleanFilter; +import jp.co.yahoo.yosegi.spread.expression.AndExpressionNode; +import jp.co.yahoo.yosegi.spread.expression.ExecuterNode; +import jp.co.yahoo.yosegi.spread.expression.IExpressionNode; +import jp.co.yahoo.yosegi.spread.expression.NotExpressionNode; +import jp.co.yahoo.yosegi.spread.expression.OrExpressionNode; +import jp.co.yahoo.yosegi.spread.expression.StringExtractNode; +import jp.co.yahoo.yosegi.writer.YosegiWriter; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +public class TestBooleanBlockIndex { + + /* + col1: true,true,true + col2: false,false,false + col3: true,true,false + col4: true,false,true + col5: false,false,true + col6: false,true,false + col7: true,(null),(null) + col8: false,(null),(null) + col9: true,true,(null) + col10: true,false,(null) + col11: false,false,(null) + col12: false, true,(null) + col13: true,(null),true + col14: true,(null),false + col15: false,(null),false + col16: false,(null),true + */ + public byte[] getData() throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Configuration writerConfig = new Configuration(); + + JacksonMessageReader messageReader = new JacksonMessageReader(); + BufferedReader jsonIn = + new BufferedReader( + new InputStreamReader( + this.getClass() + .getClassLoader() + .getResource("blackbox/TestBooleanBlockIndex.json") + .openStream())); + String line = jsonIn.readLine(); + Spread writeSpread = new Spread(); + try (YosegiWriter writer = new YosegiWriter(out, writerConfig)) { + while (line != null) { + IParser parser = messageReader.create(line); + writeSpread.addParserRow(parser); + line = jsonIn.readLine(); + } + writer.append(writeSpread); + } + + return out.toByteArray(); + } + + public static Stream D_blackbox_1() { + return Stream.of( + // list{pair, pair ...} + arguments( + new ArrayList<>( + Arrays.asList( + new Pair<>("col1", new ArrayList<>(Arrays.asList(true, true, true))), + new Pair<>("col2", new ArrayList<>(Arrays.asList(false, false, false))), + new Pair<>("col3", new ArrayList<>(Arrays.asList(true, true, false))), + new Pair<>("col4", new ArrayList<>(Arrays.asList(true, false, true))), + new Pair<>("col5", new ArrayList<>(Arrays.asList(false, false, true))), + new Pair<>("col6", new ArrayList<>(Arrays.asList(false, true, false))), + new Pair<>("col7", new ArrayList<>(Arrays.asList(true, null, null))), + new Pair<>("col8", new ArrayList<>(Arrays.asList(false, null, null))), + new Pair<>("col9", new ArrayList<>(Arrays.asList(true, true, null))), + new Pair<>("col10", new ArrayList<>(Arrays.asList(true, false, null))), + new Pair<>("col11", new ArrayList<>(Arrays.asList(false, false, null))), + new Pair<>("col12", new ArrayList<>(Arrays.asList(false, true, null))), + new Pair<>("col13", new ArrayList<>(Arrays.asList(true, null, true))), + new Pair<>("col14", new ArrayList<>(Arrays.asList(true, null, false))), + new Pair<>("col15", new ArrayList<>(Arrays.asList(false, null, false))), + new Pair<>("col16", new ArrayList<>(Arrays.asList(false, null, true))))))); + } + + @ParameterizedTest + @MethodSource("D_blackbox_1") + public void T_blackbox_1(final List>> expecteds) throws IOException { + try (YosegiReader reader = new YosegiReader()) { + Configuration readerConfig = new Configuration(); + byte[] data = getData(); + InputStream in = new ByteArrayInputStream(data); + reader.setNewStream(in, data.length, readerConfig); + while (reader.hasNext()) { + Spread spread = reader.next(); + for (Pair> expected : expecteds) { + IColumn col = spread.getColumn(expected.getKey()); + for (int i = 0; i < spread.size(); i++) { + List expectedValues = expected.getValue(); + if (expectedValues.get(i) == null) { + assertEquals(ColumnType.NULL, col.get(i).getType()); + } else { + assertEquals( + expectedValues.get(i), ((PrimitiveObject) col.get(i).getRow()).getBoolean()); + } + } + } + } + } + } + + public static Stream D_blackbox_2() { + return Stream.of( + // columnName, filterValue, expected(hasNext) + arguments("col1", true, true), + arguments("col1", false, false), + arguments("col2", true, false), + arguments("col2", false, true), + arguments("col3", true, true), + arguments("col3", false, true), + arguments("col4", true, true), + arguments("col4", false, true), + arguments("col5", true, true), + arguments("col6", false, true), + arguments("col7", true, true), + arguments("col7", false, false), + arguments("col8", true, false), + arguments("col8", false, true), + arguments("col9", true, true), + arguments("col9", false, false), + arguments("col10", true, true), + arguments("col10", false, true), + arguments("col11", true, false), + arguments("col11", false, true), + arguments("col12", true, true), + arguments("col12", false, true), + arguments("col13", true, true), + arguments("col13", false, false), + arguments("col14", true, true), + arguments("col14", false, true), + arguments("col15", true, false), + arguments("col15", false, true), + arguments("col16", true, true), + arguments("col16", false, true)); + } + + @ParameterizedTest + @MethodSource("D_blackbox_2") + public void T_blackbox_2( + final String columnName, final Boolean filterValue, final Boolean expected) + throws IOException { + try (YosegiReader reader = new YosegiReader()) { + IExpressionNode index = new AndExpressionNode(); + index.addChildNode( + new ExecuterNode(new StringExtractNode(columnName), new BooleanFilter(filterValue))); + Configuration readerConfig = new Configuration(); + byte[] data = getData(); + InputStream in = new ByteArrayInputStream(data); + reader.setBlockSkipIndex(index); + reader.setNewStream(in, data.length, readerConfig); + assertEquals(expected, reader.hasNext()); + } + } + + public static Stream D_blackbox_3() { + return Stream.of( + // columnName1, filterValue1, columnName2, filterValue2, expected(hasNext) + arguments("col1", true, "col2", true, false), + arguments("col1", true, "col2", false, true), + arguments("col1", false, "col2", false, false), + arguments("col1", false, "col2", true, false)); + } + + @ParameterizedTest + @MethodSource("D_blackbox_3") + public void T_blackbox_3( + final String columnName1, + final Boolean filterValue1, + final String columnName2, + final Boolean filterValue2, + final Boolean expected) + throws IOException { + try (YosegiReader reader = new YosegiReader()) { + IExpressionNode index = new AndExpressionNode(); + index.addChildNode( + new ExecuterNode(new StringExtractNode(columnName1), new BooleanFilter(filterValue1))); + index.addChildNode( + new ExecuterNode(new StringExtractNode(columnName2), new BooleanFilter(filterValue2))); + Configuration readerConfig = new Configuration(); + byte[] data = getData(); + InputStream in = new ByteArrayInputStream(data); + reader.setBlockSkipIndex(index); + reader.setNewStream(in, data.length, readerConfig); + assertEquals(expected, reader.hasNext()); + } + } + + public static Stream D_blackbox_4() { + return Stream.of( + // columnName1, filterValue1, columnName2, filterValue2, list{pair, pair ...} + arguments( + "col1", + true, + "col2", + false, + new ArrayList<>( + Arrays.asList( + new Pair<>("col1", new ArrayList<>(Arrays.asList(true, true, true))), + new Pair<>("col2", new ArrayList<>(Arrays.asList(false, false, false))), + new Pair<>("col3", new ArrayList<>(Arrays.asList(true, true, false))), + new Pair<>("col4", new ArrayList<>(Arrays.asList(true, false, true))), + new Pair<>("col5", new ArrayList<>(Arrays.asList(false, false, true))), + new Pair<>("col6", new ArrayList<>(Arrays.asList(false, true, false))), + new Pair<>("col7", new ArrayList<>(Arrays.asList(true, null, null))), + new Pair<>("col8", new ArrayList<>(Arrays.asList(false, null, null))), + new Pair<>("col9", new ArrayList<>(Arrays.asList(true, true, null))), + new Pair<>("col10", new ArrayList<>(Arrays.asList(true, false, null))), + new Pair<>("col11", new ArrayList<>(Arrays.asList(false, false, null))), + new Pair<>("col12", new ArrayList<>(Arrays.asList(false, true, null))), + new Pair<>("col13", new ArrayList<>(Arrays.asList(true, null, true))), + new Pair<>("col14", new ArrayList<>(Arrays.asList(true, null, false))), + new Pair<>("col15", new ArrayList<>(Arrays.asList(false, null, false))), + new Pair<>("col16", new ArrayList<>(Arrays.asList(false, null, true))))))); + } + + @ParameterizedTest + @MethodSource("D_blackbox_4") + public void T_blackbox_4( + final String columnName1, + final Boolean filterValue1, + final String columnName2, + final Boolean filterValue2, + List>> expecteds) + throws IOException { + try (YosegiReader reader = new YosegiReader()) { + IExpressionNode index = new AndExpressionNode(); + index.addChildNode( + new ExecuterNode(new StringExtractNode(columnName1), new BooleanFilter(filterValue1))); + index.addChildNode( + new ExecuterNode(new StringExtractNode(columnName2), new BooleanFilter(filterValue2))); + Configuration readerConfig = new Configuration(); + byte[] data = getData(); + InputStream in = new ByteArrayInputStream(data); + reader.setBlockSkipIndex(index); + reader.setNewStream(in, data.length, readerConfig); + while (reader.hasNext()) { + Spread spread = reader.next(); + for (Pair> expected : expecteds) { + IColumn col = spread.getColumn(expected.getKey()); + for (int i = 0; i < spread.size(); i++) { + List expectedValues = expected.getValue(); + if (expectedValues.get(i) == null) { + assertEquals(ColumnType.NULL, col.get(i).getType()); + } else { + assertEquals( + expectedValues.get(i), ((PrimitiveObject) col.get(i).getRow()).getBoolean()); + } + } + } + } + } + } + + public static Stream D_blackbox_5() { + return Stream.of( + // columnName, filterValue, expected(hasNext) + arguments("col1", true, true), + arguments("col1", false, false), + arguments("col2", true, false), + arguments("col2", false, true), + arguments("col3", true, true), + arguments("col3", false, true), + arguments("col4", true, true), + arguments("col4", false, true), + arguments("col5", true, true), + arguments("col6", false, true), + arguments("col7", true, true), + arguments("col7", false, false), + arguments("col8", true, false), + arguments("col8", false, true), + arguments("col9", true, true), + arguments("col9", false, false), + arguments("col10", true, true), + arguments("col10", false, true), + arguments("col11", true, false), + arguments("col11", false, true), + arguments("col12", true, true), + arguments("col12", false, true), + arguments("col13", true, true), + arguments("col13", false, false), + arguments("col14", true, true), + arguments("col14", false, true), + arguments("col15", true, false), + arguments("col15", false, true), + arguments("col16", true, true), + arguments("col16", false, true)); + } + + @ParameterizedTest + @MethodSource("D_blackbox_5") + public void T_blackbox_5( + final String columnName, final Boolean filterValue, final Boolean expected) + throws IOException { + try (YosegiReader reader = new YosegiReader()) { + IExpressionNode index = new OrExpressionNode(); + index.addChildNode( + new ExecuterNode(new StringExtractNode(columnName), new BooleanFilter(filterValue))); + Configuration readerConfig = new Configuration(); + byte[] data = getData(); + InputStream in = new ByteArrayInputStream(data); + reader.setBlockSkipIndex(index); + reader.setNewStream(in, data.length, readerConfig); + assertEquals(expected, reader.hasNext()); + } + } + + public static Stream D_blackbox_6() { + return Stream.of( + // columnName1, filterValue1, columnName2, filterValue2, expected(hasNext) + arguments("col1", true, "col2", true, true), + arguments("col1", true, "col2", false, true), + arguments("col1", false, "col2", false, true), + arguments("col1", false, "col2", true, false)); + } + + @ParameterizedTest + @MethodSource("D_blackbox_6") + public void T_blackbox_6( + final String columnName1, + final Boolean filterValue1, + final String columnName2, + final Boolean filterValue2, + final Boolean expected) + throws IOException { + try (YosegiReader reader = new YosegiReader()) { + IExpressionNode index = new OrExpressionNode(); + index.addChildNode( + new ExecuterNode(new StringExtractNode(columnName1), new BooleanFilter(filterValue1))); + index.addChildNode( + new ExecuterNode(new StringExtractNode(columnName2), new BooleanFilter(filterValue2))); + Configuration readerConfig = new Configuration(); + byte[] data = getData(); + InputStream in = new ByteArrayInputStream(data); + reader.setBlockSkipIndex(index); + reader.setNewStream(in, data.length, readerConfig); + assertEquals(expected, reader.hasNext()); + } + } + + public static Stream D_blackbox_7() { + return Stream.of( + // columnName1, filterValue1, columnName2, filterValue2, list{pair, pair ...} + arguments( + "col1", + true, + "col2", + false, + new ArrayList<>( + Arrays.asList( + new Pair<>("col1", new ArrayList<>(Arrays.asList(true, true, true))), + new Pair<>("col2", new ArrayList<>(Arrays.asList(false, false, false))), + new Pair<>("col3", new ArrayList<>(Arrays.asList(true, true, false))), + new Pair<>("col4", new ArrayList<>(Arrays.asList(true, false, true))), + new Pair<>("col5", new ArrayList<>(Arrays.asList(false, false, true))), + new Pair<>("col6", new ArrayList<>(Arrays.asList(false, true, false))), + new Pair<>("col7", new ArrayList<>(Arrays.asList(true, null, null))), + new Pair<>("col8", new ArrayList<>(Arrays.asList(false, null, null))), + new Pair<>("col9", new ArrayList<>(Arrays.asList(true, true, null))), + new Pair<>("col10", new ArrayList<>(Arrays.asList(true, false, null))), + new Pair<>("col11", new ArrayList<>(Arrays.asList(false, false, null))), + new Pair<>("col12", new ArrayList<>(Arrays.asList(false, true, null))), + new Pair<>("col13", new ArrayList<>(Arrays.asList(true, null, true))), + new Pair<>("col14", new ArrayList<>(Arrays.asList(true, null, false))), + new Pair<>("col15", new ArrayList<>(Arrays.asList(false, null, false))), + new Pair<>("col16", new ArrayList<>(Arrays.asList(false, null, true)))))), + arguments( + "col1", + true, + "col2", + true, + new ArrayList<>( + Arrays.asList( + new Pair<>("col1", new ArrayList<>(Arrays.asList(true, true, true))), + new Pair<>("col2", new ArrayList<>(Arrays.asList(false, false, false))), + new Pair<>("col3", new ArrayList<>(Arrays.asList(true, true, false))), + new Pair<>("col4", new ArrayList<>(Arrays.asList(true, false, true))), + new Pair<>("col5", new ArrayList<>(Arrays.asList(false, false, true))), + new Pair<>("col6", new ArrayList<>(Arrays.asList(false, true, false))), + new Pair<>("col7", new ArrayList<>(Arrays.asList(true, null, null))), + new Pair<>("col8", new ArrayList<>(Arrays.asList(false, null, null))), + new Pair<>("col9", new ArrayList<>(Arrays.asList(true, true, null))), + new Pair<>("col10", new ArrayList<>(Arrays.asList(true, false, null))), + new Pair<>("col11", new ArrayList<>(Arrays.asList(false, false, null))), + new Pair<>("col12", new ArrayList<>(Arrays.asList(false, true, null))), + new Pair<>("col13", new ArrayList<>(Arrays.asList(true, null, true))), + new Pair<>("col14", new ArrayList<>(Arrays.asList(true, null, false))), + new Pair<>("col15", new ArrayList<>(Arrays.asList(false, null, false))), + new Pair<>("col16", new ArrayList<>(Arrays.asList(false, null, true)))))), + arguments( + "col1", + false, + "col2", + false, + new ArrayList<>( + Arrays.asList( + new Pair<>("col1", new ArrayList<>(Arrays.asList(true, true, true))), + new Pair<>("col2", new ArrayList<>(Arrays.asList(false, false, false))), + new Pair<>("col3", new ArrayList<>(Arrays.asList(true, true, false))), + new Pair<>("col4", new ArrayList<>(Arrays.asList(true, false, true))), + new Pair<>("col5", new ArrayList<>(Arrays.asList(false, false, true))), + new Pair<>("col6", new ArrayList<>(Arrays.asList(false, true, false))), + new Pair<>("col7", new ArrayList<>(Arrays.asList(true, null, null))), + new Pair<>("col8", new ArrayList<>(Arrays.asList(false, null, null))), + new Pair<>("col9", new ArrayList<>(Arrays.asList(true, true, null))), + new Pair<>("col10", new ArrayList<>(Arrays.asList(true, false, null))), + new Pair<>("col11", new ArrayList<>(Arrays.asList(false, false, null))), + new Pair<>("col12", new ArrayList<>(Arrays.asList(false, true, null))), + new Pair<>("col13", new ArrayList<>(Arrays.asList(true, null, true))), + new Pair<>("col14", new ArrayList<>(Arrays.asList(true, null, false))), + new Pair<>("col15", new ArrayList<>(Arrays.asList(false, null, false))), + new Pair<>("col16", new ArrayList<>(Arrays.asList(false, null, true))))))); + } + + @ParameterizedTest + @MethodSource("D_blackbox_7") + public void T_blackbox_7( + final String columnName1, + final Boolean filterValue1, + final String columnName2, + final Boolean filterValue2, + List>> expecteds) + throws IOException { + try (YosegiReader reader = new YosegiReader()) { + IExpressionNode index = new OrExpressionNode(); + index.addChildNode( + new ExecuterNode(new StringExtractNode(columnName1), new BooleanFilter(filterValue1))); + index.addChildNode( + new ExecuterNode(new StringExtractNode(columnName2), new BooleanFilter(filterValue2))); + Configuration readerConfig = new Configuration(); + byte[] data = getData(); + InputStream in = new ByteArrayInputStream(data); + reader.setBlockSkipIndex(index); + reader.setNewStream(in, data.length, readerConfig); + while (reader.hasNext()) { + Spread spread = reader.next(); + for (Pair> expected : expecteds) { + IColumn col = spread.getColumn(expected.getKey()); + for (int i = 0; i < spread.size(); i++) { + List expectedValues = expected.getValue(); + if (expectedValues.get(i) == null) { + assertEquals(ColumnType.NULL, col.get(i).getType()); + } else { + assertEquals( + expectedValues.get(i), ((PrimitiveObject) col.get(i).getRow()).getBoolean()); + } + } + } + } + } + } + + public static Stream D_blackbox_8() { + return Stream.of( + // NOTE: NotExpressionNode does not work. + // columnName, filterValue, expected(hasNext) + arguments("col1", true, true), // false + arguments("col1", false, true), + arguments("col2", true, true), + arguments("col2", false, true), // false + arguments("col3", true, true), // false + arguments("col3", false, true), // false + arguments("col4", true, true), // false + arguments("col4", false, true), // false + arguments("col5", true, true), // false + arguments("col6", false, true), // false + arguments("col7", true, true), // false + arguments("col7", false, true), + arguments("col8", true, true), + arguments("col8", false, true), + arguments("col9", true, true), // false + arguments("col9", false, true), + arguments("col10", true, true), // false + arguments("col10", false, true), // false + arguments("col11", true, true), + arguments("col11", false, true), // false + arguments("col12", true, true), // false + arguments("col12", false, true), // false + arguments("col13", true, true), // false + arguments("col13", false, true), + arguments("col14", true, true), // false + arguments("col14", false, true), // false + arguments("col15", true, true), + arguments("col15", false, true), // false + arguments("col16", true, true), // false + arguments("col16", false, true)); // false + } + + @ParameterizedTest + @MethodSource("D_blackbox_8") + public void T_blackbox_8( + final String columnName, final Boolean filterValue, final Boolean expected) + throws IOException { + try (YosegiReader reader = new YosegiReader()) { + IExpressionNode index = new NotExpressionNode(); + index.addChildNode( + new ExecuterNode(new StringExtractNode(columnName), new BooleanFilter(filterValue))); + Configuration readerConfig = new Configuration(); + byte[] data = getData(); + InputStream in = new ByteArrayInputStream(data); + reader.setBlockSkipIndex(index); + reader.setNewStream(in, data.length, readerConfig); + assertEquals(expected, reader.hasNext()); + } + } + + public static Stream D_blackbox_9() { + return Stream.of( + // columnName, filterValue, list{pair, pair ...} + arguments( + "col1", + true, + new ArrayList<>( + Arrays.asList( + new Pair<>("col1", new ArrayList<>(Arrays.asList(true, true, true))), + new Pair<>("col2", new ArrayList<>(Arrays.asList(false, false, false))), + new Pair<>("col3", new ArrayList<>(Arrays.asList(true, true, false))), + new Pair<>("col4", new ArrayList<>(Arrays.asList(true, false, true))), + new Pair<>("col5", new ArrayList<>(Arrays.asList(false, false, true))), + new Pair<>("col6", new ArrayList<>(Arrays.asList(false, true, false))), + new Pair<>("col7", new ArrayList<>(Arrays.asList(true, null, null))), + new Pair<>("col8", new ArrayList<>(Arrays.asList(false, null, null))), + new Pair<>("col9", new ArrayList<>(Arrays.asList(true, true, null))), + new Pair<>("col10", new ArrayList<>(Arrays.asList(true, false, null))), + new Pair<>("col11", new ArrayList<>(Arrays.asList(false, false, null))), + new Pair<>("col12", new ArrayList<>(Arrays.asList(false, true, null))), + new Pair<>("col13", new ArrayList<>(Arrays.asList(true, null, true))), + new Pair<>("col14", new ArrayList<>(Arrays.asList(true, null, false))), + new Pair<>("col15", new ArrayList<>(Arrays.asList(false, null, false))), + new Pair<>("col16", new ArrayList<>(Arrays.asList(false, null, true)))))), + arguments( + "col1", + false, + new ArrayList<>( + Arrays.asList( + new Pair<>("col1", new ArrayList<>(Arrays.asList(true, true, true))), + new Pair<>("col2", new ArrayList<>(Arrays.asList(false, false, false))), + new Pair<>("col3", new ArrayList<>(Arrays.asList(true, true, false))), + new Pair<>("col4", new ArrayList<>(Arrays.asList(true, false, true))), + new Pair<>("col5", new ArrayList<>(Arrays.asList(false, false, true))), + new Pair<>("col6", new ArrayList<>(Arrays.asList(false, true, false))), + new Pair<>("col7", new ArrayList<>(Arrays.asList(true, null, null))), + new Pair<>("col8", new ArrayList<>(Arrays.asList(false, null, null))), + new Pair<>("col9", new ArrayList<>(Arrays.asList(true, true, null))), + new Pair<>("col10", new ArrayList<>(Arrays.asList(true, false, null))), + new Pair<>("col11", new ArrayList<>(Arrays.asList(false, false, null))), + new Pair<>("col12", new ArrayList<>(Arrays.asList(false, true, null))), + new Pair<>("col13", new ArrayList<>(Arrays.asList(true, null, true))), + new Pair<>("col14", new ArrayList<>(Arrays.asList(true, null, false))), + new Pair<>("col15", new ArrayList<>(Arrays.asList(false, null, false))), + new Pair<>("col16", new ArrayList<>(Arrays.asList(false, null, true))))))); + } + + @ParameterizedTest + @MethodSource("D_blackbox_9") + public void T_blackbox_9( + final String columnName, + final Boolean filterValue, + List>> expecteds) + throws IOException { + try (YosegiReader reader = new YosegiReader()) { + IExpressionNode index = new NotExpressionNode(); + index.addChildNode( + new ExecuterNode(new StringExtractNode(columnName), new BooleanFilter(filterValue))); + Configuration readerConfig = new Configuration(); + byte[] data = getData(); + InputStream in = new ByteArrayInputStream(data); + reader.setBlockSkipIndex(index); + reader.setNewStream(in, data.length, readerConfig); + while (reader.hasNext()) { + Spread spread = reader.next(); + for (Pair> expected : expecteds) { + IColumn col = spread.getColumn(expected.getKey()); + for (int i = 0; i < spread.size(); i++) { + List expectedValues = expected.getValue(); + if (expectedValues.get(i) == null) { + assertEquals(ColumnType.NULL, col.get(i).getType()); + } else { + assertEquals( + expectedValues.get(i), ((PrimitiveObject) col.get(i).getRow()).getBoolean()); + } + } + } + } + } + } +} diff --git a/src/test/java/jp/co/yahoo/yosegi/blackbox/TestBooleanCellIndex.java b/src/test/java/jp/co/yahoo/yosegi/blackbox/TestBooleanCellIndex.java index d2c64dd8..71dcfc2c 100644 --- a/src/test/java/jp/co/yahoo/yosegi/blackbox/TestBooleanCellIndex.java +++ b/src/test/java/jp/co/yahoo/yosegi/blackbox/TestBooleanCellIndex.java @@ -49,7 +49,8 @@ public static Stream data1() throws IOException{ return Stream.of( arguments( createBooleanTestData( "jp.co.yahoo.yosegi.binary.maker.DumpBooleanColumnBinaryMaker" ) ), arguments( createBooleanTestData( "jp.co.yahoo.yosegi.binary.maker.OptimizedNullArrayDumpBooleanColumnBinaryMaker" ) ), - arguments( createBytesTestData( "jp.co.yahoo.yosegi.binary.maker.DumpBytesColumnBinaryMaker" ) ) + arguments( createBytesTestData( "jp.co.yahoo.yosegi.binary.maker.DumpBytesColumnBinaryMaker" ) ), + arguments( createBooleanTestData( "jp.co.yahoo.yosegi.binary.maker.FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker" ) ) ); } diff --git a/src/test/java/jp/co/yahoo/yosegi/blackbox/TestBooleanPrimitiveColumn.java b/src/test/java/jp/co/yahoo/yosegi/blackbox/TestBooleanPrimitiveColumn.java index e7493511..97a96eff 100644 --- a/src/test/java/jp/co/yahoo/yosegi/blackbox/TestBooleanPrimitiveColumn.java +++ b/src/test/java/jp/co/yahoo/yosegi/blackbox/TestBooleanPrimitiveColumn.java @@ -48,7 +48,8 @@ public class TestBooleanPrimitiveColumn { public static Stream data1() throws IOException{ return Stream.of( arguments( "jp.co.yahoo.yosegi.binary.maker.DumpBooleanColumnBinaryMaker" ), - arguments( "jp.co.yahoo.yosegi.binary.maker.OptimizedNullArrayDumpBooleanColumnBinaryMaker" ) + arguments( "jp.co.yahoo.yosegi.binary.maker.OptimizedNullArrayDumpBooleanColumnBinaryMaker" ), + arguments( "jp.co.yahoo.yosegi.binary.maker.FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker" ) ); } diff --git a/src/test/java/jp/co/yahoo/yosegi/blackbox/TestCalcLogicalDataSizeBoolean.java b/src/test/java/jp/co/yahoo/yosegi/blackbox/TestCalcLogicalDataSizeBoolean.java index a4ecc0dd..37407494 100644 --- a/src/test/java/jp/co/yahoo/yosegi/blackbox/TestCalcLogicalDataSizeBoolean.java +++ b/src/test/java/jp/co/yahoo/yosegi/blackbox/TestCalcLogicalDataSizeBoolean.java @@ -39,7 +39,8 @@ public class TestCalcLogicalDataSizeBoolean { public static Stream data1() throws IOException { return Stream.of( arguments( "jp.co.yahoo.yosegi.binary.maker.DumpBooleanColumnBinaryMaker" ), - arguments( "jp.co.yahoo.yosegi.binary.maker.OptimizedNullArrayDumpBooleanColumnBinaryMaker" ) + arguments( "jp.co.yahoo.yosegi.binary.maker.OptimizedNullArrayDumpBooleanColumnBinaryMaker" ), + arguments( "jp.co.yahoo.yosegi.binary.maker.FlagIndexedOptimizedNullArrayDumpBooleanColumnBinaryMaker" ) ); } diff --git a/src/test/java/jp/co/yahoo/yosegi/blockindex/TestRangeBlockIndexNameShortCut.java b/src/test/java/jp/co/yahoo/yosegi/blockindex/TestBlockIndexNameShortCut.java similarity index 84% rename from src/test/java/jp/co/yahoo/yosegi/blockindex/TestRangeBlockIndexNameShortCut.java rename to src/test/java/jp/co/yahoo/yosegi/blockindex/TestBlockIndexNameShortCut.java index c30da31a..3b724300 100644 --- a/src/test/java/jp/co/yahoo/yosegi/blockindex/TestRangeBlockIndexNameShortCut.java +++ b/src/test/java/jp/co/yahoo/yosegi/blockindex/TestBlockIndexNameShortCut.java @@ -21,7 +21,6 @@ import java.util.stream.Stream; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.Arguments; @@ -29,7 +28,7 @@ import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.params.provider.Arguments.arguments; -public class TestRangeBlockIndexNameShortCut{ +public class TestBlockIndexNameShortCut { public static Stream data1() { return Stream.of( @@ -39,15 +38,17 @@ public static Stream data1() { arguments( "jp.co.yahoo.yosegi.blockindex.LongRangeBlockIndex" , "R3" ), arguments( "jp.co.yahoo.yosegi.blockindex.FloatRangeBlockIndex" , "R4" ), arguments( "jp.co.yahoo.yosegi.blockindex.DoubleRangeBlockIndex" , "R5" ), - arguments( "jp.co.yahoo.yosegi.blockindex.StringRangeBlockIndex" , "R6" ) + arguments( "jp.co.yahoo.yosegi.blockindex.StringRangeBlockIndex" , "R6" ), + arguments( "jp.co.yahoo.yosegi.blockindex.FullRangeBlockIndex" , "FR0" ), + arguments( "jp.co.yahoo.yosegi.blockindex.BooleanBlockIndex" , "BI0" ) ); } @ParameterizedTest @MethodSource( "data1" ) public void T_get_1( final String c , final String sc ) throws IOException{ - assertEquals( sc , RangeBlockIndexNameShortCut.getShortCutName( c ) ); - assertEquals( c , RangeBlockIndexNameShortCut.getClassName( sc ) ); + assertEquals( sc , BlockIndexNameShortCut.getShortCutName( c ) ); + assertEquals( c , BlockIndexNameShortCut.getClassName( sc ) ); } } diff --git a/src/test/java/jp/co/yahoo/yosegi/blockindex/TestBooleanBlockIndex.java b/src/test/java/jp/co/yahoo/yosegi/blockindex/TestBooleanBlockIndex.java new file mode 100644 index 00000000..b330a78a --- /dev/null +++ b/src/test/java/jp/co/yahoo/yosegi/blockindex/TestBooleanBlockIndex.java @@ -0,0 +1,235 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package jp.co.yahoo.yosegi.blockindex; + +import jp.co.yahoo.yosegi.spread.column.filter.BooleanFilter; +import jp.co.yahoo.yosegi.spread.column.filter.IFilter; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import static org.junit.jupiter.params.provider.Arguments.arguments; + +class TestBooleanBlockIndex { + + @Test + public void T_newInstance_1() { + BooleanBlockIndex blockIndex = new BooleanBlockIndex(); + assertFalse(blockIndex.hasTrue()); + assertFalse(blockIndex.hasFalse()); + assertFalse(blockIndex.hasNull()); + } + + public static Stream D_newInstance_2() { + // hasTrue, hasFalse, hasNull + return Stream.of( + arguments(true, true, true), + arguments(true, true, false), + arguments(true, false, true), + arguments(true, false, false), + arguments(false, false, false), + arguments(false, false, true), + arguments(false, true, false), + arguments(false, true, true)); + } + + @ParameterizedTest + @MethodSource("D_newInstance_2") + public void T_newInstance_2( + final boolean hasTrue, final boolean hasFalse, final boolean hasNull) { + BooleanBlockIndex blockIndex = new BooleanBlockIndex(hasTrue, hasFalse, hasNull); + assertEquals(hasTrue, blockIndex.hasTrue()); + assertEquals(hasFalse, blockIndex.hasFalse()); + assertEquals(hasNull, blockIndex.hasNull()); + } + + @Test + public void T_getBlockIndexType_1() { + BooleanBlockIndex blockIndex = new BooleanBlockIndex(); + assertEquals(BlockIndexType.BOOLEAN, blockIndex.getBlockIndexType()); + } + + public static Stream D_merge_1() { + return Stream.of( + // initial values, merge values, expected values + arguments( + new Boolean[] {false, false, false}, + new Boolean[] {true, false, false}, + new Boolean[] {true, false, false}), + arguments( + new Boolean[] {false, false, false}, + new Boolean[] {false, true, false}, + new Boolean[] {false, true, false}), + arguments( + new Boolean[] {false, false, false}, + new Boolean[] {false, false, true}, + new Boolean[] {false, false, true}), + arguments( + new Boolean[] {true, false, false}, + new Boolean[] {false, false, false}, + new Boolean[] {true, false, false}), + arguments( + new Boolean[] {false, true, false}, + new Boolean[] {false, false, false}, + new Boolean[] {false, true, false}), + arguments( + new Boolean[] {true, true, true}, + new Boolean[] {true, true, true}, + new Boolean[] {true, true, true}), + arguments( + new Boolean[] {false, false, false}, + new Boolean[] {false, false, false}, + new Boolean[] {false, false, false}), + arguments( + new Boolean[] {false, false, false}, + new Boolean[] {true, true, true}, + new Boolean[] {true, true, true}), + arguments( + new Boolean[] {true, true, true}, + new Boolean[] {false, false, false}, + new Boolean[] {true, true, true})); + } + + @ParameterizedTest + @MethodSource("D_merge_1") + public void T_merge_1(final Boolean[] initial, final Boolean[] merge, final Boolean[] expected) { + BooleanBlockIndex blockIndex = new BooleanBlockIndex(initial[0], initial[1], initial[2]); + assertTrue(blockIndex.merge(new BooleanBlockIndex(merge[0], merge[1], merge[2]))); + assertEquals(expected[0], blockIndex.hasTrue()); + assertEquals(expected[1], blockIndex.hasFalse()); + assertEquals(expected[2], blockIndex.hasNull()); + } + + @Test + public void T_getBinarySize_1() { + BooleanBlockIndex blockIndex = new BooleanBlockIndex(true, true, true); + assertEquals(Byte.BYTES, blockIndex.getBinarySize()); + } + + public static Stream D_binary_1() { + return Stream.of( + arguments(true, true, true), + arguments(true, true, false), + arguments(true, false, true), + arguments(true, false, false), + arguments(false, false, false), + arguments(false, false, true), + arguments(false, true, false), + arguments(false, true, true)); + } + + @ParameterizedTest + @MethodSource("D_binary_1") + public void T_binary_0(final boolean hasTrue, final boolean hasFalse, final boolean hasNull) { + BooleanBlockIndex blockIndex = new BooleanBlockIndex(hasTrue, hasFalse, hasNull); + byte[] binary = blockIndex.toBinary(); + assertEquals(binary.length, blockIndex.getBinarySize()); + BooleanBlockIndex blockIndex2 = new BooleanBlockIndex(); + assertFalse(blockIndex2.hasTrue()); + assertFalse(blockIndex2.hasFalse()); + assertFalse(blockIndex2.hasNull()); + blockIndex2.setFromBinary(binary, 0, binary.length); + assertEquals(hasTrue, blockIndex2.hasTrue()); + assertEquals(hasFalse, blockIndex2.hasFalse()); + assertEquals(hasNull, blockIndex2.hasNull()); + } + + public static Stream D_canBlockSkip_1() { + return Stream.of( + arguments(new BooleanBlockIndex(true, false, false), new BooleanFilter(true), false), + arguments(new BooleanBlockIndex(true, false, false), new BooleanFilter(false), true), + arguments(new BooleanBlockIndex(false, true, false), new BooleanFilter(true), true), + arguments(new BooleanBlockIndex(false, true, false), new BooleanFilter(false), false), + arguments(new BooleanBlockIndex(false, false, true), new BooleanFilter(true), true), + arguments(new BooleanBlockIndex(false, false, true), new BooleanFilter(false), true), + arguments(new BooleanBlockIndex(true, true, false), new BooleanFilter(true), false), + arguments(new BooleanBlockIndex(true, true, false), new BooleanFilter(false), false), + arguments(new BooleanBlockIndex(true, false, true), new BooleanFilter(true), false), + arguments(new BooleanBlockIndex(true, false, true), new BooleanFilter(false), true), + arguments(new BooleanBlockIndex(false, true, true), new BooleanFilter(true), true), + arguments(new BooleanBlockIndex(false, true, true), new BooleanFilter(false), false), + arguments(new BooleanBlockIndex(true, true, true), new BooleanFilter(true), false), + arguments(new BooleanBlockIndex(true, true, true), new BooleanFilter(false), false)); + } + + @ParameterizedTest + @MethodSource("D_canBlockSkip_1") + public void T_canBlockSkip_1( + final IBlockIndex blockIndex, final IFilter filter, final boolean result) { + if (result) { + assertEquals(result, blockIndex.getBlockSpreadIndex(filter).isEmpty()); + } else { + assertNull(blockIndex.getBlockSpreadIndex(filter)); + } + } + + public static Stream D_bitFlags_1() { + return Stream.of( + // bitFlags, hasTrue, hasFalse, hasNull + arguments((byte) 0b111, true, true, true), + arguments((byte) 0b110, false, true, true), + arguments((byte) 0b101, true, false, true), + arguments((byte) 0b100, false, false, true), + arguments((byte) 0b000, false, false, false), + arguments((byte) 0b001, true, false, false), + arguments((byte) 0b010, false, true, false), + arguments((byte) 0b011, true, true, false)); + } + + @ParameterizedTest + @MethodSource("D_bitFlags_1") + public void T_bitFlags_1( + final byte flags, final boolean hasTrue, final boolean hasFalse, final boolean hasNull) { + BooleanBlockIndex.BitFlags bitFlags = new BooleanBlockIndex.BitFlags(flags); + assertEquals(hasTrue, bitFlags.hasTrue()); + assertEquals(hasFalse, bitFlags.hasFalse()); + assertEquals(hasNull, bitFlags.hasNull()); + assertEquals(flags, bitFlags.getBitFlags()); + } + + public static Stream D_bitFlags_2() { + return Stream.of( + // bitFlags, hasTrue, hasFalse, hasNull + arguments((byte) 0b111, true, true, true), + arguments((byte) 0b110, false, true, true), + arguments((byte) 0b101, true, false, true), + arguments((byte) 0b100, false, false, true), + arguments((byte) 0b000, false, false, false), + arguments((byte) 0b001, true, false, false), + arguments((byte) 0b010, false, true, false), + arguments((byte) 0b011, true, true, false)); + } + + @ParameterizedTest + @MethodSource("D_bitFlags_2") + public void T_bitFlags_2( + final byte flags, final boolean hasTrue, final boolean hasFalse, final boolean hasNull) { + BooleanBlockIndex.BitFlags bitFlags = + new BooleanBlockIndex.BitFlags(hasTrue, hasFalse, hasNull); + assertEquals(hasTrue, bitFlags.hasTrue()); + assertEquals(hasFalse, bitFlags.hasFalse()); + assertEquals(hasNull, bitFlags.hasNull()); + assertEquals(flags, bitFlags.getBitFlags()); + } +} diff --git a/src/test/resources/blackbox/TestBooleanBlockIndex.json b/src/test/resources/blackbox/TestBooleanBlockIndex.json new file mode 100644 index 00000000..395cf1ba --- /dev/null +++ b/src/test/resources/blackbox/TestBooleanBlockIndex.json @@ -0,0 +1,3 @@ +{"col1": true, "col2": false, "col3": true, "col4": true, "col5": false, "col6": false, "col7": true, "col8": false, "col9": true, "col10": true, "col11": false, "col12": false, "col13": true, "col14": true, "col15": false, "col16": false} +{"col1": true, "col2": false, "col3": true, "col4": false, "col5": false, "col6": true, "col9": true, "col10": false, "col11": false, "col12": true} +{"col1": true, "col2": false, "col3": false, "col4": true, "col5": true, "col6": false, "col13": true, "col14": false, "col15": false, "col16": true}