Search in sources :

Example 1 with IndexRecordFunction

use of com.apple.foundationdb.record.metadata.IndexRecordFunction in project fdb-record-layer by FoundationDB.

the class SyntheticRecordPlannerTest method rankJoinIndex.

@Test
public void rankJoinIndex() throws Exception {
    final JoinedRecordTypeBuilder joined = metaDataBuilder.addJoinedRecordType("JoinedForRank");
    joined.addConstituent("simple", "MySimpleRecord");
    joined.addConstituent("other", "MyOtherRecord");
    joined.addJoin("simple", "other_rec_no", "other", "rec_no");
    final GroupingKeyExpression group = field("simple").nest("num_value_2").groupBy(field("other").nest("num_value"));
    metaDataBuilder.addIndex(joined, new Index("simple.num_value_2_by_other.num_value", group, IndexTypes.RANK));
    try (FDBRecordContext context = openContext()) {
        final FDBRecordStore recordStore = recordStoreBuilder.setContext(context).create();
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < i; j++) {
                TestRecordsJoinIndexProto.MySimpleRecord.Builder simple = TestRecordsJoinIndexProto.MySimpleRecord.newBuilder();
                simple.setRecNo(100 * i + j).setOtherRecNo(1000 + i);
                simple.setNumValue2(i + j);
                recordStore.saveRecord(simple.build());
            }
            TestRecordsJoinIndexProto.MyOtherRecord.Builder other = TestRecordsJoinIndexProto.MyOtherRecord.newBuilder();
            other.setRecNo(1000 + i);
            other.setNumValue(i % 2);
            recordStore.saveRecord(other.build());
        }
        context.commit();
    }
    try (FDBRecordContext context = openContext()) {
        final FDBRecordStore recordStore = recordStoreBuilder.setContext(context).open();
        Index index = recordStore.getRecordMetaData().getIndex("simple.num_value_2_by_other.num_value");
        RecordCursor<IndexEntry> cursor = recordStore.scanIndex(index, IndexScanType.BY_RANK, TupleRange.allOf(Tuple.from(0, 1)), null, ScanProperties.FORWARD_SCAN);
        Tuple pkey = cursor.first().get().map(IndexEntry::getPrimaryKey).orElse(null);
        assertFalse(cursor.getNext().hasNext());
        // 201, 1002 and 200, 1003 both have score 3, but in different groups.
        assertEquals(Tuple.from(-1, Tuple.from(201), Tuple.from(1002)), pkey);
        FDBSyntheticRecord record = recordStore.loadSyntheticRecord(pkey).join();
        IndexRecordFunction<Long> rankFunction = ((IndexRecordFunction<Long>) Query.rank(group).getFunction()).cloneWithIndex(index.getName());
        assertEquals(1, recordStore.evaluateRecordFunction(rankFunction, record).join().longValue());
    }
}
Also used : IndexRecordFunction(com.apple.foundationdb.record.metadata.IndexRecordFunction) GroupingKeyExpression(com.apple.foundationdb.record.metadata.expressions.GroupingKeyExpression) FDBSyntheticRecord(com.apple.foundationdb.record.provider.foundationdb.FDBSyntheticRecord) IndexEntry(com.apple.foundationdb.record.IndexEntry) Index(com.apple.foundationdb.record.metadata.Index) FDBRecordStore(com.apple.foundationdb.record.provider.foundationdb.FDBRecordStore) JoinedRecordTypeBuilder(com.apple.foundationdb.record.metadata.JoinedRecordTypeBuilder) FDBRecordContext(com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext) Tuple(com.apple.foundationdb.tuple.Tuple) Test(org.junit.jupiter.api.Test)

Example 2 with IndexRecordFunction

use of com.apple.foundationdb.record.metadata.IndexRecordFunction in project fdb-record-layer by FoundationDB.

the class OnlineIndexerBuildRankIndexTest method rankRebuild.

