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.
- #allow_filtering! ⇒ Object
- #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)
950 951 952 |
# File 'lib/cequel/record/record_set.rb', line 950 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
699 700 701 |
# File 'lib/cequel/record/record_set.rb', line 699 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 |
#allow_filtering! ⇒ Object
Filtering can incurr a significant performance overhead or even timeout on a large data-set.
479 480 481 |
# File 'lib/cequel/record/record_set.rb', line 479 def allow_filtering! scoped(allow_filtering: true) end |
#assert_fully_specified! ⇒ Object
690 691 692 693 694 |
# File 'lib/cequel/record/record_set.rb', line 690 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
562 563 564 |
# File 'lib/cequel/record/record_set.rb', line 562 def count raise Cequel::Record::DangerousQueryError.new end |
#data_set ⇒ Cequel::Metal::DataSet
Returns the data set underlying this record set.
668 669 670 |
# File 'lib/cequel/record/record_set.rb', line 668 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
681 682 683 684 685 686 687 |
# File 'lib/cequel/record/record_set.rb', line 681 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
577 578 579 |
# File 'lib/cequel/record/record_set.rb', line 577 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
592 593 594 595 |
# File 'lib/cequel/record/record_set.rb', line 592 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.
628 629 630 631 |
# File 'lib/cequel/record/record_set.rb', line 628 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`
608 609 610 611 612 613 |
# File 'lib/cequel/record/record_set.rb', line 608 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.
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 |
# File 'lib/cequel/record/record_set.rb', line 645 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
521 522 523 |
# File 'lib/cequel/record/record_set.rb', line 521 def first(count = nil) count ? limit(count).entries : limit(1).each.first end |
#first! ⇒ Record
Returns the first record.
536 537 538 539 540 541 |
# File 'lib/cequel/record/record_set.rb', line 536 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.
528 529 530 |
# File 'lib/cequel/record/record_set.rb', line 528 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
554 555 556 557 558 |
# File 'lib/cequel/record/record_set.rb', line 554 def last(count = nil) reverse.first(count).tap do |results| results.reverse! if count end end |
#last_page? ⇒ Boolean
507 508 509 |
# File 'lib/cequel/record/record_set.rb', line 507 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
503 504 505 |
# File 'lib/cequel/record/record_set.rb', line 503 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.
489 490 491 |
# File 'lib/cequel/record/record_set.rb', line 489 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.
499 500 501 |
# File 'lib/cequel/record/record_set.rb', line 499 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.
676 677 678 |
# File 'lib/cequel/record/record_set.rb', line 676 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
704 705 706 |
# File 'lib/cequel/record/record_set.rb', line 704 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 |