module DatastaxRails::Schema::Cassandra

Methods for managing the Cassandra schema

Public Instance Methods

cassandra_index_cql_name(cf, column) click to toggle source

Computes the expected cassandra index name as reported by CQL.

# File lib/datastax_rails/schema/cassandra.rb, line 103
def cassandra_index_cql_name(cf, column)
  "#{cf}_#{column}_idx"
end
check_missing_schema(model) click to toggle source

Check for missing columns or columns needing cassandra indexes

# File lib/datastax_rails/schema/cassandra.rb, line 6
def check_missing_schema(model) # rubocop:disable MethodLength
  count = 0
  model.attribute_definitions.each do |attribute, definition|
    unless column_exists?(model.column_family.to_s, attribute.to_s)
      count += 1
      say "Adding column '#{attribute}'", :subitem
      DatastaxRails::Cql::AlterColumnFamily.new(model.column_family).add(attribute => definition.cql_type).execute
    end
    if definition.options[:cql_index] && !definition.options[:solr_index]
      unless index_exists?(model.column_family.to_s, attribute.to_s)
        if index_exists?(model.column_family.to_s, attribute.to_s)
          count += 1
          say "Dropping solr index on #{attribute}", :subitem
          DatastaxRails::Cql::DropIndex.new(solr_index_cql_name(model.column_family.to_s, attribute.to_s)).execute
        end
        count += 1
        say "Creating cassandra index on #{attribute}", :subitem
        DatastaxRails::Cql::CreateIndex.new(
          cassandra_index_cql_name(
            model.column_family.to_s, attribute.to_s)
        ).on(model.column_family.to_s).column(attribute.to_s).execute
      end
    elsif definition.options[:cql_index]
      unless column_exists?(model.column_family.to_s, "__#{attribute}")
        # Create and populate the new column
        count += 1
        say "Adding column '__#{attribute}'", :subitem
        DatastaxRails::Cql::AlterColumnFamily.new(model.column_family)
          .add("__#{attribute}" => definition.cql_type).execute
        say "Populating column '__#{attribute}' (this might take a while)", :subitem
        export = "echo \"copy #{model.column_family} (key, #{attribute}) " \
                 "TO 'dsr_export.csv';\" | cqlsh #{model.current_server}"
        import = "echo \"copy #{model.column_family} (key, __#{attribute}) " \
                 "FROM 'dsr_export.csv';\" | cqlsh #{model.current_server}"
        if system(export)
          system(import)
        else
          @errors << "Looks like you don't have a working cqlsh command in your path.\n" \
                     "Run the following two commands from a server with cqlsh:\n\n#{export}\n#{import}"
        end
      end
      count += 1
      say "Creating cassandra index on __#{attribute}", :subitem
      DatastaxRails::Cql::CreateIndex.new(
        cassandra_index_cql_name(
          model.column_family.to_s, "__#{attribute}")
      ).on(model.column_family.to_s).column("__#{attribute}").execute
    end
  end
  count
end
column_exists?(cf, col) click to toggle source

Checks the Cassandra system tables to see if a column exists on a column family

# File lib/datastax_rails/schema/cassandra.rb, line 124
def column_exists?(cf, col)
  klass = OpenStruct.new(column_family: 'system.schema_columns', default_consistency: 'QUORUM')
  cql = DatastaxRails::Cql::ColumnFamily.new(klass)
  results = cql.select('count(*)')
            .conditions('keyspace_name' => @keyspace, 'columnfamily_name' => cf, 'column_name' => col).execute
  exists = results.first['count'] > 0
  unless exists
    # We need to check if it's part of an alias (ugh)
    klass = OpenStruct.new(column_family: 'system.schema_columnfamilies', default_consistency: 'QUORUM')
    cql = DatastaxRails::Cql::ColumnFamily.new(klass)
    results = cql.select('column_aliases, key_aliases, value_alias')
              .conditions('keyspace_name' => @keyspace, 'columnfamily_name' => cf).execute
    row = results.first
    exists = row['key_aliases'].include?(col.to_s) ||
             row['column_aliases'].include?(col.to_s) ||
             (row['value_alias'] && row['value_alias'].include?(col.to_s))
  end
  exists
