Search in sources :

Example 61 with QueryComponent

use of com.apple.foundationdb.record.query.expressions.QueryComponent 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 62 with QueryComponent

use of com.apple.foundationdb.record.query.expressions.QueryComponent 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 63 with QueryComponent

use of com.apple.foundationdb.record.query.expressions.QueryComponent in project fdb-record-layer by FoundationDB.

the class FDBFullTextQueryTest method delayFetchOnUnionOfFullTextScans.

@ParameterizedTest
@BooleanSource
public void delayFetchOnUnionOfFullTextScans(boolean shouldDeferFetch) throws Exception {
    final List<TestRecordsTextProto.SimpleDocument> documents = TextIndexTestUtils.toSimpleDocuments(Arrays.asList(TextSamples.ANGSTROM, TextSamples.AETHELRED, TextSamples.ROMEO_AND_JULIET_PROLOGUE, TextSamples.FRENCH));
    try (FDBRecordContext context = openContext()) {
        openRecordStore(context);
        documents.forEach(recordStore::saveRecord);
        commit(context);
    }
    try (FDBRecordContext context = openContext()) {
        openRecordStore(context);
        final QueryComponent filter1 = Query.field("text").text().containsPhrase("civil blood makes civil hands unclean");
        final Comparisons.Comparison comparison1 = new Comparisons.TextComparison(Comparisons.Type.TEXT_CONTAINS_PHRASE, "civil blood makes civil hands unclean", null, DefaultTextTokenizer.NAME);
        final QueryComponent filter2 = Query.field("text").text().containsPrefix("th");
        final Comparisons.Comparison comparison2 = new Comparisons.TextComparison(Comparisons.Type.TEXT_CONTAINS_PREFIX, Collections.singletonList("th"), null, DefaultTextTokenizer.NAME);
        // Query for full records
        RecordQuery query = RecordQuery.newBuilder().setRecordType(TextIndexTestUtils.SIMPLE_DOC).setFilter(Query.or(filter1, filter2)).build();
        setDeferFetchAfterUnionAndIntersection(shouldDeferFetch);
        // Unordered(TextIndex(SimpleDocument$text null, TEXT_CONTAINS_PHRASE civil blood makes civil hands unclean, null) ∪ TextIndex(SimpleDocument$text null, TEXT_CONTAINS_PREFIX [th], null) | UnorderedPrimaryKeyDistinct()) | UnorderedPrimaryKeyDistinct()
        // Fetch(Unordered(Covering(TextIndex(SimpleDocument$text null, TEXT_CONTAINS_PHRASE civil blood makes civil hands unclean, null) -> [doc_id: KEY[1]]) ∪ Covering(TextIndex(SimpleDocument$text null, TEXT_CONTAINS_PREFIX [th], null) -> [doc_id: KEY[1]]) | UnorderedPrimaryKeyDistinct()) | UnorderedPrimaryKeyDistinct())
        RecordQueryPlan plan = planner.plan(query);
        if (shouldDeferFetch) {
            assertThat(plan, fetch(primaryKeyDistinct(unorderedUnion(coveringIndexScan(textIndexScan(allOf(indexName(TextIndexTestUtils.SIMPLE_DEFAULT_NAME), textComparison(equalTo(comparison1))))), primaryKeyDistinct(coveringIndexScan(textIndexScan(allOf(indexName(TextIndexTestUtils.SIMPLE_DEFAULT_NAME), textComparison(equalTo(comparison2))))))))));
            assertEquals(-683922391, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
            assertEquals(-833837033, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
            assertEquals(-464003585, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
        } else {
            assertThat(plan, primaryKeyDistinct(unorderedUnion(textIndexScan(allOf(indexName(TextIndexTestUtils.SIMPLE_DEFAULT_NAME), textComparison(equalTo(comparison1)))), primaryKeyDistinct(textIndexScan(allOf(indexName(TextIndexTestUtils.SIMPLE_DEFAULT_NAME), textComparison(equalTo(comparison2))))))));
            assertEquals(515863556, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
            assertEquals(1307589440, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
            assertEquals(1677422888, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
        }
        List<Long> primaryKeys = recordStore.executeQuery(plan).map(FDBQueriedRecord::getPrimaryKey).map(t -> t.getLong(0)).asList().get();
        assertEquals(ImmutableSet.of(0L, 1L, 2L, 3L), ImmutableSet.copyOf(primaryKeys));
        if (shouldDeferFetch) {
            assertLoadRecord(4, context);
        } else {
            assertLoadRecord(8, context);
        }
    }
}
Also used : 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) Comparisons(com.apple.foundationdb.record.query.expressions.Comparisons) RecordQuery(com.apple.foundationdb.record.query.RecordQuery) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) BooleanSource(com.apple.test.BooleanSource)

Example 64 with QueryComponent

use of com.apple.foundationdb.record.query.expressions.QueryComponent in project fdb-record-layer by FoundationDB.

the class FDBRepeatedFieldQueryTest method testPrefixRepeatedNested.

/**
 * Verifies that a query of a nested, 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 testPrefixRepeatedNested() throws Exception {
    final RecordMetaDataHook hook = metaData -> {
        metaData.getRecordType("MyRecord").setPrimaryKey(field("header").nest(field("rec_no")));
        metaData.addIndex("MyRecord", "fanout_index", concat(field("header").nest("path"), field("repeated_int", FanType.FanOut)));
    };
    try (FDBRecordContext context = openContext()) {
        openRecordWithHeader(context, hook);
        recordStore.saveRecord(TestRecordsWithHeaderProto.MyRecord.newBuilder().setHeader(TestRecordsWithHeaderProto.HeaderRecord.newBuilder().setRecNo(1L).setPath("foo").build()).clearRepeatedInt().build());
        recordStore.saveRecord(TestRecordsWithHeaderProto.MyRecord.newBuilder().setHeader(TestRecordsWithHeaderProto.HeaderRecord.newBuilder().setRecNo(2L).setPath("bar").build()).clearRepeatedInt().addRepeatedInt(1000L).addRepeatedInt(2000L).build());
        recordStore.saveRecord(TestRecordsWithHeaderProto.MyRecord.newBuilder().setHeader(TestRecordsWithHeaderProto.HeaderRecord.newBuilder().setRecNo(3L).setPath("baz").build()).clearRepeatedInt().addRepeatedInt(1000L).addRepeatedInt(2000L).addRepeatedInt(3000L).build());
        commit(context);
    }
    final QueryComponent filter = Query.field("header").matches(Query.field("path").startsWith("b"));
    RecordQuery query = RecordQuery.newBuilder().setRecordType("MyRecord").setFilter(filter).build();
    RecordQueryPlan plan = planner.plan(query);
    assertThat(plan, filter(filter, scan(unbounded())));
    try (FDBRecordContext context = openContext()) {
        openRecordWithHeader(context, hook);
        assertEquals(LongStream.range(2L, 4L).mapToObj(Long::valueOf).collect(Collectors.toList()), recordStore.executeQuery(plan).map(FDBQueriedRecord::getRecord).map(this::parseMyRecord).map(myRecord -> myRecord.getHeader().getRecNo()).asList().join());
    }
}
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) QueryComponent(com.apple.foundationdb.record.query.expressions.QueryComponent) FDBRecordContext(com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext) RecordQuery(com.apple.foundationdb.record.query.RecordQuery)

Example 65 with QueryComponent

use of com.apple.foundationdb.record.query.expressions.QueryComponent in project fdb-record-layer by FoundationDB.

the class FDBSortQueryIndexSelectionTest method sortNested.

/**
 * Verify that sorts on nested fields are implemented using nested record field indexes.
 */
@SuppressWarnings("java:S5961")
@DualPlannerTest
void sortNested() {
    try (FDBRecordContext context = openContext()) {
        RecordMetaDataBuilder builder = RecordMetaData.newBuilder().setRecords(TestRecordsWithHeaderProto.getDescriptor());
        builder.getRecordType("MyRecord").setPrimaryKey(field("header").nest(concatenateFields("path", "rec_no")));
        builder.addIndex("MyRecord", "MyRecord$header_num", concat(field("header").nest("num"), field("str_value")));
        RecordMetaData metaData = builder.getRecordMetaData();
        createOrOpenRecordStore(context, metaData);
        for (int i = 0; i < 100; i++) {
            TestRecordsWithHeaderProto.MyRecord.Builder recBuilder = TestRecordsWithHeaderProto.MyRecord.newBuilder();
            TestRecordsWithHeaderProto.HeaderRecord.Builder headerBuilder = recBuilder.getHeaderBuilder();
            // Carter-Wegman hash, with large enough prime
            headerBuilder.setRecNo((1096 * i + 722) % 1289);
            headerBuilder.setPath("root");
            headerBuilder.setNum(i);
            recBuilder.setStrValue(Integer.toString(i));
            recordStore.saveRecord(recBuilder.build());
        }
        {
            RecordQuery query = RecordQuery.newBuilder().setRecordType("MyRecord").setSort(field("header").nest("num")).build();
            // Index(MyRecord$header_num <,>)
            RecordQueryPlan plan = planner.plan(query);
            assertThat(plan, indexScan(allOf(indexName("MyRecord$header_num"), unbounded())));
            assertEquals(-1173952475, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
            assertEquals(1008825832, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
            assertEquals(1008825832, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
            int i = 0;
            try (RecordCursorIterator<FDBQueriedRecord<Message>> cursor = recordStore.executeQuery(plan).asIterator()) {
                while (cursor.hasNext()) {
                    FDBQueriedRecord<Message> rec = cursor.next();
                    TestRecordsWithHeaderProto.MyRecord.Builder myrec = TestRecordsWithHeaderProto.MyRecord.newBuilder();
                    myrec.mergeFrom(Objects.requireNonNull(rec).getRecord());
                    assertEquals(i++, myrec.getHeader().getNum());
                }
            }
            assertEquals(100, i);
            assertDiscardedNone(context);
        }
        {
            RecordQuery query = RecordQuery.newBuilder().setRecordType("MyRecord").setFilter(Query.field("header").matches(Query.field("num").lessThan(50))).setSort(field("header").nest("num")).build();
            // Index(MyRecord$header_num ([null],[50]))
            RecordQueryPlan plan = planner.plan(query);
            assertThat(plan, indexScan(allOf(indexName("MyRecord$header_num"), bounds(hasTupleString("([null],[50])")))));
            if (planner instanceof RecordQueryPlanner) {
                assertEquals(2008179964, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
                assertEquals(2049006062, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
                assertEquals(-204519612, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
            } else {
                assertEquals(2008179964, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
                // TODO strictly sorted is not set
                assertEquals(2049006068, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
                // TODO strictly sorted is not set
                assertEquals(-204519606, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
            }
            int i = 0;
            try (RecordCursorIterator<FDBQueriedRecord<Message>> cursor = recordStore.executeQuery(plan).asIterator()) {
                while (cursor.hasNext()) {
                    FDBQueriedRecord<Message> rec = cursor.next();
                    TestRecordsWithHeaderProto.MyRecord.Builder myrec = TestRecordsWithHeaderProto.MyRecord.newBuilder();
                    myrec.mergeFrom(Objects.requireNonNull(rec).getRecord());
                    assertEquals(i++, myrec.getHeader().getNum());
                }
            }
            assertEquals(50, i);
            assertDiscardedNone(context);
        }
        {
            RecordQuery query = RecordQuery.newBuilder().setRecordType("MyRecord").setFilter(Query.field("header").matches(Query.field("num").equalsValue(1))).setSort(field("str_value")).build();
            // Index(MyRecord$header_num [[1],[1]])
            RecordQueryPlan plan = planner.plan(query);
            assertThat(plan, indexScan(allOf(indexName("MyRecord$header_num"), bounds(hasTupleString("[[1],[1]]")))));
            assertEquals(878861315, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
            assertEquals(653879397, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
            assertEquals(998239886, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
            int i = 0;
            try (RecordCursorIterator<FDBQueriedRecord<Message>> cursor = recordStore.executeQuery(plan).asIterator()) {
                while (cursor.hasNext()) {
                    FDBQueriedRecord<Message> rec = cursor.next();
                    TestRecordsWithHeaderProto.MyRecord.Builder myrec = TestRecordsWithHeaderProto.MyRecord.newBuilder();
                    myrec.mergeFrom(Objects.requireNonNull(rec).getRecord());
                    assertEquals(1, myrec.getHeader().getNum());
                    i++;
                }
            }
            assertEquals(1, i);
            assertDiscardedNone(context);
        }
        {
            RecordQuery query = RecordQuery.newBuilder().setRecordType("MyRecord").setFilter(Query.and(Query.field("header").matches(Query.field("num").isNull()), Query.field("str_value").greaterThan("middle"))).setSort(field("str_value")).build();
            // Index(MyRecord$header_num ([null, middle],[null]])
            RecordQueryPlan plan = planner.plan(query);
            assertThat(plan, indexScan(allOf(indexName("MyRecord$header_num"), bounds(hasTupleString("([null, middle],[null]]")))));
            assertEquals(1553479768, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
            assertEquals(1072001836, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
            assertEquals(-1653404355, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
        }
        {
            final QueryComponent filter = Query.field("header").matches(Query.field("rec_no").greaterThan(0L));
            RecordQuery query = RecordQuery.newBuilder().setRecordType("MyRecord").setFilter(filter).setSort(field("header").nest("num")).build();
            // Fetch(Covering(Index(MyRecord$header_num <,>) -> [str_value: KEY[1], header: [num: KEY[0], path: KEY[2], rec_no: KEY[3]]]) | header/{rec_no GREATER_THAN 0})
            RecordQueryPlan plan = planner.plan(query);
            assertThat(plan, fetch(filter(filter, coveringIndexScan(indexScan(allOf(indexName("MyRecord$header_num"), unbounded()))))));
            if (planner instanceof RecordQueryPlanner) {
                assertEquals(673903077, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
                assertEquals(-582153460, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
                assertEquals(-421343502, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
            } else {
                assertEquals(749683984, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
                assertEquals(87367824, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
                assertEquals(248177782, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
            }
            try (RecordCursorIterator<FDBQueriedRecord<Message>> cursor = recordStore.executeQuery(plan).asIterator()) {
                while (cursor.hasNext()) {
                    FDBQueriedRecord<Message> rec = cursor.next();
                    TestRecordsWithHeaderProto.MyRecord.Builder myrec = TestRecordsWithHeaderProto.MyRecord.newBuilder();
                    myrec.mergeFrom(Objects.requireNonNull(rec).getRecord());
                    assertTrue(myrec.hasHeader(), "Retrieved record missing header");
                    assertTrue(myrec.getHeader().hasRecNo(), "Retrieved record missing rec_no");
                    long recNo = myrec.getHeader().getRecNo();
                    assertTrue(recNo > 0L, "Record does not match filter (rec_no " + recNo + "<= 0)");
                }
            }
            assertDiscardedExactly(0, context);
            clearStoreCounter(context);
        }
        {
            RecordQuery query = RecordQuery.newBuilder().setRecordType("MyRecord").setFilter(Query.field("header").matches(Query.and(Query.field("rec_no").greaterThan(10L), Query.field("num").lessThan(50)))).setSort(field("header").nest("num")).build();
            // Fetch(Covering(Index(MyRecord$header_num ([null],[50])) -> [str_value: KEY[1], header: [num: KEY[0], path: KEY[2], rec_no: KEY[3]]]) | header/{rec_no GREATER_THAN 10})
            RecordQueryPlan plan = planner.plan(query);
            assertThat(plan, fetch(filter(Query.field("header").matches(Query.field("rec_no").greaterThan(10L)), coveringIndexScan(indexScan(allOf(indexName("MyRecord$header_num"), bounds(hasTupleString("([null],[50])"))))))));
            if (planner instanceof RecordQueryPlanner) {
                assertEquals(1473993740, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
                assertEquals(1598662608, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
                assertEquals(619653398, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
            } else {
                assertEquals(1549774647, plan.planHash(PlanHashable.PlanHashKind.LEGACY));
                assertEquals(-2026783218, plan.planHash(PlanHashable.PlanHashKind.FOR_CONTINUATION));
                assertEquals(1289174868, plan.planHash(PlanHashable.PlanHashKind.STRUCTURAL_WITHOUT_LITERALS));
            }
            try (RecordCursorIterator<FDBQueriedRecord<Message>> cursor = recordStore.executeQuery(plan).asIterator()) {
                while (cursor.hasNext()) {
                    FDBQueriedRecord<Message> rec = cursor.next();
                    TestRecordsWithHeaderProto.MyRecord.Builder myrec = TestRecordsWithHeaderProto.MyRecord.newBuilder();
                    myrec.mergeFrom(Objects.requireNonNull(rec).getRecord());
                    assertTrue(myrec.hasHeader(), "Retrieved record missing header");
                    assertTrue(myrec.getHeader().hasRecNo(), "Retrieved record missing rec_no");
                    assertTrue(myrec.getHeader().hasNum(), "Retrieved record missing num");
                    long recNo = myrec.getHeader().getRecNo();
                    int num = myrec.getHeader().getNum();
                    assertTrue(recNo > 10L && num < 50, "Retrieved record does not match filter (rec_no = " + recNo + " and num = " + num + ")");
                }
            }
            assertDiscardedExactly(0, context);
        }
    }
}
Also used : RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) RecordMetaData(com.apple.foundationdb.record.RecordMetaData) TestRecordsWithHeaderProto(com.apple.foundationdb.record.TestRecordsWithHeaderProto) QueryComponent(com.apple.foundationdb.record.query.expressions.QueryComponent) Message(com.google.protobuf.Message) RecordMetaDataBuilder(com.apple.foundationdb.record.RecordMetaDataBuilder) RecordCursorIterator(com.apple.foundationdb.record.RecordCursorIterator) FDBQueriedRecord(com.apple.foundationdb.record.provider.foundationdb.FDBQueriedRecord) FDBRecordContext(com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext) RecordMetaDataBuilder(com.apple.foundationdb.record.RecordMetaDataBuilder) RecordQueryPlanner(com.apple.foundationdb.record.query.plan.RecordQueryPlanner) RecordQuery(com.apple.foundationdb.record.query.RecordQuery)

Aggregations

QueryComponent (com.apple.foundationdb.record.query.expressions.QueryComponent)96 RecordQueryPlan (com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan)58 RecordQuery (com.apple.foundationdb.record.query.RecordQuery)44 Test (org.junit.jupiter.api.Test)41 FDBRecordContext (com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext)37 ArrayList (java.util.ArrayList)30 ParameterizedTest (org.junit.jupiter.params.ParameterizedTest)29 FDBQueriedRecord (com.apple.foundationdb.record.provider.foundationdb.FDBQueriedRecord)25 Index (com.apple.foundationdb.record.metadata.Index)24 Nonnull (javax.annotation.Nonnull)23 Comparisons (com.apple.foundationdb.record.query.expressions.Comparisons)22 Message (com.google.protobuf.Message)22 Query (com.apple.foundationdb.record.query.expressions.Query)20 RecordQueryPlanner (com.apple.foundationdb.record.query.plan.RecordQueryPlanner)20 List (java.util.List)20 Collections (java.util.Collections)19 Nullable (javax.annotation.Nullable)18 Assertions.assertEquals (org.junit.jupiter.api.Assertions.assertEquals)18 PlanHashable (com.apple.foundationdb.record.PlanHashable)17 Expressions.field (com.apple.foundationdb.record.metadata.Key.Expressions.field)17