use of com.apple.foundationdb.record.query.plan.RecordQueryPlanner in project fdb-record-layer by FoundationDB.
the class FDBNestedFieldQueryTest method nested2.
/**
* Verify that nested field comparisons with fanout can scan indexes.
*/
@DualPlannerTest
public void nested2() throws Exception {
RecordMetaDataHook hook = metaData -> {
metaData.addIndex("RestaurantRecord", "complex", concat(field("name"), field("rest_no"), field("reviews", KeyExpression.FanType.FanOut).nest(concat(field("reviewer"), field("rating")))));
metaData.addIndex("RestaurantRecord", "composite", concat(field("name"), field("rest_no")));
metaData.addIndex("RestaurantRecord", "duplicates", concat(field("name"), field("name")));
};
try (FDBRecordContext context = openContext()) {
openNestedRecordStore(context, hook);
TestRecords4Proto.RestaurantReviewer.Builder reviewerBuilder = TestRecords4Proto.RestaurantReviewer.newBuilder();
reviewerBuilder.setId(1);
reviewerBuilder.setName("Lemuel");
recordStore.saveRecord(reviewerBuilder.build());
reviewerBuilder.setId(2);
reviewerBuilder.setName("Gulliver");
recordStore.saveRecord(reviewerBuilder.build());
TestRecords4Proto.RestaurantRecord.Builder recBuilder = TestRecords4Proto.RestaurantRecord.newBuilder();
recBuilder.setRestNo(101);
recBuilder.setName("The Emperor's Three Tables");
TestRecords4Proto.RestaurantReview.Builder reviewBuilder = recBuilder.addReviewsBuilder();
reviewBuilder.setReviewer(1);
reviewBuilder.setRating(10);
reviewBuilder = recBuilder.addReviewsBuilder();
reviewBuilder.setReviewer(2);
reviewBuilder.setRating(3);
TestRecords4Proto.RestaurantTag.Builder tagBuilder = recBuilder.addTagsBuilder();
tagBuilder.setValue("Lilliput");
tagBuilder.setWeight(5);
recordStore.saveRecord(recBuilder.build());
recBuilder = TestRecords4Proto.RestaurantRecord.newBuilder();
recBuilder.setRestNo(102);
recBuilder.setName("Small Fry's Fried Victuals");
reviewBuilder = recBuilder.addReviewsBuilder();
reviewBuilder.setReviewer(1);
reviewBuilder.setRating(5);
reviewBuilder = recBuilder.addReviewsBuilder();
reviewBuilder.setReviewer(2);
reviewBuilder.setRating(5);
tagBuilder = recBuilder.addTagsBuilder();
tagBuilder.setValue("Lilliput");
tagBuilder.setWeight(1);
recordStore.saveRecord(recBuilder.build());
commit(context);
}
final QueryComponent nestedComponent = // Query.field("reviews").oneOfThem().matches(Query.field("rating").equalsValue(20))
Query.field("reviews").oneOfThem().matches(Query.and(Query.field("reviewer").equalsValue(10L), Query.field("rating").equalsValue(20)));
final RecordQuery query = RecordQuery.newBuilder().setRecordType("RestaurantRecord").setFilter(Query.and(Query.field("name").equalsValue("something"), Query.field("name").equalsValue("something"), Query.field("rest_no").equalsValue(1L), nestedComponent)).build();
RecordQueryPlan plan = planner.plan(query);
if (planner instanceof RecordQueryPlanner) {
// Does not understand duplicate condition
assertThat(plan, filter(nestedComponent, indexScan(allOf(indexName("duplicates"), bounds(hasTupleString("[[something, something, 1],[something, something, 1]]"))))));
} else {
assertThat(plan, fetch(primaryKeyDistinct(coveringIndexScan(indexScan(allOf(indexName("complex"), bounds(hasTupleString("[[something, 1, 10, 20],[something, 1, 10, 20]]"))))))));
}
}
use of com.apple.foundationdb.record.query.plan.RecordQueryPlanner in project fdb-record-layer by FoundationDB.
the class FDBNestedFieldQueryTest method nested.
/**
* Verify that nested field comparisons with fanout can scan indexes.
*/
@DualPlannerTest
public void nested() throws Exception {
try (FDBRecordContext context = openContext()) {
openNestedRecordStore(context);
TestRecords4Proto.RestaurantReviewer.Builder reviewerBuilder = TestRecords4Proto.RestaurantReviewer.newBuilder();
reviewerBuilder.setId(1);
reviewerBuilder.setName("Lemuel");
recordStore.saveRecord(reviewerBuilder.build());
reviewerBuilder.setId(2);
reviewerBuilder.setName("Gulliver");
recordStore.saveRecord(reviewerBuilder.build());
TestRecords4Proto.RestaurantRecord.Builder recBuilder = TestRecords4Proto.RestaurantRecord.newBuilder();
recBuilder.setRestNo(101);
recBuilder.setName("The Emperor's Three Tables");
TestRecords4Proto.RestaurantReview.Builder reviewBuilder = recBuilder.addReviewsBuilder();
reviewBuilder.setReviewer(1);
reviewBuilder.setRating(10);
reviewBuilder = recBuilder.addReviewsBuilder();
reviewBuilder.setReviewer(2);
reviewBuilder.setRating(3);
TestRecords4Proto.RestaurantTag.Builder tagBuilder = recBuilder.addTagsBuilder();
tagBuilder.setValue("Lilliput");
tagBuilder.setWeight(5);
recordStore.saveRecord(recBuilder.build());
recBuilder = TestRecords4Proto.RestaurantRecord.newBuilder();
recBuilder.setRestNo(102);
recBuilder.setName("Small Fry's Fried Victuals");
reviewBuilder = recBuilder.addReviewsBuilder();
reviewBuilder.setReviewer(1);
reviewBuilder.setRating(5);
reviewBuilder = recBuilder.addReviewsBuilder();
reviewBuilder.setReviewer(2);
reviewBuilder.setRating(5);
tagBuilder = recBuilder.addTagsBuilder();
tagBuilder.setValue("Lilliput");
tagBuilder.setWeight(1);
recordStore.saveRecord(recBuilder.build());
commit(context);
}
// TODO this was originally:
// QueryExpression.field("reviews").matches(QueryExpression.field("rating").greaterThan(5)),
// which should have failed validate
RecordQuery query = RecordQuery.newBuilder().setRecordType("RestaurantRecord").setFilter(Query.field("reviews").oneOfThem().matches(Query.field("rating").greaterThan(5))).build();
// Index(review_rating ([5],>) | UnorderedPrimaryKeyDistinct()
RecordQueryPlan plan = planner.plan(query);
if (planner instanceof RecordQueryPlanner) {
assertThat(plan, primaryKeyDistinct(indexScan(allOf(indexName("review_rating"), bounds(hasTupleString("([5],>"))))));
assertEquals(1378568952, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(-2085209333, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(2129300140, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
} else {
assertThat(plan, fetch(primaryKeyDistinct(coveringIndexScan(indexScan(allOf(indexName("review_rating"), bounds(hasTupleString("([5],>"))))))));
assertEquals(1060048085, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(-1589243362, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(-1669701185, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
}
assertEquals(Arrays.asList(101L), fetchResultValues(plan, TestRecords4Proto.RestaurantRecord.REST_NO_FIELD_NUMBER, this::openNestedRecordStore, TestHelpers::assertDiscardedNone));
query = RecordQuery.newBuilder().setRecordType("RestaurantRecord").setFilter(Query.field("tags").oneOfThem().matches(Query.and(Query.field("value").equalsValue("Lilliput"), Query.field("weight").greaterThanOrEquals(5)))).build();
// Index(tag [[Lilliput, 5],[Lilliput]]) | UnorderedPrimaryKeyDistinct()
plan = planner.plan(query);
if (planner instanceof RecordQueryPlanner) {
assertThat(plan, primaryKeyDistinct(indexScan(allOf(indexName("tag"), bounds(hasTupleString("[[Lilliput, 5],[Lilliput]]"))))));
assertEquals(-1197819382, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(1570485504, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(1584619812, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
} else {
assertThat(plan, fetch(primaryKeyDistinct(coveringIndexScan(indexScan(allOf(indexName("tag"), bounds(hasTupleString("[[Lilliput, 5],[Lilliput]]"))))))));
assertEquals(205198931, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(2066451475, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(2080585783, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
}
assertEquals(Collections.singletonList(101L), fetchResultValues(plan, TestRecords4Proto.RestaurantRecord.REST_NO_FIELD_NUMBER, this::openNestedRecordStore, TestHelpers::assertDiscardedNone));
QueryComponent reviewFilter = Query.field("reviews").oneOfThem().matches(Query.and(Query.field("rating").equalsValue(5), Query.field("reviewer").equalsValue(1L)));
query = RecordQuery.newBuilder().setRecordType("RestaurantRecord").setFilter(reviewFilter).build();
plan = planner.plan(query);
if (planner instanceof RecordQueryPlanner) {
assertThat(plan, filter(reviewFilter, primaryKeyDistinct(indexScan(allOf(indexName("review_rating"), bounds(hasTupleString("[[5],[5]]")))))));
assertEquals(1252155441, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(-2056078191, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(-1471222808, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
assertEquals(Collections.singletonList(102L), fetchResultValues(plan, TestRecords4Proto.RestaurantRecord.REST_NO_FIELD_NUMBER, this::openNestedRecordStore, TestHelpers::assertDiscardedNone));
} else {
assertThat(plan, filter(reviewFilter, primaryKeyDistinct(indexScan(allOf(indexName("review_rating"), bounds(hasTupleString("[[5],[5]]")))))));
assertEquals(1252155441, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(1947915247, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(-1762196666, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
assertEquals(Collections.singletonList(102L), fetchResultValues(plan, TestRecords4Proto.RestaurantRecord.REST_NO_FIELD_NUMBER, this::openNestedRecordStore, context -> TestHelpers.assertDiscardedAtMost(3, context)));
}
}
use of com.apple.foundationdb.record.query.plan.RecordQueryPlanner in project fdb-record-layer by FoundationDB.
the class FDBNestedFieldQueryTest method nestedWithAnd.
/**
* Verify that AND clauses in queries on nested record stores are implemented so that the AND is expressed as a
* condition on the parent field, rather than as an AND of separate nested conditions.
*/
@DualPlannerTest
public void nestedWithAnd() throws Exception {
nestedWithAndSetup(null);
RecordQuery query = RecordQuery.newBuilder().setRecordType("RestaurantReviewer").setFilter(Query.field("stats").matches(Query.and(Query.field("start_date").greaterThan(0L), Query.field("school_name").equalsValue("Human University")))).build();
// Index(stats$school ([0],>) | stats/{school_name EQUALS Human University}
RecordQueryPlan plan = planner.plan(query);
assertThat(plan, filter(Query.field("stats").matches(Query.field("school_name").equalsValue("Human University")), indexScan(allOf(indexName("stats$school"), bounds(hasTupleString("([0],>"))))));
if (planner instanceof RecordQueryPlanner) {
assertEquals(-417538532, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(-808841176, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(1039128921, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
} else {
assertEquals(-2139547699, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(766602000, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(-1680395199, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
}
assertEquals(Collections.singletonList(2L), fetchResultValues(plan, TestRecords4Proto.RestaurantReviewer.ID_FIELD_NUMBER, this::openNestedRecordStore, TestHelpers::assertDiscardedNone));
query = RecordQuery.newBuilder().setRecordType("RestaurantReviewer").setFilter(Query.field("stats").matches(Query.and(Query.field("start_date").lessThanOrEquals(1000L), Query.field("school_name").lessThan("University of Procrastination"), Query.field("hometown").startsWith("H")))).build();
// Index(stats$school ([null],[1000]]) | stats/{And([school_name LESS_THAN University of Procrastination, hometown STARTS_WITH H])}
// Index(stats$school ([null],[1000]]) | And([$85876e0f-5bbb-4a78-baaf-b3b0eae60423/stats.hometown STARTS_WITH H, $85876e0f-5bbb-4a78-baaf-b3b0eae60423/stats.school_name LESS_THAN University of Procrastination])
plan = planner.plan(query);
if (planner instanceof RecordQueryPlanner) {
assertThat(plan, filter(Query.field("stats").matches(Query.and(Query.field("school_name").lessThan("University of Procrastination"), Query.field("hometown").startsWith("H"))), indexScan(allOf(indexName("stats$school"), bounds(hasTupleString("([null],[1000]]"))))));
assertEquals(1700959433, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(336906555, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(-400610616, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
} else {
assertThat(plan, filter(allOf(queryPredicateDescendant(PredicateMatchers.field("stats", "school_name").lessThan("University of Procrastination")), queryPredicateDescendant(PredicateMatchers.field("stats", "hometown").startsWith("H"))), indexScan(allOf(indexName("stats$school"), bounds(hasTupleString("([null],[1000]]"))))));
assertEquals(-1842706543, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(-1351064721, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(-1486060602, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
}
assertEquals(Collections.singletonList(1L), fetchResultValues(plan, TestRecords4Proto.RestaurantReviewer.ID_FIELD_NUMBER, this::openNestedRecordStore, TestHelpers::assertDiscardedNone));
}
use of com.apple.foundationdb.record.query.plan.RecordQueryPlanner in project fdb-record-layer by FoundationDB.
the class FDBRepeatedFieldQueryTest method testPrefixRepeated.
/**
* Verifies that a query of a non-repeated field can use an index that starts with that field only if the query also
* includes the repeated field, because repeated introduces duplicates and index entries are ordered by second field
* and so cannot be deduplicated without additional space.
*/
@DualPlannerTest
public void testPrefixRepeated() throws Exception {
RecordMetaDataHook hook = metaData -> {
metaData.addIndex("MySimpleRecord", "prefix_repeated", concat(field("num_value_2"), field("repeater", FanType.FanOut)));
};
try (FDBRecordContext context = openContext()) {
openSimpleRecordStore(context, hook);
TestRecords1Proto.MySimpleRecord.Builder recordBuilder = TestRecords1Proto.MySimpleRecord.newBuilder();
recordBuilder.setRecNo(1).setNumValue2(1).addRepeater(1).addRepeater(2);
recordStore.saveRecord(recordBuilder.build());
recordBuilder.setRecNo(2).setNumValue2(2).clearRepeater().addRepeater(2);
recordStore.saveRecord(recordBuilder.build());
recordBuilder.setRecNo(3).setNumValue2(1).clearRepeater();
recordStore.saveRecord(recordBuilder.build());
commit(context);
}
RecordQuery query1 = RecordQuery.newBuilder().setRecordType("MySimpleRecord").setFilter(Query.field("num_value_2").equalsValue(1)).build();
// Scan(<,>) | [MySimpleRecord] | num_value_2 EQUALS 1
// Scan(<,>) | [MySimpleRecord] | $e3eb3251-2675-4566-819e-4840cc2c2400/num_value_2 EQUALS 1
RecordQueryPlan plan1 = planner.plan(query1);
assertThat(plan1, filter(Query.field("num_value_2").equalsValue(1), typeFilter(anything(), scan(unbounded()))));
if (planner instanceof RecordQueryPlanner) {
assertEquals(913370523, plan1.planHash(PlanHashable.PlanHashKind.LEGACY));
// TODO: Issue https://github.com/FoundationDB/fdb-record-layer/issues/1074
// assertEquals(2040764736, plan1.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
} else {
assertEquals(-1244637276, plan1.planHash(PlanHashable.PlanHashKind.LEGACY));
}
RecordQuery query2 = RecordQuery.newBuilder().setRecordType("MySimpleRecord").setFilter(Query.and(Query.field("num_value_2").equalsValue(1), Query.field("repeater").oneOfThem().equalsValue(1))).build();
// Index(prefix_repeated [[1, 1],[1, 1]]) | UnorderedPrimaryKeyDistinct()
RecordQueryPlan plan2 = planner.plan(query2);
if (planner instanceof RecordQueryPlanner) {
assertThat(plan2, primaryKeyDistinct(indexScan(allOf(indexName("prefix_repeated"), bounds(hasTupleString("[[1, 1],[1, 1]]"))))));
assertEquals(-1387256366, plan2.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(1061828334, plan2.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(-803537906, plan2.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
} else {
assertThat(plan2, fetch(primaryKeyDistinct(coveringIndexScan(indexScan(allOf(indexName("prefix_repeated"), bounds(hasTupleString("[[1, 1],[1, 1]]"))))))));
assertEquals(-1120859957, plan2.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(1557794305, plan2.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(-307571935, plan2.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
}
try (FDBRecordContext context = openContext()) {
openSimpleRecordStore(context, hook);
List<Long> recnos = recordStore.executeQuery(plan1).map(r -> TestRecords1Proto.MySimpleRecord.newBuilder().mergeFrom(r.getRecord()).getRecNo()).asList().join();
assertEquals(Arrays.asList(1L, 3L), recnos);
assertDiscardedAtMost(1, context);
clearStoreCounter(context);
recnos = recordStore.executeQuery(plan2).map(r -> TestRecords1Proto.MySimpleRecord.newBuilder().mergeFrom(r.getRecord()).getRecNo()).asList().join();
assertEquals(Arrays.asList(1L), recnos);
assertDiscardedNone(context);
}
}
use of com.apple.foundationdb.record.query.plan.RecordQueryPlanner in project fdb-record-layer by FoundationDB.
the class FDBRepeatedFieldQueryTest method testComplexQuery7.
/**
* Verify that repeated fields can be retrieved using indexes.
* Verify that demanding unique values forces a distinctness plan at the end.
*/
@DualPlannerTest
public void testComplexQuery7() throws Exception {
RecordMetaDataHook hook = complexQuerySetupHook();
complexQuerySetup(hook);
RecordQuery query = RecordQuery.newBuilder().setRecordType("MySimpleRecord").setFilter(Query.field("repeater").oneOfThem().equalsValue(100)).build();
try (FDBRecordContext context = openContext()) {
openSimpleRecordStore(context, hook);
recordStore.saveRecord(TestRecords1Proto.MySimpleRecord.newBuilder().setRecNo(1337).addRepeater(100).addRepeater(100).build());
commit(context);
}
try (FDBRecordContext context = openContext()) {
openSimpleRecordStore(context, hook);
recordStore.saveRecord(TestRecords1Proto.MySimpleRecord.newBuilder().setRecNo(1337).addRepeater(100).build());
commit(context);
}
try (FDBRecordContext context = openContext()) {
openSimpleRecordStore(context, hook);
TestRecords1Proto.MySimpleRecord.Builder builder = TestRecords1Proto.MySimpleRecord.newBuilder();
Message byPrimary = recordStore.loadRecord(Tuple.from(1337)).getRecord();
TestRecords1Proto.MySimpleRecord simplePrimary = builder.mergeFrom(byPrimary).build();
assertEquals(1337, simplePrimary.getRecNo());
assertEquals(Collections.singletonList(100), simplePrimary.getRepeaterList());
// Index(repeater$fanout [[100],[100]]) | UnorderedPrimaryKeyDistinct()
RecordQueryPlan plan = planner.plan(query);
if (planner instanceof RecordQueryPlanner) {
assertThat(plan, primaryKeyDistinct(indexScan(allOf(indexName("repeater$fanout"), bounds(hasTupleString("[[100],[100]]"))))));
assertEquals(-784887869, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(-170585096, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(170826084, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
} else {
assertThat(plan, fetch(primaryKeyDistinct(coveringIndexScan(indexScan(allOf(indexName("repeater$fanout"), bounds(hasTupleString("[[100],[100]]"))))))));
assertEquals(-1199247774, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
assertEquals(325380875, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
assertEquals(666792055, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
}
List<Message> byQuery = recordStore.executeQuery(plan).map(FDBQueriedRecord::getRecord).asList().get();
assertEquals(1, byQuery.size());
assertDiscardedNone(context);
TestRecords1Proto.MySimpleRecord simpleByQuery = builder.clear().mergeFrom(byQuery.get(0)).build();
assertEquals(1337, simpleByQuery.getRecNo());
assertEquals(Collections.singletonList(100), simpleByQuery.getRepeaterList());
}
}
Aggregations