Class: Cequel::Record::RecordSet
- Inherits:
-
SimpleDelegator
- Object
- SimpleDelegator
- Cequel::Record::RecordSet
- Extended by:
- Util::HashAccessors, Forwardable
- Includes:
- BulkWrites, Enumerable
- Defined in:
- lib/cequel/record/record_set.rb
Overview
This class represents a subset of records from a particular table. Record sets encapsulate a CQL query, and are constructed using a chained builder interface.
The primary mechanism for specifying which rows should be returned by a CQL query is by specifying values for one or more primary key columns. A record set acts like a deeply-nested hash, where each primary key column is a level of nesting. The #[] method is used to narrow the result set by successive primary key values.
If #[] is used successively to specify all of the columns of a primary key, the result will be a single Cequel::Record or a LazyRecordCollection, depending on whether multiple values were specified for one of the key columns. In either case, the record instances will be unloaded.
Certain methods have behavior that is dependent on which primary keys have been specified using #[]. In many methods, such as #[], #values_at, #before, #after, #from, #upto, and #in, the *first unscoped primary key column* serves as implicit context for the method: the value passed to those methods is an exact or bounding value for that column.
CQL does not allow ordering by arbitrary columns; the ordering of a table is determined by its clustering column(s). You read records in reverse clustering order using #reverse.
Record sets are enumerable collections; under the hood, results are paginated. This pagination can be made explicit using #find_in_batches. RecordSets do not store their records in memory; each time #each or an ‘Enumerable` method is called, the database is queried.
All ‘RecordSet` methods are also exposed directly on Cequel::Record classes. So, for instance, `Post.limit(10)` or `Post.select(:id, :title)` work as expected.
Conversely, you may call any class method of a record class on a record set that targets that class. The class method will be executed in the context of the record set that the method is called on. See below for examples.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#target_class ⇒ Class
readonly
The Record class that this collection yields instances of.
Class Method Summary collapse
Instance Method Summary collapse
- #==(other) ⇒ Object
-
#[](*primary_key_value) ⇒ RecordSet, Record
(also: #/)
Restrict this record set to a given value for the next unscoped primary key column.
-
#after(start_key) ⇒ RecordSet
Restrict records to ones whose value in the first unscoped primary key column are strictly greater than the given start_key.
-
#all ⇒ RecordSet
Self.
- #assert_fully_specified! ⇒ Object
-
#at(*scoped_key_values) ⇒ RecordSet, Record
deprecated
Deprecated.
Use #[] instead
-
#before(end_key) ⇒ RecordSet
Restrict records to ones whose value in the first unscoped primary key column are strictly less than the given end_key.
-
#count ⇒ Integer
(also: #length, #size)
The total number of records in this record set.
-
#data_set ⇒ Cequel::Metal::DataSet
The data set underlying this record set.
-
#delete_all ⇒ void
Delete all matched records without executing callbacks.
-
#each {|record| ... } ⇒ Enumerator, void
Enumerate over the records in this record set.
-
#find(*keys) ⇒ Record, LazyRecordCollection
Return a loaded Record or collection of loaded Records with the specified primary key values.
-
#find_each(options = {}) {|record| ... } ⇒ Enumerator, void
Enumerate over the records in this record set, with control over how the database is queried.
-
#find_each_row(options = {}) {|row| ... } ⇒ Enumerator, void
Enumerate over the row data for each record in this record set, without hydrating an actual Cequel::Record instance.
-
#find_in_batches(options = {}) {|batch| ... } ⇒ Enumerator, void
Enumerate over the records in this record set in batches.
-
#find_rows_in_batches(options = {}) {|batch| ... } ⇒ Enumerator, void
Enumerate over batches of row data for the records in this record set.
- #first(count = nil) ⇒ Record, Array
-
#from(start_key) ⇒ RecordSet
Restrict records to those whose value in the first unscoped primary key column are greater than or equal to the given start key.
-
#in(range) ⇒ RecordSet
Restrict records to those whose value in the first unscoped primary key column are in the given range.
-
#initialize(target_class, attributes = {}) ⇒ RecordSet
constructor
private
A new instance of RecordSet.
- #last(count = nil) ⇒ Record, Array
-
#limit(count) ⇒ RecordSet
Restrict the number of records that the RecordSet can contain.
-
#reverse ⇒ RecordSet
Reverse the order in which records will be returned from the record set.
-
#scoped_key_attributes ⇒ Hash
Map of key column names to the values that have been specified in this record set.
- #select(*columns) ⇒ Array, RecordSet
-
#upto(end_key) ⇒ RecordSet
Restrict records to those whose value in the first unscoped primary key column are less than or equal to the given start key.
-
#values_at(*primary_key_values) ⇒ RecordSet, LazyRecordCollection
Restrict the records in this record set to those containing any of a set of values.
-
#where(column_name, value) ⇒ RecordSet
Filter the record set to records containing a given value in an indexed column.
Methods included from Util::HashAccessors
hattr_accessor, hattr_inquirer, hattr_reader, hattr_writer
Methods included from BulkWrites
Constructor Details
#initialize(target_class, attributes = {}) ⇒ RecordSet
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns a new instance of RecordSet.
123 124 125 126 127 |
# File 'lib/cequel/record/record_set.rb', line 123 def initialize(target_class, attributes = {}) attributes = self.class.default_attributes.merge!(attributes) @target_class, @attributes = target_class, attributes super(target_class) end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args, &block) ⇒ Object (private)
780 781 782 |
# File 'lib/cequel/record/record_set.rb', line 780 def method_missing(method, *args, &block) target_class.with_scope(self) { super } end |
Instance Attribute Details
#target_class ⇒ Class (readonly)
Returns the Record class that this collection yields instances of.
114 115 116 |
# File 'lib/cequel/record/record_set.rb', line 114 def target_class @target_class end |
Class Method Details
.default_attributes ⇒ Object
108 109 110 |
# File 'lib/cequel/record/record_set.rb', line 108 def self.default_attributes {scoped_key_values: [], select_columns: []} end |
Instance Method Details
#==(other) ⇒ Object
627 628 629 |
# File 'lib/cequel/record/record_set.rb', line 627 def ==(other) entries == other.to_a end |
#[](*primary_key_value) ⇒ RecordSet, Record Also known as: /
Accepting multiple arguments is deprecated behavior. Use #values_at instead.
Restrict this record set to a given value for the next unscoped primary key column
Record sets can be thought of like deeply-nested hashes, where each primary key column is a level of nesting. For instance, if a table consists of a single record with primary key ‘(blog_subdomain, permalink) = (“cassandra”, “cequel”)`, the record set can be thought of like so:
“‘ruby {
"cassandra" => {
"cequel" => #<Post blog_subdomain: "cassandra",
permalink: "cequel", title: "Cequel">
}
} “‘
If ‘[]` is invoked enough times to specify all primary keys, then an unloaded `Record` instance is returned; this is the same behavior you would expect from a `Hash`. If only some subset of the primary keys have been specified, the result is still a `RecordSet`.
268 269 270 271 272 273 274 275 276 277 278 279 280 |
# File 'lib/cequel/record/record_set.rb', line 268 def [](*primary_key_value) if primary_key_value.many? warn "Calling #[] with multiple arguments is deprecated. Use " \ "#values_at" return values_at(*primary_key_value) end primary_key_value = cast_range_key(primary_key_value.first) scope_and_resolve do |attributes| attributes[:scoped_key_values] << primary_key_value end end |
#after(start_key) ⇒ RecordSet
Restrict records to ones whose value in the first unscoped primary key column are strictly greater than the given start_key.
363 364 365 |
# File 'lib/cequel/record/record_set.rb', line 363 def after(start_key) scoped(lower_bound: bound(true, false, start_key)) end |
#all ⇒ RecordSet
Returns self.
132 133 134 |
# File 'lib/cequel/record/record_set.rb', line 132 def all self end |
#assert_fully_specified! ⇒ Object
618 619 620 621 622 |
# File 'lib/cequel/record/record_set.rb', line 618 def assert_fully_specified! raise ArgumentError, "Missing key component(s) " \ "#{unscoped_key_names.join(', ')}" end |
#at(*scoped_key_values) ⇒ RecordSet, Record
Use #[] instead
Scope to values for one or more primary key columns
225 226 227 228 |
# File 'lib/cequel/record/record_set.rb', line 225 def at(*scoped_key_values) warn "`at` is deprecated. Use `[]` instead" traverse(*scoped_key_values) end |
#before(end_key) ⇒ RecordSet
Restrict records to ones whose value in the first unscoped primary key column are strictly less than the given end_key.
376 377 378 |
# File 'lib/cequel/record/record_set.rb', line 376 def before(end_key) scoped(upper_bound: bound(false, false, end_key)) end |
#count ⇒ Integer Also known as: length, size
Returns the total number of records in this record set.
490 491 492 |
# File 'lib/cequel/record/record_set.rb', line 490 def count data_set.count end |
#data_set ⇒ Cequel::Metal::DataSet
Returns the data set underlying this record set.
596 597 598 |
# File 'lib/cequel/record/record_set.rb', line 596 def data_set @data_set ||= construct_data_set end |
#delete_all ⇒ void
This method returns an undefined value.
Delete all matched records without executing callbacks
609 610 611 612 613 614 615 |
# File 'lib/cequel/record/record_set.rb', line 609 def delete_all if partition_specified? data_set.delete else super end end |
#each {|record| ... } ⇒ Enumerator, void
Enumerate over the records in this record set
505 506 507 |
# File 'lib/cequel/record/record_set.rb', line 505 def each(&block) find_each(&block) end |
#find(*keys) ⇒ Record, LazyRecordCollection
Return a loaded Record or collection of loaded Records with the specified primary key values
Multiple arguments are mapped onto unscoped key columns. To specify multiple values for a given key column, use an array.
346 347 348 349 350 351 352 |
# File 'lib/cequel/record/record_set.rb', line 346 def find(*keys) return super if block_given? keys = [keys] if almost_fully_specified? && keys.many? records = traverse(*keys).assert_fully_specified!.load! force_array = keys.any? { |value| value.is_a?(Array) } force_array ? Array.wrap(records) : records end |
#find_each(options = {}) {|record| ... } ⇒ Enumerator, void
Enumerate over the records in this record set, with control over how the database is queried
520 521 522 523 |
# File 'lib/cequel/record/record_set.rb', line 520 def find_each( = {}) return enum_for(:find_each, ) unless block_given? find_each_row() { |row| yield target_class.hydrate(row) } end |
#find_each_row(options = {}) {|row| ... } ⇒ Enumerator, void
Enumerate over the row data for each record in this record set, without hydrating an actual Cequel::Record instance. Useful for operations where speed is at a premium.
556 557 558 559 |
# File 'lib/cequel/record/record_set.rb', line 556 def find_each_row( = {}, &block) return enum_for(:find_each_row, ) unless block find_rows_in_batches() { |rows| rows.each(&block) } end |
#find_in_batches(options = {}) {|batch| ... } ⇒ Enumerator, void
Enumerate over the records in this record set in batches. Note that the given batch_size controls the maximum number of records that can be returned per query, but no batch is guaranteed to be exactly the given ‘batch_size`
536 537 538 539 540 541 |
# File 'lib/cequel/record/record_set.rb', line 536 def find_in_batches( = {}) return enum_for(:find_in_batches, ) unless block_given? find_rows_in_batches() do |rows| yield rows.map { |row| target_class.hydrate(row) } end end |
#find_rows_in_batches(options = {}) {|batch| ... } ⇒ Enumerator, void
Enumerate over batches of row data for the records in this record set.
573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 |
# File 'lib/cequel/record/record_set.rb', line 573 def find_rows_in_batches( = {}, &block) return find_rows_in_single_batch(, &block) if row_limit .assert_valid_keys(:batch_size) batch_size = .fetch(:batch_size, 1000) batch_record_set = base_record_set = limit(batch_size) more_results = true while more_results rows = batch_record_set.find_rows_in_single_batch yield rows if rows.any? more_results = rows.length == batch_size last_row = rows.last if more_results find_nested_batches_from(last_row, , &block) batch_record_set = base_record_set.next_batch_from(last_row) end end end |
#first ⇒ Record #first(count) ⇒ Array
466 467 468 |
# File 'lib/cequel/record/record_set.rb', line 466 def first(count = nil) count ? limit(count).entries : limit(1).each.first end |
#from(start_key) ⇒ RecordSet
Restrict records to those whose value in the first unscoped primary key column are greater than or equal to the given start key.
411 412 413 414 415 416 417 418 |
# File 'lib/cequel/record/record_set.rb', line 411 def from(start_key) unless partition_specified? fail IllegalQuery, "Can't construct exclusive range on partition key " \ "#{range_key_name}" end scoped(lower_bound: bound(true, true, start_key)) end |
#in(range) ⇒ RecordSet
Restrict records to those whose value in the first unscoped primary key column are in the given range. Will accept both inclusive ranges (‘1..5`) and end-exclusive ranges (`1…5`). If you need a range with an exclusive start value, use #after, which can be combined with #before or #from to create a range.
395 396 397 398 399 400 |
# File 'lib/cequel/record/record_set.rb', line 395 def in(range) scoped( lower_bound: bound(true, true, range.first), upper_bound: bound(false, !range.exclude_end?, range.last) ) end |
#last ⇒ Record #last(count) ⇒ Array
481 482 483 484 485 |
# File 'lib/cequel/record/record_set.rb', line 481 def last(count = nil) reverse.first(count).tap do |results| results.reverse! if count end end |
#limit(count) ⇒ RecordSet
Restrict the number of records that the RecordSet can contain.
175 176 177 |
# File 'lib/cequel/record/record_set.rb', line 175 def limit(count) scoped(row_limit: count) end |
#reverse ⇒ RecordSet
This method can only be called on record sets whose partition key columns are fully specified. See #[] for a discussion of partition key scoping.
Reverse the order in which records will be returned from the record set
447 448 449 450 451 452 453 454 |
# File 'lib/cequel/record/record_set.rb', line 447 def reverse unless partition_specified? fail IllegalQuery, "Can't reverse without scoping to partition key " \ "#{range_key_name}" end scoped(reversed: !reversed?) end |
#scoped_key_attributes ⇒ Hash
Returns map of key column names to the values that have been specified in this record set.
604 605 606 |
# File 'lib/cequel/record/record_set.rb', line 604 def scoped_key_attributes Hash[scoped_key_columns.map { |col| col.name }.zip(scoped_key_values)] end |
#select {|record| ... } ⇒ Array #select(*columns) ⇒ RecordSet
160 161 162 163 |
# File 'lib/cequel/record/record_set.rb', line 160 def select(*columns) return super if block_given? scoped { |attributes| attributes[:select_columns].concat(columns) } end |
#upto(end_key) ⇒ RecordSet
Restrict records to those whose value in the first unscoped primary key column are less than or equal to the given start key.
429 430 431 432 433 434 435 436 |
# File 'lib/cequel/record/record_set.rb', line 429 def upto(end_key) unless partition_specified? fail IllegalQuery, "Can't construct exclusive range on partition key " \ "#{range_key_name}" end scoped(upper_bound: bound(false, true, end_key)) end |
#values_at(*primary_key_values) ⇒ RecordSet, LazyRecordCollection
Restrict the records in this record set to those containing any of a set of values
298 299 300 301 302 303 304 305 306 307 308 309 310 |
# File 'lib/cequel/record/record_set.rb', line 298 def values_at(*primary_key_values) unless next_unscoped_key_column_valid_for_in_query? fail IllegalQuery, "Only the last partition key column and the last clustering " \ "column can match multiple values" end primary_key_values = primary_key_values.map(&method(:cast_range_key)) scope_and_resolve do |attributes| attributes[:scoped_key_values] << primary_key_values end end |
#where(column_name, value) ⇒ RecordSet
This should only be used with data columns that have secondary indexes. To filter a record set using a primary key, use #[]
Only one secondary index filter can be used in a given query
Secondary index filters cannot be mixed with primary key filters
Filter the record set to records containing a given value in an indexed column
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/cequel/record/record_set.rb', line 196 def where(column_name, value) column = target_class.reflect_on_column(column_name) if scoped_indexed_column fail IllegalQuery, "Can't scope by more than one indexed column in the same query" end unless column fail ArgumentError, "No column #{column_name} configured for #{target_class.name}" end unless column.data_column? fail ArgumentError, "Use the `at` method to restrict scope by primary key" end unless column.indexed? fail ArgumentError, "Can't scope by non-indexed column #{column_name}" end scoped(scoped_indexed_column: {column_name => column.cast(value)}) end |