Search in sources :

Example 56 with RecordQueryPlanner

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]]"))))))));
    }
}
Also used : Arrays(java.util.Arrays) TestRecords3Proto(com.apple.foundationdb.record.TestRecords3Proto) FDBRecordContext(com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext) PlanMatchers.bounds(com.apple.foundationdb.record.query.plan.match.PlanMatchers.bounds) RecordQueryPlanner(com.apple.foundationdb.record.query.plan.RecordQueryPlanner) IndexScanType(com.apple.foundationdb.record.IndexScanType) Tuple(com.apple.foundationdb.tuple.Tuple) RecordCursorResult(com.apple.foundationdb.record.RecordCursorResult) TestHelpers(com.apple.foundationdb.record.TestHelpers) Assertions.assertFalse(org.junit.jupiter.api.Assertions.assertFalse) Expressions.concatenateFields(com.apple.foundationdb.record.metadata.Key.Expressions.concatenateFields) Expressions.concat(com.apple.foundationdb.record.metadata.Key.Expressions.concat) GroupingKeyExpression(com.apple.foundationdb.record.metadata.expressions.GroupingKeyExpression) Tag(org.junit.jupiter.api.Tag) PlanMatchers.coveringIndexScan(com.apple.foundationdb.record.query.plan.match.PlanMatchers.coveringIndexScan) PlanMatchers.indexScanType(com.apple.foundationdb.record.query.plan.match.PlanMatchers.indexScanType) TestRecords4Proto(com.apple.foundationdb.record.TestRecords4Proto) Query(com.apple.foundationdb.record.query.expressions.Query) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) Matchers.allOf(org.hamcrest.Matchers.allOf) Test(org.junit.jupiter.api.Test) PlanMatchers.hasTupleString(com.apple.foundationdb.record.query.plan.match.PlanMatchers.hasTupleString) PlanMatchers.indexName(com.apple.foundationdb.record.query.plan.match.PlanMatchers.indexName) EvaluationContext(com.apple.foundationdb.record.EvaluationContext) FDBQueriedRecord(com.apple.foundationdb.record.provider.foundationdb.FDBQueriedRecord) Assertions.assertTrue(org.junit.jupiter.api.Assertions.assertTrue) IndexTypes(com.apple.foundationdb.record.metadata.IndexTypes) TestRecordsWithHeaderProto(com.apple.foundationdb.record.TestRecordsWithHeaderProto) RecordMetaData(com.apple.foundationdb.record.RecordMetaData) Assertions.assertNotNull(org.junit.jupiter.api.Assertions.assertNotNull) PlanMatchers.fetch(com.apple.foundationdb.record.query.plan.match.PlanMatchers.fetch) QueryRecordFunction(com.apple.foundationdb.record.query.expressions.QueryRecordFunction) PlanMatchers.primaryKeyDistinct(com.apple.foundationdb.record.query.plan.match.PlanMatchers.primaryKeyDistinct) PlanMatchers.indexScan(com.apple.foundationdb.record.query.plan.match.PlanMatchers.indexScan) PlanMatchers.queryPredicateDescendant(com.apple.foundationdb.record.query.plan.match.PlanMatchers.queryPredicateDescendant) RecordQuery(com.apple.foundationdb.record.query.RecordQuery) RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) TestRecordsNestedMapProto(com.apple.foundationdb.record.TestRecordsNestedMapProto) PlanHashable(com.apple.foundationdb.record.PlanHashable) PlanMatchers.filter(com.apple.foundationdb.record.query.plan.match.PlanMatchers.filter) Key(com.apple.foundationdb.record.metadata.Key) PlanMatchers.scan(com.apple.foundationdb.record.query.plan.match.PlanMatchers.scan) MatcherAssert.assertThat(org.hamcrest.MatcherAssert.assertThat) Assertions.assertEquals(org.junit.jupiter.api.Assertions.assertEquals) Expressions.field(com.apple.foundationdb.record.metadata.Key.Expressions.field) FDBStoredRecord(com.apple.foundationdb.record.provider.foundationdb.FDBStoredRecord) RecordMetaDataBuilder(com.apple.foundationdb.record.RecordMetaDataBuilder) Tags(com.apple.test.Tags) PredicateMatchers(com.apple.foundationdb.record.query.predicates.match.PredicateMatchers) TestRecords5Proto(com.apple.foundationdb.record.TestRecords5Proto) Index(com.apple.foundationdb.record.metadata.Index) Message(com.google.protobuf.Message) RecordCursor(com.apple.foundationdb.record.RecordCursor) QueryComponent(com.apple.foundationdb.record.query.expressions.QueryComponent) Collections(java.util.Collections) RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) QueryComponent(com.apple.foundationdb.record.query.expressions.QueryComponent) FDBRecordContext(com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext) RecordQueryPlanner(com.apple.foundationdb.record.query.plan.RecordQueryPlanner) RecordQuery(com.apple.foundationdb.record.query.RecordQuery)

