Class: Cequel::Record::RecordSet
- Inherits:
-
SimpleDelegator
- Object
- SimpleDelegator
- Cequel::Record::RecordSet
- Extended by:
- Util::Forwardable, Util::HashAccessors
- 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.
-
#consistency(consistency) ⇒ RecordSet
Set the consistency at which to read records into the record set.
- #count ⇒ Object (also: #length, #size)
-
#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
-
#first! ⇒ Record
The first record.
-
#first_or_initialize ⇒ Record
The first record or a new instance.
-
#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
- #last_page? ⇒ Boolean
-
#limit(count) ⇒ RecordSet
Restrict the number of records that the RecordSet can contain.
- #next_paging_state ⇒ Object
-
#page_size(page_size) ⇒ RecordSet
Set the page_size at which to read records into the record set.
-
#paging_state(paging_state) ⇒ RecordSet
Set the paging_state at which to read records into the record set.
-
#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
- #to_ary ⇒ Object
-
#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(*args) ⇒ Object
Filter the record set to records containing a given value in an indexed column.
Methods included from Util::Forwardable
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.
124 125 126 127 128 |
# File 'lib/cequel/record/record_set.rb', line 124 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)
933 934 935 |
# File 'lib/cequel/record/record_set.rb', line 933 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.
115 116 117 |
# File 'lib/cequel/record/record_set.rb', line 115 def target_class @target_class end |
Class Method Details
.default_attributes ⇒ Object
109 110 111 |
# File 'lib/cequel/record/record_set.rb', line 109 def self.default_attributes {scoped_key_values: [], select_columns: []} end |
Instance Method Details
#==(other) ⇒ Object
683 684 685 |
# File 'lib/cequel/record/record_set.rb', line 683 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`.
269 270 271 272 273 274 275 276 277 278 279 280 281 |
# File 'lib/cequel/record/record_set.rb', line 269 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.
364 365 366 |
# File 'lib/cequel/record/record_set.rb', line 364 def after(start_key) scoped(lower_bound: bound(true, false, start_key)) end |
#all ⇒ RecordSet
Returns self.
133 134 135 |
# File 'lib/cequel/record/record_set.rb', line 133 def all self end |
#assert_fully_specified! ⇒ Object
674 675 676 677 678 |
# File 'lib/cequel/record/record_set.rb', line 674 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
226 227 228 229 |
# File 'lib/cequel/record/record_set.rb', line 226 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.
377 378 379 |
# File 'lib/cequel/record/record_set.rb', line 377 def before(end_key) scoped(upper_bound: bound(false, false, end_key)) end |
#consistency(consistency) ⇒ RecordSet
Set the consistency at which to read records into the record set.
463 464 465 |
# File 'lib/cequel/record/record_set.rb', line 463 def consistency(consistency) scoped(query_consistency: consistency) end |
#count ⇒ Object Also known as: length, size
546 547 548 |
# File 'lib/cequel/record/record_set.rb', line 546 def count raise Cequel::Record::DangerousQueryError.new end |
#data_set ⇒ Cequel::Metal::DataSet
Returns the data set underlying this record set.
652 653 654 |
# File 'lib/cequel/record/record_set.rb', line 652 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
665 666 667 668 669 670 671 |
# File 'lib/cequel/record/record_set.rb', line 665 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
561 562 563 |
# File 'lib/cequel/record/record_set.rb', line 561 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.
347 348 349 350 351 352 353 |
# File 'lib/cequel/record/record_set.rb', line 347 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
576 577 578 579 |
# File 'lib/cequel/record/record_set.rb', line 576 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.
612 613 614 615 |
# File 'lib/cequel/record/record_set.rb', line 612 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`
592 593 594 595 596 597 |
# File 'lib/cequel/record/record_set.rb', line 592 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.
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 |
# File 'lib/cequel/record/record_set.rb', line 629 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
505 506 507 |
# File 'lib/cequel/record/record_set.rb', line 505 def first(count = nil) count ? limit(count).entries : limit(1).each.first end |
#first! ⇒ Record
Returns the first record.
520 521 522 523 524 525 |
# File 'lib/cequel/record/record_set.rb', line 520 def first! first or fail(RecordNotFound, "Couldn't find record with keys: #{ scoped_key_attributes.map { |k, v| "#{k}: #{v}" }.join(', ')}") end |
#first_or_initialize ⇒ Record
Returns the first record or a new instance.
512 513 514 |
# File 'lib/cequel/record/record_set.rb', line 512 def first_or_initialize first || new 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.
412 413 414 415 416 417 418 419 |
# File 'lib/cequel/record/record_set.rb', line 412 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.
396 397 398 399 400 401 |
# File 'lib/cequel/record/record_set.rb', line 396 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
538 539 540 541 542 |
# File 'lib/cequel/record/record_set.rb', line 538 def last(count = nil) reverse.first(count).tap do |results| results.reverse! if count end end |
#last_page? ⇒ Boolean
491 492 493 |
# File 'lib/cequel/record/record_set.rb', line 491 def last_page? data_set.last_page? end |
#limit(count) ⇒ RecordSet
Restrict the number of records that the RecordSet can contain.
176 177 178 |
# File 'lib/cequel/record/record_set.rb', line 176 def limit(count) scoped(row_limit: count) end |
#next_paging_state ⇒ Object
487 488 489 |
# File 'lib/cequel/record/record_set.rb', line 487 def next_paging_state data_set.next_paging_state end |
#page_size(page_size) ⇒ RecordSet
Set the page_size at which to read records into the record set.
473 474 475 |
# File 'lib/cequel/record/record_set.rb', line 473 def page_size(page_size) scoped(query_page_size: page_size) end |
#paging_state(paging_state) ⇒ RecordSet
Set the paging_state at which to read records into the record set.
483 484 485 |
# File 'lib/cequel/record/record_set.rb', line 483 def paging_state(paging_state) scoped(query_paging_state: paging_state) 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
448 449 450 451 452 453 454 455 |
# File 'lib/cequel/record/record_set.rb', line 448 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.
660 661 662 |
# File 'lib/cequel/record/record_set.rb', line 660 def scoped_key_attributes Hash[scoped_key_columns.map { |col| col.name }.zip(scoped_key_values)] end |
#select {|record| ... } ⇒ Array #select(*columns) ⇒ RecordSet
161 162 163 164 |
# File 'lib/cequel/record/record_set.rb', line 161 def select(*columns) return super if block_given? scoped { |attributes| attributes[:select_columns].concat(columns) } end |
#to_ary ⇒ Object
688 689 690 |
# File 'lib/cequel/record/record_set.rb', line 688 def to_ary entries 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.
430 431 432 433 434 435 436 437 |
# File 'lib/cequel/record/record_set.rb', line 430 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
299 300 301 302 303 304 305 306 307 308 309 310 311 |
# File 'lib/cequel/record/record_set.rb', line 299 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 #where(column_values) ⇒ RecordSet
Filtering on a primary key requires also filtering on all prior primary keys
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
204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/cequel/record/record_set.rb', line 204 def where(*args) if args.length == 1 column_filters = args.first.symbolize_keys elsif args.length == 2 warn "where(column_name, value) is deprecated. Use " \ "where(column_name => value) instead" column_filters = {args.first.to_sym => args.second} else fail ArgumentError, "wrong number of arguments (#{args.length} for 1..2)" end filter_columns(column_filters) end |