Class: OccamsRecord::Query
- Inherits:
-
Object
- Object
- OccamsRecord::Query
- Includes:
- Enumerable, Batches::CursorHelpers, EagerLoaders::Builder, Measureable, Pluck
- Defined in:
- lib/occams-record/query.rb
Overview
Represents a query to be run and eager associations to be loaded. Use OccamsRecord.query to create your queries instead of instantiating objects directly.
Instance Attribute Summary collapse
- #model ⇒ ActiveRecord::Base readonly
-
#scope ⇒ ActiveRecord::Relation
readonly
Scope for building the main SQL query.
Instance Method Summary collapse
-
#count ⇒ Integer
Returns the number of rows that will be returned if the query is run.
-
#cursor(name: nil, scroll: nil, hold: nil) ⇒ OccamsRecord::Cursor
Returns a cursor you can open and perform operations on.
-
#each {|OccamsRecord::Results::Row| ... } ⇒ Object
If you pass a block, each result row will be yielded to it.
-
#find_each(batch_size: 1000, use_transaction: true, append_order_by: nil) {|OccamsRecord::Results::Row| ... } ⇒ Enumerator
Load records in batches of N and yield each record to a block if given.
-
#find_in_batches(batch_size: 1000, use_transaction: true, append_order_by: nil) {|OccamsRecord::Results::Row| ... } ⇒ Enumerator
Load records in batches of N and yield each batch to a block if given.
-
#first ⇒ OccamsRecord::Results::Row
Run the query with LIMIT 1 and return the first result (which could be nil).
-
#first! ⇒ OccamsRecord::Results::Row
Run the query with LIMIT 1 and return the first result.
-
#initialize(scope, use: nil, eager_loaders: nil, query_logger: nil, measurements: nil, active_record_fallback: nil) ⇒ Query
constructor
Initialize a new query.
-
#pluck(*cols) ⇒ Array
Returns the specified column(s) as an array of values.
-
#query {|ActiveRecord::Relation| ... } ⇒ OccamsRecord::Query
Returns a new Query object with a modified scope.
-
#run {|ActiveRecord::Relation| ... } ⇒ Array<OccamsRecord::Results::Row>
(also: #to_a)
Run the query and return the results.
Methods included from Measureable
Methods included from EagerLoaders::Builder
#eager_load, #eager_load_many, #eager_load_one, #nest
Methods included from Batches::CursorHelpers
#find_each_with_cursor, #find_in_batches_with_cursor
Constructor Details
#initialize(scope, use: nil, eager_loaders: nil, query_logger: nil, measurements: nil, active_record_fallback: nil) ⇒ Query
Initialize a new query.
54 55 56 57 58 59 60 61 |
# File 'lib/occams-record/query.rb', line 54 def initialize(scope, use: nil, eager_loaders: nil, query_logger: nil, measurements: nil, active_record_fallback: nil) @model = scope.klass @scope = scope @eager_loaders = eager_loaders || EagerLoaders::Context.new(@model) @use = use @query_logger, @measurements = query_logger, measurements @active_record_fallback = active_record_fallback end |
Instance Attribute Details
#model ⇒ ActiveRecord::Base (readonly)
34 35 36 |
# File 'lib/occams-record/query.rb', line 34 def model @model end |
#scope ⇒ ActiveRecord::Relation (readonly)
Returns scope for building the main SQL query.
36 37 38 |
# File 'lib/occams-record/query.rb', line 36 def scope @scope end |
Instance Method Details
#count ⇒ Integer
Returns the number of rows that will be returned if the query is run.
125 126 127 |
# File 'lib/occams-record/query.rb', line 125 def count scope.count end |
#cursor(name: nil, scroll: nil, hold: nil) ⇒ OccamsRecord::Cursor
Returns a cursor you can open and perform operations on. A lower-level alternative to find_each_with_cursor and find_in_batches_with_cursor.
NOTE Postgres only. See the docs for OccamsRecord::Cursor for more details.
226 227 228 229 230 231 |
# File 'lib/occams-record/query.rb', line 226 def cursor(name: nil, scroll: nil, hold: nil) Cursor.new(model.connection, scope.to_sql, name: name, scroll: scroll, hold: hold, use: @use, query_logger: @query_logger, eager_loaders: @eager_loaders, ) end |
#each {|OccamsRecord::Results::Row| ... } ⇒ Object
If you pass a block, each result row will be yielded to it. If you don’t, an Enumerable will be returned.
155 156 157 158 159 160 161 |
# File 'lib/occams-record/query.rb', line 155 def each if block_given? to_a.each { |row| yield row } else to_a.each end end |
#find_each(batch_size: 1000, use_transaction: true, append_order_by: nil) {|OccamsRecord::Results::Row| ... } ⇒ Enumerator
Load records in batches of N and yield each record to a block if given. If no block is given, returns an Enumerator.
NOTE Unlike ActiveRecord’s find_each, ORDER BY is respected. The primary key will be appended to the ORDER BY clause to help ensure consistent batches. Additionally, it will be run inside of a transaction.
177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/occams-record/query.rb', line 177 def find_each(batch_size: 1000, use_transaction: true, append_order_by: nil) enum = Enumerator.new { |y| find_in_batches(batch_size: 1000, use_transaction: use_transaction, append_order_by: append_order_by).each { |batch| batch.each { |record| y.yield record } } } if block_given? enum.each { |record| yield record } else enum end end |
#find_in_batches(batch_size: 1000, use_transaction: true, append_order_by: nil) {|OccamsRecord::Results::Row| ... } ⇒ Enumerator
Load records in batches of N and yield each batch to a block if given. If no block is given, returns an Enumerator.
NOTE Unlike ActiveRecord’s find_each, ORDER BY is respected. The primary key will be appended to the ORDER BY clause to help ensure consistent batches. Additionally, it will be run inside of a transaction.
204 205 206 207 208 209 210 211 212 213 |
# File 'lib/occams-record/query.rb', line 204 def find_in_batches(batch_size: 1000, use_transaction: true, append_order_by: nil) enum = Batches::OffsetLimit::Scoped .new(model, scope, use: @use, query_logger: @query_logger, eager_loaders: @eager_loaders) .enum(batch_size: batch_size, use_transaction: use_transaction, append_order_by: append_order_by) if block_given? enum.each { |batch| yield batch } else enum end end |
#first ⇒ OccamsRecord::Results::Row
Run the query with LIMIT 1 and return the first result (which could be nil).
134 135 136 |
# File 'lib/occams-record/query.rb', line 134 def first run { |q| q.limit 1 }.first end |
#first! ⇒ OccamsRecord::Results::Row
Run the query with LIMIT 1 and return the first result. If nothing is found an OccamsRecord::NotFound exception will be raised.
144 145 146 |
# File 'lib/occams-record/query.rb', line 144 def first! first || raise(OccamsRecord::NotFound.new(model.name, scope.where_values_hash)) end |
#pluck(*cols) ⇒ Array
Returns the specified column(s) as an array of values.
If more than one column is given, the result will be an array of arrays.
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/occams-record/query.rb', line 241 def pluck(*cols) sql = (block_given? ? yield(scope).to_sql : scope).unscope(:select).select(*cols).to_sql return [] if sql.blank? # return early in case ActiveRecord::QueryMethods#none was used @query_logger << "#{@eager_loaders.tracer}: #{sql}" if @query_logger result = if measure? record_start_time! measure!(model.table_name, sql) { model.connection.exec_query sql } else model.connection.exec_query sql end pluck_results(result, model: @model) end |
#query {|ActiveRecord::Relation| ... } ⇒ OccamsRecord::Query
Returns a new Query object with a modified scope.
69 70 71 72 |
# File 'lib/occams-record/query.rb', line 69 def query scope = block_given? ? yield(@scope) : @scope Query.new(scope, use: @use, eager_loaders: @eager_loaders, query_logger: @query_logger) end |
#run {|ActiveRecord::Relation| ... } ⇒ Array<OccamsRecord::Results::Row> Also known as: to_a
Run the query and return the results.
You may optionally pass a block to modify the query just before it’s run (the change will NOT persist). This is very useful for running paginated queries.
occams = OccamsRecord.query(Widget.all)
# returns first 100 rows
occams.run { |q| q.offset(0).limit(100) }
# returns second 100 rows
occams.run { |q| q.offset(100).limit(100) }
# returns ALL rows
occams.run
Any Enumerable method (e.g. each, to_a, map, reduce, etc.) may be used instead. Additionally, ‘find_each` and `find_in_batches` are available.
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/occams-record/query.rb', line 97 def run sql = block_given? ? yield(scope).to_sql : scope.to_sql return [] if sql.blank? # return early in case ActiveRecord::QueryMethods#none was used @query_logger << "#{@eager_loaders.tracer}: #{sql}" if @query_logger result = if measure? record_start_time! measure!(model.table_name, sql) { model.connection.exec_query sql } else model.connection.exec_query sql end row_class = OccamsRecord::Results.klass(result.columns, result.column_types, @eager_loaders.names, model: model, modules: @use, tracer: @eager_loaders.tracer, active_record_fallback: @active_record_fallback) rows = result.rows.map { |row| row_class.new row } @eager_loaders.run!(rows, query_logger: @query_logger, measurements: @measurements) yield_measurements! rows end |