Example 57 with RecordQueryPlanner

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)));
    }
}
Also used : RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) Arrays(java.util.Arrays) TestRecords3Proto(com.apple.foundationdb.record.TestRecords3Proto) FDBRecordContext(com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext) PlanMatchers.bounds(com.apple.foundationdb.record.query.plan.match.PlanMatchers.bounds) RecordQueryPlanner(com.apple.foundationdb.record.query.plan.RecordQueryPlanner) IndexScanType(com.apple.foundationdb.record.IndexScanType) Tuple(com.apple.foundationdb.tuple.Tuple) RecordCursorResult(com.apple.foundationdb.record.RecordCursorResult) TestHelpers(com.apple.foundationdb.record.TestHelpers) Assertions.assertFalse(org.junit.jupiter.api.Assertions.assertFalse) Expressions.concatenateFields(com.apple.foundationdb.record.metadata.Key.Expressions.concatenateFields) Expressions.concat(com.apple.foundationdb.record.metadata.Key.Expressions.concat) GroupingKeyExpression(com.apple.foundationdb.record.metadata.expressions.GroupingKeyExpression) Tag(org.junit.jupiter.api.Tag) PlanMatchers.coveringIndexScan(com.apple.foundationdb.record.query.plan.match.PlanMatchers.coveringIndexScan) PlanMatchers.indexScanType(com.apple.foundationdb.record.query.plan.match.PlanMatchers.indexScanType) TestRecords4Proto(com.apple.foundationdb.record.TestRecords4Proto) Query(com.apple.foundationdb.record.query.expressions.Query) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) Matchers.allOf(org.hamcrest.Matchers.allOf) Test(org.junit.jupiter.api.Test) PlanMatchers.hasTupleString(com.apple.foundationdb.record.query.plan.match.PlanMatchers.hasTupleString) PlanMatchers.indexName(com.apple.foundationdb.record.query.plan.match.PlanMatchers.indexName) EvaluationContext(com.apple.foundationdb.record.EvaluationContext) FDBQueriedRecord(com.apple.foundationdb.record.provider.foundationdb.FDBQueriedRecord) Assertions.assertTrue(org.junit.jupiter.api.Assertions.assertTrue) IndexTypes(com.apple.foundationdb.record.metadata.IndexTypes) TestRecordsWithHeaderProto(com.apple.foundationdb.record.TestRecordsWithHeaderProto) RecordMetaData(com.apple.foundationdb.record.RecordMetaData) Assertions.assertNotNull(org.junit.jupiter.api.Assertions.assertNotNull) PlanMatchers.fetch(com.apple.foundationdb.record.query.plan.match.PlanMatchers.fetch) QueryRecordFunction(com.apple.foundationdb.record.query.expressions.QueryRecordFunction) PlanMatchers.primaryKeyDistinct(com.apple.foundationdb.record.query.plan.match.PlanMatchers.primaryKeyDistinct) PlanMatchers.indexScan(com.apple.foundationdb.record.query.plan.match.PlanMatchers.indexScan) PlanMatchers.queryPredicateDescendant(com.apple.foundationdb.record.query.plan.match.PlanMatchers.queryPredicateDescendant) RecordQuery(com.apple.foundationdb.record.query.RecordQuery) RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) TestRecordsNestedMapProto(com.apple.foundationdb.record.TestRecordsNestedMapProto) PlanHashable(com.apple.foundationdb.record.PlanHashable) PlanMatchers.filter(com.apple.foundationdb.record.query.plan.match.PlanMatchers.filter) Key(com.apple.foundationdb.record.metadata.Key) PlanMatchers.scan(com.apple.foundationdb.record.query.plan.match.PlanMatchers.scan) MatcherAssert.assertThat(org.hamcrest.MatcherAssert.assertThat) Assertions.assertEquals(org.junit.jupiter.api.Assertions.assertEquals) Expressions.field(com.apple.foundationdb.record.metadata.Key.Expressions.field) FDBStoredRecord(com.apple.foundationdb.record.provider.foundationdb.FDBStoredRecord) RecordMetaDataBuilder(com.apple.foundationdb.record.RecordMetaDataBuilder) Tags(com.apple.test.Tags) PredicateMatchers(com.apple.foundationdb.record.query.predicates.match.PredicateMatchers) TestRecords5Proto(com.apple.foundationdb.record.TestRecords5Proto) Index(com.apple.foundationdb.record.metadata.Index) Message(com.google.protobuf.Message) RecordCursor(com.apple.foundationdb.record.RecordCursor) QueryComponent(com.apple.foundationdb.record.query.expressions.QueryComponent) Collections(java.util.Collections) QueryComponent(com.apple.foundationdb.record.query.expressions.QueryComponent) FDBRecordContext(com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext) RecordQueryPlanner(com.apple.foundationdb.record.query.plan.RecordQueryPlanner) RecordQuery(com.apple.foundationdb.record.query.RecordQuery)