private void rankRebuild(@Nonnull List<TestRecords1Proto.MySimpleRecord> records, @Nullable List<TestRecords1Proto.MySimpleRecord> recordsWhileBuilding, int agents, boolean overlap) {
    final Index index = new Index("newRankIndex", field("num_value_2").ungrouped(), IndexTypes.RANK);
    final IndexRecordFunction<Long> recordFunction = (IndexRecordFunction<Long>) Query.rank("num_value_2").getFunction();
    final Runnable beforeBuild = new Runnable() {

        @SuppressWarnings("try")
        @Override
        public void run() {
            try (FDBRecordContext context = openContext()) {
                for (TestRecords1Proto.MySimpleRecord record : records) {
                    try {
                        recordStore.evaluateRecordFunction(recordFunction, createStoredMessage(record)).join();
                        fail("Somehow evaluated rank");
                    } catch (RecordCoreException e) {
                        assertEquals("Record function rank(Field { 'num_value_2' None} group 1) requires appropriate index on MySimpleRecord", e.getMessage());
                    }
                }
                // Either we get an exception, or the record store is empty.
                try {
                    RecordQuery query = RecordQuery.newBuilder().setRecordType("MySimpleRecord").setFilter(Query.rank("num_value_2").equalsValue(0L)).build();
                    RecordQueryPlan plan = planner.plan(query);
                    assertEquals("Scan(<,>) | [MySimpleRecord] | rank(Field { 'num_value_2' None} group 1) EQUALS 0", plan.toString());
                    Optional<?> first = recordStore.executeQuery(plan).first().join();
                    assertTrue(!first.isPresent(), "non-empty range with rank rebuild");
                } catch (CompletionException e) {
                    assertNotNull(e.getCause());
                    assertThat(e.getCause(), instanceOf(RecordCoreException.class));
                    assertEquals("Record function rank(Field { 'num_value_2' None} group 1) requires appropriate index on MySimpleRecord", e.getCause().getMessage());
                }
            }
        }
    };
    List<TestRecords1Proto.MySimpleRecord> updatedRecords;
    if (recordsWhileBuilding == null || recordsWhileBuilding.size() == 0) {
        updatedRecords = records;
    } else {
        updatedRecords = updated(records, recordsWhileBuilding);
    }
    final Runnable afterBuild = new Runnable() {

        @SuppressWarnings("try")
        @Override
        public void run() {
            try (FDBRecordContext context = openContext()) {
                for (TestRecords1Proto.MySimpleRecord record : updatedRecords) {
                    try {
                        recordStore.evaluateRecordFunction(recordFunction, createStoredMessage(record)).join();
                        fail("Somehow evaluated rank");
                    } catch (RecordCoreException e) {
                        assertEquals("Record function rank(Field { 'num_value_2' None} group 1) requires appropriate index on MySimpleRecord", e.getMessage());
                    }
                }
                // Either we get an exception, or the record store is empty.
                try {
                    RecordQuery query = RecordQuery.newBuilder().setRecordType("MySimpleRecord").setFilter(Query.rank("num_value_2").equalsValue(0L)).build();
                    RecordQueryPlan plan = planner.plan(query);
                    assertEquals("Scan(<,>) | [MySimpleRecord] | rank(Field { 'num_value_2' None} group 1) EQUALS 0", plan.toString());
                    Optional<?> first = recordStore.executeQuery(plan).first().join();
                    assertTrue(!first.isPresent(), "non-empty range with rank rebuild");
                } catch (CompletionException e) {
                    assertNotNull(e.getCause());
                    assertThat(e.getCause(), instanceOf(RecordCoreException.class));
                    assertEquals("Record function rank(Field { 'num_value_2' None} group 1) requires appropriate index on MySimpleRecord", e.getCause().getMessage());
                }
            }
        }
    };
    TreeSet<Integer> values = new TreeSet<>();
    boolean hasNull = false;
    for (TestRecords1Proto.MySimpleRecord record : updatedRecords) {
        if (!record.hasNumValue2()) {
            hasNull = true;
        } else {
            values.add(record.getNumValue2());
        }
    }
    long curr = 0;
    Map<Integer, Long> ranks = new HashMap<>();
    if (hasNull) {
        curr += 1;
    }
    for (Integer value : values) {
        ranks.put(value, curr);
        curr += 1;
    }
    final Runnable afterReadable = new Runnable() {

        @SuppressWarnings("try")
        @Override
        public void run() {
            try (FDBRecordContext context = openContext()) {
                for (TestRecords1Proto.MySimpleRecord record : updatedRecords) {
                    Long rank = recordStore.evaluateRecordFunction(recordFunction, createStoredMessage(record)).join();
                    if (!record.hasNumValue2()) {
                        assertEquals(0L, rank.longValue());
                    } else {
                        assertEquals(ranks.get(record.getNumValue2()), rank);
                    }
                    RecordQuery query = RecordQuery.newBuilder().setRecordType("MySimpleRecord").setFilter(Query.rank("num_value_2").equalsValue(rank)).build();
                    RecordQueryPlan plan = planner.plan(query);
                    assertEquals("Index(newRankIndex [[" + rank + "],[" + rank + "]] BY_RANK)", plan.toString());
                    Optional<TestRecords1Proto.MySimpleRecord> retrieved = recordStore.executeQuery(plan).map(rec -> TestRecords1Proto.MySimpleRecord.newBuilder().mergeFrom(rec.getRecord()).build()).filter(rec -> rec.getRecNo() == record.getRecNo()).first().join();
                    assertTrue(retrieved.isPresent(), "empty range after rank index build");
                    assertEquals(record, retrieved.get());
                }
            }
        }
    };
    singleRebuild(records, recordsWhileBuilding, agents, overlap, false, index, beforeBuild, afterBuild, afterReadable);
}
Also used : RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) Assertions.fail(org.junit.jupiter.api.Assertions.fail) Assertions.assertNotNull(org.junit.jupiter.api.Assertions.assertNotNull) HashMap(java.util.HashMap) Random(java.util.Random) RecordQuery(com.apple.foundationdb.record.query.RecordQuery) RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) TreeSet(java.util.TreeSet) RecordCoreException(com.apple.foundationdb.record.RecordCoreException) Map(java.util.Map) Tag(org.junit.jupiter.api.Tag) MatcherAssert.assertThat(org.hamcrest.MatcherAssert.assertThat) Assertions.assertEquals(org.junit.jupiter.api.Assertions.assertEquals) IndexRecordFunction(com.apple.foundationdb.record.metadata.IndexRecordFunction) Nonnull(javax.annotation.Nonnull) Expressions.field(com.apple.foundationdb.record.metadata.Key.Expressions.field) Nullable(javax.annotation.Nullable) Query(com.apple.foundationdb.record.query.expressions.Query) LongStream(java.util.stream.LongStream) TestRecords1Proto(com.apple.foundationdb.record.TestRecords1Proto) Tags(com.apple.test.Tags) CompletionException(java.util.concurrent.CompletionException) Collectors(java.util.stream.Collectors) Test(org.junit.jupiter.api.Test) Matchers.instanceOf(org.hamcrest.Matchers.instanceOf) List(java.util.List) Stream(java.util.stream.Stream) Index(com.apple.foundationdb.record.metadata.Index) Assertions.assertTrue(org.junit.jupiter.api.Assertions.assertTrue) IndexTypes(com.apple.foundationdb.record.metadata.IndexTypes) Optional(java.util.Optional) Comparator(java.util.Comparator) Collections(java.util.Collections) TestRecords1Proto(com.apple.foundationdb.record.TestRecords1Proto) IndexRecordFunction(com.apple.foundationdb.record.metadata.IndexRecordFunction) HashMap(java.util.HashMap) Index(com.apple.foundationdb.record.metadata.Index) RecordCoreException(com.apple.foundationdb.record.RecordCoreException) TreeSet(java.util.TreeSet) CompletionException(java.util.concurrent.CompletionException) RecordQuery(com.apple.foundationdb.record.query.RecordQuery)

