Class: DataMapper::Query
- Inherits:
-
Object
- Object
- DataMapper::Query
- Extended by:
- Equalizer
- Includes:
- Assertions
- Defined in:
- lib/dm-core/query.rb,
lib/dm-core/query/path.rb,
lib/dm-core/query/sort.rb,
lib/dm-core/query/operator.rb,
lib/dm-core/query/direction.rb,
lib/dm-core/query/conditions/operation.rb,
lib/dm-core/query/conditions/comparison.rb
Overview
Query class represents a query which will be run against the data-store. Generally Query objects can be found inside Collection objects.
Defined Under Namespace
Modules: Conditions Classes: Direction, Operator, Path, Sort
Constant Summary collapse
- OPTIONS =
%i(fields links conditions offset limit order unique add_reversed reload).to_set.freeze
Instance Attribute Summary collapse
-
#conditions ⇒ Array
readonly
Returns the conditions of the query.
-
#fields ⇒ PropertySet
readonly
Returns the fields.
-
#limit ⇒ Integer?
readonly
Returns the limit query uses.
-
#links ⇒ Array<DataMapper::Associations::Relationship>
readonly
private
Returns the links (associations) query fetches.
-
#model ⇒ Model
readonly
Returns model (class) that is used to instantiate objects from query result returned by adapter.
-
#offset ⇒ Integer
readonly
Returns the offset query uses.
-
#options ⇒ Hash
readonly
private
Returns the original options.
-
#order ⇒ Array
readonly
Returns the order.
-
#repository ⇒ Repository
readonly
Returns the repository query should be executed in.
Class Method Summary collapse
-
.target_conditions(source, source_key, target_key) ⇒ AbstractComparison, AbstractOperation
private
Extract conditions to match a Resource or Collection.
-
.target_query(repository, model, source) ⇒ Query
private
The query to match the resources with.
Instance Method Summary collapse
-
#add_reversed? ⇒ Boolean
private
Indicates if each result should be returned in reverse order.
-
#clear ⇒ self
Clear conditions.
-
#condition_properties ⇒ Set<Property>
private
Get the properties used in the conditions.
-
#difference(other) ⇒ Query
(also: #-)
Return the difference with another query.
-
#filter_records(records) ⇒ Enumerable
Takes an Enumerable of records, and destructively filters it.
-
#inspect ⇒ String
Returns detailed human readable string representation of the query.
-
#intersection(other) ⇒ Query
(also: #&)
Return the intersection with another query.
-
#limit_records(records) ⇒ Enumerable
Limits a set of records by the offset and/or limit.
-
#match_records(records) ⇒ Enumerable
Filter a set of records by the conditions.
-
#merge(other) ⇒ Query
Similar to Query#update, but acts on a duplicate.
-
#raw? ⇒ Boolean
Indicates if the Query has raw conditions.
-
#relative(options) ⇒ Object
Builds and returns new query that merges original with one given, and slices the result with respect to :limit and :offset options.
-
#reload? ⇒ Boolean
Indicates if the Query results should replace the results in the Identity Map.
-
#reverse ⇒ Query
Returns a new Query with a reversed order.
-
#reverse! ⇒ Query
Reverses the sort order of the Query.
-
#slice(*args) ⇒ Object
(also: #[])
Slices collection by adding limit and offset to the query, so a single query is executed.
-
#slice!(*args) ⇒ Object
Slices collection by adding limit and offset to the query, so a single query is executed.
-
#sort_records(records) ⇒ Enumerable
Sorts a list of Records by the order.
-
#sorted_fields ⇒ Array<Property>
private
Return a list of fields in predictable order.
-
#to_hash ⇒ Hash
private
Hash representation of a Query.
-
#to_relative_hash ⇒ Hash
private
Extract options from a Query.
-
#to_subquery ⇒ AndOperation
private
Transform Query into subquery conditions.
-
#union(other) ⇒ Query
(also: #|, #+)
Return the union with another query.
-
#unique? ⇒ Boolean
Indicates if the Query results should be unique.
-
#update(other) ⇒ Query
Updates the Query with another Query or conditions.
-
#valid? ⇒ Boolean
Indicates if the Query is valid.
Methods included from Equalizer
Methods included from Assertions
Instance Attribute Details
#conditions ⇒ Array (readonly)
Returns the conditions of the query
In the following example:
Conditions are “greater than” operator for “wins” field and exact match operator for “conference”.
179 180 181 |
# File 'lib/dm-core/query.rb', line 179 def conditions @conditions end |
#fields ⇒ PropertySet (readonly)
Returns the fields
Set in cases like the following:
154 155 156 |
# File 'lib/dm-core/query.rb', line 154 def fields @fields end |
#limit ⇒ Integer? (readonly)
Returns the limit query uses
Set in cases like the following:
207 208 209 |
# File 'lib/dm-core/query.rb', line 207 def limit @limit end |
#links ⇒ Array<DataMapper::Associations::Relationship> (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.
Returns the links (associations) query fetches
162 163 164 |
# File 'lib/dm-core/query.rb', line 162 def links @links end |
#model ⇒ Model (readonly)
Returns model (class) that is used to instantiate objects from query result returned by adapter
140 141 142 |
# File 'lib/dm-core/query.rb', line 140 def model @model end |
#offset ⇒ Integer (readonly)
Returns the offset query uses
Set in cases like the following:
193 194 195 |
# File 'lib/dm-core/query.rb', line 193 def offset @offset end |
#options ⇒ Hash (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.
Returns the original options
232 233 234 |
# File 'lib/dm-core/query.rb', line 232 def @options end |
#order ⇒ Array (readonly)
Returns the order
Set in cases like the following:
query order is a set of two ordering rules, descending on “created_at” field and descending again on “length” field
224 225 226 |
# File 'lib/dm-core/query.rb', line 224 def order @order end |
#repository ⇒ Repository (readonly)
Returns the repository query should be executed in
Set in cases like the following:
130 131 132 |
# File 'lib/dm-core/query.rb', line 130 def repository @repository end |
Class Method Details
.target_conditions(source, source_key, target_key) ⇒ AbstractComparison, AbstractOperation
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.
Extract conditions to match a Resource or Collection
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/dm-core/query.rb', line 48 def self.target_conditions(source, source_key, target_key) target_key_size = target_key.size source_values = [] if source.nil? source_values << ([nil] * target_key_size) else Array(source).each do |resource| next unless source_key.loaded?(resource) source_value = source_key.get!(resource) next unless target_key.valid?(source_value) source_values << source_value end end source_values.uniq! if target_key_size == 1 target_key = target_key.first source_values.flatten! if source_values.size == 1 Conditions::EqualToComparison.new(target_key, source_values.first) else Conditions::InclusionComparison.new(target_key, source_values) end else or_operation = Conditions::OrOperation.new source_values.each do |source_value| and_operation = Conditions::AndOperation.new target_key.zip(source_value) do |property, value| and_operation << Conditions::EqualToComparison.new(property, value) end or_operation << and_operation end or_operation end end |
.target_query(repository, model, source) ⇒ 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.
Returns the query to match the resources with.
104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/dm-core/query.rb', line 104 def self.target_query(repository, model, source) if source.respond_to?(:query) source.query elsif source.is_a?(Enumerable) key = model.key(repository.name) conditions = Query.target_conditions(source, key, key) repository.new_query(model, conditions: conditions) else raise ArgumentError, "+source+ must respond to #query or be an Enumerable, but was #{source.class}" end end |
Instance Method Details
#add_reversed? ⇒ Boolean
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.
Indicates if each result should be returned in reverse order
Set in cases like the following:
Note that :add_reversed option may be used in conditions directly, but this is rarely the case
249 250 251 |
# File 'lib/dm-core/query.rb', line 249 def add_reversed? @add_reversed end |
#clear ⇒ self
Clear conditions
477 478 479 480 |
# File 'lib/dm-core/query.rb', line 477 def clear @conditions = Conditions::Operation.new(:null) self end |
#condition_properties ⇒ Set<Property>
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.
Get the properties used in the conditions
630 631 632 633 634 635 636 637 638 639 640 641 |
# File 'lib/dm-core/query.rb', line 630 def condition_properties properties = Set.new each_comparison do |comparison| next unless comparison.respond_to?(:subject) subject = comparison.subject properties << subject if subject.is_a?(Property) end properties end |
#difference(other) ⇒ Query Also known as: -
Return the difference with another query
466 467 468 |
# File 'lib/dm-core/query.rb', line 466 def difference(other) set_operation(:difference, other) end |
#filter_records(records) ⇒ Enumerable
Takes an Enumerable of records, and destructively filters it. First finds all matching conditions, then sorts it, then does offset & limit
493 494 495 496 497 498 499 |
# File 'lib/dm-core/query.rb', line 493 def filter_records(records) records = records.uniq if unique? records = match_records(records) if conditions records = sort_records(records) if order records = limit_records(records) if limit || offset > 0 records end |
#inspect ⇒ String
Returns detailed human readable string representation of the query
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 |
# File 'lib/dm-core/query.rb', line 607 def inspect attrs = [ [:repository, repository.name], [:model, model], [:fields, fields], [:links, links], [:conditions, conditions], [:order, order], [:limit, limit], [:offset, offset], [:reload, reload?], [:unique, unique?] ] "#<#{self.class.name} #{attrs.map { |key, value| "@#{key}=#{value.inspect}" }.join(' ')}>" end |
#intersection(other) ⇒ Query Also known as: &
Return the intersection with another query
449 450 451 452 453 |
# File 'lib/dm-core/query.rb', line 449 def intersection(other) return dup if self == other set_operation(:intersection, other) end |
#limit_records(records) ⇒ Enumerable
Limits a set of records by the offset and/or limit
543 544 545 546 547 548 549 550 551 552 553 554 555 |
# File 'lib/dm-core/query.rb', line 543 def limit_records(records) offset = self.offset limit = self.limit size = records.size if offset > size - 1 [] elsif (limit && limit != size) || offset > 0 records[offset, limit || size] || [] else records.dup end end |
#match_records(records) ⇒ Enumerable
Filter a set of records by the conditions
510 511 512 513 |
# File 'lib/dm-core/query.rb', line 510 def match_records(records) conditions = self.conditions records.select { |record| conditions.matches?(record) } end |
#merge(other) ⇒ Query
Similar to Query#update, but acts on a duplicate.
388 389 390 |
# File 'lib/dm-core/query.rb', line 388 def merge(other) dup.update(other) end |
#raw? ⇒ Boolean
Indicates if the Query has raw conditions
283 284 285 |
# File 'lib/dm-core/query.rb', line 283 def raw? @raw end |
#relative(options) ⇒ Object
Builds and returns new query that merges original with one given, and slices the result with respect to :limit and :offset options
This method is used by Collection to concatenate options from multiple chained calls in cases like the following:
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 |
# File 'lib/dm-core/query.rb', line 405 def relative() = .to_hash offset = nil limit = self.limit if .key?(:offset) && (.key?(:limit) || limit) = .dup offset = .delete(:offset) limit = .delete(:limit) || (limit - offset) end query = merge() query = query.slice!(offset, limit) if offset query end |
#reload? ⇒ Boolean
Indicates if the Query results should replace the results in the Identity Map
TODO: needs example
261 262 263 |
# File 'lib/dm-core/query.rb', line 261 def reload? @reload end |
#reverse ⇒ Query
Returns a new Query with a reversed order
Will execute a single query with correct order
309 310 311 |
# File 'lib/dm-core/query.rb', line 309 def reverse dup.reverse! end |
#reverse! ⇒ Query
Reverses the sort order of the Query
Will execute a single query with original order and then reverse collection in the Ruby space
326 327 328 329 330 331 332 333 334 |
# File 'lib/dm-core/query.rb', line 326 def reverse! # reverse the sort order @order.map! { |direction| direction.dup.reverse! } # copy the order to the options @options = @options.merge(order: @order).freeze self end |
#slice(*args) ⇒ Object Also known as: []
Slices collection by adding limit and offset to the query, so a single query is executed
will execute query with the following limit and offset (when repository uses DataObjects adapter, and thus queries use SQL):
LIMIT 5 OFFSET 3
571 572 573 |
# File 'lib/dm-core/query.rb', line 571 def slice(*args) dup.slice!(*args) end |
#slice!(*args) ⇒ Object
Slices collection by adding limit and offset to the query, so a single query is executed
will execute query with the following limit (when repository uses DataObjects adapter, and thus queries use SQL):
LIMIT 10
and then takes a slice of collection in the Ruby space
593 594 595 596 597 598 599 |
# File 'lib/dm-core/query.rb', line 593 def slice!(*args) offset, limit = extract_slice_arguments(*args) offset, limit = get_relative_position(offset, limit) if self.limit || self.offset > 0 update(offset: offset, limit: limit) end |
#sort_records(records) ⇒ Enumerable
Sorts a list of Records by the order
524 525 526 527 528 529 530 531 532 |
# File 'lib/dm-core/query.rb', line 524 def sort_records(records) sort_order = order.map { |direction| [direction.target, direction.operator == :asc] } records.sort_by do |record| sort_order.map do |(property, ascending)| Sort.new(record_value(record, property), ascending) end end end |
#sorted_fields ⇒ Array<Property>
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 a list of fields in predictable order
649 650 651 |
# File 'lib/dm-core/query.rb', line 649 def sorted_fields fields.sort_by(&:hash) end |
#to_hash ⇒ Hash
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.
Hash representation of a Query
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 |
# File 'lib/dm-core/query.rb', line 670 def to_hash { repository: repository.name, model: model.name, fields: fields, links: links, conditions: conditions, offset: offset, limit: limit, order: order, unique: unique?, add_reversed: add_reversed?, reload: reload? } end |
#to_relative_hash ⇒ Hash
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.
Extract options from a Query
695 696 697 |
# File 'lib/dm-core/query.rb', line 695 def to_relative_hash DataMapper::Ext::Hash.only(to_hash, :fields, :order, :unique, :add_reversed, :reload) end |
#to_subquery ⇒ AndOperation
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.
Transform Query into subquery conditions
659 660 661 662 |
# File 'lib/dm-core/query.rb', line 659 def to_subquery collection = model.all(merge(fields: model_key)) Conditions::Operation.new(:and, Conditions::Comparison.new(:in, self_relationship, collection)) end |
#union(other) ⇒ Query Also known as: |, +
Return the union with another query
431 432 433 434 435 |
# File 'lib/dm-core/query.rb', line 431 def union(other) return dup if self == other set_operation(:union, other) end |
#unique? ⇒ Boolean
Indicates if the Query results should be unique
TODO: needs example
273 274 275 |
# File 'lib/dm-core/query.rb', line 273 def unique? @unique end |
#update(other) ⇒ Query
Updates the Query with another Query or conditions
Pretty unrealistic example:
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
# File 'lib/dm-core/query.rb', line 352 def update(other) = if is_a?(other.class) return self if eql?(other) assert_valid_other(other) other. else other = other.to_hash return self if other.empty? other end @options = @options.merge().freeze (@options) normalize = DataMapper::Ext::Hash.only(, *OPTIONS - [:conditions]).map do |attribute, value| instance_variable_set("@#{attribute}", DataMapper::Ext.try_dup(value)) attribute end merge_conditions([DataMapper::Ext::Hash.except(, *OPTIONS), [:conditions]]) (normalize | %i(links unique)) self end |
#valid? ⇒ Boolean
Indicates if the Query is valid
293 294 295 |
# File 'lib/dm-core/query.rb', line 293 def valid? conditions.valid? end |