Example 58 with RecordQueryPlanner

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));
}
Also used : RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) RecordQueryPlanner(com.apple.foundationdb.record.query.plan.RecordQueryPlanner) RecordQuery(com.apple.foundationdb.record.query.RecordQuery)

Example 59 with RecordQueryPlanner

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);
    }
}
Also used : Arrays(java.util.Arrays) PlanMatchers.union(com.apple.foundationdb.record.query.plan.match.PlanMatchers.union) FDBRecordContext(com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext) PlanMatchers.bounds(com.apple.foundationdb.record.query.plan.match.PlanMatchers.bounds) RecordQueryPlanner(com.apple.foundationdb.record.query.plan.RecordQueryPlanner) TestHelpers.assertDiscardedNone(com.apple.foundationdb.record.TestHelpers.assertDiscardedNone) Tuple(com.apple.foundationdb.tuple.Tuple) TestHelpers(com.apple.foundationdb.record.TestHelpers) Expressions.concat(com.apple.foundationdb.record.metadata.Key.Expressions.concat) Tag(org.junit.jupiter.api.Tag) PlanMatchers.coveringIndexScan(com.apple.foundationdb.record.query.plan.match.PlanMatchers.coveringIndexScan) TestRecords4Proto(com.apple.foundationdb.record.TestRecords4Proto) Query(com.apple.foundationdb.record.query.expressions.Query) TestRecords1Proto(com.apple.foundationdb.record.TestRecords1Proto) Matchers.allOf(org.hamcrest.Matchers.allOf) FanType(com.apple.foundationdb.record.metadata.expressions.KeyExpression.FanType) Collectors(java.util.stream.Collectors) Test(org.junit.jupiter.api.Test) PlanMatchers.hasTupleString(com.apple.foundationdb.record.query.plan.match.PlanMatchers.hasTupleString) List(java.util.List) PlanMatchers.indexName(com.apple.foundationdb.record.query.plan.match.PlanMatchers.indexName) Matchers.contains(org.hamcrest.Matchers.contains) FDBQueriedRecord(com.apple.foundationdb.record.provider.foundationdb.FDBQueriedRecord) Assertions.assertTrue(org.junit.jupiter.api.Assertions.assertTrue) TestRecordsWithHeaderProto(com.apple.foundationdb.record.TestRecordsWithHeaderProto) PlanMatchers.typeFilter(com.apple.foundationdb.record.query.plan.match.PlanMatchers.typeFilter) RecordMetaData(com.apple.foundationdb.record.RecordMetaData) PlanMatchers.fetch(com.apple.foundationdb.record.query.plan.match.PlanMatchers.fetch) PlanMatchers.primaryKeyDistinct(com.apple.foundationdb.record.query.plan.match.PlanMatchers.primaryKeyDistinct) PlanMatchers.indexScan(com.apple.foundationdb.record.query.plan.match.PlanMatchers.indexScan) TestRecords6Proto(com.apple.foundationdb.record.TestRecords6Proto) RecordQuery(com.apple.foundationdb.record.query.RecordQuery) RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) PlanHashable(com.apple.foundationdb.record.PlanHashable) PlanMatchers.filter(com.apple.foundationdb.record.query.plan.match.PlanMatchers.filter) PlanMatchers.scan(com.apple.foundationdb.record.query.plan.match.PlanMatchers.scan) BooleanSource(com.apple.test.BooleanSource) MatcherAssert.assertThat(org.hamcrest.MatcherAssert.assertThat) Assertions.assertEquals(org.junit.jupiter.api.Assertions.assertEquals) Expressions.field(com.apple.foundationdb.record.metadata.Key.Expressions.field) LongStream(java.util.stream.LongStream) RecordMetaDataBuilder(com.apple.foundationdb.record.RecordMetaDataBuilder) Tags(com.apple.test.Tags) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) TestHelpers.assertDiscardedAtMost(com.apple.foundationdb.record.TestHelpers.assertDiscardedAtMost) PlanMatchers.unbounded(com.apple.foundationdb.record.query.plan.match.PlanMatchers.unbounded) Message(com.google.protobuf.Message) QueryComponent(com.apple.foundationdb.record.query.expressions.QueryComponent) RealAnythingMatcher.anything(com.apple.foundationdb.record.TestHelpers.RealAnythingMatcher.anything) Collections(java.util.Collections) RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) FDBRecordContext(com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext) RecordQueryPlanner(com.apple.foundationdb.record.query.plan.RecordQueryPlanner) RecordQuery(com.apple.foundationdb.record.query.RecordQuery)

