class DatastaxRails::Column
Contains the metadata for a given column. Also provides a number of helper methods to take cast values to the appropriate types (i.e., Ruby, CQL, Solr)
Constants
- FALSE_VALUES
- TRUE_VALUES
Attributes
Public Class Methods
Used to convert from BLOBs to Strings
# File lib/datastax_rails/column.rb, line 245 def binary_to_string(value) # TODO: Figure out what Cassandra's blobs look like value end
Instantiates a new column in the table.
name
is the column's name as specified in the schema. e.g., 'first_name' in first_name text
. default
is the type-casted default value that will be applied to a new record if no value is given. type
is the type of the column. Usually this will match the cql_type
, but there are exceptions (e.g., date) cql_type
is the type of column as specified in the schema. e.g., 'text' in first_name text
. solr_type
overrides the normal CQL <-> SOLR type mapping (uncommon)
# File lib/datastax_rails/column.rb, line 33 def initialize(name, default, type, options = {}) @name = name @type = type.to_sym fail ArgumentError, "Unknown type #{type}" unless klass options[:holds] = 'string' if collection? && options[:holds].blank? @options = configure_options(@type, options).with_indifferent_access @cql_type = compute_cql_type(@type, @options) @solr_type = compute_solr_type(@type, @options) @default = extract_default(default) @primary = nil @coder = nil end
Used to convert from Strings to BLOBs
# File lib/datastax_rails/column.rb, line 239 def string_to_binary(value) # TODO: Figure out what Cassandra's blobs look like value end
# File lib/datastax_rails/column.rb, line 268 def string_to_dummy_time(string) return string unless string.is_a?(String) return nil if string.empty? dummy_time_string = "2000-01-01 #{string}" fast_string_to_time(dummy_time_string) || begin time_hash = Date._parse(dummy_time_string) return nil if time_hash[:hour].nil? new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction)) end end
# File lib/datastax_rails/column.rb, line 261 def string_to_time(string) return string unless string.is_a?(String) return nil if string.empty? fast_string_to_time(string) || fallback_string_to_time(string) end
convert something to a boolean
# File lib/datastax_rails/column.rb, line 282 def value_to_boolean(value) if value.is_a?(String) && value.empty? nil else TRUE_VALUES.include?(value) end end
# File lib/datastax_rails/column.rb, line 250 def value_to_date(value) if value.is_a?(String) return nil if value.empty? fast_string_to_date(value) || fallback_string_to_date(value) elsif value.respond_to?(:to_date) value.to_date else value end end
convert something to a BigDecimal
# File lib/datastax_rails/column.rb, line 301 def value_to_decimal(value) return nil if value.blank? # Using .class is faster than .is_a? and # subclasses of BigDecimal will be handled # in the else clause if value.class == BigDecimal value elsif value.respond_to?(:to_d) value.to_d else value.to_s.to_d end end
Used to convert values to integer.
# File lib/datastax_rails/column.rb, line 291 def value_to_integer(value) case value when TrueClass, FalseClass value ? 1 : 0 else value.blank? ? nil : value.to_i rescue nil end end
convert something to a TimeUuid
# File lib/datastax_rails/column.rb, line 316 def value_to_uuid(value) if value.is_a?(::Cql::Uuid) value else ::Cql::TimeUuid.new(value) rescue nil end end
Protected Class Methods
# File lib/datastax_rails/column.rb, line 371 def fallback_string_to_date(string) new_date(*::Date._parse(string, false).values_at(:year, :mon, :mday)) end
# File lib/datastax_rails/column.rb, line 375 def fallback_string_to_time(string) time_hash = Date._parse(string) time_hash[:sec_fraction] = microseconds(time_hash) new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction)) end
# File lib/datastax_rails/column.rb, line 351 def fast_string_to_date(string) if string =~ Format::ISO_DATE new_date Regexp.last_match[1].to_i, Regexp.last_match[2].to_i, Regexp.last_match[3].to_i end end
Doesn't handle time zones.
# File lib/datastax_rails/column.rb, line 358 def fast_string_to_time(string) return unless string =~ Format::ISO_DATETIME microsec = (Regexp.last_match[7].to_r * 1_000_000).to_i new_time(Regexp.last_match[1].to_i, Regexp.last_match[2].to_i, Regexp.last_match[3].to_i, Regexp.last_match[4].to_i, Regexp.last_match[5].to_i, Regexp.last_match[6].to_i, microsec ) end
'0.123456' -> 123456 '1.123456' -> 123456
# File lib/datastax_rails/column.rb, line 328 def microseconds(time) time[:sec_fraction] ? (time[:sec_fraction] * 1_000_000).to_i : 0 end
# File lib/datastax_rails/column.rb, line 332 def new_date(year, mon, mday) year && year != 0 && Date.new(year, mon, mday) rescue nil end
# File lib/datastax_rails/column.rb, line 336 def new_time(year, mon, mday, hour, min, sec, microsec, offset = nil) # rubocop:disable Metrics/ParameterLists # Treat 0000-00-00 00:00:00 as nil. return nil if year.nil? || (year == 0 && mon == 0 && mday == 0) if offset time = Time.utc(year, mon, mday, hour, min, sec, microsec) rescue nil return nil unless time time -= offset Base.default_timezone == :utc ? time : time.getlocal else Time.public_send(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil end end
Public Instance Methods
Returns true
if the column is of type binary
# File lib/datastax_rails/column.rb, line 86 def binary? [:binary].include?(type) end
# File lib/datastax_rails/column.rb, line 90 def collection? [:set, :list, :map].include?(type) end
# File lib/datastax_rails/column.rb, line 46 def configure_options(type, options) case type.to_sym when :set, :list, :map then configure_options(options[:holds], options).merge(multi_valued: true) when :binary then { solr_index: false, solr_store: false, multi_valued: false, sortable: false, tokenized: false, fulltext: false, cql_index: false } when :boolean, :date, :time, :timestamp, :datetime, :float, :integer, :uuid, :long, :double then { solr_index: true, solr_store: true, multi_valued: false, sortable: true, tokenized: false, fulltext: false, cql_index: false } when :string then { solr_index: true, solr_store: true, multi_valued: false, sortable: true, tokenized: false, fulltext: true, cql_index: false } when :text then { solr_index: true, solr_store: true, multi_valued: false, sortable: false, tokenized: true, fulltext: true, cql_index: false } else fail ArgumentError, "Unknown Type: #{type}" end.merge(options) end
# File lib/datastax_rails/column.rb, line 94 def default? default.present? end
# File lib/datastax_rails/column.rb, line 98 def empty_value extract_default(nil) end
# File lib/datastax_rails/column.rb, line 215 def extract_default(default) default || case type when :map then {} when :list then [] when :set then Set.new else default end end
# File lib/datastax_rails/column.rb, line 229 def full_solr_range if %w(date uuid integer int double long float).include? solr_type '[* TO *]' else '[\"\" TO *]' end end
Returns the Ruby class that corresponds to the abstract data type.
# File lib/datastax_rails/column.rb, line 103 def klass case type when :integer, :long then Fixnum when :float then Float when :double then BigDecimal when :timestamp, :time, :datetime then Time when :date then Date when :text, :string, :binary, :ascii then String when :boolean then Object when :uuid then ::Cql::TimeUuid when :list then DatastaxRails::Types::DynamicList when :set then DatastaxRails::Types::DynamicSet when :map then DatastaxRails::Types::DynamicMap end end
# File lib/datastax_rails/column.rb, line 198 def list_to_cql3_value(value) value.map { |v| type_cast_for_cql3(v, @options[:holds].to_sym) } end
# File lib/datastax_rails/column.rb, line 190 def list_to_solr_value(value) value.map { |v| type_cast_for_solr(v, @options[:holds].to_sym) } end
# File lib/datastax_rails/column.rb, line 202 def map_to_cql3_value(value) value.dup.each { |k, v| value[k] = type_cast_for_cql3(v, @options[:holds].to_sym) } value end
# File lib/datastax_rails/column.rb, line 194 def map_to_solr_value(value) value.each { |k, v| value[k] = type_cast_for_solr(v, @options[:holds].to_sym) } end
Returns true
if the column is either of type integer, float or decimal.
# File lib/datastax_rails/column.rb, line 81 def number? [:double, :float, :integer, :long].include?(type) end
Used to convert from Strings to BLOBs
# File lib/datastax_rails/column.rb, line 225 def string_to_binary(value) self.class.string_to_binary(value) end
Returns true
if the column is either of type ascii or text.
# File lib/datastax_rails/column.rb, line 76 def text? [:ascii, :text].include?(type) end
Casts value (which can be a String) to an appropriate instance.
# File lib/datastax_rails/column.rb, line 120 def type_cast(value, record = nil, dest_type = nil) # rubocop:disable Metrics/CyclomaticComplexity value = @default if value.nil? return nil if value.nil? return coder.load(value) if encoded? klass = self.class case dest_type || type when :string, :text then value.to_s when :ascii then value.force_encoding('ascii') when :integer, :long then klass.value_to_integer(value) when :float then value.to_f when :decimal then klass.value_to_decimal(value) when :datetime, :timestamp then klass.string_to_time(value) when :time then klass.string_to_dummy_time(value) when :date then klass.value_to_date(value) when :binary then klass.binary_to_string(value) when :boolean then klass.value_to_boolean(value) when :uuid, :timeuuid uuid = klass.value_to_uuid(value) uuid.is_a?(::Cql::Uuid) ? uuid.to_s : uuid when :list, :set wrap_collection(Array(value).map { |v| type_cast(v, record, @options[:holds]) }.reject(&:blank?), record) when :map wrap_collection(value.each { |k, v| value[k] = type_cast(v, record, @options[:holds]) }.stringify_keys, record) else value end end
Cql-rb does a really good job of typecasting, so for the most part we just pass in the native types. The only exceptions are for UUIDs that are passed in as strings and dates.
# File lib/datastax_rails/column.rb, line 158 def type_cast_for_cql3(value, dest_type = nil) return nil if value.nil? return coder.dump(value) if encoded? case (dest_type || type) when :uuid then value.is_a?(::Cql::Uuid) ? value : self.class.value_to_uuid(value) when :time, :datetime, :timestamp then value.to_time.utc when :date then value.to_time.utc when :set then Set.new(list_to_cql3_value(value)) when :list then list_to_cql3_value(value) when :map then map_to_cql3_value(value) else value end end
By contrast, since Solr isn't doing things like prepared statements it doesn't know what the types are so we have to handle any casting or encoding ourselves.
# File lib/datastax_rails/column.rb, line 176 def type_cast_for_solr(value, dest_type = nil) return nil if value.nil? return coder.dump(value) if encoded? case (dest_type || type) when :boolean then value ? 'true' : 'false' when :date, :time, :datetime, :timestamp then value.to_time.utc.strftime(Format::SOLR_TIME_FORMAT) when :list, :set then list_to_solr_value(value) when :map then map_to_solr_value(value) when :uuid, :timeuuid then value.to_s else value end end
# File lib/datastax_rails/column.rb, line 149 def wrap_collection(collection, record) Types::DirtyCollection.ignore_modifications do klass.new(record, name, collection) end end
Private Instance Methods
# File lib/datastax_rails/column.rb, line 385 def compute_cql_type(field_type, options) options[:cql_type] || case field_type.to_sym when :integer then 'int' when :long then 'bigint' when :time, :date, :timestamp, :datetime then 'timestamp' when :binary then 'blob' when :list then "list<#{compute_cql_type(options[:holds], options)}>" when :set then "set<#{compute_cql_type(options[:holds], options)}>" when :map then "map<text, #{compute_cql_type(options[:holds], options)}>" when :string then 'text' else field_type.to_s end end
# File lib/datastax_rails/column.rb, line 400 def compute_solr_type(field_type, options) options[:solr_type] || case field_type.to_sym when :integer then 'int' when :timestamp, :time, :datetime then 'date' when :list, :set, :map then compute_solr_type(options[:holds], options) else field_type.to_s end end