Example 3 with IndexRecordFunction

use of com.apple.foundationdb.record.metadata.IndexRecordFunction in project fdb-record-layer by FoundationDB.

the class RankIndexTest method writeOnlyLookup.

@Test
public void writeOnlyLookup() throws Exception {
    assertThrows(RecordCoreException.class, () -> {
        fdb = FDBDatabaseFactory.instance().getDatabase();
        try (FDBRecordContext context = openContext()) {
            openRecordStore(context);
            recordStore.markIndexWriteOnly("rank_by_gender").join();
            // Re-open to reload state.
            openRecordStore(context);
            TestRecordsRankProto.BasicRankedRecord record = TestRecordsRankProto.BasicRankedRecord.newBuilder().setName("achilles").setGender("M").setScore(10).build();
            FDBStoredRecord<Message> storedRecord = recordStore.saveRecord(record);
            IndexRecordFunction<Long> function = (IndexRecordFunction<Long>) Query.rank(Key.Expressions.field("score").groupBy(Key.Expressions.field("gender"))).getFunction();
            recordStore.evaluateRecordFunction(function, storedRecord);
        }
    });
}
Also used : IndexRecordFunction(com.apple.foundationdb.record.metadata.IndexRecordFunction) Message(com.google.protobuf.Message) FDBRecordContext(com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext) TestRecordsRankProto(com.apple.foundationdb.record.TestRecordsRankProto) Test(org.junit.jupiter.api.Test)