Example 60 with RecordQueryPlanner

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());
    }
}
Also used : RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) TestRecords1Proto(com.apple.foundationdb.record.TestRecords1Proto) Message(com.google.protobuf.Message) FDBQueriedRecord(com.apple.foundationdb.record.provider.foundationdb.FDBQueriedRecord) FDBRecordContext(com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext) RecordQueryPlanner(com.apple.foundationdb.record.query.plan.RecordQueryPlanner) RecordQuery(com.apple.foundationdb.record.query.RecordQuery)

Aggregations

RecordQueryPlanner (com.apple.foundationdb.record.query.plan.RecordQueryPlanner)71 RecordQuery (com.apple.foundationdb.record.query.RecordQuery)65 RecordQueryPlan (com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan)65 FDBRecordContext (com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext)58 FDBQueriedRecord (com.apple.foundationdb.record.provider.foundationdb.FDBQueriedRecord)48 Message (com.google.protobuf.Message)47 Test (org.junit.jupiter.api.Test)37 Query (com.apple.foundationdb.record.query.expressions.Query)36 Tags (com.apple.test.Tags)36 MatcherAssert.assertThat (org.hamcrest.MatcherAssert.assertThat)36 Assertions.assertEquals (org.junit.jupiter.api.Assertions.assertEquals)36 Tag (org.junit.jupiter.api.Tag)36 PlanHashable (com.apple.foundationdb.record.PlanHashable)35 Index (com.apple.foundationdb.record.metadata.Index)35 Expressions.field (com.apple.foundationdb.record.metadata.Key.Expressions.field)35 Assertions.assertTrue (org.junit.jupiter.api.Assertions.assertTrue)35 QueryComponent (com.apple.foundationdb.record.query.expressions.QueryComponent)33 Collections (java.util.Collections)33 Assertions.assertFalse (org.junit.jupiter.api.Assertions.assertFalse)33 Key (com.apple.foundationdb.record.metadata.Key)32