use of com.apple.foundationdb.record.RecordCoreException in project fdb-record-layer by FoundationDB.
the class IndexFunctionHelper method getThenSubKey.
protected static KeyExpression getThenSubKey(ThenKeyExpression then, int columnStart, int columnEnd) {
final List<KeyExpression> children = then.getChildren();
int columnPosition = 0;
int startChildPosition = -1;
int startChildStart = -1;
int startChildEnd = -1;
for (int childPosition = 0; childPosition < children.size(); childPosition++) {
final KeyExpression child = children.get(childPosition);
final int childColumns = child.getColumnSize();
if (startChildPosition < 0 && columnPosition + childColumns > columnStart) {
startChildPosition = childPosition;
startChildStart = columnStart - columnPosition;
startChildEnd = childColumns;
}
if (columnPosition + childColumns >= columnEnd) {
int endChildEnd = columnEnd - columnPosition;
if (childPosition == startChildPosition) {
// Just one child spans column start, end.
if (startChildPosition == 0 && endChildEnd == childColumns) {
return child;
} else {
return getSubKey(child, startChildStart, endChildEnd);
}
}
if (startChildStart == 0 && endChildEnd == childColumns) {
return new ThenKeyExpression(children.subList(startChildPosition, childPosition + 1));
}
final List<KeyExpression> keys = new ArrayList<>(childPosition - startChildPosition + 1);
keys.add(getSubKey(children.get(startChildPosition), startChildStart, startChildEnd));
if (childPosition > startChildPosition + 1) {
keys.addAll(children.subList(startChildPosition + 1, childPosition));
}
keys.add(getSubKey(child, 0, endChildEnd));
return new ThenKeyExpression(keys);
}
columnPosition += childColumns;
}
throw new RecordCoreException("column counts are not consistent");
}
use of com.apple.foundationdb.record.RecordCoreException in project fdb-record-layer by FoundationDB.
the class IndexFunctionHelper method recordFunctionIndexEntry.
/**
* Get the index entry for use by the given index to evaluate the given record function.
*
* In most cases, this is the same as {@link KeyExpression#evaluateSingleton}. But if {@code recordFunction} is
* the result of {@link #recordFunctionWithSubrecordCondition}, a matching entry will be found.
* @param store store against which function will be evaluated
* @param index index for which to evaluate
* @param context context for parameter bindings
* @param recordFunction record function for which to evaluate
* @param record record against which to evaluate
* @param groupSize grouping size for the given index
* @return an index entry or {@code null} if none matches a bound condition
*/
@Nullable
public static Key.Evaluated recordFunctionIndexEntry(@Nonnull FDBRecordStore store, @Nonnull Index index, @Nonnull EvaluationContext context, @Nullable IndexRecordFunction<?> recordFunction, @Nonnull FDBRecord<?> record, int groupSize) {
final KeyExpression expression = index.getRootExpression();
if (!(recordFunction instanceof IndexRecordFunctionWithSubrecordValues)) {
return expression.evaluateSingleton(record);
}
final IndexRecordFunctionWithSubrecordValues<?> recordFunctionWithSubrecordValues = (IndexRecordFunctionWithSubrecordValues<?>) recordFunction;
final int scalarPrefixCount = recordFunctionWithSubrecordValues.getScalarPrefixCount();
final List<Object> toMatch = recordFunctionWithSubrecordValues.getValues(store, context).values();
List<Object> prev = null;
Key.Evaluated match = null;
for (Key.Evaluated key : expression.evaluate(record)) {
final List<Object> subrecord = key.values();
for (int i = 0; i < groupSize; i++) {
if (i < scalarPrefixCount) {
if (prev != null) {
if (!Objects.equals(prev.get(i), subrecord.get(i))) {
throw new RecordCoreException("All subrecords should match for non-constrained keys");
}
}
} else {
if (toMatch.get(i - scalarPrefixCount).equals(subrecord.get(i))) {
if (match != null) {
throw new RecordCoreException("More than one matching subrecord");
}
match = key;
}
}
}
prev = subrecord;
}
return match;
}
use of com.apple.foundationdb.record.RecordCoreException in project fdb-record-layer by FoundationDB.
the class LiteralKeyExpression method fromProtoValue.
@Nullable
public static Object fromProtoValue(RecordMetaDataProto.Value proto) {
int found = 0;
Object value = null;
if (proto.hasDoubleValue()) {
++found;
value = proto.getDoubleValue();
}
if (proto.hasFloatValue()) {
++found;
value = proto.getFloatValue();
}
if (proto.hasLongValue()) {
++found;
value = proto.getLongValue();
}
if (proto.hasBoolValue()) {
++found;
value = proto.getBoolValue();
}
if (proto.hasStringValue()) {
++found;
value = proto.getStringValue();
}
if (proto.hasBytesValue()) {
++found;
value = proto.getBytesValue().toByteArray();
}
if (found == 0) {
++found;
}
if (found > 1) {
throw new RecordCoreException("More than one value encoded in value").addLogInfo("encoded_value", proto);
}
return value;
}
use of com.apple.foundationdb.record.RecordCoreException in project fdb-record-layer by FoundationDB.
the class TimeWindowLeaderboardIndexMaintainer method scan.
@Nonnull
@Override
public RecordCursor<IndexEntry> scan(@Nonnull IndexScanBounds scanBounds, @Nullable byte[] continuation, @Nonnull ScanProperties scanProperties) {
final IndexScanType scanType = scanBounds.getScanType();
if (scanType != IndexScanType.BY_VALUE && scanType != IndexScanType.BY_RANK && scanType != IndexScanType.BY_TIME_WINDOW) {
throw new RecordCoreException("Can only scan leaderboard index by time window, rank or value.");
}
// Decode range arguments.
final int type;
final long timestamp;
final TupleRange leaderboardRange;
if (scanType == IndexScanType.BY_TIME_WINDOW) {
// Get oldest leaderboard of type containing timestamp.
if (scanBounds instanceof TimeWindowScanRange) {
TimeWindowScanRange scanRange = (TimeWindowScanRange) scanBounds;
type = scanRange.getLeaderboardType();
timestamp = scanRange.getLeaderboardTimestamp();
leaderboardRange = scanRange.getScanRange();
} else {
// TODO: For compatibility, accept scan with BY_TIME_WINDOW and TupleRange for a while.
// This code can be removed when we are confident all callers have been converted.
IndexScanRange scanRange = (IndexScanRange) scanBounds;
TupleRange rankRange = scanRange.getScanRange();
final Tuple lowRank = rankRange.getLow();
final Tuple highRank = rankRange.getHigh();
type = (int) lowRank.getLong(0);
timestamp = lowRank.getLong(1);
leaderboardRange = new TupleRange(Tuple.fromList(lowRank.getItems().subList(2, lowRank.size())), Tuple.fromList(highRank.getItems().subList(2, highRank.size())), rankRange.getLowEndpoint(), rankRange.getHighEndpoint());
}
} else {
// Get the all-time leaderboard for unqualified rank or value.
IndexScanRange scanRange = (IndexScanRange) scanBounds;
type = TimeWindowLeaderboard.ALL_TIME_LEADERBOARD_TYPE;
// Any value would do.
timestamp = 0;
leaderboardRange = scanRange.getScanRange();
}
final int groupPrefixSize = getGroupingCount();
final CompletableFuture<TimeWindowLeaderboard> leaderboardFuture = oldestLeaderboardMatching(type, timestamp);
final CompletableFuture<TupleRange> scoreRangeFuture;
if (scanType == IndexScanType.BY_VALUE) {
scoreRangeFuture = leaderboardFuture.thenApply(leaderboard -> leaderboard == null ? null : leaderboardRange);
} else {
scoreRangeFuture = leaderboardFuture.thenCompose(leaderboard -> {
if (leaderboard == null) {
return CompletableFuture.completedFuture(null);
}
final Subspace extraSubspace = getSecondarySubspace();
final Subspace leaderboardSubspace = extraSubspace.subspace(leaderboard.getSubspaceKey());
final RankedSet.Config leaderboardConfig = config.toBuilder().setNLevels(leaderboard.getNLevels()).build();
return RankedSetIndexHelper.rankRangeToScoreRange(state, groupPrefixSize, leaderboardSubspace, leaderboardConfig, leaderboardRange);
});
}
// Add leaderboard's key to the front and take it off of the results.
return RecordCursor.flatMapPipelined(ignore -> RecordCursor.fromFuture(getExecutor(), scoreRangeFuture), (scoreRange, ignore) -> {
if (scoreRange == null) {
return RecordCursor.empty(getExecutor());
}
// Already waited in scoreRangeFuture.
final TimeWindowLeaderboard leaderboard = state.context.joinNow(leaderboardFuture);
final CompletableFuture<Boolean> highStoreFirstFuture;
if (scanType == IndexScanType.BY_VALUE) {
final Tuple lowGroup = scoreRange.getLow() != null && scoreRange.getLow().size() > groupPrefixSize ? TupleHelpers.subTuple(scoreRange.getLow(), 0, groupPrefixSize) : null;
final Tuple highGroup = scoreRange.getHigh() != null && scoreRange.getHigh().size() > groupPrefixSize ? TupleHelpers.subTuple(scoreRange.getHigh(), 0, groupPrefixSize) : null;
if (lowGroup != null && lowGroup.equals(highGroup)) {
highStoreFirstFuture = isHighScoreFirst(leaderboard.getDirectory(), lowGroup);
} else {
highStoreFirstFuture = CompletableFuture.completedFuture(leaderboard.getDirectory().isHighScoreFirst());
}
} else {
highStoreFirstFuture = AsyncUtil.READY_FALSE;
}
if (highStoreFirstFuture.isDone()) {
return scanLeaderboard(leaderboard, state.context.joinNow(highStoreFirstFuture), scoreRange, continuation, scanProperties);
} else {
return RecordCursor.flatMapPipelined(ignore2 -> RecordCursor.fromFuture(getExecutor(), highStoreFirstFuture), (highScoreFirst, ignore2) -> scanLeaderboard(leaderboard, highScoreFirst, scoreRange, continuation, scanProperties), null, 1);
}
}, null, 1).mapPipelined(kv -> getIndexEntry(kv, groupPrefixSize, state.context.joinNow(leaderboardFuture).getDirectory()), 1);
}
use of com.apple.foundationdb.record.RecordCoreException in project fdb-record-layer by FoundationDB.
the class TimeWindowLeaderboardIndexMaintainer method timeWindowRankAndEntry.
@Nonnull
public <M extends Message> CompletableFuture<Pair<Long, Tuple>> timeWindowRankAndEntry(@Nonnull FDBRecord<M> record, int type, long timestamp) {
final List<IndexEntry> indexEntries = evaluateIndex(record);
final CompletableFuture<TimeWindowLeaderboard> leaderboardFuture = oldestLeaderboardMatching(type, timestamp);
return leaderboardFuture.thenCompose(leaderboard -> {
if (leaderboard == null) {
return CompletableFuture.completedFuture(null);
}
return groupOrderedScoreIndexKeys(indexEntries, leaderboard.getDirectory(), true).thenCompose(groupedScores -> {
if (groupedScores.isEmpty()) {
return CompletableFuture.completedFuture(null);
}
if (groupedScores.size() > 1) {
throw new RecordCoreException("Record has more than one group of scores");
}
Map.Entry<Tuple, Collection<OrderedScoreIndexKey>> groupEntry = groupedScores.entrySet().iterator().next();
Optional<OrderedScoreIndexKey> bestContainedScore = groupEntry.getValue().stream().filter(score -> leaderboard.containsTimestamp(score.timestamp)).findFirst();
if (!bestContainedScore.isPresent()) {
return CompletableFuture.completedFuture(null);
}
// bestContainedScore should be the one stored in the leaderboard's ranked set; get its rank there.
final Tuple groupKey = groupEntry.getKey();
return isHighScoreFirst(leaderboard.getDirectory(), groupKey).thenCompose(highScoreFirst -> {
final OrderedScoreIndexKey indexKey = bestContainedScore.get();
final Tuple leaderboardGroupKey = leaderboard.getSubspaceKey().addAll(groupKey);
final Subspace extraSubspace = getSecondarySubspace();
final Subspace rankSubspace = extraSubspace.subspace(leaderboardGroupKey);
final RankedSet.Config leaderboardConfig = config.toBuilder().setNLevels(leaderboard.getNLevels()).build();
final RankedSet rankedSet = new RankedSetIndexHelper.InstrumentedRankedSet(state, rankSubspace, leaderboardConfig);
// Undo any negation needed to find entry.
final Tuple entry = highScoreFirst ? negateScoreForHighScoreFirst(indexKey.scoreKey, 0) : indexKey.scoreKey;
return RankedSetIndexHelper.rankForScore(state, rankedSet, indexKey.scoreKey, true).thenApply(rank -> Pair.of(rank, entry));
});
});
});
}
Aggregations