end
column_family_exists?(cf) click to toggle source

Checks the Cassandra system tables to see if a column family exists

# File lib/datastax_rails/schema/cassandra.rb, line 116
def column_family_exists?(cf)
  klass = OpenStruct.new(column_family: 'system.schema_columnfamilies', default_consistency: 'QUORUM')
  cql = DatastaxRails::Cql::ColumnFamily.new(klass)
  results = cql.select('count(*)').conditions('keyspace_name' => @keyspace, 'columnfamily_name' => cf).execute
  results.first['count'] > 0
end
create_cql3_column_family(model) click to toggle source

Creates a CQL3 backed column family

# File lib/datastax_rails/schema/cassandra.rb, line 59
def create_cql3_column_family(model)
  say 'Creating Column Family via CQL3', :subitem
  columns = {}
  model.attribute_definitions.each { |k, col| columns[k] = col.cql_type }
  pk = model.primary_key.to_s
  if model.respond_to?(:cluster_by) && model.cluster_by.present?
    pk += ", #{model.cluster_by}"
  end
  cql = DatastaxRails::Cql::CreateColumnFamily.new(model.column_family).primary_key(pk).columns(columns)
  cql.with(model.create_options) if model.create_options
  cql.execute
end
create_keyspace(keyspace, options = {}) click to toggle source

Creates the named keyspace

# File lib/datastax_rails/schema/cassandra.rb, line 73
def create_keyspace(keyspace, options = {})
  opts = { name:           keyspace.to_s,
           strategy_class: 'org.apache.cassandra.locator.NetworkTopologyStrategy' }
         .with_indifferent_access.merge(options)

  if keyspace_exists?(keyspace.to_s)
    say "Keyspace #{keyspace} already exists"
    return false
  else
    cql = DatastaxRails::Cql::CreateKeyspace.new(opts.delete(:name))
    cql.strategy_class(opts.delete(:strategy_class))
    strategy_options = opts.delete('strategy_options')
    cql.strategy_options(strategy_options.symbolize_keys)
    say "Creating keyspace #{keyspace}"
    cql.execute
    return true
  end
end
drop_keyspace() click to toggle source
# File lib/datastax_rails/schema/cassandra.rb, line 92
def drop_keyspace
  say "Dropping keyspace #{@keyspace}"
  DatastaxRails::Cql::DropKeyspace.new(@keyspace.to_s).execute
end
index_exists?(cf, col) click to toggle source

Checks the Cassandra system tables to see if an index exists on a column family

# File lib/datastax_rails/schema/cassandra.rb, line 145
def index_exists?(cf, col)
  klass = OpenStruct.new(column_family: 'system.schema_columns', default_consistency: 'QUORUM')
  cql = DatastaxRails::Cql::ColumnFamily.new(klass)
  results = cql.select('index_name')
            .conditions('keyspace_name' => @keyspace, 'columnfamily_name' => cf, 'column_name' => col).execute
  results.first['index_name'] != nil
end
keyspace_exists?(keyspace) click to toggle source

Checks the Cassandra system tables to see if a keyspace exists

# File lib/datastax_rails/schema/cassandra.rb, line 108
def keyspace_exists?(keyspace)
  klass = OpenStruct.new(column_family: 'system.schema_keyspaces', default_consistency: 'QUORUM')
  cql = DatastaxRails::Cql::ColumnFamily.new(klass)
  results = cql.select('count(*)').conditions('keyspace_name' => keyspace).execute
  results.first['count'].to_i > 0
end
solr_index_cql_name(cf, column) click to toggle source

Computes the expected solr index name as reported by CQL.

# File lib/datastax_rails/schema/cassandra.rb, line 98
def solr_index_cql_name(cf, column)
  "#{@keyspace}_#{cf}_#{column}_index"
end