Repository: kylin Updated Branches: refs/heads/master 71f373507 -> ed643e6b2
KYLIN-2055 Add an encoder for Boolean type Project: http://git-wip-us.apache.org/repos/asf/kylin/repo Commit: http://git-wip-us.apache.org/repos/asf/kylin/commit/cb2b12b3 Tree: http://git-wip-us.apache.org/repos/asf/kylin/tree/cb2b12b3 Diff: http://git-wip-us.apache.org/repos/asf/kylin/diff/cb2b12b3 Branch: refs/heads/master Commit: cb2b12b3b619ac86efbb9c7ca708418882683daf Parents: 71f3735 Author: shaofengshi <shaofeng...@apache.org> Authored: Mon Oct 10 13:30:27 2016 +0800 Committer: shaofengshi <shaofeng...@apache.org> Committed: Mon Oct 10 13:30:27 2016 +0800 ---------------------------------------------------------------------- .../apache/kylin/dimension/BooleanDimEnc.java | 196 +++++++++++++++++++ .../dimension/DimensionEncodingFactory.java | 1 + .../kylin/dimension/BooleanDimEncTest.java | 95 +++++++++ .../cubeDesigner/advanced_settings.html | 2 +- 4 files changed, 293 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/kylin/blob/cb2b12b3/core-metadata/src/main/java/org/apache/kylin/dimension/BooleanDimEnc.java ---------------------------------------------------------------------- diff --git a/core-metadata/src/main/java/org/apache/kylin/dimension/BooleanDimEnc.java b/core-metadata/src/main/java/org/apache/kylin/dimension/BooleanDimEnc.java new file mode 100644 index 0000000..f32724c --- /dev/null +++ b/core-metadata/src/main/java/org/apache/kylin/dimension/BooleanDimEnc.java @@ -0,0 +1,196 @@ +/* + * 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 org.apache.kylin.dimension; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Map; + +import com.google.common.collect.Maps; +import org.apache.kylin.common.util.Bytes; +import org.apache.kylin.common.util.BytesUtil; +import org.apache.kylin.metadata.datatype.DataTypeSerializer; + +/** + * Encoding Boolean values to bytes + */ +public class BooleanDimEnc extends DimensionEncoding { + private static final long serialVersionUID = 1L; + + public static final String ENCODING_NAME = "boolean"; + + //NOTE: when add new value, append to the array tail, DO NOT insert! + public static String[] ALLOWED_VALUES = new String[] { "", "true", "false", "TRUE", "FALSE", "True", "False", "t", "f", "T", "F", "yes", "no", "YES", "NO", "Yes", "No", "y", "n", "Y", "N", "1", "0" }; + + public static final Map<String, Integer> map = Maps.newHashMap(); + + static { + for (int i = 0; i < ALLOWED_VALUES.length; i++) { + map.put(ALLOWED_VALUES[i], i); + } + } + + public static class Factory extends DimensionEncodingFactory { + @Override + public String getSupportedEncodingName() { + return ENCODING_NAME; + } + + @Override + public DimensionEncoding createDimensionEncoding(String encodingName, String[] args) { + return new BooleanDimEnc(); + } + }; + + // ============================================================================ + + private static int fixedLen = 1; + + //no-arg constructor is required for Externalizable + public BooleanDimEnc() { + } + + @Override + public int getLengthOfEncoding() { + return fixedLen; + } + + @Override + public void encode(byte[] value, int valueLen, byte[] output, int outputOffset) { + if (value == null) { + Arrays.fill(output, outputOffset, outputOffset + fixedLen, NULL); + return; + } + + encode(Bytes.toString(value, 0, valueLen), output, outputOffset); + } + + void encode(String valueStr, byte[] output, int outputOffset) { + if (valueStr == null) { + Arrays.fill(output, outputOffset, outputOffset + fixedLen, NULL); + return; + } + + Integer encodeValue = map.get(valueStr); + if (encodeValue == null) { + throw new IllegalArgumentException("Value '" + valueStr + "' is not a recognized boolean value."); + } + + BytesUtil.writeLong(encodeValue, output, outputOffset, fixedLen); + } + + @Override + public String decode(byte[] bytes, int offset, int len) { + if (isNull(bytes, offset, len)) { + return null; + } + + int x = (int) BytesUtil.readLong(bytes, offset, len); + if (x >= ALLOWED_VALUES.length) { + throw new IllegalStateException(); + } + + return ALLOWED_VALUES[x]; + } + + @Override + public DataTypeSerializer<Object> asDataTypeSerializer() { + return new BooleanSerializer(); + } + + private class BooleanSerializer extends DataTypeSerializer<Object> { + // be thread-safe and avoid repeated obj creation + private ThreadLocal<byte[]> current = new ThreadLocal<byte[]>(); + + private byte[] currentBuf() { + byte[] buf = current.get(); + if (buf == null) { + buf = new byte[fixedLen]; + current.set(buf); + } + return buf; + } + + @Override + public void serialize(Object value, ByteBuffer out) { + byte[] buf = currentBuf(); + String valueStr = value == null ? null : value.toString(); + encode(valueStr, buf, 0); + out.put(buf); + } + + @Override + public Object deserialize(ByteBuffer in) { + byte[] buf = currentBuf(); + in.get(buf); + return decode(buf, 0, buf.length); + } + + @Override + public int peekLength(ByteBuffer in) { + return fixedLen; + } + + @Override + public int maxLength() { + return fixedLen; + } + + @Override + public int getStorageBytesEstimate() { + return fixedLen; + } + + @Override + public Object valueOf(String str) { + return str; + } + } + + @Override + public void writeExternal(ObjectOutput out) throws IOException { + out.writeShort(fixedLen); + } + + @Override + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + fixedLen = in.readShort(); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + BooleanDimEnc that = (BooleanDimEnc) o; + + return fixedLen == that.fixedLen; + + } + + @Override + public int hashCode() { + return fixedLen; + } +} http://git-wip-us.apache.org/repos/asf/kylin/blob/cb2b12b3/core-metadata/src/main/java/org/apache/kylin/dimension/DimensionEncodingFactory.java ---------------------------------------------------------------------- diff --git a/core-metadata/src/main/java/org/apache/kylin/dimension/DimensionEncodingFactory.java b/core-metadata/src/main/java/org/apache/kylin/dimension/DimensionEncodingFactory.java index 27bebd7..aba0c26 100644 --- a/core-metadata/src/main/java/org/apache/kylin/dimension/DimensionEncodingFactory.java +++ b/core-metadata/src/main/java/org/apache/kylin/dimension/DimensionEncodingFactory.java @@ -79,6 +79,7 @@ public abstract class DimensionEncodingFactory { map.put(FixedLenHexDimEnc.ENCODING_NAME, new FixedLenHexDimEnc.Factory()); map.put(DateDimEnc.ENCODING_NAME, new DateDimEnc.Factory()); map.put(TimeDimEnc.ENCODING_NAME, new TimeDimEnc.Factory()); + map.put(BooleanDimEnc.ENCODING_NAME, new BooleanDimEnc.Factory()); // custom encodings String[] clsNames = KylinConfig.getInstanceFromEnv().getCubeDimensionCustomEncodingFactories(); http://git-wip-us.apache.org/repos/asf/kylin/blob/cb2b12b3/core-metadata/src/test/java/org/apache/kylin/dimension/BooleanDimEncTest.java ---------------------------------------------------------------------- diff --git a/core-metadata/src/test/java/org/apache/kylin/dimension/BooleanDimEncTest.java b/core-metadata/src/test/java/org/apache/kylin/dimension/BooleanDimEncTest.java new file mode 100644 index 0000000..c6c1416 --- /dev/null +++ b/core-metadata/src/test/java/org/apache/kylin/dimension/BooleanDimEncTest.java @@ -0,0 +1,95 @@ +/* + * 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 org.apache.kylin.dimension; + +import java.nio.ByteBuffer; + +import org.apache.kylin.common.util.Bytes; +import org.apache.kylin.metadata.datatype.DataTypeSerializer; +import org.junit.Assert; +import org.junit.Test; + +public class BooleanDimEncTest { + + @Test + public void testNull() { + BooleanDimEnc enc = new BooleanDimEnc(); + + byte[] buf = new byte[enc.getLengthOfEncoding()]; + enc.encode(null, 0, buf, 0); + Assert.assertTrue(DimensionEncoding.isNull(buf, 0, buf.length)); + String decode = enc.decode(buf, 0, buf.length); + Assert.assertEquals(null, decode); + + buf = new byte[enc.getLengthOfEncoding()]; + DataTypeSerializer<Object> ser = enc.asDataTypeSerializer(); + ser.serialize(null, ByteBuffer.wrap(buf)); + Assert.assertTrue(DimensionEncoding.isNull(buf, 0, buf.length)); + decode = (String) ser.deserialize(ByteBuffer.wrap(buf)); + Assert.assertEquals(null, decode); + } + + @Test + public void testEncodeDecode() { + BooleanDimEnc enc = new BooleanDimEnc(); + + for (String x : BooleanDimEnc.ALLOWED_VALUES) { + testEncodeDecode(enc, x); + } + + try { + testEncodeDecode(enc, "FAlse"); + Assert.fail(); + } catch (Throwable e) { + Assert.assertEquals("Value 'FAlse' is not a recognized boolean value.", e.getMessage()); + } + } + + private void testEncodeDecode(BooleanDimEnc enc, String valueStr) { + byte[] buf = new byte[enc.getLengthOfEncoding()]; + byte[] bytes = Bytes.toBytes(valueStr); + enc.encode(bytes, bytes.length, buf, 0); + String decode = enc.decode(buf, 0, buf.length); + Assert.assertEquals(valueStr, decode); + } + + @Test + public void testSerDes() { + BooleanDimEnc enc = new BooleanDimEnc(); + for (String x : BooleanDimEnc.ALLOWED_VALUES) { + testSerDes(enc, x); + } + + try { + testSerDes(enc, "FAlse"); + Assert.fail(); + } catch (Throwable e) { + Assert.assertEquals("Value 'FAlse' is not a recognized boolean value.", e.getMessage()); + } + } + + private void testSerDes(BooleanDimEnc enc, String valueStr) { + DataTypeSerializer<Object> ser = enc.asDataTypeSerializer(); + byte[] buf = new byte[enc.getLengthOfEncoding()]; + ser.serialize(valueStr, ByteBuffer.wrap(buf)); + String decode = (String) ser.deserialize(ByteBuffer.wrap(buf)); + Assert.assertEquals(valueStr, decode); + } + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/cb2b12b3/webapp/app/partials/cubeDesigner/advanced_settings.html ---------------------------------------------------------------------- diff --git a/webapp/app/partials/cubeDesigner/advanced_settings.html b/webapp/app/partials/cubeDesigner/advanced_settings.html index e8cbf9e..34fd492 100755 --- a/webapp/app/partials/cubeDesigner/advanced_settings.html +++ b/webapp/app/partials/cubeDesigner/advanced_settings.html @@ -244,7 +244,7 @@ <!--Column Length --> <input type="text" class="form-control" placeholder="Column Length.." ng-if="state.mode=='edit'" tooltip="rowkey column length.." tooltip-trigger="focus" - ng-disabled="rowkey_column.encoding=='dict'||rowkey_column.encoding=='date'||rowkey_column.encoding=='time'" + ng-disabled="rowkey_column.encoding=='dict'||rowkey_column.encoding=='date'||rowkey_column.encoding=='time'||rowkey_column.encoding=='boolean'" ng-change="refreshRowKey(convertedRowkeys,$index,rowkey_column);" ng-model="rowkey_column.valueLength" class="form-control">