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.
-
#consistency(consistency) ⇒ RecordSet
Set the consistency at which to read records into the record set.
-
#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
- #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::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)
886 887 888 |
# File 'lib/cequel/record/record_set.rb', line 886 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
638 639 640 |
# File 'lib/cequel/record/record_set.rb', line 638 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
629 630 631 632 633 |
# File 'lib/cequel/record/record_set.rb', line 629 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 ⇒ Integer Also known as: length, size
Returns the total number of records in this record set.
501 502 503 |
# File 'lib/cequel/record/record_set.rb', line 501 def count data_set.count end |
#data_set ⇒ Cequel::Metal::DataSet
Returns the data set underlying this record set.
607 608 609 |
# File 'lib/cequel/record/record_set.rb', line 607 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
620 621 622 623 624 625 626 |
# File 'lib/cequel/record/record_set.rb', line 620 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
516 517 518 |
# File 'lib/cequel/record/record_set.rb', line 516 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
531 532 533 534 |
# File 'lib/cequel/record/record_set.rb', line 531 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.
567 568 569 570 |
# File 'lib/cequel/record/record_set.rb', line 567 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`
547 548 549 550 551 552 |
# File 'lib/cequel/record/record_set.rb', line 547 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.
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 |
# File 'lib/cequel/record/record_set.rb', line 584 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
477 478 479 |
# File 'lib/cequel/record/record_set.rb', line 477 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.
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
492 493 494 495 496 |
# File 'lib/cequel/record/record_set.rb', line 492 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.
176 177 178 |
# File 'lib/cequel/record/record_set.rb', line 176 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
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.
615 616 617 |
# File 'lib/cequel/record/record_set.rb', line 615 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
643 644 645 |
# File 'lib/cequel/record/record_set.rb', line 643 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 |