All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.astyanax.cql.reads.FlatTableRowQueryGen Maven / Gradle / Ivy

package com.netflix.astyanax.cql.reads;

import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;

import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicReference;

import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.RegularStatement;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.querybuilder.Select.Selection;
import com.netflix.astyanax.cql.schema.CqlColumnFamilyDefinitionImpl;
import com.netflix.astyanax.ddl.ColumnDefinition;

/**
 * Read query generator for queries on flat tables i.e tables with no clustering keys.
 * 
 * The class lives along other implementations like {@link CFRowQueryGen}, {@link CFRowRangeQueryGen} and {@link CFRowKeysQueryGen}
 * The structure of queries for flat tables was different enough that they warranted their own class. If your schema contains clustering keys
 * then see {@link CFRowQueryGen}, {@link CFRowRangeQueryGen} and {@link CFRowKeysQueryGen} for implementation details. 
 * 
 * Note that the class manages several individual query generators for different use cases like 
 * 1. Selecting the entire row
 * 2. Performing a column slice operation i.e column collection
 *  
 * Each of these query generators uses the {@link QueryGenCache} to maintain a cached reference to the {@link PreparedStatement}
 * that it creates, which can then be leveraged by subsequent flat table queries that have the same signature. 
 * 
 * Note the one must use caching for flat table queries with EXTREME CAUTION. The cacheability of a query depends on the actual 
 * signature of a query. If you use different queries with different signatures for the same column slice operations, then caching will 
 * not work. Here is an example where caching will break queries.
 * 
 *  Consider a query where you want to perform a column slice operation i.e cherry pick some column for a given row. 
 *  The Astyanax query for that will look somewhat like this 
 *  
 *         ks.prepareQuery( myCF )
 *           .getRow( 1 )
 *           .getColumn( first_name )
 *           .execute(); 
 *           
 *  Now if the table is a flat table, then the query for this will look something like 
 *  
 *       SELECT first_name FROM ks.myCF WHERE key = ? ;
 *       
 *  Note the bind marker for the row key. That is the only parameter here which is dynamic and the column name here i.e "first_name" is not
 *  and hence is part of the signature of this query. 
 *  
 *  Now if we were to attempt to re-use the same prepared statement for a query like this 
 *  
 *         ks.prepareQuery( myCF )
 *           .getRow( 1 )
 *           .getColumn( last_name )  <------ NOTE THAT WE ARE CHANGING OUR COLUMN SLICE AND HENCE VIOLATING THE QUERY SIGNATURE
 *           .execute(); 
 *  
 *  Then this will break since the CQL query required for this is 
 *  
 *       SELECT first_name FROM ks.myCF WHERE key = ? ;
 *       
 *   In cases like this, DO NOT use statement caching. 
 *  
 * @author poberai
 *
 */
public class FlatTableRowQueryGen {
	
	// Reference to the session that is needed for "preparing" the statements
	private AtomicReference sessionRef = new AtomicReference(null);
	private final String keyspace; 
	private final CqlColumnFamilyDefinitionImpl cfDef;

	private final String partitionKeyCol;
	private final String[] allPrimayKeyCols;
	private final List regularCols;
	
	private static final String BIND_MARKER = "?";

	/**
	 * Constructor
	 * @param session
	 * @param keyspaceName
	 * @param cfDefinition
	 */
	public FlatTableRowQueryGen(Session session, String keyspaceName, CqlColumnFamilyDefinitionImpl cfDefinition) {

		this.keyspace = keyspaceName;
		this.cfDef = cfDefinition;
		this.sessionRef.set(session);
		
		partitionKeyCol = cfDef.getPartitionKeyColumnDefinition().getName();
		allPrimayKeyCols = cfDef.getAllPkColNames();
		regularCols = cfDef.getRegularColumnDefinitionList();
	}
	
	/**
	 * Query generator that generates a query to read the entire row, i.e all the columns. 
	 * Note that since it implements the {@link QueryGenCache} it also maintains an inner cached reference 
	 * to the {@link PreparedStatement} that it creates which can then be re-used by subsequent queries that 
	 * have the same signature (i.e read all columns)
	 */
	private QueryGenCache> SelectEntireRow = new QueryGenCache>(sessionRef) {

		@Override
		public Callable getQueryGen(CqlRowQueryImpl rowQuery) {

			return new Callable() {

				@Override
				public RegularStatement call() throws Exception {
					Selection select = QueryBuilder.select();

					for (int i=0; i rowQuery) {
			return pStatement.bind(rowQuery.getRowKey());
		}
	};

	/**
	 * Query generator that generates a query to peform a column slice operation on the specified row. 
	 * Note that performing column slice operations on flat tables is dangerous since the query signature is not the same,
	 * hence use this with caution. See above for an explanation on query signatures and query cacheability. 
	 * 
	 * Note that since it implements the {@link QueryGenCache} it also maintains an inner cached reference 
	 * to the {@link PreparedStatement} that it creates which can then be re-used by subsequent queries that 
	 * have the same signature (i.e read the same column slice for a given row)
	 */
	private QueryGenCache> SelectColumnSlice = new QueryGenCache>(sessionRef) {

		@Override
		public Callable getQueryGen(final CqlRowQueryImpl rowQuery) {

			return new Callable() {

				@Override
				public RegularStatement call() throws Exception {

					Select.Selection select = QueryBuilder.select();
					select.column(partitionKeyCol);

					for (Object col : rowQuery.getColumnSlice().getColumns()) {
						String columnName = (String)col;
						select.column(columnName).ttl(columnName).writeTime(columnName);
					}

					return select.from(keyspace, cfDef.getName()).where(eq(partitionKeyCol, BIND_MARKER));
				}
			};
		}

		@Override
		public BoundStatement bindValues(PreparedStatement pStatement, CqlRowQueryImpl rowQuery) {
			return pStatement.bind(rowQuery.getRowKey());
		}
	};
	
	public Statement getQueryStatement(final CqlRowQueryImpl rowQuery, boolean useCaching)  {
		
		switch (rowQuery.getQueryType()) {
		
		case AllColumns:
			return SelectEntireRow.getBoundStatement(rowQuery, useCaching);
		case ColumnSlice:
			return SelectColumnSlice.getBoundStatement(rowQuery, useCaching);
		case ColumnRange:
			throw new RuntimeException("Cannot perform col range query with current schema, missing pk cols");
		default :
			throw new RuntimeException("Flat table RowQuery use case not supported. Fix this!!");
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy