use of com.apple.foundationdb.record.query.expressions.QueryComponent in project fdb-record-layer by FoundationDB.
the class FDBSortQueryIndexSelectionTest method testComplexQuery8x.
/**
* Verify that a query with a filter on one field and a sort on another uses the index of the sort preferentially,
* and falls back to filtering to implement the filter if an appropriate multi-field index is not available.
*/
@DualPlannerTest
public void testComplexQuery8x() throws Exception {
RecordMetaDataHook hook = complexQuerySetupHook();
complexQuerySetup(hook);
final QueryComponent filter = Query.field("str_value_indexed").equalsValue("even");
RecordQuery query = RecordQuery.newBuilder().setRecordType("MySimpleRecord").setFilter(filter).setSort(field("num_value_3_indexed")).build();
RecordQueryPlan plan = planner.plan(query);
assertThat(plan, filter(filter, indexScan(allOf(indexName("MySimpleRecord$num_value_3_indexed"), unbounded()))));
if (planner instanceof RecordQueryPlanner) {
assertEquals(-1429997503, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
// TODO: Issue https://github.com/FoundationDB/fdb-record-layer/issues/1074
// assertEquals(-1729416480, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
} else {
assertEquals(952181942, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
}
try (FDBRecordContext context = openContext()) {
openSimpleRecordStore(context, hook);
int i = 0;
try (RecordCursorIterator<FDBQueriedRecord<Message>> cursor = recordStore.executeQuery(plan).asIterator()) {
while (cursor.hasNext()) {
FDBQueriedRecord<Message> rec = cursor.next();
TestRecords1Proto.MySimpleRecord.Builder myrec = TestRecords1Proto.MySimpleRecord.newBuilder();
myrec.mergeFrom(Objects.requireNonNull(rec).getRecord());
assertEquals("even", myrec.getStrValueIndexed());
i++;
}
}
assertEquals(50, i);
assertDiscardedAtMost(50, context);
}
}
use of com.apple.foundationdb.record.query.expressions.QueryComponent in project fdb-record-layer by FoundationDB.
the class FDBSortQueryIndexSelectionTest method sortWithNonScannableFilterOnIndex.
/**
* Verify that if there is an index on the sorted field but it does not satisfy the comparison
* of a filter (because that comparison cannot be accomplished with a scan) that the index
* is still used solely to accomplish sorting.
*/
@ParameterizedTest
@MethodSource("hooks")
void sortWithNonScannableFilterOnIndex(RecordMetaDataHook hook, PlannableIndexTypes indexTypes) throws Exception {
setupSimpleRecordStore(hook, (i, builder) -> builder.setRecNo(i).setNumValue2(i % 2).setNumValue3Indexed(i % 3));
final QueryComponent filter = Query.field("num_value_3_indexed").notEquals(1);
RecordQuery query = RecordQuery.newBuilder().setRecordType("MySimpleRecord").setFilter(filter).setSort(field("num_value_3_indexed")).build();
setupPlanner(indexTypes);
// Fetch(Covering(Index(MySimpleRecord$num_value_3_indexed <,>) -> [num_value_3_indexed: KEY[0], rec_no: KEY[1]]) | num_value_3_indexed NOT_EQUALS 1)
RecordQueryPlan plan = planner.plan(query);
assertThat(plan, fetch(filter(filter, coveringIndexScan(indexScan(allOf(indexName("MySimpleRecord$num_value_3_indexed"), unbounded()))))));
assertEquals(-1303978120, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(1548200279, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(-856491930, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
AtomicInteger lastNumValue3 = new AtomicInteger(Integer.MIN_VALUE);
int returned = querySimpleRecordStore(hook, plan, EvaluationContext::empty, builder -> {
assertThat(builder.getNumValue3Indexed(), not(equalTo(1)));
assertThat(builder.getNumValue3Indexed(), greaterThanOrEqualTo(lastNumValue3.get()));
lastNumValue3.set(builder.getNumValue3Indexed());
}, context -> assertDiscardedAtMost(33, context));
assertEquals(67, returned);
// reset planner
setupPlanner(null);
}
use of com.apple.foundationdb.record.query.expressions.QueryComponent in project fdb-record-layer by FoundationDB.
the class FilterVisitor method partitionFilters.
public static void partitionFilters(@Nonnull final List<QueryComponent> filters, @Nonnull final AvailableFields availableFields, @Nonnull final List<QueryComponent> indexFilters, @Nonnull final List<QueryComponent> residualFilters, @Nullable final Set<KeyExpression> allReferencedFields) {
for (final QueryComponent filter : filters) {
final Set<KeyExpression> referencedFields = new HashSet<>();
if (findFilterReferencedFields(filter, referencedFields)) {
if (availableFields.containsAll(referencedFields)) {
indexFilters.add(filter);
if (allReferencedFields != null) {
allReferencedFields.addAll(referencedFields);
}
continue;
}
}
residualFilters.add(filter);
}
}
use of com.apple.foundationdb.record.query.expressions.QueryComponent in project fdb-record-layer by FoundationDB.
the class UnionVisitor method postVisit.
@Nonnull
@Override
public RecordQueryPlan postVisit(@Nonnull final RecordQueryPlan recordQueryPlan) {
if (recordQueryPlan instanceof RecordQueryUnionPlanBase) {
RecordQueryUnionPlanBase unionPlan = (RecordQueryUnionPlanBase) recordQueryPlan;
final Set<KeyExpression> requiredFields = unionPlan.getRequiredFields();
boolean shouldPullOutFilter = false;
QueryComponent filter = null;
if (unionPlan.getChildren().stream().allMatch(child -> child instanceof RecordQueryFilterPlan)) {
filter = ((RecordQueryFilterPlan) unionPlan.getChildren().get(0)).getConjunctedFilter();
// needed for lambda expression
final QueryComponent finalFilter = filter;
shouldPullOutFilter = unionPlan.getChildren().stream().allMatch(plan -> ((RecordQueryFilterPlan) plan).getConjunctedFilter().equals(finalFilter));
}
List<ExpressionRef<RecordQueryPlan>> newChildren = new ArrayList<>(unionPlan.getChildren().size());
for (RecordQueryPlan plan : unionPlan.getChildren()) {
if (shouldPullOutFilter) {
// Check if the plan under the filter can have its index fetch removed.
if (!(plan instanceof RecordQueryFilterPlan)) {
throw new RecordCoreException("serious logic error: thought this was a filter plan but it wasn't");
}
plan = ((RecordQueryFilterPlan) plan).getChild();
}
@Nullable RecordQueryPlan newPlan = removeIndexFetch(plan, requiredFields);
if (newPlan == null) {
// can't remove index fetch, so give up
return recordQueryPlan;
}
newChildren.add(GroupExpressionRef.of(newPlan));
}
RecordQueryPlan newUnionPlan = new RecordQueryFetchFromPartialRecordPlan(unionPlan.withChildrenReferences(newChildren), TranslateValueFunction.unableToTranslate());
if (shouldPullOutFilter) {
return new RecordQueryFilterPlan(newUnionPlan, filter);
} else {
return newUnionPlan;
}
}
return recordQueryPlan;
}
use of com.apple.foundationdb.record.query.expressions.QueryComponent in project fdb-record-layer by FoundationDB.
the class LuceneQueryIntegrationTest method selectsFromMultipleNestedIndexes.
@Test
void selectsFromMultipleNestedIndexes() throws Exception {
useRewritePlanner = false;
try (FDBRecordContext context = openContext()) {
openRecordStore(context, metaData -> {
metaData.addIndex(TextIndexTestUtils.COMPLEX_DOC, new Index(nestedDualIndex.toProto()));
metaData.addIndex(TextIndexTestUtils.COMPLEX_DOC, new Index(nestedDualIndex2.toProto()));
});
setupPlanner(null);
QueryComponent filter = new LuceneQueryComponent("text2:test", Arrays.asList("text2"));
RecordQuery rq = RecordQuery.newBuilder().setRecordType(TextIndexTestUtils.COMPLEX_DOC).setFilter(filter).build();
final RecordQueryPlan plan = planner.plan(rq);
Set<String> appliedIndexNames = plan.getUsedIndexes();
Assertions.assertEquals(1, appliedIndexNames.size(), "index selection is incorrect");
Assertions.assertTrue(appliedIndexNames.contains(nestedDualIndex2.getName()), "Did not select the correct index");
}
}
Aggregations