Search in sources :

Example 11 with TimeFormatExtractionFn

use of org.apache.druid.query.extraction.TimeFormatExtractionFn in project druid by druid-io.

the class SearchQueryRunnerTest method testSearchOnTime.

@Test
public void testSearchOnTime() {
    SearchQuery searchQuery = Druids.newSearchQueryBuilder().dataSource(QueryRunnerTestHelper.DATA_SOURCE).granularity(QueryRunnerTestHelper.ALL_GRAN).intervals(QueryRunnerTestHelper.FULL_ON_INTERVAL_SPEC).query("Friday").dimensions(new ExtractionDimensionSpec(ColumnHolder.TIME_COLUMN_NAME, "__time2", new TimeFormatExtractionFn("EEEE", null, null, null, false))).build();
    List<SearchHit> expectedHits = new ArrayList<>();
    expectedHits.add(new SearchHit("__time2", "Friday", 169));
    checkSearchQuery(searchQuery, expectedHits);
}
Also used : TimeFormatExtractionFn(org.apache.druid.query.extraction.TimeFormatExtractionFn) ArrayList(java.util.ArrayList) ExtractionDimensionSpec(org.apache.druid.query.dimension.ExtractionDimensionSpec) InitializedNullHandlingTest(org.apache.druid.testing.InitializedNullHandlingTest) Test(org.junit.Test)

Example 12 with TimeFormatExtractionFn

use of org.apache.druid.query.extraction.TimeFormatExtractionFn in project druid by druid-io.

the class Expressions method toSimpleLeafFilter.

/**
 * Translates to a simple leaf filter, i.e. not an "expression" type filter. Note that the filter may still
 * reference expression virtual columns, if and only if "virtualColumnRegistry" is defined.
 *
 * @param plannerContext        planner context
 * @param rowSignature          input row signature
 * @param virtualColumnRegistry re-usable virtual column references, may be null if virtual columns aren't allowed
 * @param rexNode               Calcite row expression
 */
