This is an automated email from the ASF dual-hosted git repository.
yqm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git
The following commit(s) were added to refs/heads/master by this push:
new 259e62e3e18 Adds support for compute JSON from dictionary. JSON data
can now be computed from its dictionary/ index, with an option to skip storing
the raw JSON entirely (#18589)
259e62e3e18 is described below
commit 259e62e3e18a37f52ddfd7940fc288d33f653481
Author: Cece Mei <[email protected]>
AuthorDate: Thu Oct 9 16:59:07 2025 -0700
Adds support for compute JSON from dictionary. JSON data can now be
computed from its dictionary/ index, with an option to skip storing the raw
JSON entirely (#18589)
* derive-json
* default-read-raw
* object-encoding
* default
* buffer
* format
* lazy-supplier
* revert-column-config
* serializer
* supplier
* value-provider
* test
* javadoc
* get-row-value
* nested
* format
* test
* trigger ci / empty commit
* static
---
.../concrete/ColumnHolderRACColumn.java | 25 +-
.../AtomicIntegerReadableOffset.java} | 34 +-
.../nested/CompressedNestedDataComplexColumn.java | 884 ++++++++++++---------
.../segment/nested/NestedDataColumnSerializer.java | 29 +-
.../segment/nested/NestedDataColumnSupplier.java | 27 +-
.../druid/segment/nested/NestedDataColumnV5.java | 3 +-
.../segment/nested/NestedDataComplexColumn.java | 6 +-
.../nested/NestedFieldDictionaryEncodedColumn.java | 18 +
.../segment/nested/ObjectStorageEncoding.java | 1 +
.../segment/nested/StructuredDataBuilder.java | 174 ++++
.../apache/druid/query/NestedDataTestUtils.java | 54 +-
.../druid/query/scan/NestedDataScanQueryTest.java | 32 +-
.../nested/NestedDataColumnSupplierTest.java | 50 +-
.../segment/nested/StructuredDataBuilderTest.java | 203 +++++
.../sql/calcite/CalciteNestedDataQueryTest.java | 283 ++++---
15 files changed, 1253 insertions(+), 570 deletions(-)
diff --git
a/processing/src/main/java/org/apache/druid/query/rowsandcols/concrete/ColumnHolderRACColumn.java
b/processing/src/main/java/org/apache/druid/query/rowsandcols/concrete/ColumnHolderRACColumn.java
index 5e299f6aa98..fe6b117653b 100644
---
a/processing/src/main/java/org/apache/druid/query/rowsandcols/concrete/ColumnHolderRACColumn.java
+++
b/processing/src/main/java/org/apache/druid/query/rowsandcols/concrete/ColumnHolderRACColumn.java
@@ -19,14 +19,13 @@
package org.apache.druid.query.rowsandcols.concrete;
-import org.apache.druid.query.monomorphicprocessing.RuntimeShapeInspector;
import org.apache.druid.query.rowsandcols.column.Column;
import org.apache.druid.query.rowsandcols.column.ColumnAccessor;
import org.apache.druid.segment.ColumnValueSelector;
import org.apache.druid.segment.column.BaseColumn;
import org.apache.druid.segment.column.BaseColumnHolder;
import org.apache.druid.segment.column.ColumnType;
-import org.apache.druid.segment.data.ReadableOffset;
+import org.apache.druid.segment.data.AtomicIntegerReadableOffset;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -161,26 +160,4 @@ public class ColumnHolderRACColumn implements Column,
Closeable
}
return baseColumn;
}
-
- private static class AtomicIntegerReadableOffset implements ReadableOffset
- {
- private final AtomicInteger offset;
-
- public AtomicIntegerReadableOffset(AtomicInteger offset)
- {
- this.offset = offset;
- }
-
- @Override
- public int getOffset()
- {
- return offset.get();
- }
-
- @Override
- public void inspectRuntimeShape(RuntimeShapeInspector inspector)
- {
-
- }
- }
}
diff --git
a/processing/src/main/java/org/apache/druid/segment/nested/ObjectStorageEncoding.java
b/processing/src/main/java/org/apache/druid/segment/data/AtomicIntegerReadableOffset.java
similarity index 59%
copy from
processing/src/main/java/org/apache/druid/segment/nested/ObjectStorageEncoding.java
copy to
processing/src/main/java/org/apache/druid/segment/data/AtomicIntegerReadableOffset.java
index 3b1c823dbe4..e063c6b0e21 100644
---
a/processing/src/main/java/org/apache/druid/segment/nested/ObjectStorageEncoding.java
+++
b/processing/src/main/java/org/apache/druid/segment/data/AtomicIntegerReadableOffset.java
@@ -17,29 +17,33 @@
* under the License.
*/
-package org.apache.druid.segment.nested;
+package org.apache.druid.segment.data;
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonValue;
-import org.apache.druid.java.util.common.StringUtils;
+import org.apache.druid.query.monomorphicprocessing.RuntimeShapeInspector;
-public enum ObjectStorageEncoding
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * A {@link ReadableOffset} implementation that wraps an AtomicInteger.
+ */
+public class AtomicIntegerReadableOffset implements ReadableOffset
{
- SMILE;
+ private final AtomicInteger offset;
+
+ public AtomicIntegerReadableOffset(AtomicInteger offset)
+ {
+ this.offset = offset;
+ }
- @JsonValue
@Override
- public String toString()
+ public int getOffset()
{
- return StringUtils.toLowerCase(this.name());
+ return offset.get();
}
- @JsonCreator
- public static ObjectStorageEncoding fromString(String name)
+ @Override
+ public void inspectRuntimeShape(RuntimeShapeInspector inspector)
{
- if (name == null) {
- return SMILE;
- }
- return valueOf(StringUtils.toUpperCase(name));
+
}
}
diff --git
a/processing/src/main/java/org/apache/druid/segment/nested/CompressedNestedDataComplexColumn.java
b/processing/src/main/java/org/apache/druid/segment/nested/CompressedNestedDataComplexColumn.java
index 4777dee66da..906a2770b1b 100644
---
a/processing/src/main/java/org/apache/druid/segment/nested/CompressedNestedDataComplexColumn.java
+++
b/processing/src/main/java/org/apache/druid/segment/nested/CompressedNestedDataComplexColumn.java
@@ -19,9 +19,9 @@
package org.apache.druid.segment.nested;
-import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.common.primitives.Doubles;
import org.apache.druid.collections.bitmap.ImmutableBitmap;
@@ -29,6 +29,7 @@ import org.apache.druid.common.semantic.SemanticUtils;
import org.apache.druid.error.DruidException;
import org.apache.druid.java.util.common.IAE;
import org.apache.druid.java.util.common.ISE;
+import org.apache.druid.java.util.common.Pair;
import org.apache.druid.java.util.common.RE;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.java.util.common.io.Closer;
@@ -41,17 +42,16 @@ import org.apache.druid.segment.ColumnValueSelector;
import org.apache.druid.segment.DimensionSelector;
import org.apache.druid.segment.NilColumnValueSelector;
import org.apache.druid.segment.ObjectColumnSelector;
-import org.apache.druid.segment.column.BaseColumn;
import org.apache.druid.segment.column.BaseColumnHolder;
import org.apache.druid.segment.column.ColumnBuilder;
import org.apache.druid.segment.column.ColumnConfig;
-import org.apache.druid.segment.column.ColumnHolder;
import org.apache.druid.segment.column.ColumnIndexSupplier;
import org.apache.druid.segment.column.ColumnType;
import org.apache.druid.segment.column.DictionaryEncodedColumn;
import org.apache.druid.segment.column.StringEncodingStrategies;
import org.apache.druid.segment.column.TypeStrategies;
import org.apache.druid.segment.column.TypeStrategy;
+import org.apache.druid.segment.data.AtomicIntegerReadableOffset;
import org.apache.druid.segment.data.BitmapSerdeFactory;
import org.apache.druid.segment.data.ColumnarDoubles;
import org.apache.druid.segment.data.ColumnarInts;
@@ -93,7 +93,9 @@ import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
+import java.util.stream.Collectors;
/**
* Implementation of {@link NestedDataComplexColumn} which uses a {@link
CompressedVariableSizedBlobColumn} for the
@@ -117,6 +119,7 @@ public abstract class
CompressedNestedDataComplexColumn<TKeyDictionary extends I
public static final IntTypeStrategy INT_TYPE_STRATEGY = new
IntTypeStrategy();
private final ColumnConfig columnConfig;
private final Closer closer;
+ @Nullable
private final CompressedVariableSizedBlobColumnSupplier
compressedRawColumnSupplier;
private final ImmutableBitmap nullValues;
private final Supplier<TKeyDictionary> fieldsSupplier;
@@ -139,7 +142,7 @@ public abstract class
CompressedNestedDataComplexColumn<TKeyDictionary extends I
String columnName,
ColumnType logicalType,
@SuppressWarnings("unused") ColumnConfig columnConfig,
- CompressedVariableSizedBlobColumnSupplier compressedRawColumnSupplier,
+ @Nullable CompressedVariableSizedBlobColumnSupplier
compressedRawColumnSupplier,
ImmutableBitmap nullValues,
Supplier<TKeyDictionary> fieldsSupplier,
FieldTypeInfo fieldInfo,
@@ -180,12 +183,10 @@ public abstract class
CompressedNestedDataComplexColumn<TKeyDictionary extends I
@Override
public SortedMap<String, FieldTypeInfo.MutableTypeSet> getFieldTypeInfo()
{
- final TKeyDictionary fields = fieldsSupplier.get();
- final SortedMap<String, FieldTypeInfo.MutableTypeSet> fieldMap = new
TreeMap<>();
- for (int i = 0; i < fields.size(); i++) {
- String fieldPath = StringUtils.fromUtf8(fields.get(i));
- FieldTypeInfo.TypeSet types = fieldInfo.getTypes(i);
- fieldMap.put(fieldPath, new
FieldTypeInfo.MutableTypeSet(types.getByteValue()));
+ SortedMap<String, FieldTypeInfo.MutableTypeSet> fieldMap = new TreeMap<>();
+ for (NestedField field : getAllNestedFields()) {
+ FieldTypeInfo.TypeSet types = fieldInfo.getTypes(field.fieldIndex);
+ fieldMap.put(field.fieldName, new
FieldTypeInfo.MutableTypeSet(types.getByteValue()));
}
return fieldMap;
}
@@ -199,15 +200,9 @@ public abstract class
CompressedNestedDataComplexColumn<TKeyDictionary extends I
@Override
public List<List<NestedPathPart>> getNestedFields()
{
- final TKeyDictionary fields = fieldsSupplier.get();
- final List<List<NestedPathPart>> fieldParts = new
ArrayList<>(fields.size());
- for (int i = 0; i < fields.size(); i++) {
- fieldParts.add(parsePath(StringUtils.fromUtf8(fields.get(i))));
- }
- return fieldParts;
+ return getAllParsedNestedFields().stream().map(pair ->
pair.rhs).collect(Collectors.toList());
}
-
public TStringDictionary getUtf8BytesDictionary()
{
return stringDictionarySupplier.get();
@@ -329,29 +324,69 @@ public abstract class
CompressedNestedDataComplexColumn<TKeyDictionary extends I
return null;
}
- if (compressedRawColumn == null) {
+ if (compressedRawColumn == null && compressedRawColumnSupplier != null) {
compressedRawColumn = closer.register(compressedRawColumnSupplier.get());
}
- final ByteBuffer valueBuffer = compressedRawColumn.get(rowNum);
- return STRATEGY.fromByteBuffer(valueBuffer, valueBuffer.remaining());
+ if (compressedRawColumnSupplier != null) {
+ final ByteBuffer valueBuffer = compressedRawColumn.get(rowNum);
+ return STRATEGY.fromByteBuffer(valueBuffer, valueBuffer.remaining());
+ }
+
+ final List<StructuredDataBuilder.Element> elements =
getAllParsedNestedFields()
+ .stream()
+ .map(pair -> {
+ NestedFieldDictionaryEncodedColumn column =
(NestedFieldDictionaryEncodedColumn) getColumnHolder(
+ pair.lhs.fieldName,
+ pair.lhs.fieldIndex
+ ).getColumn();
+ return StructuredDataBuilder.Element.of(pair.rhs,
column.lookupObject(column.getSingleValueRow(rowNum)));
+ })
+ .collect(Collectors.toList());
+ return new StructuredDataBuilder(elements).build();
}
@Override
public ColumnValueSelector<?> makeColumnValueSelector(ReadableOffset offset)
{
- final TKeyDictionary fields = fieldsSupplier.get();
- if (!logicalType.equals(ColumnType.NESTED_DATA) && fields.size() == 1 &&
rootFieldPath.equals(StringUtils.fromUtf8(fields.get(0)))) {
+ List<NestedField> allFields = getAllNestedFields();
+ if (!logicalType.equals(ColumnType.NESTED_DATA)
+ && allFields.size() == 1
+ &&
rootFieldPath.equals(Iterables.getOnlyElement(allFields).fieldName)) {
return makeColumnValueSelector(
ImmutableList.of(),
null /* not used */,
offset
);
}
- if (compressedRawColumn == null) {
- compressedRawColumn = closer.register(compressedRawColumnSupplier.get());
+ final Supplier<Object> valueProvider;
+ if (compressedRawColumnSupplier != null) {
+ if (compressedRawColumn == null) {
+ compressedRawColumn =
closer.register(compressedRawColumnSupplier.get());
+ }
+ valueProvider = () -> {
+ final ByteBuffer valueBuffer =
compressedRawColumn.get(offset.getOffset());
+ return STRATEGY.fromByteBuffer(valueBuffer, valueBuffer.remaining());
+ };
+ } else {
+ List<Pair<List<NestedPathPart>, ColumnValueSelector>> fieldSelectors =
+ getAllParsedNestedFields().stream()
+ .map(pair -> Pair.of(
+ pair.rhs,
+ ((DictionaryEncodedColumn)
getColumnHolder(
+ pair.lhs.fieldName,
+ pair.lhs.fieldIndex
+
).getColumn()).makeColumnValueSelector(offset)
+ ))
+ .collect(Collectors.toList());
+ valueProvider = () -> {
+ List<StructuredDataBuilder.Element> elements = fieldSelectors
+ .stream()
+ .map(c -> StructuredDataBuilder.Element.of(c.lhs,
c.rhs.getObject()))
+ .collect(Collectors.toList());
+ return new StructuredDataBuilder(elements).build();
+ };
}
-
return new ObjectColumnSelector()
{
@Nullable
@@ -361,8 +396,7 @@ public abstract class
CompressedNestedDataComplexColumn<TKeyDictionary extends I
if (nullValues.get(offset.getOffset())) {
return null;
}
- final ByteBuffer valueBuffer =
compressedRawColumn.get(offset.getOffset());
- return STRATEGY.fromByteBuffer(valueBuffer, valueBuffer.remaining());
+ return valueProvider.get();
}
@Override
@@ -382,17 +416,48 @@ public abstract class
CompressedNestedDataComplexColumn<TKeyDictionary extends I
@Override
public VectorObjectSelector makeVectorObjectSelector(ReadableVectorOffset
offset)
{
- final TKeyDictionary fields = fieldsSupplier.get();
- if (!logicalType.equals(ColumnType.NESTED_DATA) && fields.size() == 1 &&
rootFieldPath.equals(StringUtils.fromUtf8(fields.get(0)))) {
+ List<Pair<NestedField, List<NestedPathPart>>> allFields =
getAllParsedNestedFields();
+ if (!logicalType.equals(ColumnType.NESTED_DATA)
+ && allFields.size() == 1
+ &&
rootFieldPath.equals(Iterables.getOnlyElement(allFields).lhs.fieldName)) {
return makeVectorObjectSelector(
Collections.emptyList(),
null /* not used */,
offset
);
}
- if (compressedRawColumn == null) {
- compressedRawColumn = closer.register(compressedRawColumnSupplier.get());
+
+ AtomicInteger atomicOffset = new AtomicInteger(-1);
+ final Supplier<Object> valueProvider;
+ if (compressedRawColumnSupplier != null) {
+ if (compressedRawColumn == null) {
+ compressedRawColumn =
closer.register(compressedRawColumnSupplier.get());
+ }
+ valueProvider = () -> {
+ final ByteBuffer valueBuffer =
compressedRawColumn.get(atomicOffset.get());
+ return STRATEGY.fromByteBuffer(valueBuffer, valueBuffer.remaining());
+ };
+ } else {
+ AtomicIntegerReadableOffset readableAtomicOffset = new
AtomicIntegerReadableOffset(atomicOffset);
+ final List<Pair<List<NestedPathPart>, ColumnValueSelector>>
fieldSelectors =
+ allFields.stream()
+ .map(pair -> Pair.of(
+ pair.rhs,
+ ((DictionaryEncodedColumn) getColumnHolder(
+ pair.lhs.fieldName,
+ pair.lhs.fieldIndex
+
).getColumn()).makeColumnValueSelector(readableAtomicOffset)
+ ))
+ .collect(Collectors.toList());
+ valueProvider = () -> {
+ List<StructuredDataBuilder.Element> elements = fieldSelectors
+ .stream()
+ .map(c -> StructuredDataBuilder.Element.of(c.lhs,
c.rhs.getObject()))
+ .collect(Collectors.toList());
+ return new StructuredDataBuilder(elements).build();
+ };
}
+
return new VectorObjectSelector()
{
final Object[] vector = new Object[offset.getMaxVectorSize()];
@@ -434,8 +499,8 @@ public abstract class
CompressedNestedDataComplexColumn<TKeyDictionary extends I
// maybe someday can use bitmap batch operations for nulls?
return null;
}
- final ByteBuffer valueBuffer = compressedRawColumn.get(offset);
- return STRATEGY.fromByteBuffer(valueBuffer, valueBuffer.remaining());
+ atomicOffset.set(offset);
+ return valueProvider.get();
}
@Override
@@ -455,8 +520,10 @@ public abstract class
CompressedNestedDataComplexColumn<TKeyDictionary extends I
@Override
public VectorValueSelector makeVectorValueSelector(ReadableVectorOffset
offset)
{
- final TKeyDictionary fields = fieldsSupplier.get();
- if (!logicalType.equals(ColumnType.NESTED_DATA) && fields.size() == 1 &&
rootFieldPath.equals(StringUtils.fromUtf8(fields.get(0)))) {
+ List<NestedField> allFields = getAllNestedFields();
+ if (!logicalType.equals(ColumnType.NESTED_DATA)
+ && allFields.size() == 1
+ &&
rootFieldPath.equals(Iterables.getOnlyElement(allFields).fieldName)) {
return makeVectorValueSelector(
Collections.emptyList(),
null /* not used */,
@@ -469,10 +536,10 @@ public abstract class
CompressedNestedDataComplexColumn<TKeyDictionary extends I
@Override
public int getLength()
{
- if (compressedRawColumn == null) {
+ if (compressedRawColumn == null && compressedRawColumnSupplier != null) {
compressedRawColumn = closer.register(compressedRawColumnSupplier.get());
}
- return compressedRawColumn.size();
+ return compressedRawColumnSupplier != null ? compressedRawColumn.size() :
-1;
}
@Override
@@ -481,6 +548,7 @@ public abstract class
CompressedNestedDataComplexColumn<TKeyDictionary extends I
CloseableUtils.closeAndWrapExceptions(closer);
}
+
/**
* Create a selector for a nested path.
*
@@ -496,52 +564,54 @@ public abstract class
CompressedNestedDataComplexColumn<TKeyDictionary extends I
ReadableOffset readableOffset
)
{
- final TKeyDictionary fields = fieldsSupplier.get();
- final String field = getField(path);
- Preconditions.checkNotNull(field, "Null field");
- final int fieldIndex = fields.indexOf(StringUtils.toUtf8ByteBuffer(field));
- if (fieldIndex >= 0) {
- DictionaryEncodedColumn<?> col = (DictionaryEncodedColumn<?>)
getColumnHolder(field, fieldIndex).getColumn();
+ final Field field = getNestedFieldOrNestedArrayElementFromPath(path);
+ if (field instanceof NestedField) {
+ DictionaryEncodedColumn<?> col = (DictionaryEncodedColumn<?>)
getColumnHolder(
+ ((NestedField) field).fieldName,
+ ((NestedField) field).fieldIndex
+ ).getColumn();
return col.makeDimensionSelector(readableOffset, extractionFn);
- }
- if (!path.isEmpty() && path.get(path.size() - 1) instanceof
NestedPathArrayElement) {
- final NestedPathPart lastPath = path.get(path.size() - 1);
- final String arrayField = getField(path.subList(0, path.size() - 1));
- final int arrayFieldIndex =
fields.indexOf(StringUtils.toUtf8ByteBuffer(arrayField));
- if (arrayFieldIndex >= 0) {
- final int elementNumber = ((NestedPathArrayElement)
lastPath).getIndex();
- if (elementNumber < 0) {
- throw new IAE("Cannot make array element selector for path [%s],
negative array index not supported for this selector", path);
- }
- DictionaryEncodedColumn<?> col = (DictionaryEncodedColumn<?>)
getColumnHolder(arrayField, arrayFieldIndex).getColumn();
- ColumnValueSelector<?> arraySelector =
col.makeColumnValueSelector(readableOffset);
- return new BaseSingleValueDimensionSelector()
+ } else if (field instanceof NestedArrayElement) {
+ final NestedArrayElement arrayField = (NestedArrayElement) field;
+ final int elementNumber = arrayField.elementNumber;
+ if (elementNumber < 0) {
+ throw new IAE(
+ "Cannot make array element selector for path [%s], negative array
index not supported for this selector",
+ path
+ );
+ }
+ DictionaryEncodedColumn<?> col = (DictionaryEncodedColumn<?>)
getColumnHolder(
+ arrayField.nestedField.fieldName,
+ arrayField.nestedField.fieldIndex
+ ).getColumn();
+ ColumnValueSelector<?> arraySelector =
col.makeColumnValueSelector(readableOffset);
+ return new BaseSingleValueDimensionSelector()
+ {
+ @Nullable
+ @Override
+ protected String getValue()
{
- @Nullable
- @Override
- protected String getValue()
- {
- Object o = arraySelector.getObject();
- if (o instanceof Object[]) {
- Object[] array = (Object[]) o;
- if (elementNumber < array.length) {
- Object element = array[elementNumber];
- if (element == null) {
- return null;
- }
- return String.valueOf(element);
+ Object o = arraySelector.getObject();
+ if (o instanceof Object[]) {
+ Object[] array = (Object[]) o;
+ if (elementNumber < array.length) {
+ Object element = array[elementNumber];
+ if (element == null) {
+ return null;
}
+ return String.valueOf(element);
}
- return null;
}
+ return null;
+ }
+
+ @Override
+ public void inspectRuntimeShape(RuntimeShapeInspector inspector)
+ {
+ arraySelector.inspectRuntimeShape(inspector);
+ }
+ };
- @Override
- public void inspectRuntimeShape(RuntimeShapeInspector inspector)
- {
- arraySelector.inspectRuntimeShape(inspector);
- }
- };
- }
}
return DimensionSelector.constant(null);
}
@@ -560,88 +630,84 @@ public abstract class
CompressedNestedDataComplexColumn<TKeyDictionary extends I
ReadableOffset readableOffset
)
{
- final TKeyDictionary fields = fieldsSupplier.get();
- final String field = getField(path);
-
- Preconditions.checkNotNull(field, "Null field");
- final int fieldIndex = fields.indexOf(StringUtils.toUtf8ByteBuffer(field));
- if (fieldIndex >= 0) {
- BaseColumn col = getColumnHolder(field, fieldIndex).getColumn();
- return col.makeColumnValueSelector(readableOffset);
- }
- if (!path.isEmpty() && path.get(path.size() - 1) instanceof
NestedPathArrayElement) {
- final NestedPathPart lastPath = path.get(path.size() - 1);
- final String arrayField = getField(path.subList(0, path.size() - 1));
- final int arrayFieldIndex =
fields.indexOf(StringUtils.toUtf8ByteBuffer(arrayField));
- if (arrayFieldIndex >= 0) {
- final int elementNumber = ((NestedPathArrayElement)
lastPath).getIndex();
- if (elementNumber < 0) {
- throw DruidException.forPersona(DruidException.Persona.USER)
-
.ofCategory(DruidException.Category.INVALID_INPUT)
- .build("Cannot make array element selector for
path [%s], negative array index not supported for this selector", path);
- }
- DictionaryEncodedColumn<?> col = (DictionaryEncodedColumn<?>)
getColumnHolder(
- arrayField,
- arrayFieldIndex
- ).getColumn();
- ColumnValueSelector arraySelector =
col.makeColumnValueSelector(readableOffset);
- return new ColumnValueSelector<>()
+ Field field = getNestedFieldOrNestedArrayElementFromPath(path);
+ if (field instanceof NestedField) {
+ final NestedField nestedField = (NestedField) field;
+ return getColumnHolder(nestedField.fieldName,
nestedField.fieldIndex).getColumn()
+
.makeColumnValueSelector(readableOffset);
+ } else if (field instanceof NestedArrayElement) {
+ final NestedArrayElement arrayField = (NestedArrayElement) field;
+ final int elementNumber = arrayField.elementNumber;
+ if (elementNumber < 0) {
+ throw DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.INVALID_INPUT)
+ .build(
+ "Cannot make array element selector for path
[%s], negative array index not supported for this selector",
+ path
+ );
+ }
+ DictionaryEncodedColumn<?> col = (DictionaryEncodedColumn<?>)
getColumnHolder(
+ arrayField.nestedField.fieldName,
+ arrayField.nestedField.fieldIndex
+ ).getColumn();
+ ColumnValueSelector arraySelector =
col.makeColumnValueSelector(readableOffset);
+ return new ColumnValueSelector<>()
+ {
+ @Override
+ public boolean isNull()
{
- @Override
- public boolean isNull()
- {
- Object o = getObject();
- return !(o instanceof Number);
- }
+ Object o = getObject();
+ return !(o instanceof Number);
+ }
- @Override
- public long getLong()
- {
- Object o = getObject();
- return o instanceof Number ? ((Number) o).longValue() : 0L;
- }
+ @Override
+ public long getLong()
+ {
+ Object o = getObject();
+ return o instanceof Number ? ((Number) o).longValue() : 0L;
+ }
- @Override
- public float getFloat()
- {
- Object o = getObject();
- return o instanceof Number ? ((Number) o).floatValue() : 0f;
- }
+ @Override
+ public float getFloat()
+ {
+ Object o = getObject();
+ return o instanceof Number ? ((Number) o).floatValue() : 0f;
+ }
- @Override
- public double getDouble()
- {
- Object o = getObject();
- return o instanceof Number ? ((Number) o).doubleValue() : 0.0;
- }
+ @Override
+ public double getDouble()
+ {
+ Object o = getObject();
+ return o instanceof Number ? ((Number) o).doubleValue() : 0.0;
+ }
- @Override
- public void inspectRuntimeShape(RuntimeShapeInspector inspector)
- {
- arraySelector.inspectRuntimeShape(inspector);
- }
+ @Override
+ public void inspectRuntimeShape(RuntimeShapeInspector inspector)
+ {
+ arraySelector.inspectRuntimeShape(inspector);
+ }
- @Nullable
- @Override
- public Object getObject()
- {
- Object o = arraySelector.getObject();
- if (o instanceof Object[]) {
- Object[] array = (Object[]) o;
- if (elementNumber < array.length) {
- return array[elementNumber];
- }
+ @Nullable
+ @Override
+ public Object getObject()
+ {
+ Object o = arraySelector.getObject();
+ if (o instanceof Object[]) {
+ Object[] array = (Object[]) o;
+ if (elementNumber < array.length) {
+ return array[elementNumber];
}
- return null;
}
+ return null;
+ }
+
+ @Override
+ public Class<?> classOfObject()
+ {
+ return Object.class;
+ }
+ };
- @Override
- public Class<?> classOfObject()
- {
- return Object.class;
- }
- };
- }
}
return NilColumnValueSelector.instance();
}
@@ -653,12 +719,13 @@ public abstract class
CompressedNestedDataComplexColumn<TKeyDictionary extends I
ReadableVectorOffset readableOffset
)
{
- final TKeyDictionary fields = fieldsSupplier.get();
- final String field = getField(path);
- Preconditions.checkNotNull(field, "Null field");
- final int fieldIndex = fields.indexOf(StringUtils.toUtf8ByteBuffer(field));
- if (fieldIndex >= 0) {
- DictionaryEncodedColumn<?> col = (DictionaryEncodedColumn<?>)
getColumnHolder(field, fieldIndex).getColumn();
+ final Field field = getNestedFieldOrNestedArrayElementFromPath(path);
+ if (field instanceof NestedField) {
+ NestedField nestedField = (NestedField) field;
+ DictionaryEncodedColumn<?> col = (DictionaryEncodedColumn<?>)
getColumnHolder(
+ nestedField.fieldName,
+ nestedField.fieldIndex
+ ).getColumn();
return col.makeSingleValueDimensionVectorSelector(readableOffset);
} else {
return NilVectorSelector.create(readableOffset);
@@ -679,74 +746,69 @@ public abstract class
CompressedNestedDataComplexColumn<TKeyDictionary extends I
ReadableVectorOffset readableOffset
)
{
- final TKeyDictionary fields = fieldsSupplier.get();
- final String field = getField(path);
- Preconditions.checkNotNull(field, "Null field");
- final int fieldIndex = fields.indexOf(StringUtils.toUtf8ByteBuffer(field));
- if (fieldIndex >= 0) {
- BaseColumn col = getColumnHolder(field, fieldIndex).getColumn();
- return col.makeVectorObjectSelector(readableOffset);
- }
- if (!path.isEmpty() && path.get(path.size() - 1) instanceof
NestedPathArrayElement) {
- final NestedPathPart lastPath = path.get(path.size() - 1);
- final String arrayField = getField(path.subList(0, path.size() - 1));
- final int arrayFieldIndex =
fields.indexOf(StringUtils.toUtf8ByteBuffer(arrayField));
- if (arrayFieldIndex >= 0) {
- final int elementNumber = ((NestedPathArrayElement)
lastPath).getIndex();
- if (elementNumber < 0) {
- throw DruidException.forPersona(DruidException.Persona.USER)
-
.ofCategory(DruidException.Category.INVALID_INPUT)
- .build("Cannot make array element selector for
path [%s], negative array index not supported for this selector", path);
- }
- DictionaryEncodedColumn<?> col = (DictionaryEncodedColumn<?>)
getColumnHolder(
- arrayField,
- arrayFieldIndex
- ).getColumn();
- VectorObjectSelector arraySelector =
col.makeVectorObjectSelector(readableOffset);
+ final Field field = getNestedFieldOrNestedArrayElementFromPath(path);
+ if (field instanceof NestedField) {
+ NestedField nestedField = (NestedField) field;
+ return getColumnHolder(nestedField.fieldName,
nestedField.fieldIndex).getColumn()
+
.makeVectorObjectSelector(readableOffset);
+ } else if (field instanceof NestedArrayElement) {
+ final NestedArrayElement arrayField = (NestedArrayElement) field;
+ final int elementNumber = arrayField.elementNumber;
+ if (elementNumber < 0) {
+ throw DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.INVALID_INPUT)
+ .build(
+ "Cannot make array element selector for path
[%s], negative array index not supported for this selector",
+ path
+ );
+ }
+ VectorObjectSelector arraySelector = getColumnHolder(
+ arrayField.nestedField.fieldName,
+ arrayField.nestedField.fieldIndex
+ ).getColumn().makeVectorObjectSelector(readableOffset);
+ return new VectorObjectSelector()
+ {
+ private final Object[] elements = new
Object[arraySelector.getMaxVectorSize()];
+ private int id = ReadableVectorInspector.NULL_ID;
- return new VectorObjectSelector()
+ @Override
+ public Object[] getObjectVector()
{
- private final Object[] elements = new
Object[arraySelector.getMaxVectorSize()];
- private int id = ReadableVectorInspector.NULL_ID;
-
- @Override
- public Object[] getObjectVector()
- {
- if (readableOffset.getId() != id) {
- final Object[] delegate = arraySelector.getObjectVector();
- for (int i = 0; i < arraySelector.getCurrentVectorSize(); i++) {
- Object maybeArray = delegate[i];
- if (maybeArray instanceof Object[]) {
- Object[] anArray = (Object[]) maybeArray;
- if (elementNumber < anArray.length) {
- final Object element = anArray[elementNumber];
- elements[i] = element;
- } else {
- elements[i] = null;
- }
+ if (readableOffset.getId() != id) {
+ final Object[] delegate = arraySelector.getObjectVector();
+ for (int i = 0; i < arraySelector.getCurrentVectorSize(); i++) {
+ Object maybeArray = delegate[i];
+ if (maybeArray instanceof Object[]) {
+ Object[] anArray = (Object[]) maybeArray;
+ if (elementNumber < anArray.length) {
+ final Object element = anArray[elementNumber];
+ elements[i] = element;
} else {
elements[i] = null;
}
+ } else {
+ elements[i] = null;
}
- id = readableOffset.getId();
}
- return elements;
+ id = readableOffset.getId();
}
+ return elements;
+ }
- @Override
- public int getMaxVectorSize()
- {
- return arraySelector.getMaxVectorSize();
- }
+ @Override
+ public int getMaxVectorSize()
+ {
+ return arraySelector.getMaxVectorSize();
+ }
- @Override
- public int getCurrentVectorSize()
- {
- return arraySelector.getCurrentVectorSize();
- }
- };
- }
+ @Override
+ public int getCurrentVectorSize()
+ {
+ return arraySelector.getCurrentVectorSize();
+ }
+ };
}
+
return NilVectorSelector.create(readableOffset);
}
@@ -764,137 +826,132 @@ public abstract class
CompressedNestedDataComplexColumn<TKeyDictionary extends I
ReadableVectorOffset readableOffset
)
{
- final TKeyDictionary fields = fieldsSupplier.get();
- final String field = getField(path);
- Preconditions.checkNotNull(field, "Null field");
- final int fieldIndex = fields.indexOf(StringUtils.toUtf8ByteBuffer(field));
- if (fieldIndex >= 0) {
- BaseColumn col = getColumnHolder(field, fieldIndex).getColumn();
- return col.makeVectorValueSelector(readableOffset);
- }
- if (!path.isEmpty() && path.get(path.size() - 1) instanceof
NestedPathArrayElement) {
- final NestedPathPart lastPath = path.get(path.size() - 1);
- final String arrayField = getField(path.subList(0, path.size() - 1));
- final int arrayFieldIndex =
fields.indexOf(StringUtils.toUtf8ByteBuffer(arrayField));
- if (arrayFieldIndex >= 0) {
- final int elementNumber = ((NestedPathArrayElement)
lastPath).getIndex();
- if (elementNumber < 0) {
- throw DruidException.forPersona(DruidException.Persona.USER)
-
.ofCategory(DruidException.Category.INVALID_INPUT)
- .build("Cannot make array element selector for
path [%s], negative array index not supported for this selector", path);
- }
- DictionaryEncodedColumn<?> col = (DictionaryEncodedColumn<?>)
getColumnHolder(
- arrayField,
- arrayFieldIndex
- ).getColumn();
- VectorObjectSelector arraySelector =
col.makeVectorObjectSelector(readableOffset);
+ final Field field = getNestedFieldOrNestedArrayElementFromPath(path);
+ if (field instanceof NestedField) {
+ NestedField nestedField = (NestedField) field;
+ return getColumnHolder(nestedField.fieldName,
nestedField.fieldIndex).getColumn()
+
.makeVectorValueSelector(readableOffset);
+ } else if (field instanceof NestedArrayElement) {
+ final NestedArrayElement arrayField = (NestedArrayElement) field;
+ final int elementNumber = arrayField.elementNumber;
+ if (elementNumber < 0) {
+ throw DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.INVALID_INPUT)
+ .build(
+ "Cannot make array element selector for path
[%s], negative array index not supported for this selector",
+ path
+ );
+ }
+ VectorObjectSelector arraySelector = getColumnHolder(
+ arrayField.nestedField.fieldName,
+ arrayField.nestedField.fieldIndex
+ ).getColumn().makeVectorObjectSelector(readableOffset);
+
+ return new VectorValueSelector()
+ {
+ private final long[] longs = new
long[readableOffset.getMaxVectorSize()];
+ private final double[] doubles = new
double[readableOffset.getMaxVectorSize()];
+ private final float[] floats = new
float[readableOffset.getMaxVectorSize()];
+ private final boolean[] nulls = new
boolean[readableOffset.getMaxVectorSize()];
+ private int id = ReadableVectorInspector.NULL_ID;
- return new VectorValueSelector()
+ private void computeNumbers()
{
- private final long[] longs = new
long[readableOffset.getMaxVectorSize()];
- private final double[] doubles = new
double[readableOffset.getMaxVectorSize()];
- private final float[] floats = new
float[readableOffset.getMaxVectorSize()];
- private final boolean[] nulls = new
boolean[readableOffset.getMaxVectorSize()];
- private int id = ReadableVectorInspector.NULL_ID;
-
- private void computeNumbers()
- {
- if (readableOffset.getId() != id) {
- final Object[] maybeArrays = arraySelector.getObjectVector();
- for (int i = 0; i < arraySelector.getCurrentVectorSize(); i++) {
- Object maybeArray = maybeArrays[i];
- if (maybeArray instanceof Object[]) {
- Object[] anArray = (Object[]) maybeArray;
- if (elementNumber < anArray.length) {
- if (anArray[elementNumber] instanceof Number) {
- Number n = (Number) anArray[elementNumber];
- longs[i] = n.longValue();
- doubles[i] = n.doubleValue();
- floats[i] = n.floatValue();
+ if (readableOffset.getId() != id) {
+ final Object[] maybeArrays = arraySelector.getObjectVector();
+ for (int i = 0; i < arraySelector.getCurrentVectorSize(); i++) {
+ Object maybeArray = maybeArrays[i];
+ if (maybeArray instanceof Object[]) {
+ Object[] anArray = (Object[]) maybeArray;
+ if (elementNumber < anArray.length) {
+ if (anArray[elementNumber] instanceof Number) {
+ Number n = (Number) anArray[elementNumber];
+ longs[i] = n.longValue();
+ doubles[i] = n.doubleValue();
+ floats[i] = n.floatValue();
+ nulls[i] = false;
+ } else {
+ Double d = anArray[elementNumber] instanceof String
+ ? Doubles.tryParse((String)
anArray[elementNumber])
+ : null;
+ if (d != null) {
+ longs[i] = d.longValue();
+ doubles[i] = d;
+ floats[i] = d.floatValue();
nulls[i] = false;
} else {
- Double d = anArray[elementNumber] instanceof String
- ? Doubles.tryParse((String)
anArray[elementNumber])
- : null;
- if (d != null) {
- longs[i] = d.longValue();
- doubles[i] = d;
- floats[i] = d.floatValue();
- nulls[i] = false;
- } else {
- nullElement(i);
- }
+ nullElement(i);
}
- } else {
- nullElement(i);
}
} else {
- // not an array?
nullElement(i);
}
+ } else {
+ // not an array?
+ nullElement(i);
}
- id = readableOffset.getId();
}
+ id = readableOffset.getId();
}
+ }
- private void nullElement(int i)
- {
- longs[i] = 0L;
- doubles[i] = 0L;
- floats[i] = 0L;
- nulls[i] = true;
- }
+ private void nullElement(int i)
+ {
+ longs[i] = 0L;
+ doubles[i] = 0L;
+ floats[i] = 0L;
+ nulls[i] = true;
+ }
- @Override
- public long[] getLongVector()
- {
- if (readableOffset.getId() != id) {
- computeNumbers();
- }
- return longs;
+ @Override
+ public long[] getLongVector()
+ {
+ if (readableOffset.getId() != id) {
+ computeNumbers();
}
+ return longs;
+ }
- @Override
- public float[] getFloatVector()
- {
- if (readableOffset.getId() != id) {
- computeNumbers();
- }
- return floats;
+ @Override
+ public float[] getFloatVector()
+ {
+ if (readableOffset.getId() != id) {
+ computeNumbers();
}
+ return floats;
+ }
- @Override
- public double[] getDoubleVector()
- {
- if (readableOffset.getId() != id) {
- computeNumbers();
- }
- return doubles;
+ @Override
+ public double[] getDoubleVector()
+ {
+ if (readableOffset.getId() != id) {
+ computeNumbers();
}
+ return doubles;
+ }
- @Nullable
- @Override
- public boolean[] getNullVector()
- {
- if (readableOffset.getId() != id) {
- computeNumbers();
- }
- return nulls;
+ @Nullable
+ @Override
+ public boolean[] getNullVector()
+ {
+ if (readableOffset.getId() != id) {
+ computeNumbers();
}
+ return nulls;
+ }
- @Override
- public int getMaxVectorSize()
- {
- return arraySelector.getMaxVectorSize();
- }
+ @Override
+ public int getMaxVectorSize()
+ {
+ return arraySelector.getMaxVectorSize();
+ }
- @Override
- public int getCurrentVectorSize()
- {
- return arraySelector.getCurrentVectorSize();
- }
- };
- }
+ @Override
+ public int getCurrentVectorSize()
+ {
+ return arraySelector.getCurrentVectorSize();
+ }
+ };
}
return NilVectorSelector.create(readableOffset);
}
@@ -904,19 +961,13 @@ public abstract class
CompressedNestedDataComplexColumn<TKeyDictionary extends I
@Override
public Set<ColumnType> getFieldTypes(List<NestedPathPart> path)
{
- final TKeyDictionary fields = fieldsSupplier.get();
- String field = getField(path);
- int index = fields.indexOf(StringUtils.toUtf8ByteBuffer(field));
- // if index is negative, check for an array element accessor in the path
- if (index < 0) {
- if (!path.isEmpty() && path.get(path.size() - 1) instanceof
NestedPathArrayElement) {
- final String arrayField = getField(path.subList(0, path.size() - 1));
- index = fields.indexOf(StringUtils.toUtf8ByteBuffer(arrayField));
- }
- if (index < 0) {
- return null;
- }
- final Set<ColumnType> arrayFieldTypes =
FieldTypeInfo.convertToSet(fieldInfo.getTypes(index).getByteValue());
+ final Field field = getNestedFieldOrNestedArrayElementFromPath(path);
+ if (field instanceof NestedField) {
+ return FieldTypeInfo.convertToSet(fieldInfo.getTypes(((NestedField)
field).fieldIndex).getByteValue());
+ } else if (field instanceof NestedArrayElement) {
+ final NestedArrayElement arrayField = (NestedArrayElement) field;
+ final Set<ColumnType> arrayFieldTypes =
FieldTypeInfo.convertToSet(fieldInfo.getTypes(arrayField.nestedField.fieldIndex)
+
.getByteValue());
final Set<ColumnType> elementTypes =
Sets.newHashSetWithExpectedSize(arrayFieldTypes.size());
for (ColumnType type : arrayFieldTypes) {
if (type.isArray()) {
@@ -927,27 +978,24 @@ public abstract class
CompressedNestedDataComplexColumn<TKeyDictionary extends I
}
return elementTypes;
}
- return
FieldTypeInfo.convertToSet(fieldInfo.getTypes(index).getByteValue());
+ return null;
}
@Nullable
@Override
public ColumnType getFieldLogicalType(List<NestedPathPart> path)
{
- final String field = getField(path);
- final Set<ColumnType> fieldTypes;
- int index =
fieldsSupplier.get().indexOf(StringUtils.toUtf8ByteBuffer(field));
- ColumnType leastRestrictiveType = null;
- if (index < 0) {
- if (!path.isEmpty() && path.get(path.size() - 1) instanceof
NestedPathArrayElement) {
- final String arrayField = getField(path.subList(0, path.size() - 1));
- index =
fieldsSupplier.get().indexOf(StringUtils.toUtf8ByteBuffer(arrayField));
- }
- if (index < 0) {
- return null;
- }
- fieldTypes =
FieldTypeInfo.convertToSet(fieldInfo.getTypes(index).getByteValue());
- for (ColumnType type : fieldTypes) {
+ final Field field = getNestedFieldOrNestedArrayElementFromPath(path);
+ if (field instanceof NestedField) {
+ final Set<ColumnType> fieldTypes =
FieldTypeInfo.convertToSet(fieldInfo.getTypes(((NestedField) field).fieldIndex)
+
.getByteValue());
+ return ColumnType.leastRestrictiveType(fieldTypes);
+ } else if (field instanceof NestedArrayElement) {
+ final NestedArrayElement arrayField = (NestedArrayElement) field;
+ final Set<ColumnType> arrayFieldTypes =
FieldTypeInfo.convertToSet(fieldInfo.getTypes(arrayField.nestedField.fieldIndex)
+
.getByteValue());
+ ColumnType leastRestrictiveType = null;
+ for (ColumnType type : arrayFieldTypes) {
if (type.isArray()) {
leastRestrictiveType = ColumnType.leastRestrictiveType(
leastRestrictiveType,
@@ -957,39 +1005,34 @@ public abstract class
CompressedNestedDataComplexColumn<TKeyDictionary extends I
leastRestrictiveType =
ColumnType.leastRestrictiveType(leastRestrictiveType, type);
}
}
- } else {
- fieldTypes =
FieldTypeInfo.convertToSet(fieldInfo.getTypes(index).getByteValue());
- leastRestrictiveType = ColumnType.leastRestrictiveType(fieldTypes);
+ return leastRestrictiveType;
+
}
- return leastRestrictiveType;
+ return null;
}
@Nullable
@Override
- public ColumnHolder getColumnHolder(List<NestedPathPart> path)
+ public BaseColumnHolder getColumnHolder(List<NestedPathPart> path)
{
- final TKeyDictionary fields = fieldsSupplier.get();
- final String field = getField(path);
- final int fieldIndex = fields.indexOf(StringUtils.toUtf8ByteBuffer(field));
- return getColumnHolder(field, fieldIndex);
+ final Field field = getNestedFieldOrNestedArrayElementFromPath(path);
+ if (field instanceof NestedField) {
+ final NestedField nestedField = (NestedField) field;
+ return getColumnHolder(nestedField.fieldName, nestedField.fieldIndex);
+ }
+ return null;
}
@Nullable
@Override
public ColumnIndexSupplier getColumnIndexSupplier(List<NestedPathPart> path)
{
- final TKeyDictionary fields = fieldsSupplier.get();
- final String field = getField(path);
- int fieldIndex = fields.indexOf(StringUtils.toUtf8ByteBuffer(field));
- if (fieldIndex >= 0) {
- return getColumnHolder(field, fieldIndex).getIndexSupplier();
- }
- if (!path.isEmpty() && path.get(path.size() - 1) instanceof
NestedPathArrayElement) {
- final String arrayField = getField(path.subList(0, path.size() - 1));
- final int arrayFieldIndex =
fields.indexOf(StringUtils.toUtf8ByteBuffer(arrayField));
- if (arrayFieldIndex >= 0) {
- return NoIndexesColumnIndexSupplier.getInstance();
- }
+ final Field field = getNestedFieldOrNestedArrayElementFromPath(path);
+ if (field instanceof NestedField) {
+ final NestedField nestedField = (NestedField) field;
+ return getColumnHolder(nestedField.fieldName,
nestedField.fieldIndex).getIndexSupplier();
+ } else if (field instanceof NestedArrayElement) {
+ return NoIndexesColumnIndexSupplier.getInstance();
}
return null;
}
@@ -997,13 +1040,12 @@ public abstract class
CompressedNestedDataComplexColumn<TKeyDictionary extends I
@Override
public boolean isNumeric(List<NestedPathPart> path)
{
- final TKeyDictionary fields = fieldsSupplier.get();
- final String field = getField(path);
- final int fieldIndex = fields.indexOf(StringUtils.toUtf8ByteBuffer(field));
- if (fieldIndex < 0) {
- return true;
+ final Field field = getNestedFieldOrNestedArrayElementFromPath(path);
+ if (field instanceof NestedField) {
+ final NestedField nestedField = (NestedField) field;
+ return getColumnHolder(nestedField.fieldName,
nestedField.fieldIndex).getCapabilities().isNumeric();
}
- return getColumnHolder(field, fieldIndex).getCapabilities().isNumeric();
+ return true;
}
@SuppressWarnings("unchecked")
@@ -1206,4 +1248,94 @@ public abstract class
CompressedNestedDataComplexColumn<TKeyDictionary extends I
return Integer.compare(((Number) o1).intValue(), ((Number)
o2).intValue());
}
}
+
+ private List<NestedField> getAllNestedFields()
+ {
+ TKeyDictionary fields = fieldsSupplier.get();
+ List<NestedField> allFields = new ArrayList<>(fields.size());
+ for (int i = 0; i < fields.size(); i++) {
+ String field = StringUtils.fromUtf8(fields.get(i));
+ allFields.add(new NestedField(field, i));
+ }
+ return allFields;
+ }
+
+ private List<Pair<NestedField, List<NestedPathPart>>>
getAllParsedNestedFields()
+ {
+ TKeyDictionary fields = fieldsSupplier.get();
+ List<Pair<NestedField, List<NestedPathPart>>> allFields = new
ArrayList<>(fields.size());
+ for (int i = 0; i < fields.size(); i++) {
+ String field = StringUtils.fromUtf8(fields.get(i));
+ allFields.add(Pair.of(new NestedField(field, i), parsePath(field)));
+ }
+ return allFields;
+ }
+
+ /**
+ * Returns a representation of a field or array element within a nested
object structure, given a path.
+ * <p>
+ * Returns null if the path does not correspond to any field or array
element.
+ */
+ @Nullable
+ private Field
getNestedFieldOrNestedArrayElementFromPath(List<NestedPathPart> path)
+ {
+ TKeyDictionary fields = fieldsSupplier.get();
+ List<List<NestedPathPart>> parsed = new ArrayList<>(fields.size());
+ for (int i = 0; i < fields.size(); i++) {
+ String field = StringUtils.fromUtf8(fields.get(i));
+ parsed.add(parsePath(field));
+ if (parsed.get(i).equals(path)) {
+ return new NestedField(field, i);
+ }
+ }
+ if (!path.isEmpty() && path.get(path.size() - 1) instanceof
NestedPathArrayElement) {
+ List<NestedPathPart> arrayPath = path.subList(0, path.size() - 1);
+ for (int i = 0; i < fields.size(); i++) {
+ if (parsed.get(i).equals(arrayPath)) {
+ return new NestedArrayElement(
+ new NestedField(StringUtils.fromUtf8(fields.get(i)), i),
+ ((NestedPathArrayElement) path.get(path.size() - 1)).getIndex()
+ );
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Represents a single target element within a nested object structure.
+ */
+ interface Field
+ {
+ }
+
+ /**
+ * Represents a field located within a nested object hierarchy, could be
scalar or array.
+ */
+ private static class NestedField implements Field
+ {
+ private final String fieldName;
+ private final int fieldIndex;
+
+ NestedField(String fieldName, int fieldIndex)
+ {
+ this.fieldName = fieldName;
+ this.fieldIndex = fieldIndex;
+ }
+ }
+
+ /**
+ * Represents an element located within an array field inside a nested
object hierarchy.
+ */
+ private static class NestedArrayElement implements Field
+ {
+ private final NestedField nestedField;
+ private final int elementNumber;
+
+ NestedArrayElement(NestedField nestedField, int elementNumber)
+ {
+ this.nestedField = nestedField;
+ this.elementNumber = elementNumber;
+ }
+ }
}
diff --git
a/processing/src/main/java/org/apache/druid/segment/nested/NestedDataColumnSerializer.java
b/processing/src/main/java/org/apache/druid/segment/nested/NestedDataColumnSerializer.java
index 157750b3c7b..bae670f0554 100644
---
a/processing/src/main/java/org/apache/druid/segment/nested/NestedDataColumnSerializer.java
+++
b/processing/src/main/java/org/apache/druid/segment/nested/NestedDataColumnSerializer.java
@@ -142,7 +142,7 @@ public class NestedDataColumnSerializer extends
NestedCommonFormatColumnSerializ
private FixedIndexedWriter<Long> longDictionaryWriter;
private FixedIndexedWriter<Double> doubleDictionaryWriter;
private FrontCodedIntArrayIndexedWriter arrayDictionaryWriter;
- private CompressedVariableSizedBlobColumnSerializer rawWriter;
+ @Nullable private CompressedVariableSizedBlobColumnSerializer rawWriter;
private ByteBufferWriter<ImmutableBitmap> nullBitmapWriter;
private MutableBitmap nullRowsBitmap;
private Map<String, GlobalDictionaryEncodedFieldColumnWriter<?>>
fieldWriters;
@@ -243,12 +243,19 @@ public class NestedDataColumnSerializer extends
NestedCommonFormatColumnSerializ
@Override
public void open() throws IOException
{
- rawWriter = new CompressedVariableSizedBlobColumnSerializer(
- ColumnSerializerUtils.getInternalFileName(name, RAW_FILE_NAME),
- segmentWriteOutMedium,
- columnFormatSpec.getObjectStorageCompression()
- );
- rawWriter.open();
+ if
(ObjectStorageEncoding.NONE.equals(columnFormatSpec.getObjectStorageEncoding()))
{
+ rawWriter = null;
+ } else if
(ObjectStorageEncoding.SMILE.equals(columnFormatSpec.getObjectStorageEncoding()))
{
+ rawWriter = new CompressedVariableSizedBlobColumnSerializer(
+ ColumnSerializerUtils.getInternalFileName(name, RAW_FILE_NAME),
+ segmentWriteOutMedium,
+ columnFormatSpec.getObjectStorageCompression()
+ );
+ rawWriter.open();
+ } else {
+ throw DruidException.defensive("Unknown object storage encoding [%s]",
columnFormatSpec.getObjectStorageEncoding()
+ );
+ }
nullBitmapWriter = new ByteBufferWriter<>(
segmentWriteOutMedium,
@@ -339,7 +346,9 @@ public class NestedDataColumnSerializer extends
NestedCommonFormatColumnSerializ
if (data == null) {
nullRowsBitmap.add(rowCount);
}
- rawWriter.addValue(NestedDataComplexTypeSerde.INSTANCE.toBytes(data));
+ if (rawWriter != null) {
+ rawWriter.addValue(NestedDataComplexTypeSerde.INSTANCE.toBytes(data));
+ }
if (data != null) {
fieldProcessor.processFields(data.getValue());
}
@@ -410,7 +419,9 @@ public class NestedDataColumnSerializer extends
NestedCommonFormatColumnSerializ
writeInternal(smoosher, arrayDictionaryWriter,
ColumnSerializerUtils.ARRAY_DICTIONARY_FILE_NAME);
}
}
- writeInternal(smoosher, rawWriter, RAW_FILE_NAME);
+ if (rawWriter != null) {
+ writeInternal(smoosher, rawWriter, RAW_FILE_NAME);
+ }
if (!nullRowsBitmap.isEmpty()) {
writeInternal(smoosher, nullBitmapWriter,
ColumnSerializerUtils.NULL_BITMAP_FILE_NAME);
}
diff --git
a/processing/src/main/java/org/apache/druid/segment/nested/NestedDataColumnSupplier.java
b/processing/src/main/java/org/apache/druid/segment/nested/NestedDataColumnSupplier.java
index 204f8d2b3ba..816c7fe5f14 100644
---
a/processing/src/main/java/org/apache/druid/segment/nested/NestedDataColumnSupplier.java
+++
b/processing/src/main/java/org/apache/druid/segment/nested/NestedDataColumnSupplier.java
@@ -75,7 +75,6 @@ public class NestedDataColumnSupplier implements
Supplier<NestedCommonFormatColu
final Supplier<FrontCodedIntArrayIndexed> arrayDictionarySupplier;
-
if (parent != null) {
fieldsSupplier = parent.fieldSupplier;
fieldInfo = parent.fieldInfo;
@@ -130,22 +129,23 @@ public class NestedDataColumnSupplier implements
Supplier<NestedCommonFormatColu
);
}
-
final ByteBuffer rawBuffer =
NestedCommonFormatColumnPartSerde.loadInternalFile(
mapper,
columnName,
NestedCommonFormatColumnSerializer.RAW_FILE_NAME
);
- compressedRawColumnSupplier =
CompressedVariableSizedBlobColumnSupplier.fromByteBuffer(
- ColumnSerializerUtils.getInternalFileName(
- columnName,
- NestedCommonFormatColumnSerializer.RAW_FILE_NAME
- ),
- rawBuffer,
- byteOrder,
- byteOrder, // byte order doesn't matter since serde is byte blobs
- mapper
- );
+ compressedRawColumnSupplier = rawBuffer == null
+ ? null
+ :
CompressedVariableSizedBlobColumnSupplier.fromByteBuffer(
+
ColumnSerializerUtils.getInternalFileName(
+ columnName,
+
NestedCommonFormatColumnSerializer.RAW_FILE_NAME
+ ),
+ rawBuffer,
+ byteOrder,
+ byteOrder, // byte order doesn't
matter since serde is byte blobs
+ mapper
+ );
if (hasNulls) {
columnBuilder.setHasNulls(true);
final ByteBuffer nullIndexBuffer =
NestedCommonFormatColumnPartSerde.loadInternalFile(
@@ -186,6 +186,7 @@ public class NestedDataColumnSupplier implements
Supplier<NestedCommonFormatColu
private final String columnName;
private final Supplier<? extends Indexed<ByteBuffer>> fieldSupplier;
private final FieldTypeInfo fieldInfo;
+ @Nullable
private final CompressedVariableSizedBlobColumnSupplier
compressedRawColumnSupplier;
private final ImmutableBitmap nullValues;
private final Supplier<? extends Indexed<ByteBuffer>>
stringDictionarySupplier;
@@ -204,7 +205,7 @@ public class NestedDataColumnSupplier implements
Supplier<NestedCommonFormatColu
String columnName,
Supplier<? extends Indexed<ByteBuffer>> fieldSupplier,
FieldTypeInfo fieldInfo,
- CompressedVariableSizedBlobColumnSupplier compressedRawColumnSupplier,
+ @Nullable CompressedVariableSizedBlobColumnSupplier
compressedRawColumnSupplier,
ImmutableBitmap nullValues,
Supplier<? extends Indexed<ByteBuffer>> stringDictionarySupplier,
Supplier<FixedIndexed<Long>> longDictionarySupplier,
diff --git
a/processing/src/main/java/org/apache/druid/segment/nested/NestedDataColumnV5.java
b/processing/src/main/java/org/apache/druid/segment/nested/NestedDataColumnV5.java
index bdc90b9d9b4..2a375538422 100644
---
a/processing/src/main/java/org/apache/druid/segment/nested/NestedDataColumnV5.java
+++
b/processing/src/main/java/org/apache/druid/segment/nested/NestedDataColumnV5.java
@@ -31,6 +31,7 @@ import
org.apache.druid.segment.data.FrontCodedIntArrayIndexed;
import org.apache.druid.segment.data.Indexed;
import org.apache.druid.segment.serde.ColumnSerializerUtils;
+import javax.annotation.Nullable;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.List;
@@ -51,7 +52,7 @@ public class NestedDataColumnV5<TKeyDictionary extends
Indexed<ByteBuffer>, TStr
String columnName,
ColumnType logicalType,
ColumnConfig columnConfig,
- CompressedVariableSizedBlobColumnSupplier compressedRawColumnSupplier,
+ @Nullable CompressedVariableSizedBlobColumnSupplier
compressedRawColumnSupplier,
ImmutableBitmap nullValues,
Supplier<TKeyDictionary> fields,
FieldTypeInfo fieldInfo,
diff --git
a/processing/src/main/java/org/apache/druid/segment/nested/NestedDataComplexColumn.java
b/processing/src/main/java/org/apache/druid/segment/nested/NestedDataComplexColumn.java
index 7f584a98069..17d782785a8 100644
---
a/processing/src/main/java/org/apache/druid/segment/nested/NestedDataComplexColumn.java
+++
b/processing/src/main/java/org/apache/druid/segment/nested/NestedDataComplexColumn.java
@@ -20,7 +20,7 @@
package org.apache.druid.segment.nested;
-import org.apache.druid.segment.column.ColumnHolder;
+import org.apache.druid.segment.column.BaseColumnHolder;
import org.apache.druid.segment.column.ComplexColumn;
import javax.annotation.Nullable;
@@ -40,10 +40,10 @@ public abstract class NestedDataComplexColumn implements
NestedVectorColumnSelectorFactory
{
/**
- * Get a {@link ColumnHolder} for a nested field column to retrieve
metadata, the column itself, or indexes.
+ * Get a {@link BaseColumnHolder} for a nested field column to retrieve
metadata, the column itself, or indexes.
*/
@Nullable
- public abstract ColumnHolder getColumnHolder(List<NestedPathPart> path);
+ public abstract BaseColumnHolder getColumnHolder(List<NestedPathPart> path);
@Override
public Class<?> getClazz()
diff --git
a/processing/src/main/java/org/apache/druid/segment/nested/NestedFieldDictionaryEncodedColumn.java
b/processing/src/main/java/org/apache/druid/segment/nested/NestedFieldDictionaryEncodedColumn.java
index 11b21adbf9d..261da5ddfc7 100644
---
a/processing/src/main/java/org/apache/druid/segment/nested/NestedFieldDictionaryEncodedColumn.java
+++
b/processing/src/main/java/org/apache/druid/segment/nested/NestedFieldDictionaryEncodedColumn.java
@@ -169,6 +169,23 @@ public class
NestedFieldDictionaryEncodedColumn<TStringDictionary extends Indexe
return null;
}
+ public Object lookupObject(int id)
+ {
+ final int globalId = dictionary.get(id);
+ if (globalId < adjustArrayId) {
+ return lookupGlobalScalarObject(globalId);
+ }
+ int[] arr = globalArrayDictionary.get(globalId - adjustArrayId);
+ if (arr == null) {
+ return null;
+ }
+ final Object[] array = new Object[arr.length];
+ for (int i = 0; i < arr.length; i++) {
+ array[i] = lookupGlobalScalarObject(arr[i]);
+ }
+ return array;
+ }
+
@Override
public int lookupId(String name)
{
@@ -979,6 +996,7 @@ public class
NestedFieldDictionaryEncodedColumn<TStringDictionary extends Indexe
@Nullable
private PeekableIntIterator nullIterator = nullBitmap != null ?
nullBitmap.peekableIterator() : null;
private int offsetMark = -1;
+
@Override
public double[] getDoubleVector()
{
diff --git
a/processing/src/main/java/org/apache/druid/segment/nested/ObjectStorageEncoding.java
b/processing/src/main/java/org/apache/druid/segment/nested/ObjectStorageEncoding.java
index 3b1c823dbe4..2f21cbba64b 100644
---
a/processing/src/main/java/org/apache/druid/segment/nested/ObjectStorageEncoding.java
+++
b/processing/src/main/java/org/apache/druid/segment/nested/ObjectStorageEncoding.java
@@ -25,6 +25,7 @@ import org.apache.druid.java.util.common.StringUtils;
public enum ObjectStorageEncoding
{
+ NONE,
SMILE;
@JsonValue
diff --git
a/processing/src/main/java/org/apache/druid/segment/nested/StructuredDataBuilder.java
b/processing/src/main/java/org/apache/druid/segment/nested/StructuredDataBuilder.java
new file mode 100644
index 00000000000..584ca24d18c
--- /dev/null
+++
b/processing/src/main/java/org/apache/druid/segment/nested/StructuredDataBuilder.java
@@ -0,0 +1,174 @@
+/*
+ * 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.druid.segment.nested;
+
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import org.apache.druid.error.DruidException;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class StructuredDataBuilder
+{
+
+ private final List<Element> elements;
+
+ StructuredDataBuilder(StructuredDataBuilder.Element... elements)
+ {
+ this(List.of(elements));
+ }
+
+ StructuredDataBuilder(List<Element> elements)
+ {
+ this.elements = elements;
+ }
+
+ /**
+ * Creates a StructuredDataBuilder from a list of paths and corresponding
objects.
+ */
+ StructuredDataBuilder(List<List<NestedPathPart>> parts, List<Object> objects)
+ {
+ List<Element> elements = new ArrayList<>();
+ for (int i = 0; i < parts.size(); i++) {
+ elements.add(Element.of(parts.get(i), objects.get(i)));
+ }
+ this.elements = elements;
+ }
+
+ public StructuredData build()
+ {
+ Object subtree = buildObject();
+ return StructuredData.wrap(subtree == null ? Map.of() : subtree);
+ }
+
+ @Nullable
+ private Object buildObject()
+ {
+ Object simpleObject = null;
+ Multimap<String, Element> map = LinkedListMultimap.create();
+ ArrayList<List<Element>> list = new ArrayList<>();
+
+ for (Element element : elements) {
+ if (element.getValue() == null) {
+ // we can't distinguish between null and missing values in structured
data
+ continue;
+ }
+
+ if (element.endOfPath()) {
+ simpleObject = element.getValue();
+ continue;
+ }
+
+ NestedPathPart currentPath = element.getCurrentPath();
+ if (currentPath instanceof NestedPathField) {
+ map.put(((NestedPathField) currentPath).getField(), element.next());
+ } else if (currentPath instanceof NestedPathArrayElement) {
+ int index = ((NestedPathArrayElement) currentPath).getIndex();
+ while (list.size() <= index) {
+ list.add(new ArrayList<>());
+ }
+ list.get(index).add(element.next());
+ }
+ }
+
+ if (simpleObject != null) {
+ if (!(map.isEmpty() && list.isEmpty())) {
+ throw DruidException.defensive(
+ "Error building structured data from paths[%s], cannot have map or
array elements when root value is set",
+ elements
+ );
+ }
+ return simpleObject;
+ } else if (!map.isEmpty()) {
+ if (!list.isEmpty()) {
+ throw DruidException.defensive(
+ "Error building structured data from paths[%s], cannot have both
map and array elements at the same level",
+ elements
+ );
+ }
+ return Maps.transformValues(
+ map.asMap(),
+ (mapElements) -> new StructuredDataBuilder(new
ArrayList<>(mapElements)).buildObject()
+ );
+ } else if (!list.isEmpty()) {
+ List<Object> resultList = new ArrayList<>(list.size());
+ for (List<Element> elementList : list) {
+ resultList.add(new StructuredDataBuilder(elementList).buildObject());
+ }
+ return resultList;
+ }
+ return null;
+ }
+
+ public static class Element
+ {
+ final List<NestedPathPart> path;
+ @Nullable
+ final Object value;
+ final int depth;
+
+ Element(List<NestedPathPart> path, Object value, int depth)
+ {
+ this.path = path;
+ this.value = value;
+ this.depth = depth;
+ }
+
+ static Element of(List<NestedPathPart> path, Object value)
+ {
+ return new Element(path, value, 0);
+ }
+
+ @Nullable
+ Object getValue()
+ {
+ return value;
+ }
+
+ NestedPathPart getCurrentPath()
+ {
+ return path.get(depth);
+ }
+
+ boolean endOfPath()
+ {
+ return path.size() == depth;
+ }
+
+ Element next()
+ {
+ return new Element(path, value, depth + 1);
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Element{" +
+ "path=" + path +
+ ", value=" + value +
+ ", depth=" + depth +
+ '}';
+ }
+ }
+}
diff --git
a/processing/src/test/java/org/apache/druid/query/NestedDataTestUtils.java
b/processing/src/test/java/org/apache/druid/query/NestedDataTestUtils.java
index d41a51f2ea8..63280ac9eb1 100644
--- a/processing/src/test/java/org/apache/druid/query/NestedDataTestUtils.java
+++ b/processing/src/test/java/org/apache/druid/query/NestedDataTestUtils.java
@@ -27,6 +27,7 @@ import org.apache.druid.data.input.InputRowSchema;
import org.apache.druid.data.input.InputSource;
import org.apache.druid.data.input.ResourceInputSource;
import org.apache.druid.data.input.impl.DelimitedInputFormat;
+import org.apache.druid.data.input.impl.DimensionSchema;
import org.apache.druid.data.input.impl.DimensionsSpec;
import org.apache.druid.data.input.impl.LocalInputSource;
import org.apache.druid.data.input.impl.TimestampSpec;
@@ -39,6 +40,7 @@ import org.apache.druid.query.aggregation.AggregatorFactory;
import org.apache.druid.query.aggregation.CountAggregatorFactory;
import org.apache.druid.query.expression.TestExprMacroTable;
import org.apache.druid.segment.AutoTypeColumnSchema;
+import org.apache.druid.segment.DefaultColumnFormatConfig;
import org.apache.druid.segment.IncrementalIndexSegment;
import org.apache.druid.segment.IndexBuilder;
import org.apache.druid.segment.IndexSpec;
@@ -49,6 +51,7 @@ import org.apache.druid.segment.TestHelper;
import org.apache.druid.segment.TestIndex;
import org.apache.druid.segment.column.StringEncodingStrategy;
import org.apache.druid.segment.incremental.IncrementalIndexSchema;
+import org.apache.druid.segment.nested.NestedCommonFormatColumnFormatSpec;
import org.apache.druid.segment.transform.ExpressionTransform;
import org.apache.druid.segment.transform.TransformSpec;
import org.apache.druid.timeline.SegmentId;
@@ -66,6 +69,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
+import java.util.stream.Collectors;
public class NestedDataTestUtils
{
@@ -91,34 +95,15 @@ public class NestedDataTestUtils
DimensionsSpec.builder()
.useSchemaDiscovery(true)
.build();
+ private static final List<String> COLUMN_NAMES = Arrays.asList(
+ "dim",
+ "nest_json",
+ "nester_json",
+ "variant_json",
+ "list_json",
+ "nonexistent"
+ );
- public static final DimensionsSpec TSV_SCHEMA =
- DimensionsSpec.builder()
- .setDimensions(
- Arrays.asList(
- AutoTypeColumnSchema.of("dim"),
- AutoTypeColumnSchema.of("nest_json"),
- AutoTypeColumnSchema.of("nester_json"),
- AutoTypeColumnSchema.of("variant_json"),
- AutoTypeColumnSchema.of("list_json"),
- AutoTypeColumnSchema.of("nonexistent")
- )
- )
- .build();
-
- public static final DimensionsSpec TSV_NESTED_SCHEMA =
- DimensionsSpec.builder()
- .setDimensions(
- Arrays.asList(
- new NestedDataColumnSchema("dim", 5),
- new NestedDataColumnSchema("nest_json", 5),
- new NestedDataColumnSchema("nester_json", 5),
- new NestedDataColumnSchema("variant_json", 5),
- new NestedDataColumnSchema("list_json", 5),
- new NestedDataColumnSchema("nonexistent", 5)
- )
- )
- .build();
public static final InputRowSchema AUTO_SCHEMA = new InputRowSchema(
TIMESTAMP_SPEC,
AUTO_DISCOVERY,
@@ -163,30 +148,41 @@ public class NestedDataTestUtils
public static List<Segment> createSimpleSegmentsTsv(
TemporaryFolder tempFolder,
+ NestedCommonFormatColumnFormatSpec spec,
Closer closer
)
throws Exception
{
+ List<DimensionSchema> dimensionsSpecs =
+ COLUMN_NAMES.stream()
+ .map(name -> (DimensionSchema) new
AutoTypeColumnSchema(name, null, spec))
+ .collect(Collectors.toList());
return createSimpleNestedTestDataTsvSegments(
tempFolder,
closer,
Granularities.NONE,
- TSV_SCHEMA,
+ DimensionsSpec.builder().setDimensions(dimensionsSpecs).build(),
true
);
}
public static List<Segment> createSimpleSegmentsTsvNested(
TemporaryFolder tempFolder,
+ NestedCommonFormatColumnFormatSpec spec,
Closer closer
)
throws Exception
{
+ DefaultColumnFormatConfig config = new DefaultColumnFormatConfig(null,
null, null);
+ List<DimensionSchema> dimensionsSpecs =
+ COLUMN_NAMES.stream()
+ .map(name -> (DimensionSchema) new
NestedDataColumnSchema(name, 5, spec, config))
+ .collect(Collectors.toList());
return createSimpleNestedTestDataTsvSegments(
tempFolder,
closer,
Granularities.NONE,
- TSV_NESTED_SCHEMA,
+ DimensionsSpec.builder().setDimensions(dimensionsSpecs).build(),
true
);
}
diff --git
a/processing/src/test/java/org/apache/druid/query/scan/NestedDataScanQueryTest.java
b/processing/src/test/java/org/apache/druid/query/scan/NestedDataScanQueryTest.java
index e297cedc425..645b4f2840a 100644
---
a/processing/src/test/java/org/apache/druid/query/scan/NestedDataScanQueryTest.java
+++
b/processing/src/test/java/org/apache/druid/query/scan/NestedDataScanQueryTest.java
@@ -22,6 +22,9 @@ package org.apache.druid.query.scan;
import com.fasterxml.jackson.databind.Module;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+import junitparams.naming.TestCaseName;
import org.apache.druid.data.input.impl.DimensionsSpec;
import org.apache.druid.guice.BuiltInTypesModule;
import org.apache.druid.java.util.common.Intervals;
@@ -46,6 +49,8 @@ import org.apache.druid.segment.IndexSpec;
import org.apache.druid.segment.Segment;
import org.apache.druid.segment.TestIndex;
import org.apache.druid.segment.column.ColumnType;
+import org.apache.druid.segment.nested.NestedCommonFormatColumnFormatSpec;
+import org.apache.druid.segment.nested.ObjectStorageEncoding;
import org.apache.druid.segment.transform.TransformSpec;
import org.apache.druid.segment.virtual.NestedFieldVirtualColumn;
import org.apache.druid.testing.InitializedNullHandlingTest;
@@ -54,11 +59,13 @@ import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
+@RunWith(JUnitParamsRunner.class)
public class NestedDataScanQueryTest extends InitializedNullHandlingTest
{
private static final Logger LOG = new Logger(NestedDataScanQueryTest.class);
@@ -69,6 +76,18 @@ public class NestedDataScanQueryTest extends
InitializedNullHandlingTest
@Rule
public final TemporaryFolder tempFolder = new TemporaryFolder();
+ public static Object[] getNestedColumnFormatSpec()
+ {
+ return new Object[]{
+ new Object[]{"default", null},
+ new Object[]{
+ "noneObjectStorageEncoding",
+ NestedCommonFormatColumnFormatSpec.builder()
+
.setObjectStorageEncoding(ObjectStorageEncoding.NONE).build()
+ }
+ };
+ }
+
@After
public void teardown() throws IOException
{
@@ -267,7 +286,9 @@ public class NestedDataScanQueryTest extends
InitializedNullHandlingTest
}
@Test
- public void testIngestAndScanSegmentsTsvV4() throws Exception
+ @Parameters(method = "getNestedColumnFormatSpec")
+ @TestCaseName("{0}")
+ public void testIngestAndScanSegmentsTsvV4(String name,
NestedCommonFormatColumnFormatSpec spec) throws Exception
{
Query<ScanResultValue> scanQuery = Druids.newScanQueryBuilder()
.dataSource("test_datasource")
@@ -285,7 +306,7 @@ public class NestedDataScanQueryTest extends
InitializedNullHandlingTest
.limit(100)
.context(ImmutableMap.of())
.build();
- List<Segment> segs =
NestedDataTestUtils.createSimpleSegmentsTsvNested(tempFolder, closer);
+ List<Segment> segs =
NestedDataTestUtils.createSimpleSegmentsTsvNested(tempFolder, spec, closer);
final Sequence<ScanResultValue> seq = helper.runQueryOnSegmentsObjs(segs,
scanQuery);
@@ -295,9 +316,10 @@ public class NestedDataScanQueryTest extends
InitializedNullHandlingTest
logResults(results);
}
-
@Test
- public void testIngestAndScanSegmentsTsv() throws Exception
+ @Parameters(method = "getNestedColumnFormatSpec")
+ @TestCaseName("{0}")
+ public void testIngestAndScanSegmentsTsv(String name,
NestedCommonFormatColumnFormatSpec spec) throws Exception
{
Query<ScanResultValue> scanQuery = Druids.newScanQueryBuilder()
.dataSource("test_datasource")
@@ -315,7 +337,7 @@ public class NestedDataScanQueryTest extends
InitializedNullHandlingTest
.limit(100)
.context(ImmutableMap.of())
.build();
- List<Segment> segs =
NestedDataTestUtils.createSimpleSegmentsTsv(tempFolder, closer);
+ List<Segment> segs =
NestedDataTestUtils.createSimpleSegmentsTsv(tempFolder, spec, closer);
final Sequence<ScanResultValue> seq = helper.runQueryOnSegmentsObjs(segs,
scanQuery);
diff --git
a/processing/src/test/java/org/apache/druid/segment/nested/NestedDataColumnSupplierTest.java
b/processing/src/test/java/org/apache/druid/segment/nested/NestedDataColumnSupplierTest.java
index 39109f5ed04..9338a8415ba 100644
---
a/processing/src/test/java/org/apache/druid/segment/nested/NestedDataColumnSupplierTest.java
+++
b/processing/src/test/java/org/apache/druid/segment/nested/NestedDataColumnSupplierTest.java
@@ -170,11 +170,15 @@ public class NestedDataColumnSupplierTest extends
InitializedNullHandlingTest
.setLongColumnCompression(CompressionStrategy.LZF)
.setDoubleColumnCompression(CompressionStrategy.LZF)
.build();
+
+ NestedCommonFormatColumnFormatSpec noRawStorage =
+
NestedCommonFormatColumnFormatSpec.builder().setObjectStorageEncoding(ObjectStorageEncoding.NONE).build();
final List<Object[]> constructors = ImmutableList.of(
new Object[]{defaultSpec},
new Object[]{frontCodedKeysAndDicts},
new Object[]{zstdRaw},
- new Object[]{lzf}
+ new Object[]{lzf},
+ new Object[]{noRawStorage}
);
return constructors;
@@ -455,11 +459,22 @@ public class NestedDataColumnSupplierTest extends
InitializedNullHandlingTest
Assert.assertEquals(ImmutableList.of(nullishPath, vPath, xPath, yPath,
zPath), column.getNestedFields());
for (int i = 0; i < DATA.size(); i++) {
- Map row = DATA.get(i);
+ final Map<String, Object> row;
+ if
(ObjectStorageEncoding.NONE.equals(columnFormatSpec.getObjectStorageEncoding()))
{
+ // if raw object is not stored, the derived object will have sorted
key and no nulls
+ row = new TreeMap<>(DATA.get(i));
+ row.entrySet().removeIf(entry -> entry.getValue() == null);
+ } else {
+ row = DATA.get(i);
+ }
Assert.assertEquals(
JSON_MAPPER.writeValueAsString(row),
JSON_MAPPER.writeValueAsString(StructuredData.unwrap(rawSelector.getObject()))
);
+ Assert.assertEquals(
+ JSON_MAPPER.writeValueAsString(row),
+
JSON_MAPPER.writeValueAsString(StructuredData.unwrap(column.getRowValue(i)))
+ );
testPath(row, i, "v", vSelector, vDimSelector, vValueIndex,
vPredicateIndex, vNulls, null);
testPath(row, i, "x", xSelector, xDimSelector, xValueIndex,
xPredicateIndex, xNulls, ColumnType.LONG);
@@ -594,11 +609,22 @@ public class NestedDataColumnSupplierTest extends
InitializedNullHandlingTest
int rowCounter = 0;
while (offset.withinBounds()) {
- Map row = ARRAY_TEST_DATA.get(rowCounter);
+ final Map<String, Object> row;
+ if
(ObjectStorageEncoding.NONE.equals(columnFormatSpec.getObjectStorageEncoding()))
{
+ // if raw object is not stored, the derived object will have sorted
key and no nulls
+ row = new TreeMap<>(ARRAY_TEST_DATA.get(rowCounter));
+ row.entrySet().removeIf(entry -> entry.getValue() == null);
+ } else {
+ row = ARRAY_TEST_DATA.get(rowCounter);
+ }
Assert.assertEquals(
JSON_MAPPER.writeValueAsString(row),
JSON_MAPPER.writeValueAsString(StructuredData.unwrap(rawSelector.getObject()))
);
+ Assert.assertEquals(
+ JSON_MAPPER.writeValueAsString(row),
+
JSON_MAPPER.writeValueAsString(StructuredData.unwrap(column.getRowValue(rowCounter)))
+ );
Object[] s = (Object[]) row.get("s");
Object[] l = (Object[]) row.get("l");
@@ -650,7 +676,14 @@ public class NestedDataColumnSupplierTest extends
InitializedNullHandlingTest
for (int i = 0; i < vectorOffset.getCurrentVectorSize(); i++,
rowCounter++) {
- Map row = ARRAY_TEST_DATA.get(rowCounter);
+ final Map<String, Object> row;
+ if
(ObjectStorageEncoding.NONE.equals(columnFormatSpec.getObjectStorageEncoding()))
{
+ // if raw object is not stored, the derived object will have sorted
key and no nulls
+ row = new TreeMap<>(ARRAY_TEST_DATA.get(rowCounter));
+ row.entrySet().removeIf(entry -> entry.getValue() == null);
+ } else {
+ row = ARRAY_TEST_DATA.get(rowCounter);
+ }
Assert.assertEquals(
JSON_MAPPER.writeValueAsString(row),
JSON_MAPPER.writeValueAsString(StructuredData.unwrap(rawVector[i]))
@@ -699,7 +732,14 @@ public class NestedDataColumnSupplierTest extends
InitializedNullHandlingTest
final boolean[] dElementNulls =
dElementFilteredVectorSelector.getNullVector();
for (int i = 0; i < bitmapVectorOffset.getCurrentVectorSize(); i++,
rowCounter += 2) {
- Map row = ARRAY_TEST_DATA.get(rowCounter);
+ final Map<String, Object> row;
+ if
(ObjectStorageEncoding.NONE.equals(columnFormatSpec.getObjectStorageEncoding()))
{
+ // if raw object is not stored, the derived object will have sorted
key and no nulls
+ row = new TreeMap<>(ARRAY_TEST_DATA.get(rowCounter));
+ row.entrySet().removeIf(entry -> entry.getValue() == null);
+ } else {
+ row = ARRAY_TEST_DATA.get(rowCounter);
+ }
Assert.assertEquals(
JSON_MAPPER.writeValueAsString(row),
JSON_MAPPER.writeValueAsString(StructuredData.unwrap(rawVector[i]))
diff --git
a/processing/src/test/java/org/apache/druid/segment/nested/StructuredDataBuilderTest.java
b/processing/src/test/java/org/apache/druid/segment/nested/StructuredDataBuilderTest.java
new file mode 100644
index 00000000000..fa49e1104dc
--- /dev/null
+++
b/processing/src/test/java/org/apache/druid/segment/nested/StructuredDataBuilderTest.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 org.apache.druid.segment.nested;
+
+import org.apache.druid.error.DruidException;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+public class StructuredDataBuilderTest
+{
+ @Test
+ public void testBuildSingleDepth()
+ {
+ Object[] array = new Object[]{1, 2};
+ StructuredDataBuilder.Element childArrayElement = new
StructuredDataBuilder.Element(
+ List.of(new NestedPathArrayElement(1)),
+ array,
+ 1
+ );
+ Assert.assertEquals(StructuredData.wrap(array), new
StructuredDataBuilder(childArrayElement).build());
+
+ // [null, [1, 2]]
+ StructuredDataBuilder.Element arrayElement = new
StructuredDataBuilder.Element(
+ List.of(new NestedPathArrayElement(1)),
+ array,
+ 0
+ );
+ Assert.assertEquals(
+ StructuredData.wrap(Arrays.asList(null, array)),
+ new StructuredDataBuilder(arrayElement).build()
+ );
+
+ StructuredDataBuilder.Element nullElement = new
StructuredDataBuilder.Element(
+ List.of(new NestedPathField("y")),
+ null,
+ 0
+ );
+ Assert.assertEquals(StructuredData.wrap(Map.of()), new
StructuredDataBuilder(nullElement).build());
+
+ // {"x": "hi"}
+ StructuredDataBuilder.Element mapElement = new
StructuredDataBuilder.Element(
+ List.of(new NestedPathField("x")),
+ "hi",
+ 0
+ );
+ Assert.assertEquals(
+ StructuredData.wrap(Map.of("x", "hi")),
+ new StructuredDataBuilder(mapElement, nullElement).build()
+ );
+ }
+
+ @Test
+ public void testBuildRootPath()
+ {
+ // "root-val"
+ StructuredDataBuilder.Element rootElement = new
StructuredDataBuilder.Element(
+ List.of(),
+ "root-val",
+ 0
+ );
+ Assert.assertEquals(StructuredData.wrap("root-val"), new
StructuredDataBuilder(rootElement).build());
+ }
+
+ @Test
+ public void testBuildArrayMultipleDepths()
+ {
+ // [[1], [null, [2]]]
+ Object[] array = new Object[]{2};
+ StructuredDataBuilder.Element element1 = new StructuredDataBuilder.Element(
+ List.of(new NestedPathArrayElement(0), new NestedPathArrayElement(0)),
+ 1,
+ 0
+ );
+ StructuredDataBuilder.Element element2 = new StructuredDataBuilder.Element(
+ List.of(new NestedPathArrayElement(1), new NestedPathArrayElement(1)),
+ array,
+ 0
+ );
+ List<Object> expected = List.of(List.of(1), Arrays.asList(null, array));
+ Assert.assertEquals(StructuredData.wrap(expected), new
StructuredDataBuilder(element1, element2).build());
+ }
+
+ @Test
+ public void testBuildMapMultipleDepths()
+ {
+ // {"x": {"y": "hi-xy", "z": "hi-xz"}, "yz": {"z": "hi-yz"}}
+ StructuredDataBuilder.Element xyElement = new
StructuredDataBuilder.Element(
+ List.of(new NestedPathField("x"), new NestedPathField("y")),
+ "hi-xy",
+ 0
+ );
+ StructuredDataBuilder.Element xzElement = new
StructuredDataBuilder.Element(
+ List.of(new NestedPathField("x"), new NestedPathField("z")),
+ "hi-xz",
+ 0
+ );
+ StructuredDataBuilder.Element yzElement = new
StructuredDataBuilder.Element(
+ List.of(new NestedPathField("yz"), new NestedPathField("z")),
+ "hi-yz",
+ 0
+ );
+ Map<String, Object> expected = Map.of("x", Map.of("y", "hi-xy", "z",
"hi-xz"), "yz", Map.of("z", "hi-yz"));
+ Assert.assertEquals(
+ StructuredData.wrap(expected),
+ new StructuredDataBuilder(xyElement, xzElement, yzElement).build()
+ );
+ }
+
+ @Test
+ public void testBuildMixedMultipleDepths()
+ {
+ // {"x": {"y": "hi-xy", "array": ["hi-x-array-0", null, "hi-x-array-2"]}}
+ StructuredDataBuilder.Element xyElement = new
StructuredDataBuilder.Element(
+ List.of(new NestedPathField("x"), new NestedPathField("y")),
+ "hi-xy",
+ 0
+ );
+ StructuredDataBuilder.Element xArray = new StructuredDataBuilder.Element(
+ List.of(new NestedPathField("x"), new NestedPathField("array"), new
NestedPathArrayElement(0)),
+ "hi-x-array-0",
+ 0
+ );
+ StructuredDataBuilder.Element xArray2 = new StructuredDataBuilder.Element(
+ List.of(new NestedPathField("x"), new NestedPathField("array"), new
NestedPathArrayElement(2)),
+ "hi-x-array-2",
+ 0
+ );
+
+ Map<String, Object> expected = Map.of(
+ "x",
+ Map.of("y", "hi-xy", "array", Arrays.asList("hi-x-array-0", null,
"hi-x-array-2"))
+ );
+ Assert.assertEquals(StructuredData.wrap(expected), new
StructuredDataBuilder(xyElement, xArray, xArray2).build());
+ }
+
+ @Test
+ public void testBuildExceptions()
+ {
+ StructuredDataBuilder.Element rootElement = new
StructuredDataBuilder.Element(
+ List.of(),
+ "root-val",
+ 0
+ );
+ StructuredDataBuilder.Element mapElement = new
StructuredDataBuilder.Element(
+ List.of(new NestedPathField("x")),
+ "hi",
+ 0
+ );
+ StructuredDataBuilder.Element arrayElement = new
StructuredDataBuilder.Element(
+ List.of(new NestedPathArrayElement(0)),
+ 1,
+ 0
+ );
+ DruidException e1 = Assert.assertThrows(
+ DruidException.class,
+ () -> new StructuredDataBuilder(rootElement, mapElement).build()
+ );
+ Assert.assertEquals(
+ "Error building structured data from paths[[Element{path=[],
value=root-val, depth=0}, Element{path=[NestedPathField{field='x'}], value=hi,
depth=0}]], "
+ + "cannot have map or array elements when root value is set",
+ e1.getMessage()
+ );
+ DruidException e2 = Assert.assertThrows(
+ DruidException.class,
+ () -> new StructuredDataBuilder(rootElement, arrayElement).build()
+ );
+ Assert.assertEquals(
+ "Error building structured data from paths[[Element{path=[],
value=root-val, depth=0}, Element{path=[NestedPathArrayElement{index=0}],
value=1, depth=0}]], "
+ + "cannot have map or array elements when root value is set",
+ e2.getMessage()
+ );
+ DruidException e3 = Assert.assertThrows(
+ DruidException.class,
+ () -> new StructuredDataBuilder(mapElement, arrayElement).build()
+ );
+ Assert.assertEquals(
+ "Error building structured data from
paths[[Element{path=[NestedPathField{field='x'}], value=hi, depth=0},
Element{path=[NestedPathArrayElement{index=0}], value=1, depth=0}]], "
+ + "cannot have both map and array elements at the same level",
+ e3.getMessage()
+ );
+ }
+}
diff --git
a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteNestedDataQueryTest.java
b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteNestedDataQueryTest.java
index 97db549de27..1325dc93373 100644
---
a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteNestedDataQueryTest.java
+++
b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteNestedDataQueryTest.java
@@ -64,7 +64,9 @@ import org.apache.druid.segment.column.ColumnType;
import org.apache.druid.segment.column.RowSignature;
import org.apache.druid.segment.incremental.IncrementalIndex;
import org.apache.druid.segment.incremental.IncrementalIndexSchema;
+import org.apache.druid.segment.nested.NestedCommonFormatColumnFormatSpec;
import org.apache.druid.segment.nested.NestedPathField;
+import org.apache.druid.segment.nested.ObjectStorageEncoding;
import org.apache.druid.segment.virtual.ExpressionVirtualColumn;
import org.apache.druid.segment.virtual.NestedFieldVirtualColumn;
import
org.apache.druid.segment.writeout.OffHeapMemorySegmentWriteOutMediumFactory;
@@ -79,7 +81,9 @@ import org.hamcrest.CoreMatchers;
import org.junit.Assert;
import org.junit.internal.matchers.ThrowableMessageMatcher;
import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
import java.util.Arrays;
import java.util.Collections;
@@ -89,7 +93,7 @@ import java.util.stream.Collectors;
import static org.hamcrest.MatcherAssert.assertThat;
@SqlTestFrameworkConfig.ComponentSupplier(NestedComponentSupplier.class)
-public class CalciteNestedDataQueryTest extends BaseCalciteQueryTest
+public abstract class CalciteNestedDataQueryTest extends BaseCalciteQueryTest
{
public static final String DATA_SOURCE = "nested";
public static final String DATA_SOURCE_MIXED = "nested_mix";
@@ -103,7 +107,7 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
.put("t", "2000-01-01")
.put("string", "aaa")
.put("string_sparse", "zzz")
- .put("nest", ImmutableMap.of("x", 100L, "y", 2.02, "z",
"300", "mixed", 1L, "mixed2", "1"))
+ .put("nest", ImmutableMap.of("mixed", 1L, "mixed2", "1",
"x", 100L, "y", 2.02, "z", "300"))
.put(
"nester",
ImmutableMap.of("array", ImmutableList.of("a", "b"),
"n", ImmutableMap.of("x", "hello"))
@@ -120,7 +124,7 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
.put("t", "2000-01-01")
.put("string", "ccc")
.put("string_sparse", "10")
- .put("nest", ImmutableMap.of("x", 200L, "y", 3.03, "z",
"abcdef", "mixed", 1.1, "mixed2", 1L))
+ .put("nest", ImmutableMap.of("mixed", 1.1, "mixed2", 1L,
"x", 200L, "y", 3.03, "z", "abcdef"))
.put("long", 3L)
.build(),
ImmutableMap.<String, Object>builder()
@@ -138,7 +142,7 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
ImmutableMap.<String, Object>builder()
.put("t", "2000-01-02")
.put("string", "aaa")
- .put("nest", ImmutableMap.of("x", 100L, "y", 2.02, "z",
"400", "mixed2", 1.1))
+ .put("nest", ImmutableMap.of("mixed2", 1.1, "x", 100L, "y",
2.02, "z", "400"))
.put("nester", ImmutableMap.of("array",
ImmutableList.of("a", "b"), "n", ImmutableMap.of("x", 1L)))
.put("long", 5L)
.build(),
@@ -150,41 +154,80 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
.build()
);
- public static final InputRowSchema ALL_JSON_COLUMNS = new InputRowSchema(
- new TimestampSpec("t", "iso", null),
- DimensionsSpec.builder().setDimensions(
- ImmutableList.<DimensionSchema>builder()
- .add(AutoTypeColumnSchema.of("string"))
- .add(AutoTypeColumnSchema.of("nest"))
- .add(AutoTypeColumnSchema.of("nester"))
- .add(AutoTypeColumnSchema.of("long"))
- .add(AutoTypeColumnSchema.of("string_sparse"))
- .build()
- ).build(),
- null
- );
+ @Nested
+ public static class DefaultCalciteNestedDataQueryTest extends
CalciteNestedDataQueryTest
+ {
+ }
- public static final InputRowSchema JSON_AND_SCALAR_MIX = new InputRowSchema(
- new TimestampSpec("t", "iso", null),
- DimensionsSpec.builder().setDimensions(
- ImmutableList.<DimensionSchema>builder()
- .add(new StringDimensionSchema("string"))
- .add(AutoTypeColumnSchema.of("nest"))
- .add(AutoTypeColumnSchema.of("nester"))
- .add(new LongDimensionSchema("long"))
- .add(new StringDimensionSchema("string_sparse"))
- .build()
- ).build(),
- null
- );
- public static final List<InputRow> ROWS =
- RAW_ROWS.stream().map(raw -> TestDataBuilder.createRow(raw,
ALL_JSON_COLUMNS)).collect(Collectors.toList());
+ @Nested
+ public static class NoneObjectStorageCalciteNestedDataQueryTest extends
CalciteNestedDataQueryTest
+ {
+ public NoneObjectStorageCalciteNestedDataQueryTest()
+ {
+ super();
+ // Override with none object storage
+ NestedCommonFormatColumnFormatSpec noneObjectStorage =
+
NestedCommonFormatColumnFormatSpec.builder().setObjectStorageEncoding(ObjectStorageEncoding.NONE).build();
+ Mockito.when(ALL_JSON_COLUMNS.getDimensionsSpec()).thenReturn(
+ DimensionsSpec.builder().setDimensions(
+ ImmutableList.<DimensionSchema>builder()
+ .add(new AutoTypeColumnSchema("string", null,
noneObjectStorage))
+ .add(new AutoTypeColumnSchema("nest", null,
noneObjectStorage))
+ .add(new AutoTypeColumnSchema("nester", null,
noneObjectStorage))
+ .add(new AutoTypeColumnSchema("long", null,
noneObjectStorage))
+ .add(new AutoTypeColumnSchema("string_sparse",
null, noneObjectStorage))
+ .build()
+ ).build());
+ Mockito.when(JSON_AND_SCALAR_MIX.getDimensionsSpec()).thenReturn(
+ DimensionsSpec.builder().setDimensions(
+ ImmutableList.<DimensionSchema>builder()
+ .add(new StringDimensionSchema("string"))
+ .add(new AutoTypeColumnSchema("nest", null,
noneObjectStorage))
+ .add(new AutoTypeColumnSchema("nester", null,
noneObjectStorage))
+ .add(new LongDimensionSchema("long"))
+ .add(new StringDimensionSchema("string_sparse"))
+ .build()
+ ).build());
+ }
+ }
+
+ public static final InputRowSchema ALL_JSON_COLUMNS =
Mockito.mock(InputRowSchema.class);
- public static final List<InputRow> ROWS_MIX =
- RAW_ROWS.stream().map(raw -> TestDataBuilder.createRow(raw,
JSON_AND_SCALAR_MIX)).collect(Collectors.toList());
+ public static final InputRowSchema JSON_AND_SCALAR_MIX =
Mockito.mock(InputRowSchema.class);
+
+ public static List<InputRow> constructInputRows(InputRowSchema
inputRowSchema)
+ {
+ return RAW_ROWS.stream().map(raw -> TestDataBuilder.createRow(raw,
inputRowSchema)).collect(Collectors.toList());
+ }
public static class NestedComponentSupplier extends StandardComponentSupplier
{
+ static {
+ Mockito.when(ALL_JSON_COLUMNS.getTimestampSpec()).thenReturn(
+ new TimestampSpec("t", "iso", null));
+ Mockito.when(ALL_JSON_COLUMNS.getDimensionsSpec()).thenReturn(
+ DimensionsSpec.builder().setDimensions(
+ ImmutableList.<DimensionSchema>builder()
+ .add(AutoTypeColumnSchema.of("string"))
+ .add(AutoTypeColumnSchema.of("nest"))
+ .add(AutoTypeColumnSchema.of("nester"))
+ .add(AutoTypeColumnSchema.of("long"))
+ .add(AutoTypeColumnSchema.of("string_sparse"))
+ .build()
+ ).build());
+ Mockito.when(JSON_AND_SCALAR_MIX.getTimestampSpec()).thenReturn(new
TimestampSpec("t", "iso", null));
+ Mockito.when(JSON_AND_SCALAR_MIX.getDimensionsSpec()).thenReturn(
+ DimensionsSpec.builder().setDimensions(
+ ImmutableList.<DimensionSchema>builder()
+ .add(new StringDimensionSchema("string"))
+ .add(AutoTypeColumnSchema.of("nest"))
+ .add(AutoTypeColumnSchema.of("nester"))
+ .add(new LongDimensionSchema("long"))
+ .add(new StringDimensionSchema("string_sparse"))
+ .build()
+ ).build());
+ }
+
public NestedComponentSupplier(TempDirProducer tempFolderProducer)
{
super(tempFolderProducer);
@@ -207,7 +250,7 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
.withRollup(false)
.build()
)
- .rows(ROWS)
+ .rows(constructInputRows(ALL_JSON_COLUMNS))
.buildMMappedIndex();
final QueryableIndex indexMix11 =
@@ -223,7 +266,7 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
.withRollup(false)
.build()
)
- .rows(ROWS)
+ .rows(constructInputRows(ALL_JSON_COLUMNS))
.buildMMappedIndex();
@@ -240,7 +283,7 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
.withRollup(false)
.build()
)
- .rows(ROWS_MIX)
+ .rows(constructInputRows(JSON_AND_SCALAR_MIX))
.buildMMappedIndex();
final QueryableIndex indexMix21 =
@@ -256,7 +299,7 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
.withRollup(false)
.build()
)
- .rows(ROWS_MIX)
+ .rows(constructInputRows(JSON_AND_SCALAR_MIX))
.buildMMappedIndex();
final QueryableIndex indexMix22 =
@@ -272,7 +315,7 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
.withRollup(false)
.build()
)
- .rows(ROWS)
+ .rows(constructInputRows(ALL_JSON_COLUMNS))
.buildMMappedIndex();
final QueryableIndex indexArrays =
@@ -582,7 +625,12 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
.setInterval(querySegmentSpec(Filtration.eternity()))
.setGranularity(Granularities.ALL)
.setVirtualColumns(
- new ExpressionVirtualColumn("v0",
"strlen(\"string\")", ColumnType.LONG, queryFramework().macroTable())
+ new ExpressionVirtualColumn(
+ "v0",
+ "strlen(\"string\")",
+ ColumnType.LONG,
+ queryFramework().macroTable()
+ )
)
.setDimensions(dimensions(new
DefaultDimensionSpec("nester", "d0", ColumnType.NESTED_DATA)))
.setAggregatorSpecs(aggregators(new
LongSumAggregatorFactory("a0", "v0")))
@@ -611,7 +659,12 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
.setInterval(querySegmentSpec(Filtration.eternity()))
.setGranularity(Granularities.ALL)
.setVirtualColumns(
- new ExpressionVirtualColumn("v0",
"strlen(\"string\")", ColumnType.LONG, queryFramework().macroTable())
+ new ExpressionVirtualColumn(
+ "v0",
+ "strlen(\"string\")",
+ ColumnType.LONG,
+ queryFramework().macroTable()
+ )
)
.setDimensions(dimensions(new
DefaultDimensionSpec("nester", "d0", ColumnType.NESTED_DATA)))
.setAggregatorSpecs(aggregators(new
LongSumAggregatorFactory("a0", "v0")))
@@ -1163,7 +1216,12 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
new NestedFieldVirtualColumn("arrayNestedLong",
"$[0]", "v3", ColumnType.LONG_ARRAY)
)
.columns("v0", "v1", "v2", "v3")
- .columnTypes(ColumnType.STRING_ARRAY,
ColumnType.LONG_ARRAY, ColumnType.DOUBLE_ARRAY, ColumnType.LONG_ARRAY)
+ .columnTypes(
+ ColumnType.STRING_ARRAY,
+ ColumnType.LONG_ARRAY,
+ ColumnType.DOUBLE_ARRAY,
+ ColumnType.LONG_ARRAY
+ )
.resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST)
.build()
)
@@ -1669,23 +1727,23 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
.queryContext(QUERY_CONTEXT_NO_STRINGIFY_ARRAY)
.expectedQuery(
GroupByQuery.builder()
- .setDataSource(
- UnnestDataSource.create(
- TableDataSource.create(DATA_SOURCE_ARRAYS),
- expressionVirtualColumn("j0.unnest",
"\"arrayLongNulls\"", ColumnType.LONG_ARRAY),
- null
- )
- )
- .setInterval(querySegmentSpec(Filtration.eternity()))
- .setGranularity(Granularities.ALL)
- .setDimensions(
- dimensions(
- new DefaultDimensionSpec("j0.unnest", "d0",
ColumnType.LONG)
- )
- )
- .setAggregatorSpecs(aggregators(new
LongSumAggregatorFactory("a0", "cnt")))
- .setContext(QUERY_CONTEXT_NO_STRINGIFY_ARRAY)
- .build()
+ .setDataSource(
+ UnnestDataSource.create(
+ TableDataSource.create(DATA_SOURCE_ARRAYS),
+ expressionVirtualColumn("j0.unnest",
"\"arrayLongNulls\"", ColumnType.LONG_ARRAY),
+ null
+ )
+ )
+ .setInterval(querySegmentSpec(Filtration.eternity()))
+ .setGranularity(Granularities.ALL)
+ .setDimensions(
+ dimensions(
+ new DefaultDimensionSpec("j0.unnest", "d0",
ColumnType.LONG)
+ )
+ )
+ .setAggregatorSpecs(aggregators(new
LongSumAggregatorFactory("a0", "cnt")))
+ .setContext(QUERY_CONTEXT_NO_STRINGIFY_ARRAY)
+ .build()
)
.expectedResults(
ImmutableList.of(
@@ -2753,7 +2811,7 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
ImmutableList.of(
new Object[]{
"aaa",
-
"[{\"x\":100,\"y\":2.02,\"z\":\"300\",\"mixed\":1,\"mixed2\":\"1\"},{\"x\":100,\"y\":2.02,\"z\":\"400\",\"mixed2\":1.1}]",
+
"[{\"mixed\":1,\"mixed2\":\"1\",\"x\":100,\"y\":2.02,\"z\":\"300\"},{\"mixed2\":1.1,\"x\":100,\"y\":2.02,\"z\":\"400\"}]",
2L
},
new Object[]{
@@ -2763,7 +2821,7 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
},
new Object[]{
"ccc",
-
"[{\"x\":200,\"y\":3.03,\"z\":\"abcdef\",\"mixed\":1.1,\"mixed2\":1}]",
+
"[{\"mixed\":1.1,\"mixed2\":1,\"x\":200,\"y\":3.03,\"z\":\"abcdef\"}]",
1L
},
new Object[]{
@@ -4646,8 +4704,8 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
),
ImmutableList.of(
new Object[]{null, 4L},
- new Object[]{"[\"x\",\"y\",\"z\",\"mixed\",\"mixed2\"]", 2L},
- new Object[]{"[\"x\",\"y\",\"z\",\"mixed2\"]", 1L}
+ new Object[]{"[\"mixed\",\"mixed2\",\"x\",\"y\",\"z\"]", 2L},
+ new Object[]{"[\"mixed2\",\"x\",\"y\",\"z\"]", 1L}
),
RowSignature.builder()
.add("EXPR$0", ColumnType.STRING_ARRAY)
@@ -4912,9 +4970,7 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
"nest",
"v1",
ColumnType.STRING,
- ImmutableList.of(
- new NestedPathField("x")
- ),
+ ImmutableList.of(new NestedPathField("x")),
false,
null,
false
@@ -5020,7 +5076,12 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
)
)
.columns("string", "v0", "v1", "v2")
- .columnTypes(ColumnType.STRING, ColumnType.NESTED_DATA,
ColumnType.NESTED_DATA, ColumnType.NESTED_DATA)
+ .columnTypes(
+ ColumnType.STRING,
+ ColumnType.NESTED_DATA,
+ ColumnType.NESTED_DATA,
+ ColumnType.NESTED_DATA
+ )
.resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST)
.build()
),
@@ -5987,7 +6048,44 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
"cObjectArray",
"cnt"
)
- .columnTypes(ColumnType.LONG, ColumnType.STRING,
ColumnType.LONG, ColumnType.DOUBLE, ColumnType.LONG, ColumnType.STRING,
ColumnType.DOUBLE, ColumnType.ofComplex("json"), ColumnType.LONG_ARRAY,
ColumnType.STRING_ARRAY, ColumnType.ofComplex("json"),
ColumnType.ofComplex("json"), ColumnType.STRING_ARRAY, ColumnType.STRING_ARRAY,
ColumnType.LONG_ARRAY, ColumnType.LONG_ARRAY, ColumnType.DOUBLE_ARRAY,
ColumnType.DOUBLE_ARRAY, ColumnType.STRING_ARRAY, ColumnType.LONG_ARRAY, Co
[...]
+ .columnTypes(
+ ColumnType.LONG,
+ ColumnType.STRING,
+ ColumnType.LONG,
+ ColumnType.DOUBLE,
+ ColumnType.LONG,
+ ColumnType.STRING,
+ ColumnType.DOUBLE,
+ ColumnType.ofComplex("json"),
+ ColumnType.LONG_ARRAY,
+ ColumnType.STRING_ARRAY,
+ ColumnType.ofComplex("json"),
+ ColumnType.ofComplex("json"),
+ ColumnType.STRING_ARRAY,
+ ColumnType.STRING_ARRAY,
+ ColumnType.LONG_ARRAY,
+ ColumnType.LONG_ARRAY,
+ ColumnType.DOUBLE_ARRAY,
+ ColumnType.DOUBLE_ARRAY,
+ ColumnType.STRING_ARRAY,
+ ColumnType.LONG_ARRAY,
+ ColumnType.ofComplex("json"),
+ ColumnType.ofComplex("json"),
+ ColumnType.STRING,
+ ColumnType.STRING,
+ ColumnType.LONG,
+ ColumnType.DOUBLE,
+ ColumnType.ofComplex("json"),
+ ColumnType.STRING_ARRAY,
+ ColumnType.LONG_ARRAY,
+ ColumnType.DOUBLE_ARRAY,
+ ColumnType.LONG_ARRAY,
+ ColumnType.ofComplex("json"),
+ ColumnType.LONG_ARRAY,
+ ColumnType.ofComplex("json"),
+ ColumnType.ofComplex("json"),
+ ColumnType.LONG
+ )
.resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST)
.build()
),
@@ -6317,9 +6415,9 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
.build()
),
ImmutableList.of(
- new
Object[]{"{\"x\":100,\"y\":2.02,\"z\":\"300\",\"mixed\":1,\"mixed2\":\"1\"}"},
- new
Object[]{"{\"x\":200,\"y\":3.03,\"z\":\"abcdef\",\"mixed\":1.1,\"mixed2\":1}"},
- new Object[]{"{\"x\":100,\"y\":2.02,\"z\":\"400\",\"mixed2\":1.1}"}
+ new
Object[]{"{\"mixed\":1,\"mixed2\":\"1\",\"x\":100,\"y\":2.02,\"z\":\"300\"}"},
+ new
Object[]{"{\"mixed\":1.1,\"mixed2\":1,\"x\":200,\"y\":3.03,\"z\":\"abcdef\"}"},
+ new Object[]{"{\"mixed2\":1.1,\"x\":100,\"y\":2.02,\"z\":\"400\"}"}
),
RowSignature.builder()
.add("nest", ColumnType.NESTED_DATA)
@@ -6364,25 +6462,30 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
testBuilder()
.sql(
"select c,long,coalesce(c,long) as col "
- + " from druid.all_auto, unnest(json_value(arrayNestedLong,
'$[1]' returning bigint array)) as u(c) "
+ + " from druid.all_auto, unnest(json_value(arrayNestedLong, '$[1]'
returning bigint array)) as u(c) "
)
.expectedQueries(
ImmutableList.of(
Druids.newScanQueryBuilder()
- .dataSource(
- UnnestDataSource.create(
- new TableDataSource(DATA_SOURCE_ALL),
- new NestedFieldVirtualColumn("arrayNestedLong",
"$[1]", "j0.unnest", ColumnType.LONG_ARRAY),
- null
- )
- )
- .virtualColumns(expressionVirtualColumn("v0",
"nvl(\"j0.unnest\",\"long\")", ColumnType.LONG))
- .intervals(querySegmentSpec(Filtration.eternity()))
- .columns("j0.unnest", "long", "v0")
- .columnTypes(ColumnType.LONG, ColumnType.LONG,
ColumnType.LONG)
-
.resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST)
- .context(QUERY_CONTEXT_DEFAULT)
- .build()
+ .dataSource(
+ UnnestDataSource.create(
+ new TableDataSource(DATA_SOURCE_ALL),
+ new NestedFieldVirtualColumn(
+ "arrayNestedLong",
+ "$[1]",
+ "j0.unnest",
+ ColumnType.LONG_ARRAY
+ ),
+ null
+ )
+ )
+ .virtualColumns(expressionVirtualColumn("v0",
"nvl(\"j0.unnest\",\"long\")", ColumnType.LONG))
+ .intervals(querySegmentSpec(Filtration.eternity()))
+ .columns("j0.unnest", "long", "v0")
+ .columnTypes(ColumnType.LONG, ColumnType.LONG,
ColumnType.LONG)
+
.resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST)
+ .context(QUERY_CONTEXT_DEFAULT)
+ .build()
)
)
.expectedResults(
@@ -6399,10 +6502,10 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
)
.expectedSignature(
RowSignature.builder()
- .add("c", ColumnType.LONG)
- .add("long", ColumnType.LONG)
- .add("col", ColumnType.LONG)
- .build()
+ .add("c", ColumnType.LONG)
+ .add("long", ColumnType.LONG)
+ .add("col", ColumnType.LONG)
+ .build()
)
.run();
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]