
com.stratio.cassandra.lucene.IndexQueryHandler Maven / Gradle / Ivy
/**
* Copyright (C) 2014 Stratio (http://stratio.com)
*
* 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 com.stratio.cassandra.lucene;
import com.stratio.cassandra.lucene.search.Search;
import com.stratio.cassandra.lucene.util.TimeCounter;
import org.apache.cassandra.cql3.*;
import org.apache.cassandra.cql3.statements.BatchStatement;
import org.apache.cassandra.cql3.statements.IndexTarget;
import org.apache.cassandra.cql3.statements.ParsedStatement;
import org.apache.cassandra.cql3.statements.SelectStatement;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.ConsistencyLevel;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.db.ReadQuery;
import org.apache.cassandra.db.filter.RowFilter;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.MD5Digest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Map;
import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;
/**
* {@link QueryHandler} to be used with Lucene searches.
*
* @author Andres de la Pena {@literal }
*/
class IndexQueryHandler implements QueryHandler {
private static final Logger logger = LoggerFactory.getLogger(IndexQueryHandler.class);
/** {@inheritDoc} */
@Override
public ResultMessage.Prepared prepare(String query, QueryState state, Map customPayload) {
return QueryProcessor.instance.prepare(query, state);
}
/** {@inheritDoc} */
@Override
public ParsedStatement.Prepared getPrepared(MD5Digest id) {
return QueryProcessor.instance.getPrepared(id);
}
/** {@inheritDoc} */
@Override
public ParsedStatement.Prepared getPreparedForThrift(Integer id) {
return QueryProcessor.instance.getPreparedForThrift(id);
}
/** {@inheritDoc} */
@Override
public ResultMessage processBatch(BatchStatement statement,
QueryState state,
BatchQueryOptions options,
Map customPayload) {
return QueryProcessor.instance.processBatch(statement, state, options, customPayload);
}
/** {@inheritDoc} */
@Override
public ResultMessage processPrepared(CQLStatement statement,
QueryState state,
QueryOptions options,
Map customPayload) {
QueryProcessor.metrics.preparedStatementsExecuted.inc();
return processStatement(statement, state, options);
}
/** {@inheritDoc} */
@Override
public ResultMessage process(String query,
QueryState state,
QueryOptions options,
Map customPayload) {
ParsedStatement.Prepared p = QueryProcessor.getStatement(query, state.getClientState());
options.prepare(p.boundNames);
CQLStatement prepared = p.statement;
if (prepared.getBoundTerms() != options.getValues().size()) {
throw new InvalidRequestException("Invalid amount of bind variables");
}
if (!state.getClientState().isInternal) {
QueryProcessor.metrics.regularStatementsExecuted.inc();
}
return processStatement(prepared, state, options);
}
private ResultMessage processStatement(CQLStatement statement, QueryState state, QueryOptions options) {
logger.trace("Process {} @CL.{}", statement, options.getConsistency());
ClientState clientState = state.getClientState();
statement.checkAccess(clientState);
statement.validate(clientState);
// Intercept Lucene index searches
if (statement instanceof SelectStatement) {
SelectStatement select = (SelectStatement) statement;
List expressions = select.getRowFilter(options).getExpressions();
for (RowFilter.Expression expression : expressions) {
if (expression.isCustom()) {
RowFilter.CustomExpression customExpression = (RowFilter.CustomExpression) expression;
String clazz = customExpression.getTargetIndex().options.get(IndexTarget.CUSTOM_INDEX_OPTION_NAME);
if (clazz.equals(Index.class.getCanonicalName())) {
TimeCounter time = TimeCounter.create().start();
try {
return process(select, state, options, customExpression);
} catch (ReflectiveOperationException e) {
throw new IndexException(e);
} finally {
logger.debug("Lucene search total time: {}\n", time.stop());
}
}
}
}
}
return execute(statement, state, options);
}
private ResultMessage process(SelectStatement select,
QueryState state,
QueryOptions options,
RowFilter.CustomExpression expression) throws ReflectiveOperationException {
// Validate expression
ColumnFamilyStore cfs = Keyspace.open(select.keyspace()).getColumnFamilyStore(select.columnFamily());
Index index = (Index) cfs.indexManager.getIndex(expression.getTargetIndex());
Search search = index.validate(expression);
// Check paging
int limit = select.getLimit(options);
int page = getPageSize(select, options);
if (search.isTopK()) {
// Avoid IN operator
if (select.getRestrictions().getPartitionKeys(options).size() > 1) {
throw new InvalidRequestException(
"Top-k searches can't be directed to more than one partition, you should either " +
"direct them to one single partition or don't use any partition restrictions.");
}
// Avoid unlimited
if (limit == Integer.MAX_VALUE) {
throw new InvalidRequestException(
"Top-k searches don't support paging, so a cautious LIMIT clause should be provided " +
"to prevent excessive memory consumption.");
} else if (page < limit) {
String json = UTF8Type.instance.compose(expression.getValue());
logger.warn("Disabling paging of {} rows per page for top-k search requesting {} rows: {}",
page,
limit,
json);
return executeWithoutPaging(select, state, options);
}
}
// Process
return execute(select, state, options);
}
private ResultMessage execute(CQLStatement statement, QueryState state, QueryOptions options) {
ResultMessage result = statement.execute(state, options);
return result == null ? new ResultMessage.Void() : result;
}
private int getPageSize(SelectStatement select, QueryOptions options) throws ReflectiveOperationException {
Method method = select.getClass().getDeclaredMethod("getPageSize", QueryOptions.class);
method.setAccessible(true);
return (int) method.invoke(select, options);
}
private ResultMessage.Rows executeWithoutPaging(SelectStatement select, QueryState state, QueryOptions options)
throws ReflectiveOperationException {
ConsistencyLevel cl = options.getConsistency();
checkNotNull(cl, "Invalid empty consistency level");
cl.validateForRead(select.keyspace());
int nowInSec = FBUtilities.nowInSeconds();
int userLimit = select.getLimit(options);
ReadQuery query = select.getQuery(options, nowInSec, userLimit);
Method method = select.getClass()
.getDeclaredMethod("execute",
ReadQuery.class,
QueryOptions.class,
QueryState.class,
int.class,
int.class);
method.setAccessible(true);
return (ResultMessage.Rows) method.invoke(select, query, options, state, nowInSec, userLimit);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy