Module: Cassie::Model::ClassMethods
- Defined in:
- lib/cassie/model.rb
Instance Method Summary collapse
-
#batch(options = nil) ⇒ Object
All insert, update, and delete calls within the block will be sent as a single batch to Cassandra.
-
#column(name, type, as: nil) ⇒ Object
Define a column name and type from the table.
-
#column_name(name_or_alias) ⇒ Object
Returns the internal column name after resolving any aliases.
-
#column_names ⇒ Object
Returns an array of the defined column names as symbols.
-
#connection ⇒ Object
Returns the Cassie instance used to communicate with Cassandra.
-
#count(where = nil) ⇒ Object
Return the count of rows in the table.
-
#create(attributes) ⇒ Object
Returns a newly created record.
-
#create!(attributes) ⇒ Object
Returns a newly created record or raises an ActiveRecord::RecordInvalid error if the record is not valid.
-
#delete_all(key_hash) ⇒ Object
Delete all rows from the table that match the key hash.
-
#find(where) ⇒ Object
Find a single record that matches the
whereargument. -
#find!(where) ⇒ Object
Find a single record that matches the
whereargument or raise an ActiveRecord::RecordNotFound error if none is found. -
#find_all(where:, select: nil, order: nil, limit: nil, options: nil) ⇒ Object
Find all records.
-
#full_table_name ⇒ Object
Return the full table name including the keyspace.
-
#keyspace ⇒ Object
Return the keyspace name where the table is located.
-
#keyspace=(name) ⇒ Object
Set the keyspace for the table.
-
#offset_to_id(key, offset, order: nil, batch_size: 1000, min: nil, max: nil) ⇒ Object
Since Cassandra doesn’t support offset we need to find the order key of record at the specified the offset.
-
#ordering_key(name, order) ⇒ Object
Define and ordering key for the table.
-
#primary_key ⇒ Object
Return an array of column names for the table primary key.
-
#primary_key=(value) ⇒ Object
Set the primary key for the table.
Instance Method Details
#batch(options = nil) ⇒ Object
All insert, update, and delete calls within the block will be sent as a single batch to Cassandra. The consistency level will default to the write consistency level if it’s been set.
335 336 337 338 339 340 |
# File 'lib/cassie/model.rb', line 335 def batch( = nil) = (write_consistency, ) connection.batch() do yield end end |
#column(name, type, as: nil) ⇒ Object
Define a column name and type from the table. Columns must be defined in order to be used. This method will handle defining the getter and setter methods as well.
The type specified must be a valid CQL data type.
Because Cassandra stores column names with each row it is beneficial to use very short column names. You can specify the :as option to define a more human readable version. This will add the appropriate getter and setter methods as well as allow you to use the alias name in the methods that take an attributes hash.
Defining a column will also define getter and setter methods for both the column name and the alias name (if specified). So ‘column :i, :int, as: :id` will define the methods `i`, `i=`, `id`, and `id=`.
If you define a counter column then it will define methods for ‘increment_i!` and `decrement_i!` which take an optional amount argument. Note that if you have a counter column you cannot have any other non-primary key columns and you cannot call create, update, or save and must use the increment and decrement commands.
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/cassie/model.rb', line 100 def column(name, type, as: nil) name = name.to_sym type_class = nil type_name = type.to_s.downcase.classify # Backward compatibility with older driver versions. type_name = "Text" if type_name == "Varchar" begin type_class = "Cassandra::Types::#{type_name}".constantize rescue NameError raise ArgumentError.new("#{type.inspect} is not an allowed Cassandra type") end self._columns = _columns.merge(name => type_class) self._column_aliases = self._column_aliases.merge(name => name) aliased = (as && as.to_s != name.to_s) if aliased self._column_aliases = self._column_aliases.merge(as => name) end if type.to_s == "counter".freeze self._counter_table = true define_method(name){ instance_variable_get(:"@#{name}") || 0 } define_method("#{name}="){ |value| instance_variable_set(:"@#{name}", value.to_i) } define_method("increment_#{name}!"){ |amount=1, ttl: nil| send(:adjust_counter!, name, amount, ttl: ttl) } define_method("decrement_#{name}!"){ |amount=1, ttl: nil| send(:adjust_counter!, name, -amount, ttl: ttl) } if aliased define_method(as){ send(name) } define_method("increment_#{as}!"){ |amount=1, ttl: nil| send("increment_#{name}!", amount, ttl: ttl) } define_method("decrement_#{as}!"){ |amount=1, ttl: nil| send("increment_#{name}!", amount, ttl: ttl) } end else attr_reader name define_method("#{name}="){ |value| instance_variable_set(:"@#{name}", self.class.send(:coerce, value, type_class)) } attr_reader name if aliased define_method(as){ send(name) } define_method("#{as}="){|value| send("#{name}=", value) } end end end |
#column_name(name_or_alias) ⇒ Object
Returns the internal column name after resolving any aliases.
150 151 152 |
# File 'lib/cassie/model.rb', line 150 def column_name(name_or_alias) name = _column_aliases[name_or_alias] || name_or_alias end |
#column_names ⇒ Object
Returns an array of the defined column names as symbols.
145 146 147 |
# File 'lib/cassie/model.rb', line 145 def column_names _columns.keys end |
#connection ⇒ Object
Returns the Cassie instance used to communicate with Cassandra.
343 344 345 |
# File 'lib/cassie/model.rb', line 343 def connection Cassie.instance end |
#count(where = nil) ⇒ Object
Return the count of rows in the table. If the where argument is specified then it will be added as the WHERE clause.
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
# File 'lib/cassie/model.rb', line 285 def count(where = nil) = nil if where.is_a?(Hash) && where.include?(:options) where = where.dup = where.delete(:options) end cql = "SELECT COUNT(*) FROM #{self.full_table_name}" values = nil if where where_clause, values = cql_where_clause(where) cql << " WHERE #{where_clause}" else where = connection.prepare(cql) end results = connection.find(cql, values, (read_consistency, )) results.rows.first["count"] end |
#create(attributes) ⇒ Object
Returns a newly created record. If the record is not valid then it won’t be persisted.
308 309 310 311 312 |
# File 'lib/cassie/model.rb', line 308 def create(attributes) record = new(attributes) record.save record end |
#create!(attributes) ⇒ Object
Returns a newly created record or raises an ActiveRecord::RecordInvalid error if the record is not valid.
316 317 318 319 320 |
# File 'lib/cassie/model.rb', line 316 def create!(attributes) record = new(attributes) record.save! record end |
#delete_all(key_hash) ⇒ Object
Delete all rows from the table that match the key hash. This method bypasses any destroy callbacks defined on the model.
324 325 326 327 328 329 330 |
# File 'lib/cassie/model.rb', line 324 def delete_all(key_hash) cleanup_up_hash = {} key_hash.each do |name, value| cleanup_up_hash[column_name(name)] = value end connection.delete(full_table_name, cleanup_up_hash, :consistency => write_consistency) end |
#find(where) ⇒ Object
Find a single record that matches the where argument.
266 267 268 269 270 271 272 273 |
# File 'lib/cassie/model.rb', line 266 def find(where) = nil if where.is_a?(Hash) && where.include?(:options) where = where.dup = where.delete(:options) end find_all(where: where, limit: 1, options: ).first end |
#find!(where) ⇒ Object
Find a single record that matches the where argument or raise an ActiveRecord::RecordNotFound error if none is found.
277 278 279 280 281 |
# File 'lib/cassie/model.rb', line 277 def find!(where) record = find(where) raise Cassie::RecordNotFound unless record record end |
#find_all(where:, select: nil, order: nil, limit: nil, options: nil) ⇒ Object
Find all records.
The where argument can be a Hash, Array, or String WHERE clause to filter the rows returned. It is required so that you don’t accidentally release code that returns all rows. If you really want to select all rows from a table you can specify the value :all.
The select argument can be used to limit which columns are returned and should be passed as an array of column names which can include aliases.
The order argument is a CQL fragment indicating the order. Note that Cassandra will only allow ordering by rows in the primary key.
The limit argument specifies how many rows to return.
You can provide a block to this method in which case it will yield each record as it is foundto the block instead of returning them.
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/cassie/model.rb', line 216 def find_all(where:, select: nil, order: nil, limit: nil, options: nil) start_time = Time.now columns = (select ? Array(select).collect{|c| column_name(c)} : column_names) cql = "SELECT #{columns.join(', ')} FROM #{full_table_name}" values = nil raise ArgumentError.new("Where clause cannot be blank. Pass :all to find all records.") if where.blank? if where && where != :all where_clause, values = cql_where_clause(where) else values = [] end cql << " WHERE #{where_clause}" if where_clause if order cql << " ORDER BY #{order}" end if limit cql << " LIMIT ?" values << Integer(limit) end results = connection.find(cql, values, (read_consistency, )) records = [] unless block_given? row_count = 0 loop do row_count += results.size results.each do |row| record = new(row) record.instance_variable_set(:@persisted, true) if block_given? yield record else records << record end end break if results.last_page? results = results.next_page end if find_subscribers && !find_subscribers.empty? payload = FindMessage.new(cql, values, , Time.now - start_time, row_count) find_subscribers.each{|subscriber| subscriber.call(payload)} end records end |
#full_table_name ⇒ Object
Return the full table name including the keyspace.
191 192 193 194 195 196 197 |
# File 'lib/cassie/model.rb', line 191 def full_table_name if _keyspace "#{keyspace}.#{table_name}" else table_name end end |
#keyspace ⇒ Object
Return the keyspace name where the table is located.
186 187 188 |
# File 'lib/cassie/model.rb', line 186 def keyspace connection.config.keyspace(_keyspace) end |
#keyspace=(name) ⇒ Object
Set the keyspace for the table. The name should be an abstract keyspace name that is mapped to an actual keyspace name in the configuration. If the name provided is not mapped in the configuration, then the raw value will be used.
181 182 183 |
# File 'lib/cassie/model.rb', line 181 def keyspace=(name) self._keyspace = name.to_s end |
#offset_to_id(key, offset, order: nil, batch_size: 1000, min: nil, max: nil) ⇒ Object
Since Cassandra doesn’t support offset we need to find the order key of record at the specified the offset.
The key is a Hash describing the primary keys to search minus the last column defined for the primary key. This column is assumed to be an ordering key. If it isn’t, this method will fail.
The order argument can be used to specify an order for the ordering key (:asc or :desc). It will default to the natural order of the last ordering key as defined by the ordering_key method.
The min and max can be used to limit the offset calculation to a range of values (exclusive).
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 |
# File 'lib/cassie/model.rb', line 358 def offset_to_id(key, offset, order: nil, batch_size: 1000, min: nil, max: nil) ordering_key = primary_key.last cluster_order = _ordering_keys[ordering_key] || :asc order ||= cluster_order order_cql = "#{ordering_key} #{order}" unless order == cluster_order from = (order == :desc ? max : min) to = (order == :desc ? min : max) loop do limit = (offset > batch_size ? batch_size : offset + 1) conditions_cql = [] conditions = [] if from conditions_cql << "#{ordering_key} #{order == :desc ? '<' : '>'} ?" conditions << from end if to conditions_cql << "#{ordering_key} #{order == :desc ? '>' : '<'} ?" conditions << to end key.each do |name, value| conditions_cql << "#{column_name(name)} = ?" conditions << value end conditions.unshift(conditions_cql.join(" AND ")) results = find_all(:select => [ordering_key], :where => conditions, :limit => limit, :order => order_cql) last_row = results.last if results.size == limit last_id = last_row.send(ordering_key) if last_row if last_id.nil? return nil elsif limit >= offset return last_id else offset -= results.size from = last_id end end end |
#ordering_key(name, order) ⇒ Object
Define and ordering key for the table. The order attribute should be either :asc or :desc
172 173 174 175 176 |
# File 'lib/cassie/model.rb', line 172 def ordering_key(name, order) order = order.to_sym raise ArgumentError.new("order must be either :asc or :desc") unless order == :asc || order == :desc _ordering_keys[name.to_sym] = order end |
#primary_key ⇒ Object
Return an array of column names for the table primary key.
167 168 169 |
# File 'lib/cassie/model.rb', line 167 def primary_key _primary_key end |
#primary_key=(value) ⇒ Object
Set the primary key for the table. The value should be set as an array with the clustering key first.
156 157 158 159 160 161 162 163 164 |
# File 'lib/cassie/model.rb', line 156 def primary_key=(value) self._primary_key = Array(value).map { |column| if column.is_a?(Array) column.map(&:to_sym) else column.to_sym end }.flatten end |