Example 4 with IndexRecordFunction

use of com.apple.foundationdb.record.metadata.IndexRecordFunction in project fdb-record-layer by FoundationDB.

the class RankComparisons method findComparison.

private void findComparison(@Nonnull QueryRecordFunctionWithComparison comparison, @Nonnull List<Index> indexes, @Nonnull List<QueryComponent> potentialGroupFilters, @Nonnull AtomicInteger counter) {
    RecordFunction<?> recordFunction = comparison.getFunction();
    // TODO: Should share with indexMaintainerForAggregateFunction
    // TODO: Move index-specific query planning behavior outside of planner (https://github.com/FoundationDB/fdb-record-layer/issues/17)
    List<String> requiredIndexTypes;
    if (recordFunction.getName().equals(FunctionNames.RANK)) {
        requiredIndexTypes = Arrays.asList(IndexTypes.RANK, IndexTypes.TIME_WINDOW_LEADERBOARD);
    } else if (recordFunction.getName().equals(FunctionNames.TIME_WINDOW_RANK)) {
        requiredIndexTypes = Collections.singletonList(IndexTypes.TIME_WINDOW_LEADERBOARD);
    } else {
        requiredIndexTypes = null;
    }
    if (requiredIndexTypes != null) {
        final GroupingKeyExpression operand = ((IndexRecordFunction) recordFunction).getOperand();
        Optional<Index> matchingIndex = indexes.stream().filter(index -> requiredIndexTypes.contains(index.getType()) && index.getRootExpression().equals(operand)).min(Comparator.comparing(Index::getColumnSize));
        if (matchingIndex.isPresent()) {
            final KeyExpression groupBy = operand.getGroupingSubKey();
            final List<QueryComponent> groupFilters = new ArrayList<>();
            final List<Comparisons.Comparison> groupComparisons = new ArrayList<>();
            if (!GroupingValidator.findGroupKeyFilters(potentialGroupFilters, groupBy, groupFilters, groupComparisons)) {
                return;
            }
            QueryComponent substitute = null;
            String bindingName = null;
            final Comparisons.Type comparisonType = comparison.getComparison().getType();
            if (!operand.createsDuplicates() && !comparisonType.isUnary()) {
                bindingName = Bindings.Internal.RANK.bindingName(Integer.toString(counter.getAndIncrement()));
                Comparisons.Comparison substituteComparison = new Comparisons.ParameterComparison(comparisonType, bindingName, Bindings.Internal.RANK);
                final KeyExpression grouped = operand.getGroupedSubKey();
                if (grouped instanceof FieldKeyExpression) {
                    substitute = new FieldWithComparison(((FieldKeyExpression) grouped).getFieldName(), substituteComparison);
                } else if (grouped instanceof NestingKeyExpression) {
                    NestingKeyExpression nesting = (NestingKeyExpression) grouped;
                    if (nesting.getChild() instanceof FieldKeyExpression) {
                        substitute = new NestedField(nesting.getParent().getFieldName(), new FieldWithComparison(((FieldKeyExpression) nesting.getChild()).getFieldName(), substituteComparison));
                    }
                }
                if (substitute == null) {
                    bindingName = null;
                }
            }
            comparisons.put(comparison, new RankComparison(comparison, matchingIndex.get(), groupFilters, groupComparisons, substitute, bindingName));
        }
    }
}
Also used : FunctionNames(com.apple.foundationdb.record.FunctionNames) RecordMetaData(com.apple.foundationdb.record.RecordMetaData) Arrays(java.util.Arrays) IndexAggregateFunction(com.apple.foundationdb.record.metadata.IndexAggregateFunction) Bindings(com.apple.foundationdb.record.Bindings) HashMap(java.util.HashMap) RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) Function(java.util.function.Function) ArrayList(java.util.ArrayList) FieldKeyExpression(com.apple.foundationdb.record.metadata.expressions.FieldKeyExpression) Tuple(com.apple.foundationdb.tuple.Tuple) QueryRecordFunctionWithComparison(com.apple.foundationdb.record.query.expressions.QueryRecordFunctionWithComparison) RecordCoreException(com.apple.foundationdb.record.RecordCoreException) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) NestingKeyExpression(com.apple.foundationdb.record.metadata.expressions.NestingKeyExpression) Map(java.util.Map) GroupingKeyExpression(com.apple.foundationdb.record.metadata.expressions.GroupingKeyExpression) IndexRecordFunction(com.apple.foundationdb.record.metadata.IndexRecordFunction) Nonnull(javax.annotation.Nonnull) Nullable(javax.annotation.Nullable) FieldWithComparison(com.apple.foundationdb.record.query.expressions.FieldWithComparison) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) Set(java.util.Set) ScanComparisons(com.apple.foundationdb.record.query.plan.ScanComparisons) RecordQueryScoreForRankPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryScoreForRankPlan) AndComponent(com.apple.foundationdb.record.query.expressions.AndComponent) Collectors(java.util.stream.Collectors) AndOrComponent(com.apple.foundationdb.record.query.expressions.AndOrComponent) Comparisons(com.apple.foundationdb.record.query.expressions.Comparisons) List(java.util.List) NestedField(com.apple.foundationdb.record.query.expressions.NestedField) Index(com.apple.foundationdb.record.metadata.Index) IndexTypes(com.apple.foundationdb.record.metadata.IndexTypes) QueryComponent(com.apple.foundationdb.record.query.expressions.QueryComponent) Optional(java.util.Optional) API(com.apple.foundationdb.annotation.API) RecordFunction(com.apple.foundationdb.record.RecordFunction) Comparator(java.util.Comparator) ComponentWithChildren(com.apple.foundationdb.record.query.expressions.ComponentWithChildren) Collections(java.util.Collections) QueryComponent(com.apple.foundationdb.record.query.expressions.QueryComponent) IndexRecordFunction(com.apple.foundationdb.record.metadata.IndexRecordFunction) GroupingKeyExpression(com.apple.foundationdb.record.metadata.expressions.GroupingKeyExpression) FieldKeyExpression(com.apple.foundationdb.record.metadata.expressions.FieldKeyExpression) NestingKeyExpression(com.apple.foundationdb.record.metadata.expressions.NestingKeyExpression) GroupingKeyExpression(com.apple.foundationdb.record.metadata.expressions.GroupingKeyExpression) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) ArrayList(java.util.ArrayList) Index(com.apple.foundationdb.record.metadata.Index) NestedField(com.apple.foundationdb.record.query.expressions.NestedField) FieldKeyExpression(com.apple.foundationdb.record.metadata.expressions.FieldKeyExpression) NestingKeyExpression(com.apple.foundationdb.record.metadata.expressions.NestingKeyExpression) QueryRecordFunctionWithComparison(com.apple.foundationdb.record.query.expressions.QueryRecordFunctionWithComparison) FieldWithComparison(com.apple.foundationdb.record.query.expressions.FieldWithComparison) ScanComparisons(com.apple.foundationdb.record.query.plan.ScanComparisons) Comparisons(com.apple.foundationdb.record.query.expressions.Comparisons) FieldWithComparison(com.apple.foundationdb.record.query.expressions.FieldWithComparison)

Aggregations

IndexRecordFunction (com.apple.foundationdb.record.metadata.IndexRecordFunction)4 Index (com.apple.foundationdb.record.metadata.Index)3 Test (org.junit.jupiter.api.Test)3 RecordCoreException (com.apple.foundationdb.record.RecordCoreException)2 IndexTypes (com.apple.foundationdb.record.metadata.IndexTypes)2 GroupingKeyExpression (com.apple.foundationdb.record.metadata.expressions.GroupingKeyExpression)2 FDBRecordContext (com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext)2 RecordQueryPlan (com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan)2 Collections (java.util.Collections)2 Comparator (java.util.Comparator)2 HashMap (java.util.HashMap)2 List (java.util.List)2 Map (java.util.Map)2 Optional (java.util.Optional)2 Collectors (java.util.stream.Collectors)2 Nonnull (javax.annotation.Nonnull)2 Nullable (javax.annotation.Nullable)2 API (com.apple.foundationdb.annotation.API)1 Bindings (com.apple.foundationdb.record.Bindings)1 FunctionNames (com.apple.foundationdb.record.FunctionNames)1