use of com.apple.foundationdb.record.metadata.RecordType in project fdb-record-layer by FoundationDB.
the class RecordQueryPlanner method planCoveringAggregateIndex.
@Nullable
public RecordQueryCoveringIndexPlan planCoveringAggregateIndex(@Nonnull RecordQuery query, @Nonnull Index index, @Nonnull KeyExpression indexExpr) {
final Collection<RecordType> recordTypes = metaData.recordTypesForIndex(index);
if (recordTypes.size() != 1) {
// Unfortunately, since we materialize partial records, we need a unique type for them.
return null;
}
final RecordType recordType = recordTypes.iterator().next();
final PlanContext planContext = getPlanContext(query);
planContext.rankComparisons = new RankComparisons(query.getFilter(), planContext.indexes);
// Repeated fields will be scanned one at a time by covering aggregate, so there is no issue with fan out.
planContext.allowDuplicates = true;
final CandidateScan candidateScan = new CandidateScan(planContext, index, query.isSortReverse());
final ScoredPlan scoredPlan = planCandidateScan(candidateScan, indexExpr, BooleanNormalizer.forConfiguration(configuration).normalizeIfPossible(query.getFilter()), query.getSort());
// It would be possible to handle unsatisfiedFilters if they, too, only involved group key (covering) fields.
if (scoredPlan == null || !scoredPlan.unsatisfiedFilters.isEmpty() || !(scoredPlan.plan instanceof RecordQueryIndexPlan)) {
return null;
}
final IndexKeyValueToPartialRecord.Builder builder = IndexKeyValueToPartialRecord.newBuilder(recordType);
final List<KeyExpression> keyFields = index.getRootExpression().normalizeKeyForPositions();
final List<KeyExpression> valueFields = Collections.emptyList();
for (KeyExpression resultField : query.getRequiredResults()) {
if (!addCoveringField(resultField, builder, keyFields, valueFields)) {
return null;
}
}
builder.addRequiredMessageFields();
if (!builder.isValid(true)) {
return null;
}
RecordQueryIndexPlan plan = (RecordQueryIndexPlan) scoredPlan.plan;
IndexScanParameters scanParameters = new IndexScanComparisons(IndexScanType.BY_GROUP, plan.getComparisons());
plan = new RecordQueryIndexPlan(plan.getIndexName(), scanParameters, plan.isReverse());
return new RecordQueryCoveringIndexPlan(plan, recordType.getName(), AvailableFields.NO_FIELDS, builder.build());
}
use of com.apple.foundationdb.record.metadata.RecordType in project fdb-record-layer by FoundationDB.
the class RecordQueryPlanner method getPlanContext.
@Nonnull
private PlanContext getPlanContext(@Nonnull RecordQuery query) {
final List<Index> indexes = new ArrayList<>();
@Nullable final KeyExpression commonPrimaryKey;
recordStoreState.beginRead();
try {
if (query.getRecordTypes().isEmpty()) {
// ALL_TYPES
commonPrimaryKey = RecordMetaData.commonPrimaryKey(metaData.getRecordTypes().values());
} else {
final List<RecordType> recordTypes = query.getRecordTypes().stream().map(metaData::getRecordType).collect(Collectors.toList());
if (recordTypes.size() == 1) {
final RecordType recordType = recordTypes.get(0);
indexes.addAll(readableOf(recordType.getIndexes()));
indexes.addAll(readableOf(recordType.getMultiTypeIndexes()));
commonPrimaryKey = recordType.getPrimaryKey();
} else {
boolean first = true;
for (RecordType recordType : recordTypes) {
if (first) {
indexes.addAll(readableOf(recordType.getMultiTypeIndexes()));
first = false;
} else {
indexes.retainAll(readableOf(recordType.getMultiTypeIndexes()));
}
}
commonPrimaryKey = RecordMetaData.commonPrimaryKey(recordTypes);
}
}
indexes.addAll(readableOf(metaData.getUniversalIndexes()));
} finally {
recordStoreState.endRead();
}
indexes.removeIf(query.hasAllowedIndexes() ? index -> !query.getAllowedIndexes().contains(index.getName()) : index -> !query.getIndexQueryabilityFilter().isQueryable(index));
return new PlanContext(query, indexes, commonPrimaryKey);
}
use of com.apple.foundationdb.record.metadata.RecordType in project fdb-record-layer by FoundationDB.
the class BindingFunctions method javaComparisonType.
@Nullable
private static Descriptors.FieldDescriptor.JavaType javaComparisonType(@Nonnull QueryComponent fieldComparison, @Nonnull Index index, @Nonnull RecordMetaData metaData) {
Descriptors.FieldDescriptor.JavaType javaType = null;
for (RecordType recordType : metaData.recordTypesForIndex(index)) {
Descriptors.Descriptor descriptor = recordType.getDescriptor();
QueryComponent component = fieldComparison;
while (component instanceof NestedField) {
Descriptors.FieldDescriptor fieldDescriptor = descriptor.findFieldByName(((NestedField) component).getName());
if (fieldDescriptor == null) {
return null;
}
descriptor = fieldDescriptor.getMessageType();
component = ((NestedField) component).getChild();
}
if (component instanceof FieldWithComparison) {
Descriptors.FieldDescriptor fieldDescriptor = descriptor.findFieldByName(((FieldWithComparison) component).getName());
if (fieldDescriptor == null) {
return null;
}
if (javaType == null) {
javaType = fieldDescriptor.getJavaType();
} else if (javaType != fieldDescriptor.getJavaType()) {
return null;
}
} else {
return null;
}
}
return javaType;
}
use of com.apple.foundationdb.record.metadata.RecordType in project fdb-record-layer by FoundationDB.
the class QueryPlanUtils method getCoveringIndexEntryToPartialRecordFunction.
/**
* The method to get a function from an {@link IndexEntry} to a {@link FDBQueriedRecord} representing a partial record.
*/
@SuppressWarnings("unchecked")
public static <M extends Message> Function<IndexEntry, FDBQueriedRecord<M>> getCoveringIndexEntryToPartialRecordFunction(@Nonnull final FDBRecordStoreBase<M> store, @Nonnull final String recordTypeName, @Nonnull final String indexName, @Nonnull final IndexKeyValueToPartialRecord toRecord, @Nonnull final IndexScanType scanType) {
final RecordMetaData metaData = store.getRecordMetaData();
final RecordType recordType = metaData.getRecordType(recordTypeName);
final Index index = metaData.getIndex(indexName);
final Descriptors.Descriptor recordDescriptor = recordType.getDescriptor();
boolean hasPrimaryKey = scanType != IndexScanType.BY_GROUP && scanType != IndexScanType.BY_LUCENE_AUTO_COMPLETE && scanType != IndexScanType.BY_LUCENE_SPELLCHECK;
return indexEntry -> store.coveredIndexQueriedRecord(index, indexEntry, recordType, (M) toRecord.toRecord(recordDescriptor, indexEntry), hasPrimaryKey);
}
use of com.apple.foundationdb.record.metadata.RecordType in project fdb-record-layer by FoundationDB.
the class FDBRecordStore method updateSecondaryIndexes.
@Nonnull
private <M extends Message> CompletableFuture<Void> updateSecondaryIndexes(@Nullable final FDBStoredRecord<M> oldRecord, @Nullable final FDBStoredRecord<M> newRecord) {
if (oldRecord == null && newRecord == null) {
return AsyncUtil.DONE;
}
if (recordStoreStateRef.get() == null) {
return preloadRecordStoreStateAsync().thenCompose(vignore -> updateSecondaryIndexes(oldRecord, newRecord));
}
final List<CompletableFuture<Void>> futures = new ArrayList<>();
final RecordType sameRecordType;
if (oldRecord == null) {
sameRecordType = newRecord.getRecordType();
} else if (newRecord == null) {
sameRecordType = oldRecord.getRecordType();
} else if (oldRecord.getRecordType() == newRecord.getRecordType()) {
sameRecordType = newRecord.getRecordType();
} else {
sameRecordType = null;
}
beginRecordStoreStateRead();
boolean haveFuture = false;
try {
if (sameRecordType != null) {
updateSecondaryIndexes(oldRecord, newRecord, futures, getEnabledIndexes(sameRecordType));
updateSecondaryIndexes(oldRecord, newRecord, futures, getEnabledUniversalIndexes());
updateSecondaryIndexes(oldRecord, newRecord, futures, getEnabledMultiTypeIndexes(sameRecordType));
} else {
final List<Index> oldIndexes = new ArrayList<>();
if (oldRecord != null) {
final RecordType oldRecordType = oldRecord.getRecordType();
oldIndexes.addAll(getEnabledIndexes(oldRecordType));
oldIndexes.addAll(getEnabledUniversalIndexes());
oldIndexes.addAll(getEnabledMultiTypeIndexes(oldRecordType));
}
List<Index> newIndexes = new ArrayList<>();
if (newRecord != null) {
final RecordType newRecordType = newRecord.getRecordType();
newIndexes.addAll(getEnabledIndexes(newRecordType));
newIndexes.addAll(getEnabledUniversalIndexes());
newIndexes.addAll(getEnabledMultiTypeIndexes(newRecordType));
}
List<Index> commonIndexes = new ArrayList<>(oldIndexes);
commonIndexes.retainAll(newIndexes);
oldIndexes.removeAll(commonIndexes);
newIndexes.removeAll(commonIndexes);
updateSecondaryIndexes(oldRecord, null, futures, oldIndexes);
updateSecondaryIndexes(null, newRecord, futures, newIndexes);
updateSecondaryIndexes(oldRecord, newRecord, futures, commonIndexes);
}
if (!getRecordMetaData().getSyntheticRecordTypes().isEmpty()) {
updateSyntheticIndexes(oldRecord, newRecord, futures);
}
haveFuture = true;
} finally {
if (!haveFuture) {
endRecordStoreStateRead();
}
}
if (futures.isEmpty()) {
endRecordStoreStateRead();
return AsyncUtil.DONE;
} else if (futures.size() == 1) {
return futures.get(0).whenComplete((v, t) -> endRecordStoreStateRead());
} else {
return AsyncUtil.whenAll(futures).whenComplete((v, t) -> endRecordStoreStateRead());
}
}
Aggregations