in processing/src/main/java/org/apache/druid/segment/virtual/NestedFieldVirtualColumn.java [501:637]
public VectorObjectSelector makeVectorObjectSelector(
String columnName,
ColumnSelector columnSelector,
ReadableVectorOffset offset
)
{
ColumnHolder holder = columnSelector.getColumnHolder(fieldSpec.columnName);
if (holder == null) {
return NilVectorSelector.create(offset);
}
BaseColumn column = holder.getColumn();
if (column instanceof NestedDataComplexColumn) {
final NestedDataComplexColumn complexColumn = (NestedDataComplexColumn) column;
if (fieldSpec.processFromRaw) {
// processFromRaw is true, that means JSON_QUERY, which can return partial results, otherwise this virtual column
// is JSON_VALUE which only returns literals, so we can use the nested columns value selector
return new RawFieldVectorObjectSelector(complexColumn.makeVectorObjectSelector(offset), fieldSpec.parts);
}
Set<ColumnType> types = complexColumn.getColumnTypes(fieldSpec.parts);
ColumnType leastRestrictiveType = null;
if (types != null) {
for (ColumnType type : types) {
leastRestrictiveType = ColumnType.leastRestrictiveType(leastRestrictiveType, type);
}
}
if (leastRestrictiveType != null && leastRestrictiveType.isNumeric() && !Types.isNumeric(fieldSpec.expectedType)) {
return ExpressionVectorSelectors.castValueSelectorToObject(
offset,
columnName,
complexColumn.makeVectorValueSelector(fieldSpec.parts, offset),
leastRestrictiveType,
fieldSpec.expectedType == null ? ColumnType.STRING : fieldSpec.expectedType
);
}
final VectorObjectSelector objectSelector = complexColumn.makeVectorObjectSelector(fieldSpec.parts, offset);
if (leastRestrictiveType != null &&
leastRestrictiveType.isArray() &&
fieldSpec.expectedType != null &&
!fieldSpec.expectedType.isArray()
) {
final ExpressionType elementType = ExpressionType.fromColumnTypeStrict(leastRestrictiveType.getElementType());
final ExpressionType castTo = ExpressionType.fromColumnTypeStrict(fieldSpec.expectedType);
return makeVectorArrayToScalarObjectSelector(offset, objectSelector, elementType, castTo);
}
return objectSelector;
}
// not a nested column, but we can still do stuff if the path is the 'root', indicated by an empty path parts
if (fieldSpec.parts.isEmpty()) {
ColumnCapabilities capabilities = holder.getCapabilities();
// expectedType shouldn't possibly be null if we are being asked for an object selector and the underlying column
// is numeric, else we would have been asked for a value selector
Preconditions.checkArgument(
fieldSpec.expectedType != null,
"Asked for a VectorObjectSelector on a numeric column, 'expectedType' must not be null"
);
if (capabilities.isNumeric()) {
return ExpressionVectorSelectors.castValueSelectorToObject(
offset,
fieldSpec.columnName,
column.makeVectorValueSelector(offset),
capabilities.toColumnType(),
fieldSpec.expectedType
);
}
// if the underlying column is array typed, the vector object selector it spits out will homogenize stuff to
// make all of the objects a consistent type, which is typically a good thing, but if we are doing mixed type
// stuff and expect the output type to be scalar typed, then we should coerce things to only extract the scalars
if (capabilities.isArray() && !fieldSpec.expectedType.isArray()) {
final VectorObjectSelector delegate = column.makeVectorObjectSelector(offset);
final ExpressionType elementType = ExpressionType.fromColumnTypeStrict(capabilities.getElementType());
final ExpressionType castTo = ExpressionType.fromColumnTypeStrict(fieldSpec.expectedType);
return makeVectorArrayToScalarObjectSelector(offset, delegate, elementType, castTo);
}
return column.makeVectorObjectSelector(offset);
}
if (fieldSpec.parts.size() == 1 && fieldSpec.parts.get(0) instanceof NestedPathArrayElement && column instanceof VariantColumn) {
final VariantColumn<?> arrayColumn = (VariantColumn<?>) column;
final ExpressionType elementType = ExpressionType.fromColumnTypeStrict(
arrayColumn.getLogicalType().isArray() ? arrayColumn.getLogicalType().getElementType() : arrayColumn.getLogicalType()
);
final ExpressionType castTo = fieldSpec.expectedType == null
? ExpressionType.STRING
: ExpressionType.fromColumnTypeStrict(fieldSpec.expectedType);
VectorObjectSelector arraySelector = arrayColumn.makeVectorObjectSelector(offset);
final int elementNumber = ((NestedPathArrayElement) fieldSpec.parts.get(0)).getIndex();
if (elementNumber < 0) {
throw new IAE("Cannot make array element selector, negative array index not supported");
}
return new VectorObjectSelector()
{
private final Object[] elements = new Object[arraySelector.getMaxVectorSize()];
private int id = ReadableVectorInspector.NULL_ID;
@Override
public Object[] getObjectVector()
{
if (offset.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) {
elements[i] = ExprEval.ofType(elementType, anArray[elementNumber]).castTo(castTo).value();
} else {
elements[i] = null;
}
} else {
elements[i] = null;
}
}
id = offset.getId();
}
return elements;
}
@Override
public int getMaxVectorSize()
{
return arraySelector.getMaxVectorSize();
}
@Override
public int getCurrentVectorSize()
{
return arraySelector.getCurrentVectorSize();
}
};
}
// we are not a nested column and are being asked for a path that will never exist, so we are nil selector
return NilVectorSelector.create(offset);
}