public Filter group(Filter filter) { if (filter instanceof FilterList) { FilterList fList = (FilterList) filter; // We need to create a new FL here taking up only the filters of our interest FilterList newFList = new FilterList(fList.getOperator()); List<Filter> filters = fList.getFilters(); if (fList.getOperator() == Operator.MUST_PASS_ONE) { for (Filter subFilter : filters) { Filter resultFilter = handleFilterWithinOR(subFilter); // If result filter is not SingleColumnValueFilter or filter list that means OR branch is // having different type of filter other than SCVF. In that case we should not consider // the OR branch for scanning. if (resultFilter instanceof FilterList) { newFList.addFilter(resultFilter); } else if (resultFilter != null) { // This means OR filter list have at least one filter other than SCVF(may be other // child OR branches). return null; } } addORColsToFinalList(newFList); if (newFList.getFilters().isEmpty()) { return null; } return newFList; } else { // AND condition as long as the condition is AND in one sub tree all those can be // grouped under one AND parent(new one). for (Filter subFilter : filters) { Filter group = handleFilterWithinAND(subFilter); // group is null means, all are AND conditions and will be handled at once with the // below createFinalFilter if (group != null) { newFList.addFilter(group); } } addANDColsToFinalList(newFList); if (newFList.getFilters().isEmpty()) { return null; } return newFList; } } else if (filter instanceof SingleColumnValueFilter || filter instanceof SingleColumnRangeFilter) { return filter; } return null; }
/** * Since you can use Filter Lists as children of Filter Lists, you can create a hierarchy of * filters to be evaluated. In the hierarchy if OR branch having any filter type other than SCVF * as child then we should not consider the branch for scanning because we cannot fetch seek * points from other type of filters without column and value details. Ex: AND AND * __________|_______ | | | --> SCVF OR SCVF _______|______ | | ROWFILTER SVCF If the OR is root * then we should skip index table scanning for this filter. OR _______|______ --> null | | * ROWFILTER SVCF If the OR is child of another OR branch then parent OR branch will be excluded * for scanning. Ex: AND AND __________|_______ | | | --> SCVF OR SCVF _______|______ | | OR SVCF * _______|______ | | ROWFILTER SVCF * * @param filter * @return if filter is filter list with AND condition then we will return AND branch after * grouping. if filter is filter list with OR condition return null if no children is of type * other than SCVF or filter list else return different filter. if filter is SCVF then return * null. returning null means we are combining the filter(s) with children of parent OR filter * to perform optimizations. */ private Filter handleFilterWithinOR(Filter filter) { if (filter instanceof FilterList) { FilterList fList = (FilterList) filter; if (fList.getOperator() == Operator.MUST_PASS_ONE) { List<Filter> filters = fList.getFilters(); Filter resultFilter = null; for (Filter subFilter : filters) { // If this OR branch in the filter list have filter type other than SCVF we should report // it to parent by returning the other type of filter in such a way that the branch will // be skipped from index scan. resultFilter = handleFilterWithinOR(subFilter); if (resultFilter == null || (resultFilter instanceof FilterList)) { continue; } else { return resultFilter; } } return null; } else { return new FilterGroupingWorker().group(fList); } } else if (filter instanceof SingleColumnValueFilter) { handleScvfOfOR((SingleColumnValueFilter) filter); return null; } // TODO when we expose SingleColumnRangeFilter to handle that also here. // filter other than SingleColumnValueFilter. return filter; }
@Test public void testParsedFilter() { String q1 = "@cluster = \"cluster1\" and @datacenter = \"dc1\" and @field3 = 100000"; try { FilterList filterList = (FilterList) buildFilter(q1); Assert.assertEquals(FilterList.Operator.MUST_PASS_ONE, filterList.getOperator()); Assert.assertEquals(1, filterList.getFilters().size()); Assert.assertEquals(2, ((FilterList) filterList.getFilters().get(0)).getFilters().size()); } catch (EagleQueryParseException e) { Assert.fail(e.getMessage()); } String q2 = "@cluster = \"cluster1\" and @datacenter = \"dc1\" and ( @field3 = 100000 or @field3 < 100000)"; try { FilterList filterList = (FilterList) buildFilter(q2); Assert.assertEquals(FilterList.Operator.MUST_PASS_ONE, filterList.getOperator()); Assert.assertEquals(2, filterList.getFilters().size()); Assert.assertEquals(2, ((FilterList) filterList.getFilters().get(0)).getFilters().size()); } catch (EagleQueryParseException e) { Assert.fail(e.getMessage()); } // Test parse success but bad type of value String q3 = "@cluster = \"cluster1\" and @datacenter = \"dc1\" and ( @field3 = 100000 or @field3 < \"bad_int_100000\")"; boolean q3Ex = false; try { Assert.assertNull(buildFilter(q3)); } catch (EagleQueryParseException e) { Assert.fail(e.getMessage()); } catch (IllegalArgumentException e) { LOG.debug("Expect: ", e); Assert.assertTrue(e.getCause() instanceof NumberFormatException); q3Ex = true; } Assert.assertTrue(q3Ex); }
private Filter handleFilterWithinAND(Filter filter) { if (filter instanceof FilterList) { FilterList fList = (FilterList) filter; if (fList.getOperator() == Operator.MUST_PASS_ONE) { return new FilterGroupingWorker().group(fList); } else { List<Filter> filters = fList.getFilters(); for (Filter subFilter : filters) { handleFilterWithinAND(subFilter); } } } else if (filter instanceof SingleColumnValueFilter) { handleScvf((SingleColumnValueFilter) filter); } // TODO when we expose SingleColumnRangeFilter to handle that also here. return null; }
/** * Process a {@link org.plasma.query.model.LogicalOperator logical operator} query traversal start * event. If the {@link FilterList filter list} on the top of the filter stack is not an 'OR' * filter, since it's immutable and we cannot modify its operator, create an 'OR' filter and swaps * out the existing filters into the new 'OR' {@link FilterList filter list}. */ public void start(LogicalOperator operator) { switch (operator.getValue()) { case AND: break; // default filter list oper is must-pass-all (AND) case OR: FilterList top = this.filterStack.peek(); if (top.getOperator().ordinal() != FilterList.Operator.MUST_PASS_ONE.ordinal()) { FilterList orList = new FilterList(FilterList.Operator.MUST_PASS_ONE); for (Filter filter : top.getFilters()) orList.addFilter(filter); top.getFilters().clear(); this.filterStack.pop(); FilterList previous = this.filterStack.peek(); if (!previous.getFilters().remove(top)) throw new IllegalStateException("could not remove filter list"); previous.addFilter(orList); this.filterStack.push(orList); } break; } super.start(operator); }