/**
* diqube: Distributed Query Base.
*
* Copyright (C) 2015 Bastian Gloeckle
*
* This file is part of diqube.
*
* diqube is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diqube.loader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import org.diqube.data.column.ColumnPage;
import org.diqube.data.column.ColumnType;
import org.diqube.data.table.TableShard;
import org.diqube.data.types.lng.LongStandardColumnShard;
import org.diqube.name.RepeatedColumnNameGenerator;
import org.diqube.util.BigByteBuffer;
import org.diqube.util.Pair;
import org.diqube.util.Triple;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.google.common.collect.Iterables;
/**
* Tests for {@link JsonLoader}.
*
* @author Bastian Gloeckle
*/
public class JsonLoaderTest {
private static final String TABLE = "Test";
private JsonLoader loader;
private LoaderColumnInfo colInfo;
private AnnotationConfigApplicationContext dataContext;
private RepeatedColumnNameGenerator repeatedColNames;
@BeforeMethod
public void before() {
dataContext = new AnnotationConfigApplicationContext();
dataContext.scan("org.diqube");
dataContext.refresh();
repeatedColNames = dataContext.getBean(RepeatedColumnNameGenerator.class);
loader = dataContext.getBean(JsonLoader.class);
colInfo = new LoaderColumnInfo(ColumnType.STRING);
}
@Test
public void simpleJson() throws LoadException {
// GIVEN
String json = "[ { \"a\": 1, \"b\": 1},{\"a\": 2, \"b\": 3}]";
// WHEN
TableShard tableShard =
Iterables.getOnlyElement(loader.load(0L, new BigByteBuffer(json.getBytes()), TABLE, colInfo));
// THEN
Assert.assertEquals(tableShard.getLongColumns().size(), 2, "Expected both long columns to be available");
Set<Pair<Long, Long>> expectedValues = new HashSet<>();
expectedValues.add(new Pair<>(1L, 1L));
expectedValues.add(new Pair<>(2L, 3L));
Set<Pair<Long, Long>> actualValues = new HashSet<>();
LongStandardColumnShard colA = tableShard.getLongColumns().get("a");
LongStandardColumnShard colB = tableShard.getLongColumns().get("b");
for (long i = tableShard.getLowestRowId(); i < tableShard.getLowestRowId()
+ tableShard.getNumberOfRowsInShard(); i++) {
Long valueA = resolveSingleRowValue(colA, i);
Long valueB = resolveSingleRowValue(colB, i);
actualValues.add(new Pair<>(valueA, valueB));
}
Assert.assertEquals(actualValues, expectedValues, "Expected correct values to be encoded");
}
@Test
public void simpleArrayJson() throws LoadException {
// GIVEN
String json = "[ { \"a\": 1, \"b\": 1, \"c\": [4, 5,6]},{\"a\": 2, \"b\": 3, \"c\": [7, 8, 9 ]}]";
// WHEN
TableShard tableShard =
Iterables.getOnlyElement(loader.load(0L, new BigByteBuffer(json.getBytes()), TABLE, colInfo));
// THEN
Assert.assertEquals(tableShard.getLongColumns().size(), 6, "Expected all long columns to be available");
Set<Triple<Long, Long, List<Long>>> expectedValues = new HashSet<>();
expectedValues.add(new Triple<>(1L, 1L, Arrays.asList(new Long[] { 4L, 5L, 6L })));
expectedValues.add(new Triple<>(2L, 3L, Arrays.asList(new Long[] { 7L, 8L, 9L })));
Set<Triple<Long, Long, List<Long>>> actualValues = new HashSet<>();
LongStandardColumnShard colA = tableShard.getLongColumns().get("a");
LongStandardColumnShard colB = tableShard.getLongColumns().get("b");
LongStandardColumnShard colC0 = tableShard.getLongColumns().get(repeatedColNames.repeatedAtIndex("c", 0));
LongStandardColumnShard colC1 = tableShard.getLongColumns().get(repeatedColNames.repeatedAtIndex("c", 1));
LongStandardColumnShard colC2 = tableShard.getLongColumns().get(repeatedColNames.repeatedAtIndex("c", 2));
LongStandardColumnShard colCLength = tableShard.getLongColumns().get(repeatedColNames.repeatedLength("c"));
for (long i = tableShard.getLowestRowId(); i < tableShard.getLowestRowId()
+ tableShard.getNumberOfRowsInShard(); i++) {
Long valueA = resolveSingleRowValue(colA, i);
Long valueB = resolveSingleRowValue(colB, i);
Long valueC0 = resolveSingleRowValue(colC0, i);
Long valueC1 = resolveSingleRowValue(colC1, i);
Long valueC2 = resolveSingleRowValue(colC2, i);
actualValues.add(new Triple<>(valueA, valueB, Arrays.asList(new Long[] { valueC0, valueC1, valueC2 })));
Assert.assertEquals((long) resolveSingleRowValue(colCLength, i), 3L,
"Correct value of length col expected for row " + i);
}
Assert.assertEquals(actualValues, expectedValues, "Expected correct values to be encoded");
}
@Test
public void arrayContainingObjectsJson() throws LoadException {
// GIVEN
String json = "[ { \"a\": 1, \"c\": [{ \"d\" : 4, \"e\" : 4 }, { \"d\" : 5, \"e\" : 5 }]},"
+ "{\"a\": 2, \"c\": [ { \"d\" : 7, \"e\" : 7 }, { \"d\" : 8, \"e\" : 8 }] } ]";
// WHEN
TableShard tableShard =
Iterables.getOnlyElement(loader.load(0L, new BigByteBuffer(json.getBytes()), TABLE, colInfo));
// THEN
Assert.assertEquals(tableShard.getLongColumns().size(), 6, "Expected all long columns to be available");
Set<Triple<Long, List<Long>, List<Long>>> expectedValues = new HashSet<>();
expectedValues.add(new Triple<>(1L, Arrays.asList(new Long[] { 4L, 5L }), Arrays.asList(new Long[] { 4L, 5L })));
expectedValues.add(new Triple<>(2L, Arrays.asList(new Long[] { 7L, 8L }), Arrays.asList(new Long[] { 7L, 8L })));
Set<Triple<Long, List<Long>, List<Long>>> actualValues = new HashSet<>();
LongStandardColumnShard colA = tableShard.getLongColumns().get("a");
LongStandardColumnShard colC0D = tableShard.getLongColumns().get(repeatedColNames.repeatedAtIndex("c", 0) + ".d");
LongStandardColumnShard colC1D = tableShard.getLongColumns().get(repeatedColNames.repeatedAtIndex("c", 1) + ".d");
LongStandardColumnShard colC0E = tableShard.getLongColumns().get(repeatedColNames.repeatedAtIndex("c", 0) + ".e");
LongStandardColumnShard colC1E = tableShard.getLongColumns().get(repeatedColNames.repeatedAtIndex("c", 1) + ".e");
LongStandardColumnShard colCLength = tableShard.getLongColumns().get(repeatedColNames.repeatedLength("c"));
for (long i = tableShard.getLowestRowId(); i < tableShard.getLowestRowId()
+ tableShard.getNumberOfRowsInShard(); i++) {
Long valueA = resolveSingleRowValue(colA, i);
Long valueC0D = resolveSingleRowValue(colC0D, i);
Long valueC1D = resolveSingleRowValue(colC1D, i);
Long valueC0E = resolveSingleRowValue(colC0E, i);
Long valueC1E = resolveSingleRowValue(colC1E, i);
actualValues.add(new Triple<>(valueA, Arrays.asList(new Long[] { valueC0D, valueC1D }),
Arrays.asList(new Long[] { valueC0E, valueC1E })));
Assert.assertEquals((long) resolveSingleRowValue(colCLength, i), 2L,
"Correct value of length col expected for row " + i);
}
Assert.assertEquals(actualValues, expectedValues, "Expected correct values to be encoded");
}
@Test
public void arrayContainingDifferentLengthLongsJson() throws LoadException {
// GIVEN
String json = "[ { \"a\": 1, \"c\": [4, 5, 6]}," + "{\"a\": 2, \"c\": [ 1 ] } ]";
// WHEN
TableShard tableShard =
Iterables.getOnlyElement(loader.load(0L, new BigByteBuffer(json.getBytes()), TABLE, colInfo));
// THEN
Assert.assertEquals(tableShard.getLongColumns().size(), 5, "Expected all long columns to be available");
Set<Pair<Long, List<Long>>> expectedValues = new HashSet<>();
expectedValues.add(new Pair<>(1L, Arrays.asList(new Long[] { 4L, 5L, 6L })));
expectedValues.add(new Pair<>(2L, Arrays.asList(new Long[] { 1L })));
Set<Pair<Long, List<Long>>> actualValues = new HashSet<>();
LongStandardColumnShard colA = tableShard.getLongColumns().get("a");
LongStandardColumnShard[] colC =
new LongStandardColumnShard[] { tableShard.getLongColumns().get(repeatedColNames.repeatedAtIndex("c", 0)),
tableShard.getLongColumns().get(repeatedColNames.repeatedAtIndex("c", 1)),
tableShard.getLongColumns().get(repeatedColNames.repeatedAtIndex("c", 2)) };
LongStandardColumnShard colCLength = tableShard.getLongColumns().get(repeatedColNames.repeatedLength("c"));
for (long i = tableShard.getLowestRowId(); i < tableShard.getLowestRowId()
+ tableShard.getNumberOfRowsInShard(); i++) {
Long valueA = resolveSingleRowValue(colA, i);
List<Long> valueC = new ArrayList<>();
for (int c = 0; c < resolveSingleRowValue(colCLength, i); c++)
valueC.add(resolveSingleRowValue(colC[c], i));
actualValues.add(new Pair<>(valueA, valueC));
}
Assert.assertEquals(actualValues, expectedValues, "Expected correct values to be encoded");
}
@Test
public void arrayContainingDifferentLengthObjectsJson() throws LoadException {
// GIVEN
String json =
"[ { \"a\": 1, \"c\": [ { \"d\": 4 }, { \"d\": 5}, { \"d\": 6 } ]}," + "{\"a\": 2, \"c\": [ { \"d\": 1 } ] } ]";
// WHEN
TableShard tableShard =
Iterables.getOnlyElement(loader.load(0L, new BigByteBuffer(json.getBytes()), TABLE, colInfo));
// THEN
Assert.assertEquals(tableShard.getLongColumns().size(), 5, "Expected all long columns to be available");
Set<Pair<Long, List<Long>>> expectedValues = new HashSet<>();
expectedValues.add(new Pair<>(1L, Arrays.asList(new Long[] { 4L, 5L, 6L })));
expectedValues.add(new Pair<>(2L, Arrays.asList(new Long[] { 1L })));
Set<Pair<Long, List<Long>>> actualValues = new HashSet<>();
LongStandardColumnShard colA = tableShard.getLongColumns().get("a");
LongStandardColumnShard[] colD = new LongStandardColumnShard[] {
tableShard.getLongColumns().get(repeatedColNames.repeatedAtIndex("c", 0) + ".d"),
tableShard.getLongColumns().get(repeatedColNames.repeatedAtIndex("c", 1) + ".d"),
tableShard.getLongColumns().get(repeatedColNames.repeatedAtIndex("c", 2) + ".d") };
LongStandardColumnShard colCLength = tableShard.getLongColumns().get(repeatedColNames.repeatedLength("c"));
for (long i = tableShard.getLowestRowId(); i < tableShard.getLowestRowId()
+ tableShard.getNumberOfRowsInShard(); i++) {
Long valueA = resolveSingleRowValue(colA, i);
List<Long> valueD = new ArrayList<>();
for (int c = 0; c < resolveSingleRowValue(colCLength, i); c++)
valueD.add(resolveSingleRowValue(colD[c], i));
actualValues.add(new Pair<>(valueA, valueD));
}
Assert.assertEquals(actualValues, expectedValues, "Expected correct values to be encoded");
}
@Test
public void longJson() throws LoadException {
// GIVEN
String json = //
"[ { \"a\": 0,\"b\": 30 }," + //
"{ \"a\": 1,\"b\": 23 }," + //
"{ \"a\": 2,\"b\": 55 }," + //
"{ \"a\": 3,\"b\": 26 }," + //
"{ \"a\": 4,\"b\": 39 }," + //
"{ \"a\": 5,\"b\": 36 }," + //
"{ \"a\": 6,\"b\": 55 }," + //
"{ \"a\": 7,\"b\": 39 }," + //
"{ \"a\": 8,\"b\": 10 }," + //
"{ \"a\": 9,\"b\": 25 }]";
// WHEN
TableShard tableShard =
Iterables.getOnlyElement(loader.load(0L, new BigByteBuffer(json.getBytes()), TABLE, colInfo));
// THEN
Assert.assertEquals(tableShard.getLongColumns().size(), 2, "Expected both long columns to be available");
Set<Pair<Long, Long>> expectedValues = new HashSet<>();
expectedValues.add(new Pair<>(0L, 30L));
expectedValues.add(new Pair<>(1L, 23L));
expectedValues.add(new Pair<>(2L, 55L));
expectedValues.add(new Pair<>(3L, 26L));
expectedValues.add(new Pair<>(4L, 39L));
expectedValues.add(new Pair<>(5L, 36L));
expectedValues.add(new Pair<>(6L, 55L));
expectedValues.add(new Pair<>(7L, 39L));
expectedValues.add(new Pair<>(8L, 10L));
expectedValues.add(new Pair<>(9L, 25L));
Set<Pair<Long, Long>> actualValues = new HashSet<>();
LongStandardColumnShard colA = tableShard.getLongColumns().get("a");
LongStandardColumnShard colB = tableShard.getLongColumns().get("b");
for (long i = tableShard.getLowestRowId(); i < tableShard.getLowestRowId()
+ tableShard.getNumberOfRowsInShard(); i++) {
Long valueA = resolveSingleRowValue(colA, i);
Long valueB = resolveSingleRowValue(colB, i);
actualValues.add(new Pair<>(valueA, valueB));
}
Assert.assertEquals(actualValues, expectedValues, "Expected correct values to be encoded");
}
@Test(expectedExceptions = LoadException.class)
public void unparsableJson() throws LoadException {
// GIVEN
String json = //
"[ { \"a\": 0,\"b\": " + Long.MAX_VALUE + "9 }]";
// WHEN
loader.load(0L, new BigByteBuffer(json.getBytes()), TABLE, colInfo);
// THEN: exception
}
private Long resolveSingleRowValue(LongStandardColumnShard col, long rowId) {
Entry<Long, ColumnPage> activeEntry = col.getPages().floorEntry(rowId);
int idx = (int) (rowId - activeEntry.getKey());
ColumnPage page = activeEntry.getValue();
return col.getColumnShardDictionary()
.decompressValue(page.getColumnPageDict().decompressValue(page.getValues().get(idx)));
}
}