io.stargate.sgv2.graphql.schema.graphqlfirst.processor.IndexModelBuilder Maven / Gradle / Ivy
/*
* Copyright The Stargate Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.stargate.sgv2.graphql.schema.graphqlfirst.processor;
import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;
import graphql.language.Directive;
import io.stargate.bridge.grpc.TypeSpecs;
import io.stargate.bridge.proto.QueryOuterClass.TypeSpec;
import io.stargate.bridge.proto.Schema.IndexingType;
import io.stargate.bridge.proto.Schema.SupportedFeaturesResponse;
import io.stargate.sgv2.common.cql.builder.CollectionIndexingType;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
class IndexModelBuilder extends ModelBuilderBase {
private static final Splitter.MapSplitter OPTIONS_SPLITTER =
Splitter.on(',').withKeyValueSeparator(Splitter.on(':').trimResults(CharMatcher.anyOf("' ")));
private final Directive cqlIndexDirective;
private final String parentCqlName;
private final String columnCqlName;
private final TypeSpec cqlType;
private final String messagePrefix;
IndexModelBuilder(
Directive cqlIndexDirective,
String parentCqlName,
String columnCqlName,
TypeSpec cqlType,
String messagePrefix,
ProcessingContext context) {
super(context, cqlIndexDirective.getSourceLocation());
this.cqlIndexDirective = cqlIndexDirective;
this.parentCqlName = parentCqlName;
this.columnCqlName = columnCqlName;
this.cqlType = cqlType;
this.messagePrefix = messagePrefix;
}
@Override
IndexModel build() throws SkipException {
String indexName =
DirectiveHelper.getStringArgument(cqlIndexDirective, CqlDirectives.INDEX_NAME, context)
.orElse(parentCqlName + '_' + columnCqlName + "_idx");
Optional indexClass =
DirectiveHelper.getStringArgument(cqlIndexDirective, CqlDirectives.INDEX_CLASS, context);
// Some persistence backends default to SAI when no class is specified:
SupportedFeaturesResponse supportedFeatures = context.getBridge().getSupportedFeatures();
if (!indexClass.isPresent() && !supportedFeatures.getSecondaryIndexes()) {
if (!context.getBridge().getSupportedFeatures().getSai()) {
// Cannot happen with any of the existing persistence implementations, but handle it just in
// case:
invalidMapping(
"%s: the persistence backend does not support regular secondary indexes nor SAI, "
+ "indexes that don't specify %s can't be mapped",
messagePrefix, CqlDirectives.INDEX_CLASS);
throw SkipException.INSTANCE;
}
info(
"%s: using SAI for index %s because the persistence backend does not support "
+ "regular secondary indexes",
messagePrefix, indexName);
indexClass = Optional.of(IndexModel.SAI_INDEX_CLASS_NAME);
}
IndexingType indexingType =
DirectiveHelper.getEnumArgument(
cqlIndexDirective,
CqlDirectives.INDEX_TARGET,
CollectionIndexingType.class,
context)
.filter(this::validateTarget)
.map(this::toGrpc)
.orElse(TypeSpecs.isCollection(cqlType) ? IndexingType.VALUES_ : IndexingType.DEFAULT);
Map indexOptions =
DirectiveHelper.getStringArgument(cqlIndexDirective, CqlDirectives.INDEX_OPTIONS, context)
.flatMap(this::convertOptions)
.orElse(Collections.emptyMap());
if (cqlType.hasUdt() && !cqlType.getUdt().getFrozen()) {
invalidMapping(
"%s: fields that map to UDTs can only be indexed if they are frozen", messagePrefix);
throw SkipException.INSTANCE;
}
return new IndexModel(indexName, indexClass, indexingType, indexOptions);
}
private boolean validateTarget(CollectionIndexingType indexTarget) {
switch (indexTarget) {
case KEYS:
case ENTRIES:
if (!cqlType.hasMap()) {
invalidMapping(
"%s: index target %s can only be used for map columns", messagePrefix, indexTarget);
return false;
}
break;
case VALUES:
if (!TypeSpecs.isCollection(cqlType) || TypeSpecs.isFrozen(cqlType)) {
invalidMapping(
"%s: index target %s can only be used for non-frozen collection columns",
messagePrefix, indexTarget);
return false;
}
break;
case FULL:
if (!TypeSpecs.isCollection(cqlType) || !TypeSpecs.isFrozen(cqlType)) {
invalidMapping(
"%s: index target %s can only be used for frozen collection columns",
messagePrefix, indexTarget);
return false;
}
break;
default:
throw new AssertionError("Unsupported target " + indexTarget);
}
return true;
}
private IndexingType toGrpc(CollectionIndexingType type) {
switch (type) {
case KEYS:
return IndexingType.KEYS;
case VALUES:
return IndexingType.VALUES_;
case ENTRIES:
return IndexingType.ENTRIES;
case FULL:
return IndexingType.FULL;
}
throw new IllegalArgumentException("Unexpected type " + type);
}
private Optional
© 2015 - 2024 Weber Informatics LLC | Privacy Policy