use of com.apple.foundationdb.record.query.plan.RecordQueryPlanner in project fdb-record-layer by FoundationDB.
the class FDBInQueryTest method testMultipleInQueryIndexSorted.
/**
* Verify that a query with multiple INs is translated into an index scan within multiple IN joins, when the query
* sort order is compatible with the nesting of the IN joins.
*/
@DualPlannerTest
void testMultipleInQueryIndexSorted() throws Exception {
final RecordMetaDataHook recordMetaDataHook = metaData -> {
metaData.getRecordType("MyRecord").setPrimaryKey(field("str_value"));
metaData.addIndex("MyRecord", "ind", field("header").nest(field("rec_no"), field("path")));
};
setupRecordsWithHeader(recordMetaDataHook, (i, record) -> {
record.setStrValue("_" + i);
record.getHeaderBuilder().setRecNo(i % 5).setPath("String" + i % 50).setNum(i);
});
RecordQuery query = RecordQuery.newBuilder().setRecordType("MyRecord").setFilter(Query.field("header").matches(Query.and(Query.field("path").in(asList("String6", "String25", "String1", "String34")), Query.field("rec_no").in(asList(4L, 1L))))).setSort(field("header").nest(field("rec_no"), field("path"))).build();
// Index(ind [EQUALS $__in_rec_no__1, EQUALS $__in_path__0]) WHERE __in_path__0 IN [String1, String25, String34, String6] SORTED WHERE __in_rec_no__1 IN [1, 4] SORTED
RecordQueryPlan plan = planner.plan(query);
List<String> sortedStringList = asList("String1", "String25", "String34", "String6");
List<Long> sortedLongList = asList(1L, 4L);
if (planner instanceof RecordQueryPlanner) {
assertMatchesExactly(plan, inValuesJoinPlan(inValuesJoinPlan(indexPlan().where(indexName("ind")).and(scanComparisons(range("[EQUALS $__in_rec_no__1, EQUALS $__in_path__0]")))).where(inValuesList(equalsObject(sortedStringList)))).where(inValuesList(equalsObject(sortedLongList))));
assertEquals(303286809, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(-1661991116, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(1396696428, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
} else {
assertMatchesExactly(plan, fetchFromPartialRecordPlan(inValuesJoinPlan(inValuesJoinPlan(coveringIndexPlan().where(indexPlanOf(indexPlan().where(indexName("ind")).and(scanComparisons(equalities(exactly(anyParameterComparison(), anyParameterComparison()))))))).where(inValuesList(equalsObject(sortedStringList)))).where(inValuesList(equalsObject(sortedLongList)))));
assertEquals(457372810, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(-30052367, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(959442763, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
}
queryRecordsWithHeader(recordMetaDataHook, plan, cursor -> assertEquals(asList("1:String1", "1:String1", "1:String6", "1:String6", "4:String34", "4:String34"), cursor.map(m -> m.getHeader().getRecNo() + ":" + m.getHeader().getPath()).asList().get()), TestHelpers::assertDiscardedNone);
}
use of com.apple.foundationdb.record.query.plan.RecordQueryPlanner in project fdb-record-layer by FoundationDB.
the class FDBCoveringIndexQueryTest method coveringWithAdditionalFilter.
/**
* Verify that a filter not satisfied by the index scan itself but using fields present in the index
* can still allow a covering scan with the filter on the partial records.
*/
@DualPlannerTest
void coveringWithAdditionalFilter() throws Exception {
RecordMetaDataHook hook = metaData -> {
metaData.removeIndex("MySimpleRecord$num_value_3_indexed");
metaData.addIndex("MySimpleRecord", new Index("multi_index", "num_value_3_indexed", "num_value_2"));
};
complexQuerySetup(hook);
RecordQuery query = RecordQuery.newBuilder().setRecordType("MySimpleRecord").setFilter(Query.and(Query.field("num_value_3_indexed").lessThan(1), Query.field("num_value_2").lessThan(2))).setRequiredResults(Collections.singletonList(field("num_value_3_indexed"))).build();
// Covering(Index(multi_index ([null],[1])) -> [num_value_2: KEY[1], num_value_3_indexed: KEY[0], rec_no: KEY[2]]) | num_value_2 LESS_THAN 2
RecordQueryPlan plan = planner.plan(query);
if (planner instanceof RecordQueryPlanner) {
final BindingMatcher<? extends RecordQueryPlan> planMatcher = filterPlan(coveringIndexPlan().where(indexPlanOf(indexPlan().where(indexName("multi_index")).and(scanComparisons(range("([null],[1])")))))).where(queryComponents(exactly(equalsObject(Query.field("num_value_2").lessThan(2)))));
assertMatchesExactly(plan, planMatcher);
assertEquals(-1374002128, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(1359983418, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(-1492450855, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
} else {
final BindingMatcher<? extends RecordQueryPlan> planMatcher = predicatesFilterPlan(coveringIndexPlan().where(indexPlanOf(indexPlan().where(indexName("multi_index")).and(scanComparisons(range("([null],[1])")))))).where(predicates(only(valuePredicate(fieldValue("num_value_2"), new Comparisons.SimpleComparison(Comparisons.Type.LESS_THAN, 2)))));
assertMatchesExactly(plan, planMatcher);
assertEquals(762957369, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(-2135370744, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(-692837721, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
}
}
use of com.apple.foundationdb.record.query.plan.RecordQueryPlanner in project fdb-record-layer by FoundationDB.
the class FDBCoveringIndexQueryTest method queryCoveringAggregate.
/**
* Verify that selecting the group key and the aggregate function from a grouped aggregate index can be planned
* by a covering aggregate index.
*/
@Test
void queryCoveringAggregate() {
Index sumIndex = new Index("value3sum", field("num_value_3_indexed").groupBy(Key.Expressions.concatenateFields("str_value_indexed", "num_value_2")), IndexTypes.SUM);
RecordMetaDataHook hook = metaData -> metaData.addIndex("MySimpleRecord", sumIndex);
try (FDBRecordContext context = openContext()) {
openSimpleRecordStore(context, hook);
for (int i = 0; i < 20; i++) {
TestRecords1Proto.MySimpleRecord.Builder recBuilder = TestRecords1Proto.MySimpleRecord.newBuilder();
recBuilder.setRecNo(i);
recBuilder.setStrValueIndexed((i & 1) == 1 ? "odd" : "even");
recBuilder.setNumValue2(i % 3);
recBuilder.setNumValue3Indexed(i % 5);
recordStore.saveRecord(recBuilder.build());
}
commit(context);
}
RecordQuery query = RecordQuery.newBuilder().setRecordType("MySimpleRecord").setFilter(Query.field("str_value_indexed").equalsValue("even")).setRequiredResults(Arrays.asList(field("str_value_indexed"), field("num_value_2"))).build();
// This is here since the main planner doesn't currently support planning aggregates, so it's basically a
// separate "mini-planner".
// TODO: Support aggregate planning in the main query planner (https://github.com/FoundationDB/fdb-record-layer/issues/14)
RecordQueryPlan plan = ((RecordQueryPlanner) planner).planCoveringAggregateIndex(query, "value3sum");
try (FDBRecordContext context = openContext()) {
openSimpleRecordStore(context, hook);
int i = 0;
try (RecordCursorIterator<FDBQueriedRecord<Message>> cursor = recordStore.executeQuery(Objects.requireNonNull(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());
int sum = 0;
for (int j = 0; j < 20; j += 2) {
if (j % 3 == myrec.getNumValue2()) {
sum += j % 5;
}
}
assertEquals(sum, Objects.requireNonNull(rec.getIndexEntry()).getValue().getLong(0));
i++;
}
}
assertEquals(3, i);
assertDiscardedNone(context);
}
}
use of com.apple.foundationdb.record.query.plan.RecordQueryPlanner in project fdb-record-layer by FoundationDB.
the class FDBCoveringIndexQueryTest method notCoveringRecordScan.
/**
* Verify that some other index scan is used when there is no appropriate index for the returned fields.
*/
@DualPlannerTest
void notCoveringRecordScan() throws Exception {
complexQuerySetup(null);
RecordQuery query = RecordQuery.newBuilder().setRecordType("MySimpleRecord").setRequiredResults(Collections.singletonList(field("num_value_3_indexed"))).build();
// Index(MySimpleRecord$str_value_indexed <,>)
RecordQueryPlan plan = planner.plan(query);
if (planner instanceof RecordQueryPlanner) {
final BindingMatcher<? extends RecordQueryPlan> planMatcher = indexPlan().where(indexName("MySimpleRecord$str_value_indexed")).and(scanComparisons(unbounded()));
assertMatchesExactly(plan, planMatcher);
assertEquals(324762954, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(19722381, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(19722381, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
} else {
final BindingMatcher<? extends RecordQueryPlan> planMatcher = coveringIndexPlan().where(indexPlanOf(indexPlan().where(indexName("MySimpleRecord$num_value_3_indexed")).and(scanComparisons(unbounded()))));
assertMatchesExactly(plan, planMatcher);
assertEquals(413789395, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(1655846226, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(1655846226, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
}
}
use of com.apple.foundationdb.record.query.plan.RecordQueryPlanner in project fdb-record-layer by FoundationDB.
the class FDBCoveringIndexQueryTest method notCoveringWithAdditionalFilter.
/**
* Verify that if the filter contains additional parameters not satisfiable by an otherwise
* covering index that it is not used.
*/
@DualPlannerTest
void notCoveringWithAdditionalFilter() throws Exception {
complexQuerySetup(null);
RecordQuery query = RecordQuery.newBuilder().setRecordType("MySimpleRecord").setFilter(Query.and(Query.field("num_value_3_indexed").equalsValue(1), Query.field("num_value_2").lessThan(2))).setRequiredResults(Collections.singletonList(field("num_value_3_indexed"))).build();
// Index(MySimpleRecord$num_value_3_indexed [[1],[1]]) | num_value_2 LESS_THAN 2
RecordQueryPlan plan = planner.plan(query);
if (planner instanceof RecordQueryPlanner) {
final BindingMatcher<? extends RecordQueryPlan> planMatcher = filterPlan(indexPlan().where(indexName("MySimpleRecord$num_value_3_indexed")).and(scanComparisons(range("[[1],[1]]")))).where(queryComponents(exactly(equalsObject(Query.field("num_value_2").lessThan(2)))));
assertMatchesExactly(plan, planMatcher);
assertEquals(-1408807323, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(-1474845065, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(-1103679372, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
} else {
final BindingMatcher<? extends RecordQueryPlan> planMatcher = predicatesFilterPlan(indexPlan().where(indexName("MySimpleRecord$num_value_3_indexed")).and(scanComparisons(range("[[1],[1]]")))).where(predicates(only(valuePredicate(fieldValue("num_value_2"), new Comparisons.SimpleComparison(Comparisons.Type.LESS_THAN, 2)))));
assertMatchesExactly(plan, planMatcher);
assertEquals(728152174, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(-675231931, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(-304066238, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
}
}
Aggregations