package org.apache.cassandra.index.sasi.conf;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.apache.cassandra.config.ColumnDefinition;
import org.apache.cassandra.index.sasi.analyzer.AbstractAnalyzer;
import org.apache.cassandra.index.sasi.analyzer.NoOpAnalyzer;
import org.apache.cassandra.index.sasi.analyzer.NonTokenizingAnalyzer;
import org.apache.cassandra.index.sasi.analyzer.StandardAnalyzer;
import org.apache.cassandra.index.sasi.disk.OnDiskIndexBuilder.Mode;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.AsciiType;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.index.sasi.plan.Expression.Op;
import org.apache.cassandra.schema.IndexMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class IndexMode
{
private static final Logger logger = LoggerFactory.getLogger(IndexMode.class);
public static final IndexMode NOT_INDEXED = new IndexMode(Mode.PREFIX, true, false, NonTokenizingAnalyzer.class, 0);
private static final Set<AbstractType<?>> TOKENIZABLE_TYPES = new HashSet<AbstractType<?>>()
{{
add(UTF8Type.instance);
add(AsciiType.instance);
}};
private static final String INDEX_MODE_OPTION = "mode";
private static final String INDEX_ANALYZED_OPTION = "analyzed";
private static final String INDEX_ANALYZER_CLASS_OPTION = "analyzer_class";
private static final String INDEX_IS_LITERAL_OPTION = "is_literal";
private static final String INDEX_MAX_FLUSH_MEMORY_OPTION = "max_compaction_flush_memory_in_mb";
private static final double INDEX_MAX_FLUSH_DEFAULT_MULTIPLIER = 0.15;
public final Mode mode;
public final boolean isAnalyzed, isLiteral;
public final Class analyzerClass;
public final long maxCompactionFlushMemoryInMb;
private IndexMode(Mode mode, boolean isLiteral, boolean isAnalyzed, Class analyzerClass, long maxFlushMemMb)
{
this.mode = mode;
this.isLiteral = isLiteral;
this.isAnalyzed = isAnalyzed;
this.analyzerClass = analyzerClass;
this.maxCompactionFlushMemoryInMb = maxFlushMemMb;
}
public AbstractAnalyzer getAnalyzer(AbstractType<?> validator)
{
AbstractAnalyzer analyzer = new NoOpAnalyzer();
try
{
if (isAnalyzed)
{
if (analyzerClass != null)
analyzer = (AbstractAnalyzer) analyzerClass.newInstance();
else if (TOKENIZABLE_TYPES.contains(validator))
analyzer = new StandardAnalyzer();
}
}
catch (InstantiationException | IllegalAccessException e)
{
logger.error("Failed to create new instance of analyzer with class [{}]", analyzerClass.getName(), e);
}
return analyzer;
}
public static void validateAnalyzer(Map<String, String> indexOptions, ColumnDefinition cd) throws ConfigurationException
{
if (indexOptions.containsKey(INDEX_ANALYZER_CLASS_OPTION))
{
Class<?> analyzerClass;
try
{
analyzerClass = Class.forName(indexOptions.get(INDEX_ANALYZER_CLASS_OPTION));
}
catch (ClassNotFoundException e)
{
throw new ConfigurationException(String.format("Invalid analyzer class option specified [%s]",
indexOptions.get(INDEX_ANALYZER_CLASS_OPTION)));
}
AbstractAnalyzer analyzer;
try
{
analyzer = (AbstractAnalyzer) analyzerClass.newInstance();
if (!analyzer.isCompatibleWith(cd.type))
throw new ConfigurationException(String.format("%s does not support type %s",
analyzerClass.getSimpleName(),
cd.type.asCQL3Type()));
}
catch (InstantiationException | IllegalAccessException e)
{
throw new ConfigurationException(String.format("Unable to initialize analyzer class option specified [%s]",
analyzerClass.getSimpleName()));
}
}
}
public static IndexMode getMode(ColumnDefinition column, Optional<IndexMetadata> config) throws ConfigurationException
{
return getMode(column, config.isPresent() ? config.get().options : null);
}
public static IndexMode getMode(ColumnDefinition column, Map<String, String> indexOptions) throws ConfigurationException
{
if (indexOptions == null || indexOptions.isEmpty())
return IndexMode.NOT_INDEXED;
Mode mode;
try
{
mode = indexOptions.get(INDEX_MODE_OPTION) == null
? Mode.PREFIX
: Mode.mode(indexOptions.get(INDEX_MODE_OPTION));
}
catch (IllegalArgumentException e)
{
throw new ConfigurationException("Incorrect index mode: " + indexOptions.get(INDEX_MODE_OPTION));
}
boolean isAnalyzed = false;
Class analyzerClass = null;
try
{
if (indexOptions.get(INDEX_ANALYZER_CLASS_OPTION) != null)
{
analyzerClass = Class.forName(indexOptions.get(INDEX_ANALYZER_CLASS_OPTION));
isAnalyzed = indexOptions.get(INDEX_ANALYZED_OPTION) == null
? true : Boolean.parseBoolean(indexOptions.get(INDEX_ANALYZED_OPTION));
}
else if (indexOptions.get(INDEX_ANALYZED_OPTION) != null)
{
isAnalyzed = Boolean.parseBoolean(indexOptions.get(INDEX_ANALYZED_OPTION));
}
}
catch (ClassNotFoundException e)
{
logger.error("Failed to find specified analyzer class [{}]. Falling back to default analyzer",
indexOptions.get(INDEX_ANALYZER_CLASS_OPTION));
}
boolean isLiteral = false;
try
{
String literalOption = indexOptions.get(INDEX_IS_LITERAL_OPTION);
AbstractType<?> validator = column.cellValueType();
isLiteral = literalOption == null
? (validator instanceof UTF8Type || validator instanceof AsciiType)
: Boolean.parseBoolean(literalOption);
}
catch (Exception e)
{
logger.error("failed to parse {} option, defaulting to 'false'.", INDEX_IS_LITERAL_OPTION);
}
Long maxMemMb = indexOptions.get(INDEX_MAX_FLUSH_MEMORY_OPTION) == null
? (long) (1073741824 * INDEX_MAX_FLUSH_DEFAULT_MULTIPLIER)
: Long.parseLong(indexOptions.get(INDEX_MAX_FLUSH_MEMORY_OPTION));
return new IndexMode(mode, isLiteral, isAnalyzed, analyzerClass, maxMemMb);
}
public boolean supports(Op operator)
{
return mode.supports(operator);
}
}