@Nullable
private static DimFilter toSimpleLeafFilter(final PlannerContext plannerContext, final RowSignature rowSignature, @Nullable final VirtualColumnRegistry virtualColumnRegistry, final RexNode rexNode) {
    final SqlKind kind = rexNode.getKind();
    if (kind == SqlKind.IS_TRUE || kind == SqlKind.IS_NOT_FALSE) {
        return toSimpleLeafFilter(plannerContext, rowSignature, virtualColumnRegistry, Iterables.getOnlyElement(((RexCall) rexNode).getOperands()));
    } else if (kind == SqlKind.IS_FALSE || kind == SqlKind.IS_NOT_TRUE) {
        return new NotDimFilter(toSimpleLeafFilter(plannerContext, rowSignature, virtualColumnRegistry, Iterables.getOnlyElement(((RexCall) rexNode).getOperands())));
    } else if (kind == SqlKind.IS_NULL || kind == SqlKind.IS_NOT_NULL) {
        final RexNode operand = Iterables.getOnlyElement(((RexCall) rexNode).getOperands());
        final DruidExpression druidExpression = toDruidExpression(plannerContext, rowSignature, operand);
        if (druidExpression == null) {
            return null;
        }
        final DimFilter equalFilter;
        if (druidExpression.isSimpleExtraction()) {
            equalFilter = new SelectorDimFilter(druidExpression.getSimpleExtraction().getColumn(), NullHandling.defaultStringValue(), druidExpression.getSimpleExtraction().getExtractionFn());
        } else if (virtualColumnRegistry != null) {
            final String virtualColumn = virtualColumnRegistry.getOrCreateVirtualColumnForExpression(druidExpression, operand.getType());
            equalFilter = new SelectorDimFilter(virtualColumn, NullHandling.defaultStringValue(), null);
        } else {
            return null;
        }
        return kind == SqlKind.IS_NOT_NULL ? new NotDimFilter(equalFilter) : equalFilter;
    } else if (kind == SqlKind.EQUALS || kind == SqlKind.NOT_EQUALS || kind == SqlKind.GREATER_THAN || kind == SqlKind.GREATER_THAN_OR_EQUAL || kind == SqlKind.LESS_THAN || kind == SqlKind.LESS_THAN_OR_EQUAL) {
        final List<RexNode> operands = ((RexCall) rexNode).getOperands();
        Preconditions.checkState(operands.size() == 2, "Expected 2 operands, got[%,d]", operands.size());
        boolean flip = false;
        RexNode lhs = operands.get(0);
        RexNode rhs = operands.get(1);
        if (lhs.getKind() == SqlKind.LITERAL && rhs.getKind() != SqlKind.LITERAL) {
            // swap lhs, rhs
            RexNode x = lhs;
            lhs = rhs;
            rhs = x;
            flip = true;
        }
        // Flip operator, maybe.
        final SqlKind flippedKind;
        if (flip) {
            switch(kind) {
                case EQUALS:
                case NOT_EQUALS:
                    flippedKind = kind;
                    break;
                case GREATER_THAN:
                    flippedKind = SqlKind.LESS_THAN;
                    break;
                case GREATER_THAN_OR_EQUAL:
                    flippedKind = SqlKind.LESS_THAN_OR_EQUAL;
                    break;
                case LESS_THAN:
                    flippedKind = SqlKind.GREATER_THAN;
                    break;
                case LESS_THAN_OR_EQUAL:
                    flippedKind = SqlKind.GREATER_THAN_OR_EQUAL;
                    break;
                default:
                    throw new ISE("Kind[%s] not expected here", kind);
            }
        } else {
            flippedKind = kind;
        }
        // rhs must be a literal
        if (rhs.getKind() != SqlKind.LITERAL) {
            return null;
        }
        // Translate lhs to a DruidExpression.
        final DruidExpression lhsExpression = toDruidExpression(plannerContext, rowSignature, lhs);
        if (lhsExpression == null) {
            return null;
        }
        // Special handling for filters on FLOOR(__time TO granularity).
        final Granularity queryGranularity = toQueryGranularity(lhsExpression, plannerContext.getExprMacroTable());
        if (queryGranularity != null) {
            // lhs is FLOOR(__time TO granularity); rhs must be a timestamp
            final long rhsMillis = Calcites.calciteDateTimeLiteralToJoda(rhs, plannerContext.getTimeZone()).getMillis();
            return buildTimeFloorFilter(ColumnHolder.TIME_COLUMN_NAME, queryGranularity, flippedKind, rhsMillis);
        }
        final String column;
        final ExtractionFn extractionFn;
        if (lhsExpression.isSimpleExtraction()) {
            column = lhsExpression.getSimpleExtraction().getColumn();
            extractionFn = lhsExpression.getSimpleExtraction().getExtractionFn();
        } else if (virtualColumnRegistry != null) {
            column = virtualColumnRegistry.getOrCreateVirtualColumnForExpression(lhsExpression, lhs.getType());
            extractionFn = null;
        } else {
            return null;
        }
        if (column.equals(ColumnHolder.TIME_COLUMN_NAME) && extractionFn instanceof TimeFormatExtractionFn) {
            // Check if we can strip the extractionFn and convert the filter to a direct filter on __time.
            // This allows potential conversion to query-level "intervals" later on, which is ideal for Druid queries.
            final Granularity granularity = ExtractionFns.toQueryGranularity(extractionFn);
            if (granularity != null) {
                // lhs is FLOOR(__time TO granularity); rhs must be a timestamp
                final long rhsMillis = Calcites.calciteDateTimeLiteralToJoda(rhs, plannerContext.getTimeZone()).getMillis();
                final Interval rhsInterval = granularity.bucket(DateTimes.utc(rhsMillis));
                // Is rhs aligned on granularity boundaries?
                final boolean rhsAligned = rhsInterval.getStartMillis() == rhsMillis;
                // Create a BoundRefKey that strips the extractionFn and compares __time as a number.
                final BoundRefKey boundRefKey = new BoundRefKey(column, null, StringComparators.NUMERIC);
                return getBoundTimeDimFilter(flippedKind, boundRefKey, rhsInterval, rhsAligned);
            }
        }
        final String val;
        final RexLiteral rhsLiteral = (RexLiteral) rhs;
        if (SqlTypeName.NUMERIC_TYPES.contains(rhsLiteral.getTypeName())) {
            val = String.valueOf(RexLiteral.value(rhsLiteral));
        } else if (SqlTypeName.CHAR_TYPES.contains(rhsLiteral.getTypeName())) {
            val = String.valueOf(RexLiteral.stringValue(rhsLiteral));
        } else if (SqlTypeName.TIMESTAMP == rhsLiteral.getTypeName() || SqlTypeName.DATE == rhsLiteral.getTypeName()) {
            val = String.valueOf(Calcites.calciteDateTimeLiteralToJoda(rhsLiteral, plannerContext.getTimeZone()).getMillis());
        } else {
            // Don't know how to filter on this kind of literal.
            return null;
        }
        // Numeric lhs needs a numeric comparison.
        final StringComparator comparator = Calcites.getStringComparatorForRelDataType(lhs.getType());
        final BoundRefKey boundRefKey = new BoundRefKey(column, extractionFn, comparator);
        final DimFilter filter;
        // Always use BoundDimFilters, to simplify filter optimization later (it helps to remember the comparator).
        switch(flippedKind) {
            case EQUALS:
                filter = Bounds.equalTo(boundRefKey, val);
                break;
            case NOT_EQUALS:
                filter = new NotDimFilter(Bounds.equalTo(boundRefKey, val));
                break;
            case GREATER_THAN:
                filter = Bounds.greaterThan(boundRefKey, val);
                break;
            case GREATER_THAN_OR_EQUAL:
                filter = Bounds.greaterThanOrEqualTo(boundRefKey, val);
                break;
            case LESS_THAN:
                filter = Bounds.lessThan(boundRefKey, val);
                break;
            case LESS_THAN_OR_EQUAL:
                filter = Bounds.lessThanOrEqualTo(boundRefKey, val);
                break;
            default:
                throw new IllegalStateException("Shouldn't have got here");
        }
        return filter;
    } else if (rexNode instanceof RexCall) {
        final SqlOperator operator = ((RexCall) rexNode).getOperator();
        final SqlOperatorConversion conversion = plannerContext.getOperatorTable().lookupOperatorConversion(operator);
        if (conversion == null) {
            return null;
        } else {
            return conversion.toDruidFilter(plannerContext, rowSignature, virtualColumnRegistry, rexNode);
        }
    } else {
        return null;
    }
}
Also used : TimeFormatExtractionFn(org.apache.druid.query.extraction.TimeFormatExtractionFn) RexLiteral(org.apache.calcite.rex.RexLiteral) NotDimFilter(org.apache.druid.query.filter.NotDimFilter) SqlOperator(org.apache.calcite.sql.SqlOperator) SqlKind(org.apache.calcite.sql.SqlKind) Granularity(org.apache.druid.java.util.common.granularity.Granularity) StringComparator(org.apache.druid.query.ordering.StringComparator) RexCall(org.apache.calcite.rex.RexCall) ExtractionFn(org.apache.druid.query.extraction.ExtractionFn) TimeFormatExtractionFn(org.apache.druid.query.extraction.TimeFormatExtractionFn) SelectorDimFilter(org.apache.druid.query.filter.SelectorDimFilter) ArrayList(java.util.ArrayList) List(java.util.List) ISE(org.apache.druid.java.util.common.ISE) BoundRefKey(org.apache.druid.sql.calcite.filtration.BoundRefKey) SelectorDimFilter(org.apache.druid.query.filter.SelectorDimFilter) AndDimFilter(org.apache.druid.query.filter.AndDimFilter) NotDimFilter(org.apache.druid.query.filter.NotDimFilter) DimFilter(org.apache.druid.query.filter.DimFilter) OrDimFilter(org.apache.druid.query.filter.OrDimFilter) ExpressionDimFilter(org.apache.druid.query.filter.ExpressionDimFilter) RexNode(org.apache.calcite.rex.RexNode) Interval(org.joda.time.Interval) Nullable(javax.annotation.Nullable)

