in processing/src/main/java/org/apache/druid/segment/RowBasedColumnSelectorFactory.java [170:410]
private DimensionSelector makeDimensionSelectorUndecorated(DimensionSpec dimensionSpec)
{
final String dimension = dimensionSpec.getDimension();
final ExtractionFn extractionFn = dimensionSpec.getExtractionFn();
if (ColumnHolder.TIME_COLUMN_NAME.equals(dimensionSpec.getDimension())) {
if (extractionFn == null) {
throw new UnsupportedOperationException("time dimension must provide an extraction function");
}
final ToLongFunction<T> timestampFunction = adapter.timestampFunction();
return new BaseSingleValueDimensionSelector()
{
private long currentId = RowIdSupplier.INIT;
private String currentValue;
@Override
protected String getValue()
{
updateCurrentValue();
return currentValue;
}
@Override
public void inspectRuntimeShape(RuntimeShapeInspector inspector)
{
inspector.visit("row", rowSupplier);
inspector.visit("extractionFn", extractionFn);
}
private void updateCurrentValue()
{
if (rowIdSupplier == null || rowIdSupplier.getRowId() != currentId) {
currentValue = extractionFn.apply(timestampFunction.applyAsLong(rowSupplier.get()));
if (rowIdSupplier != null) {
currentId = rowIdSupplier.getRowId();
}
}
}
};
} else {
final Function<T, Object> dimFunction = adapter.columnFunction(dimension);
return new DimensionSelector()
{
private long currentId = RowIdSupplier.INIT;
private List<String> dimensionValues;
private final RangeIndexedInts indexedInts = new RangeIndexedInts();
@Override
public IndexedInts getRow()
{
updateCurrentValues();
indexedInts.setSize(dimensionValues.size());
return indexedInts;
}
@Override
public ValueMatcher makeValueMatcher(final @Nullable String value)
{
return new ValueMatcher()
{
@Override
public boolean matches(boolean includeUnknown)
{
updateCurrentValues();
if (dimensionValues.isEmpty()) {
return includeUnknown || value == null;
}
for (String dimensionValue : dimensionValues) {
if ((includeUnknown && dimensionValue == null) || Objects.equals(dimensionValue, value)) {
return true;
}
}
return false;
}
@Override
public void inspectRuntimeShape(RuntimeShapeInspector inspector)
{
inspector.visit("row", rowSupplier);
inspector.visit("extractionFn", extractionFn);
}
};
}
@Override
public ValueMatcher makeValueMatcher(final DruidPredicateFactory predicateFactory)
{
final DruidObjectPredicate<String> predicate = predicateFactory.makeStringPredicate();
return new ValueMatcher()
{
@Override
public boolean matches(boolean includeUnknown)
{
updateCurrentValues();
if (dimensionValues.isEmpty()) {
return predicate.apply(null).matches(includeUnknown);
}
for (String dimensionValue : dimensionValues) {
if (predicate.apply(dimensionValue).matches(includeUnknown)) {
return true;
}
}
return false;
}
@Override
public void inspectRuntimeShape(RuntimeShapeInspector inspector)
{
inspector.visit("row", rowSupplier);
inspector.visit("predicate", predicateFactory);
inspector.visit("extractionFn", extractionFn);
}
};
}
@Override
public int getValueCardinality()
{
return DimensionDictionarySelector.CARDINALITY_UNKNOWN;
}
@Override
public String lookupName(int id)
{
updateCurrentValues();
return dimensionValues.get(id);
}
@Override
public boolean nameLookupPossibleInAdvance()
{
return false;
}
@Nullable
@Override
public IdLookup idLookup()
{
return null;
}
@Nullable
@Override
public Object getObject()
{
updateCurrentValues();
if (dimensionValues.size() == 1) {
return dimensionValues.get(0);
}
return dimensionValues;
}
@Override
public Class classOfObject()
{
return Object.class;
}
@Override
public void inspectRuntimeShape(RuntimeShapeInspector inspector)
{
inspector.visit("row", rowSupplier);
inspector.visit("extractionFn", extractionFn);
}
private void updateCurrentValues()
{
if (rowIdSupplier == null || rowIdSupplier.getRowId() != currentId) {
try {
final Object rawValue = dimFunction.apply(rowSupplier.get());
if (rawValue == null || rawValue instanceof String) {
final String s = (String) rawValue;
if (extractionFn == null) {
dimensionValues = Collections.singletonList(s);
} else {
dimensionValues = Collections.singletonList(extractionFn.apply(s));
}
} else if (rawValue instanceof List) {
//noinspection rawtypes
final List<String> values = new ArrayList<>(((List) rawValue).size());
//noinspection rawtypes
for (final Object item : ((List) rawValue)) {
final String itemString;
if (useStringValueOfNullInLists) {
itemString = String.valueOf(item);
} else {
itemString = item == null ? null : String.valueOf(item);
}
// Behavior with null item is to convert it to string "null". This is not what most other areas of Druid
// would do when treating a null as a string, but it's consistent with Rows.objectToStrings, which is
// commonly used when retrieving strings from input-row-like objects.
if (extractionFn == null) {
values.add(itemString);
} else {
values.add(extractionFn.apply(itemString));
}
}
dimensionValues = values;
} else {
final List<String> nonExtractedValues = Rows.objectToStrings(rawValue);
dimensionValues = new ArrayList<>(nonExtractedValues.size());
for (final String value : nonExtractedValues) {
if (extractionFn == null) {
dimensionValues.add(value);
} else {
dimensionValues.add(extractionFn.apply(value));
}
}
}
}
catch (Throwable e) {
currentId = RowIdSupplier.INIT;
throw e;
}
if (rowIdSupplier != null) {
currentId = rowIdSupplier.getRowId();
}
}
}
};
}
}