Class: Lotus::Model::Adapters::Dynamodb::Query
- Inherits:
-
Object
- Object
- Lotus::Model::Adapters::Dynamodb::Query
- Extended by:
- Forwardable
- Includes:
- Enumerable
- Defined in:
- lib/lotus/model/adapters/dynamodb/query.rb
Overview
Query DynamoDB table with a powerful API.
All the methods are chainable, it allows advanced composition of options.
This works as a lazy filtering mechanism: the records are fetched from the DynamoDB only when needed.
It implements Ruby’s ‘Enumerable` and borrows some methods from `Array`. Expect a query to act like them.
Instance Attribute Summary collapse
- #operation ⇒ Object readonly private
- #options ⇒ Object readonly private
Instance Method Summary collapse
-
#all ⇒ Array
Resolves the query by fetching records from the database and translating them into entities.
-
#average(column) ⇒ Object
(also: #avg)
This method is not implemented.
-
#begins_with(condition) ⇒ Object
Perform BEGINS_WITH comparison.
-
#comparison(condition, operator) ⇒ Object
private
Perform comparison operation.
-
#consistent ⇒ Object
Tells DynamoDB to use consistent reads.
-
#contains(condition) ⇒ Object
Perform CONTAINS comparison.
-
#continue?(previous_response) ⇒ Boolean
private
private
Check if request needs to be continued.
-
#count ⇒ Fixnum
Returns a count of the records for the current conditions.
-
#desc(*columns) ⇒ Object
Specify the descending order of the records, sorted by the range key.
-
#each ⇒ Integer
Iterates over fetched records.
-
#exclude(condition) ⇒ Object
(also: #not, #ne)
Logical negation of a #where condition.
-
#exists? ⇒ TrueClass, FalseClass
(also: #exist?)
Checks if at least one record exists for the current conditions.
-
#ge(condition) ⇒ Object
Perform GE comparison.
-
#gt(condition) ⇒ Object
Perform GT comparison.
-
#index(name) ⇒ Object
Tells DynamoDB which index to use.
-
#initialize(dataset, collection, &blk) ⇒ Query
constructor
private
Initialize a query.
-
#interval(column) ⇒ Object
This method is not implemented.
-
#key_for_condition(condition) ⇒ Symbol
private
private
Return proper options key for a given condition.
-
#le(condition) ⇒ Object
Perform LE comparison.
-
#limit(number) ⇒ Object
Limit the number of records to return.
-
#lt(condition) ⇒ Object
Perform LT comparison.
-
#max(column) ⇒ Object
This method is not implemented.
-
#min(column) ⇒ Object
This method is not implemented.
-
#negate! ⇒ Object
This method is not implemented.
-
#not_contains(condition) ⇒ Object
Perform NOT_CONTAINS comparison.
-
#not_null(column) ⇒ Object
Perform NOT_NULL comparison.
-
#null(column) ⇒ Object
Perform NULL comparison.
-
#offset(number) ⇒ Object
This method is not implemented.
-
#or ⇒ Object
Sets DynamoDB ConditionalOperator to ‘OR`.
-
#order(*columns) ⇒ Object
(also: #asc)
Specify the ascending order of the records, sorted by the range key.
-
#query ⇒ Object
Set operation to be query instead of scan.
-
#range(column) ⇒ Object
This method is not implemented.
-
#run(previous_response = nil) ⇒ Array
private
private
Apply all the options and return a filtered collection.
-
#select(*columns) ⇒ Object
Select only the specified columns.
-
#serialize_condition(condition, operator: nil, negate: false) ⇒ Hash
private
private
Serialize given condition for DynamoDB API.
-
#sum(column) ⇒ Object
This method is not implemented.
-
#where(condition) ⇒ Object
(also: #eq, #in, #between)
Adds a condition that behaves like SQL ‘WHERE`.
Constructor Details
#initialize(dataset, collection, &blk) ⇒ Query
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.
Initialize a query
67 68 69 70 71 72 73 74 75 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 67 def initialize(dataset, collection, &blk) @dataset = dataset @collection = collection @operation = :scan @options = {} instance_eval(&blk) if block_given? end |
Instance Attribute Details
#operation ⇒ Object (readonly)
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.
50 51 52 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 50 def operation @operation end |
#options ⇒ Object (readonly)
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.
56 57 58 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 56 def @options end |
Instance Method Details
#all ⇒ Array
Resolves the query by fetching records from the database and translating them into entities.
83 84 85 86 87 88 89 90 91 92 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 83 def all response = run while continue?(response) @options[:exclusive_start_key] = response.last_evaluated_key response = run(response) end @collection.deserialize(response.entities) end |
#average(column) ⇒ Object Also known as: avg
This method is not implemented. DynamoDB does not provide average.
399 400 401 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 399 def average(column) raise NotImplementedError end |
#begins_with(condition) ⇒ Object
Perform BEGINS_WITH comparison.
265 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 265 def begins_with(condition); comparison(condition, 'BEGINS_WITH'); end |
#comparison(condition, operator) ⇒ Object
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.
Perform comparison operation.
287 288 289 290 291 292 293 294 295 296 297 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 287 def comparison(condition, operator) key = key_for_condition(condition) serialized = serialize_condition(condition, operator: operator) if serialized @options[key] ||= {} @options[key].merge!(serialized) end self end |
#consistent ⇒ Object
Tells DynamoDB to use consistent reads.
501 502 503 504 505 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 501 def consistent query @options[:consistent_read] = true self end |
#contains(condition) ⇒ Object
Perform CONTAINS comparison.
251 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 251 def contains(condition); comparison(condition, 'CONTAINS'); end |
#continue?(previous_response) ⇒ Boolean (private)
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.
Check if request needs to be continued.
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 604 def continue?(previous_response) return false unless previous_response.last_evaluated_key if @options[:limit] if @options[:limit] > previous_response.count @options[:limit] = @options[:limit] - previous_response.count true else false end else true end end |
#count ⇒ Fixnum
Returns a count of the records for the current conditions.
473 474 475 476 477 478 479 480 481 482 483 484 485 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 473 def count @options[:select] = "COUNT" @options.delete(:attributes_to_get) response = run while continue?(response) @options[:exclusive_start_key] = response.last_evaluated_key response = run(response) end response.count end |
#desc(*columns) ⇒ Object
Specify the descending order of the records, sorted by the range key.
346 347 348 349 350 351 352 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 346 def desc(*columns) warn "DynamoDB only supports order by range_key" if columns.any? query @options[:scan_index_forward] = false self end |
#each ⇒ Integer
Iterates over fetched records.
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 99 def each response = run entities = @collection.deserialize(response.entities) entities.each { |x| yield(x) } while continue?(response) response.entities = [] @options[:exclusive_start_key] = response.last_evaluated_key response = run(response) entities = @collection.deserialize(response.entities) entities.each { |x| yield(x) } end response.count end |
#exclude(condition) ⇒ Object Also known as: not, ne
Logical negation of a #where condition.
It accepts a ‘Hash` with only one pair. The key must be the name of the column expressed as a `Symbol`. The value is the one used by the internal filtering logic.
203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 203 def exclude(condition) key = key_for_condition(condition) serialized = serialize_condition(condition, negate: true) if serialized @options[key] ||= {} @options[key].merge!(serialized) end self end |
#exists? ⇒ TrueClass, FalseClass Also known as: exist?
Checks if at least one record exists for the current conditions.
458 459 460 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 458 def exists? !count.zero? end |
#ge(condition) ⇒ Object
Perform GE comparison.
237 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 237 def ge(condition); comparison(condition, 'GE'); end |
#gt(condition) ⇒ Object
Perform GT comparison.
244 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 244 def gt(condition); comparison(condition, 'GT'); end |
#index(name) ⇒ Object
Tells DynamoDB which index to use.
512 513 514 515 516 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 512 def index(name) query @options[:index_name] = name.to_s self end |
#interval(column) ⇒ Object
This method is not implemented. DynamoDB does not provide interval.
434 435 436 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 434 def interval(column) raise NotImplementedError end |
#key_for_condition(condition) ⇒ Symbol (private)
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.
Return proper options key for a given condition.
527 528 529 530 531 532 533 534 535 536 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 527 def key_for_condition(condition) if @dataset.key?(condition.keys.first, @options[:index_name]) query :key_conditions elsif operation == :scan :scan_filter else :query_filter end end |
#le(condition) ⇒ Object
Perform LE comparison.
223 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 223 def le(condition); comparison(condition, 'LE'); end |
#limit(number) ⇒ Object
Limit the number of records to return.
365 366 367 368 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 365 def limit(number) @options[:limit] = number self end |
#lt(condition) ⇒ Object
Perform LT comparison.
230 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 230 def lt(condition); comparison(condition, 'LT'); end |
#max(column) ⇒ Object
This method is not implemented. DynamoDB does not provide max.
412 413 414 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 412 def max(column) raise NotImplementedError end |
#min(column) ⇒ Object
This method is not implemented. DynamoDB does not provide min.
423 424 425 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 423 def min(column) raise NotImplementedError end |
#negate! ⇒ Object
This method is not implemented.
492 493 494 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 492 def negate! raise NotImplementedError end |
#not_contains(condition) ⇒ Object
Perform NOT_CONTAINS comparison.
258 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 258 def not_contains(condition); comparison(condition, 'NOT_CONTAINS'); end |
#not_null(column) ⇒ Object
Perform NOT_NULL comparison.
279 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 279 def not_null(column); comparison({ column => '' }, 'NOT_NULL'); end |
#null(column) ⇒ Object
Perform NULL comparison.
272 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 272 def null(column); comparison({ column => '' }, 'NULL'); end |
#offset(number) ⇒ Object
This method is not implemented. DynamoDB does not provide offset.
377 378 379 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 377 def offset(number) raise NotImplementedError end |
#or ⇒ Object
Sets DynamoDB ConditionalOperator to ‘OR`. This works query-wide so this method has no arguments.
178 179 180 181 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 178 def or @options[:conditional_operator] = "OR" self end |
#order(*columns) ⇒ Object Also known as: asc
Specify the ascending order of the records, sorted by the range key.
329 330 331 332 333 334 335 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 329 def order(*columns) warn "DynamoDB only supports order by range_key" if columns.any? query @options[:scan_index_forward] = true self end |
#query ⇒ Object
Set operation to be query instead of scan.
123 124 125 126 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 123 def query @operation = :query self end |
#range(column) ⇒ Object
This method is not implemented. DynamoDB does not provide range.
445 446 447 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 445 def range(column) raise NotImplementedError end |
#run(previous_response = nil) ⇒ Array (private)
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.
Apply all the options and return a filtered collection.
592 593 594 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 592 def run(previous_response = nil) @dataset.public_send(operation, @options, previous_response) end |
#select(*columns) ⇒ Object
Select only the specified columns.
By default a query selects all the mapped columns.
316 317 318 319 320 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 316 def select(*columns) @options[:select] = "SPECIFIC_ATTRIBUTES" @options[:attributes_to_get] = columns.map(&:to_s) self end |
#serialize_condition(condition, operator: nil, negate: false) ⇒ Hash (private)
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.
Serialize given condition for DynamoDB API.
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 547 def serialize_condition(condition, operator: nil, negate: false) column, value = condition.keys.first, condition.values.first operator ||= case when value.is_a?(Array) negate ? nil : "IN" when value.is_a?(Range) negate ? nil : "BETWEEN" else negate ? "NE" : "EQ" end # Operator is not supported raise NotImplementedError unless operator values ||= case when value.is_a?(Range) [value.first, value.last] else [value].flatten end serialized = { column.to_s => { comparison_operator: operator, }, } if !["NULL", "NOT_NULL"].include?(operator) serialized[column.to_s][:attribute_value_list] = values.map do |v| @dataset.format_attribute(column, v) end end serialized end |
#sum(column) ⇒ Object
This method is not implemented. DynamoDB does not provide sum.
388 389 390 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 388 def sum(column) raise NotImplementedError end |
#where(condition) ⇒ Object Also known as: eq, in, between
Adds a condition that behaves like SQL ‘WHERE`.
It accepts a ‘Hash` with only one pair. The key must be the name of the column expressed as a `Symbol`. The value is the one used by the internal filtering logic.
156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/lotus/model/adapters/dynamodb/query.rb', line 156 def where(condition) key = key_for_condition(condition) serialized = serialize_condition(condition) if serialized @options[key] ||= {} @options[key].merge!(serialized) end self end |