Class: Parse::Query
- Inherits:
- Object
- Object
- Parse::Query
- Extended by:
- ActiveModel::Callbacks
- Includes:
- Enumerable, Client::Connectable
- Defined in:
- lib/parse/query.rb,
lib/parse/model/core/actions.rb
Overview
The Query class provides the lower-level querying interface for your Parse collections by utilizing the REST Querying interface. This is the main engine behind making Parse queries on remote collections. It takes a set of constraints and generates the proper hash parameters that are passed to an API request in order to retrive matching results. The querying design pattern is inspired from / DataMapper where symbols are overloaded with specific methods with attached values.
At the core of each item is a Operation. An operation is made up of a field name and an operator. Therefore calling something like :name.eq, defines an equality operator on the field name. Using Operations with values, we can build different types of constraints, known as Constraints.
This component can be used on its own without defining your models as all results are provided in hash form.
Field-Formatter
By convention in Ruby (see Style Guide), symbols and variables are expressed in lower_snake_case form. Parse, however, prefers column names in String#columnize format (ex. `objectId`, `createdAt` and `updatedAt`). To keep in line with the style guides between the languages, we do the automatic conversion of the field names when compiling the query. This feature can be overridden by changing the value of Query.field_formatter.
# default uses :columnize
query = Parse::User.query :field_one => 1, :FieldTwo => 2, :Field_Three => 3
query.compile_where # => {"fieldOne"=>1, "fieldTwo"=>2, "fieldThree"=>3}
# turn off
Parse::Query.field_formatter = nil
query = Parse::User.query :field_one => 1, :FieldTwo => 2, :Field_Three => 3
query.compile_where # => {"field_one"=>1, "FieldTwo"=>2, "Field_Three"=>3}
# force everything camel case
Parse::Query.field_formatter = :camelize
query = Parse::User.query :field_one => 1, :FieldTwo => 2, :Field_Three => 3
query.compile_where # => {"FieldOne"=>1, "FieldTwo"=>2, "FieldThree"=>3}
Most of the constraints supported by Parse are available to `Parse::Query`. Assuming you have a column named `field`, here are some examples. For an explanation of the constraints, please see Parse Query Constraints documentation. You can build your own custom query constraints by creating a `Parse::Constraint` subclass. For all these `where` clauses assume `q` is a `Parse::Query` object.
Class Attribute Summary collapse
- .allow_scope_introspection ⇒ Symbol
The method to use when converting field names to Parse column names.
- .field_formatter ⇒ Symbol
The method to use when converting field names to Parse column names.
Instance Attribute Summary collapse
- #cache ⇒ Boolean, Integer
Set whether this query should be cached and for how long.
- #client ⇒ Parse::Client
The client to use for making the API request.
- #key ⇒ String
This parameter is used to support `select` queries where you have to pass a `key` parameter for matching different tables.
- #session_token ⇒ String
Set the session token to send with this API request.
- #table ⇒ String
The name of the Parse collection to query against.
- #use_master_key ⇒ Boolean
True or false on whether we should send the master key in this request.
Class Method Summary collapse
- .all(table, constraints = { limit: :max }) ⇒ Query
Helper method to create a query with constraints for a specific Parse collection.
- .compile_where(where) ⇒ Hash
This methods takes a set of constraints and merges them to build a final `where` constraint clause for sending to the Parse backend.
- .format_field(str) ⇒ String
Formatted string using Query.field_formatter.
Instance Method Summary collapse
- #add_constraint(operator, value = nil, opts = {}) ⇒ self
Add a constraint to the query.
- #add_constraints(list) ⇒ self
Combine a list of Constraint objects.
- #after_prepare { ... } ⇒ Object
A callback called after the query is compiled.
- #as_json(*args) ⇒ Hash
- #before_prepare { ... } ⇒ Object
A callback called before the query is compiled.
- #clause(clause_name = :where) ⇒ Object
returns the query clause for the particular clause.
- #clear(item = :results) ⇒ self
Clear a specific clause of this query.
- #compile(encode: true, includeClassName: false) ⇒ Hash
Complies the query and runs all prepare callbacks.
- #compile_where ⇒ Hash
A hash representing just the `where` clause of this query.
- #conditions(expressions = {}) ⇒ self (also: #query, #append)
Add a set of query expressions and constraints.
- #constraints(raw = false) ⇒ Array<Parse::Constraint>, Hash
- #count ⇒ Integer
Perform a count query.
- #decode(list) ⇒ Array<Parse::Object>
Builds objects based on the set of Parse JSON hashes in an array.
- #distinct(field) ⇒ Object
Queries can be made using distinct, allowing you find unique values for a specified field.
- #each { ... } ⇒ Array
- #fetch!(compiled_query) ⇒ Parse::Response (also: #execute!)
Performs the fetch request for the query.
- #first(limit = 1) ⇒ Parse::Object
The first object from the result.
- #includes(*fields) ⇒ self
Set a list of Parse Pointer columns to be fetched for matching records.
- #initialize(table, constraints = {}) ⇒ Query constructor
Constructor method to create a query with constraints for a specific Parse collection.
- #keys(*fields) ⇒ self
Restrict the fields returned by the query.
- #limit(count) ⇒ self
Limit the number of objects returned by the query.
- #map { ... } ⇒ Array
- #or_where(where_clauses = []) ⇒ Query
Combine two where clauses into an OR constraint.
- #order(*ordering) ⇒ self
Add a sorting order for the query.
- #prepared(includeClassName: false) ⇒ Hash
Returns a compiled query without encoding the where clause.
- #pretty ⇒ String
Retruns a formatted JSON string representing the query, useful for debugging.
- #related_to(field, pointer) ⇒ Object
- #results(raw: false) { ... } ⇒ Array<Hash>, Array<Parse::Object> (also: #result)
Executes the query and builds the result set of Parse::Objects that matched.
- #select { ... } ⇒ Array
- #skip(amount) ⇒ self
Use with limit to paginate through results.
- #to_a ⇒ Array
- #where(conditions = nil, opts = {}) ⇒ self
Add additional query constraints to the `where` clause.
- #where_constraints ⇒ Hash
Formats the current set of Parse::Constraint instances in the where clause as an expression hash.
- #|(other_query) ⇒ Query
The combined query with an OR clause.
Constructor Details
#new(table) ⇒ Query #new(parseSubclass) ⇒ Query
Constructor method to create a query with constraints for a specific Parse collection. Also sets the default limit count to `:max`.
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 | # File 'lib/parse/query.rb', line 286 def initialize(table, constraints = {}) table = table.to_s.to_parse_class if table.is_a?(Symbol) table = table.parse_class if table.respond_to?(:parse_class) raise ArgumentError, "First parameter should be the name of the Parse class (table)" unless table.is_a?(String) @count = 0 #non-zero/1 implies a count query request @where = [] @order = [] @keys = [] @includes = [] @limit = nil @skip = 0 @table = table @cache = true @use_master_key = true conditions constraints end |
Class Attribute Details
.allow_scope_introspection ⇒ Symbol
The method to use when converting field names to Parse column names. Default is String#columnize. By convention Parse uses lowercase-first camelcase syntax for field/column names, but ruby uses snakecase. To support this methodology we process all field constraints through the method defined by the field formatter. You may set this to nil to turn off this functionality.
# File 'lib/parse/query.rb', line 168
|
.field_formatter ⇒ Symbol
The method to use when converting field names to Parse column names. Default is String#columnize. By convention Parse uses lowercase-first camelcase syntax for field/column names, but ruby uses snakecase. To support this methodology we process all field constraints through the method defined by the field formatter. You may set this to nil to turn off this functionality.
182 183 184 | # File 'lib/parse/query.rb', line 182 def field_formatter @field_formatter end |
Instance Attribute Details
#cache ⇒ Boolean, Integer
Set whether this query should be cached and for how long. This parameter is used to cache queries when using Middleware::Caching. If the caching middleware is configured, all queries will be cached for the duration allowed by the cache, and therefore some queries could return cached results. To disable caching and cached results for this specific query, you may set this field to `false`. To specify the specific amount of time you want this query to be cached, set a duration (in number of seconds) that the caching middleware should cache this request.
152 | # File 'lib/parse/query.rb', line 152 attr_accessor :table, :client, :key, :cache, :use_master_key, :session_token |
#client ⇒ Parse::Client
Returns the client to use for making the API request.
152 | # File 'lib/parse/query.rb', line 152 attr_accessor :table, :client, :key, :cache, :use_master_key, :session_token |
#key ⇒ String
This parameter is used to support `select` queries where you have to pass a `key` parameter for matching different tables.
152 | # File 'lib/parse/query.rb', line 152 attr_accessor :table, :client, :key, :cache, :use_master_key, :session_token |
#session_token ⇒ String
Using a session_token automatically disables sending the master key in the request.
Set the session token to send with this API request. A session token is tied to a logged in User. When sending a session_token in the request, this performs the query on behalf of the user (with their allowed privileges). Using the short hand (inline) form, you can also pass an authenticated User instance or a Session instance.
152 | # File 'lib/parse/query.rb', line 152 attr_accessor :table, :client, :key, :cache, :use_master_key, :session_token |
#table ⇒ String
Returns the name of the Parse collection to query against.
152 153 154 | # File 'lib/parse/query.rb', line 152 def table @table end |
#use_master_key ⇒ Boolean
True or false on whether we should send the master key in this request. If You have provided the master_key when initializing Parse, then all requests will send the master key by default. This feature is useful when you want to make a particular query be performed with public credentials, or on behalf of a user using a #session_token. Default is set to true.
152 | # File 'lib/parse/query.rb', line 152 attr_accessor :table, :client, :key, :cache, :use_master_key, :session_token |
Class Method Details
.all(table, constraints = { limit: :max }) ⇒ Query
Helper method to create a query with constraints for a specific Parse collection. Also sets the default limit count to `:max`.
199 200 201 | # File 'lib/parse/query.rb', line 199 def all(table, constraints = { limit: :max }) self.new(table, constraints.reverse_merge({ limit: :max })) end |
.compile_where(where) ⇒ Hash
This methods takes a set of constraints and merges them to build a final `where` constraint clause for sending to the Parse backend.
207 208 209 | # File 'lib/parse/query.rb', line 207 def compile_where(where) constraint_reduce(where) end |
.format_field(str) ⇒ String
Returns formatted string using field_formatter.
186 187 188 189 190 191 192 | # File 'lib/parse/query.rb', line 186 def format_field(str) res = str.to_s.strip if field_formatter.present? && res.respond_to?(field_formatter) res = res.send(field_formatter) end res end |
Instance Method Details
#add_constraint(operator, value = nil, opts = {}) ⇒ self
Add a constraint to the query. This is mainly used internally for compiling constraints.
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 | # File 'lib/parse/query.rb', line 508 def add_constraint(operator, value = nil, opts = {}) @where ||= [] constraint = operator # assume Parse::Constraint unless constraint.is_a?(Parse::Constraint) constraint = Parse::Constraint.create(operator, value) end return unless constraint.is_a?(Parse::Constraint) # to support select queries where you have to pass a `key` parameter for matching # different tables. if constraint.operand == :key || constraint.operand == "key" @key = constraint.value return end unless opts[:filter] == false constraint.operand = Query.format_field(constraint.operand) end @where.push constraint @results = nil self #chaining end |
#add_constraints(list) ⇒ self
Combine a list of Constraint objects
486 487 488 489 490 | # File 'lib/parse/query.rb', line 486 def add_constraints(list) list = Array.wrap(list).select { |m| m.is_a?(Parse::Constraint) } @where = @where + list self end |
#after_prepare { ... } ⇒ Object
A callback called after the query is compiled
79 | # File 'lib/parse/query.rb', line 79 define_model_callbacks :prepare, only: [:after, :before] |
#as_json(*args) ⇒ Hash
839 840 841 | # File 'lib/parse/query.rb', line 839 def as_json(*args) compile.as_json end |
#before_prepare { ... } ⇒ Object
A callback called before the query is compiled
79 | # File 'lib/parse/query.rb', line 79 define_model_callbacks :prepare, only: [:after, :before] |
#clause(clause_name = :where) ⇒ Object
returns the query clause for the particular clause
360 361 362 363 | # File 'lib/parse/query.rb', line 360 def clause(clause_name = :where) return unless [:keys, :where, :order, :includes, :limit, :skip].include?(clause_name) instance_variable_get "@#{clause_name}".to_sym end |
#clear(item = :results) ⇒ self
Clear a specific clause of this query. This can be one of: :where, :order, :includes, :skip, :limit, :count, :keys or :results.
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 | # File 'lib/parse/query.rb', line 245 def clear(item = :results) case item when :where # an array of Parse::Constraint subclasses @where = [] when :order # an array of Parse::Order objects @order = [] when :includes @includes = [] when :skip @skip = 0 when :limit @limit = nil when :count @count = 0 when :keys @keys = [] end @results = nil self # chaining end |
#compile(encode: true, includeClassName: false) ⇒ Hash
Complies the query and runs all prepare callbacks.
857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 | # File 'lib/parse/query.rb', line 857 def compile(encode: true, includeClassName: false) run_callbacks :prepare do q = {} #query q[:limit] = @limit if @limit.is_a?(Numeric) && @limit > 0 q[:skip] = @skip if @skip > 0 q[:include] = @includes.join(",") unless @includes.empty? q[:keys] = @keys.join(",") unless @keys.empty? q[:order] = @order.join(",") unless @order.empty? unless @where.empty? q[:where] = Parse::Query.compile_where(@where) q[:where] = q[:where].to_json if encode end if @count && @count > 0 # if count is requested q[:limit] = 0 q[:count] = 1 end if includeClassName q[:className] = @table end q end end |
#compile_where ⇒ Hash
Returns a hash representing just the `where` clause of this query.
884 885 886 | # File 'lib/parse/query.rb', line 884 def compile_where self.class.compile_where(@where || []) end |
#conditions(expressions = {}) ⇒ self Also known as: query, append
Add a set of query expressions and constraints.
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 | # File 'lib/parse/query.rb', line 309 def conditions(expressions = {}) expressions.each do |expression, value| if expression == :order order value elsif expression == :keys keys value elsif expression == :key keys [value] elsif expression == :skip skip value elsif expression == :limit limit value elsif expression == :include || expression == :includes includes(value) elsif expression == :cache self.cache = value elsif expression == :use_master_key self.use_master_key = value elsif expression == :session # you can pass a session token or a Parse::Session self.session_token = value else add_constraint(expression, value) end end # each self #chaining end |
#constraints(raw = false) ⇒ Array<Parse::Constraint>, Hash
534 535 536 | # File 'lib/parse/query.rb', line 534 def constraints(raw = false) raw ? where_constraints : @where end |
#count ⇒ Integer
Perform a count query.
654 655 656 657 658 659 660 | # File 'lib/parse/query.rb', line 654 def count old_value = @count @count = 1 res = client.find_objects(@table, compile.as_json, _opts).count @count = old_value res end |
#decode(list) ⇒ Array<Parse::Object>
Builds objects based on the set of Parse JSON hashes in an array.
834 835 836 | # File 'lib/parse/query.rb', line 834 def decode(list) list.map { |m| Parse::Object.build(m, @table) }.compact end |
#distinct(field) ⇒ Object
This feature requires use of the Master Key in the API.
Queries can be made using distinct, allowing you find unique values for a specified field. For this to be performant, please remember to index your database.
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 | # File 'lib/parse/query.rb', line 628 def distinct(field) if field.nil? == false && field.respond_to?(:to_s) # disable counting if it was enabled. old_count_value = @count @count = nil compile_query = compile # temporary store # add distinct field compile_query[:distinct] = Query.format_field(field).to_sym @count = old_count_value # perform aggregation return client.aggregate_objects(@table, compile_query.as_json, _opts).result else raise ArgumentError, "Invalid field name passed to `distinct`." end end |
#each { ... } ⇒ Array
665 666 667 668 | # File 'lib/parse/query.rb', line 665 def each return results.enum_for(:each) unless block_given? # Sparkling magic! results.each(&Proc.new) end |
#fetch!(compiled_query) ⇒ Parse::Response Also known as: execute!
Performs the fetch request for the query.
768 769 770 771 772 773 774 | # File 'lib/parse/query.rb', line 768 def fetch!(compiled_query) response = client.find_objects(@table, compiled_query.as_json, _opts) if response.error? puts "[ParseQuery] #{response.error}" end response end |
#first(limit = 1) ⇒ Parse::Object
Returns the first object from the result.
694 695 696 697 698 | # File 'lib/parse/query.rb', line 694 def first(limit = 1) @results = nil if @limit != limit @limit = limit limit == 1 ? results.first : results.first(limit) end |
#includes(*fields) ⇒ self
Set a list of Parse Pointer columns to be fetched for matching records. You may chain multiple columns with the `.` operator.
471 472 473 474 475 476 477 478 479 480 481 | # File 'lib/parse/query.rb', line 471 def includes(*fields) @includes ||= [] fields.flatten.each do |field| if field.nil? == false && field.respond_to?(:to_s) @includes.push Query.format_field(field).to_sym end end @includes.uniq! @results = nil if fields.count > 0 self # chaining end |
#keys(*fields) ⇒ self
Use this feature with caution when working with the results, as values for the fields not specified in the query will be omitted in the resulting object.
Restrict the fields returned by the query. This is useful for larger query results set where some of the data will not be used, which reduces network traffic and deserialization performance.
379 380 381 382 383 384 385 386 387 388 389 | # File 'lib/parse/query.rb', line 379 def keys(*fields) @keys ||= [] fields.flatten.each do |field| if field.nil? == false && field.respond_to?(:to_s) @keys.push Query.format_field(field).to_sym end end @keys.uniq! @results = nil if fields.count > 0 self # chaining end |
#limit(count) ⇒ self
Limit the number of objects returned by the query. The default is 100, with Parse allowing a maximum of 1000. The framework also allows a value of `:max`. Utilizing this will have the framework continually intelligently utilize `:skip` to continue to paginate through results until no more results match the query criteria. When utilizing `all()`, `:max` is the default option for `:limit`.
439 440 441 442 443 444 445 446 447 448 449 450 | # File 'lib/parse/query.rb', line 439 def limit(count) if count.is_a?(Numeric) @limit = [0, count.to_i].max elsif count == :max @limit = :max else @limit = nil end @results = nil self #chaining end |
#map { ... } ⇒ Array
673 674 675 676 | # File 'lib/parse/query.rb', line 673 def map return results.enum_for(:map) unless block_given? # Sparkling magic! results.map(&Proc.new) end |
#or_where(where_clauses = []) ⇒ Query
Combine two where clauses into an OR constraint. Equivalent to the `$or` Parse query operation. This is useful if you want to find objects that match several queries. We overload the `|` operator in order to have a clean syntax for joining these `or` operations.
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 | # File 'lib/parse/query.rb', line 585 def or_where(where_clauses = []) where_clauses = where_clauses.where if where_clauses.is_a?(Parse::Query) where_clauses = Parse::Query.new(@table, where_clauses).where if where_clauses.is_a?(Hash) return self if where_clauses.blank? # we can only have one compound query constraint. If we need to add another OR clause # let's find the one we have (if any) compound = @where.find { |f| f.is_a?(Parse::Constraint::CompoundQueryConstraint) } # create a set of clauses that are not an OR clause. remaining_clauses = @where.select { |f| f.is_a?(Parse::Constraint::CompoundQueryConstraint) == false } # if we don't have a OR clause to reuse, then create a new one with then # current set of constraints if compound.blank? compound = Parse::Constraint::CompoundQueryConstraint.new :or, [Parse::Query.compile_where(remaining_clauses)] end # then take the where clauses from the second query and append them. compound.value.push Parse::Query.compile_where(where_clauses) #compound = Parse::Constraint::CompoundQueryConstraint.new :or, [remaining_clauses, or_where_query.where] @where = [compound] self #chaining end |
#order(*ordering) ⇒ self
Add a sorting order for the query.
401 402 403 404 405 406 407 408 409 410 411 412 | # File 'lib/parse/query.rb', line 401 def order(*ordering) @order ||= [] ordering.flatten.each do |order| order = Order.new(order) if order.respond_to?(:to_sym) if order.is_a?(Order) order.field = Query.format_field(order.field) @order.push order end end #value.each @results = nil if ordering.count > 0 self #chaining end |
#prepared(includeClassName: false) ⇒ Hash
Returns a compiled query without encoding the where clause.
847 848 849 | # File 'lib/parse/query.rb', line 847 def prepared(includeClassName: false) compile(encode: false, includeClassName: includeClassName) end |
#pretty ⇒ String
Retruns a formatted JSON string representing the query, useful for debugging.
890 891 892 | # File 'lib/parse/query.rb', line 890 def pretty JSON.pretty_generate(as_json) end |
#related_to(field, pointer) ⇒ Object
452 453 454 455 456 | # File 'lib/parse/query.rb', line 452 def (field, pointer) raise ArgumentError, "Object value must be a Parse::Pointer type" unless pointer.is_a?(Parse::Pointer) add_constraint field.to_sym., pointer self #chaining end |
#results(raw: false) { ... } ⇒ Array<Hash>, Array<Parse::Object> Also known as: result
Executes the query and builds the result set of Parse::Objects that matched. When this method is passed a block, the block is yielded for each matching item in the result, and the items are not returned. This methodology is more performant as large quantifies of objects are fetched in batches and all of them do not have to be kept in memory after the query finishes executing. This is the recommended method of processing large result sets.
799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 | # File 'lib/parse/query.rb', line 799 def results(raw: false) if @results.nil? if block_given? max_results(raw: raw, &Proc.new) elsif @limit.is_a?(Numeric) response = fetch!(compile) return [] if response.error? items = raw ? response.results : decode(response.results) return items.each(&Proc.new) if block_given? @results = items else @results = max_results(raw: raw) end end @results end |
#select { ... } ⇒ Array
681 682 683 684 | # File 'lib/parse/query.rb', line 681 def select return results.enum_for(:select) unless block_given? # Sparkling magic! results.select(&Proc.new) end |
#skip(amount) ⇒ self
Use with limit to paginate through results. Default is 0.
420 421 422 423 424 | # File 'lib/parse/query.rb', line 420 def skip(amount) @skip = [0, amount.to_i].max @results = nil self #chaining end |
#where(conditions = nil, opts = {}) ⇒ self
Add additional query constraints to the `where` clause. The `where` clause is based on utilizing a set of constraints on the defined column names in your Parse classes. The constraints are implemented as method operators on field names that are tied to a value. Any symbol/string that is not one of the main expression keywords described here will be considered as a type of query constraint for the `where` clause in the query.
559 560 561 562 563 564 565 566 567 | # File 'lib/parse/query.rb', line 559 def where(conditions = nil, opts = {}) return @where if conditions.nil? if conditions.is_a?(Hash) conditions.each do |operator, value| add_constraint(operator, value, opts) end end self #chaining end |
#where_constraints ⇒ Hash
Formats the current set of Parse::Constraint instances in the where clause as an expression hash.
541 542 543 | # File 'lib/parse/query.rb', line 541 def where_constraints @where.reduce({}) { |memo, constraint| memo[constraint.operation] = constraint.value; memo } end |
#|(other_query) ⇒ Query
Returns the combined query with an OR clause.
608 609 610 611 612 613 | # File 'lib/parse/query.rb', line 608 def |(other_query) raise ArgumentError, "Parse queries must be of the same class #{@table}." unless @table == other_query.table copy_query = self.clone copy_query.or_where other_query.where copy_query end |