Aggregations

TimeFormatExtractionFn (org.apache.druid.query.extraction.TimeFormatExtractionFn)12 ExtractionDimensionSpec (org.apache.druid.query.dimension.ExtractionDimensionSpec)9 Test (org.junit.Test)9 DefaultDimensionSpec (org.apache.druid.query.dimension.DefaultDimensionSpec)8 SelectorDimFilter (org.apache.druid.query.filter.SelectorDimFilter)7 InitializedNullHandlingTest (org.apache.druid.testing.InitializedNullHandlingTest)7 OrDimFilter (org.apache.druid.query.filter.OrDimFilter)5 List (java.util.List)3 ExtractionFn (org.apache.druid.query.extraction.ExtractionFn)3 StrlenExtractionFn (org.apache.druid.query.extraction.StrlenExtractionFn)3 ImmutableList (com.google.common.collect.ImmutableList)2 ArrayList (java.util.ArrayList)2 BitSet (java.util.BitSet)2 TimeUnit (java.util.concurrent.TimeUnit)2 NullHandling (org.apache.druid.common.config.NullHandling)2 Intervals (org.apache.druid.java.util.common.Intervals)2 Granularities (org.apache.druid.java.util.common.granularity.Granularities)2 Sequence (org.apache.druid.java.util.common.guava.Sequence)2 Closer (org.apache.druid.java.util.common.io.Closer)2 LongSumAggregatorFactory (org.apache.druid.query.aggregation.LongSumAggregatorFactory)2