Module: Parse::Core::Querying
- Included in:
- Object
- Defined in:
- lib/parse/model/core/querying.rb
Overview
Defines the querying methods applied to a Parse::Object.
Instance Method Summary collapse
-
#all(constraints = { limit: :max }) { ... } ⇒ Array<Parse::Object>
Fetch all matching objects in this collection matching the constraints.
-
#count(constraints = {}) ⇒ Interger
Creates a count request which is more performant when counting objects.
-
#count_distinct(field, constraints = {}) ⇒ Integer
Counts the number of distinct values for a specified field.
-
#cursor(constraints = {}, limit: 100, order: nil) ⇒ Parse::Cursor
Create a cursor-based paginator for efficiently traversing large datasets.
-
#distinct(field, constraints = {}) ⇒ Array
Finds the distinct values for a specified field across a single collection or view and returns the results in an array.
-
#each(constraints = {}) { ... } ⇒ Parse::Object
This methods allow you to efficiently iterate over all the records in the collection (lower memory cost) at a minor cost of performance.
-
#find(*parse_ids, type: :parallel, compact: true, cache: nil) ⇒ Parse::Object+
(also: #get)
Find objects for a given objectId in this collection.
-
#find_cached(*parse_ids, type: :parallel, compact: true) ⇒ Parse::Object+
Find objects with caching enabled.
-
#first(constraints = {}) ⇒ Object
Returns the first item matching the constraint.
-
#last_updated(constraints = {}) ⇒ Object
Returns the most recently updated object (ordered by updated_at descending).
-
#latest(constraints = {}) ⇒ Object
Returns the most recently created object (ordered by created_at descending).
- #literal_where(conditions = {}) ⇒ self
-
#newest(constraints = {}) ⇒ Array<Parse::Object>
Find objects matching the constraint ordered by the descending created_at date.
-
#oldest(constraints = {}) ⇒ Array<Parse::Object>
Find objects matching the constraint ordered by the ascending created_at date.
-
#query(constraints = {}) ⇒ Parse::Query
(also: #where)
Creates a new Query with the given constraints for this class.
-
#scope(name, body) ⇒ Symbol
This feature is a small subset of the ActiveRecord named scopes feature.
-
#subscribe(where: {}, fields: nil, session_token: nil, client: nil) ⇒ Parse::LiveQuery::Subscription
Subscribe to real-time updates for objects in this collection.
Instance Method Details
#all(constraints = { limit: :max }) { ... } ⇒ Array<Parse::Object>
This method will continually query for records by automatically incrementing the :skip parameter until no more results are returned by the server.
Fetch all matching objects in this collection matching the constraints. This will be the most common way when querying Parse objects for a subclass. When no block is passed, all objects are returned. Using a block is more memory efficient as matching objects are fetched in batches and discarded after the iteration is completed.
211 212 213 214 215 216 |
# File 'lib/parse/model/core/querying.rb', line 211 def all(constraints = { limit: :max }, &block) constraints = constraints.reverse_merge({ limit: :max }) prepared_query = query(constraints) return prepared_query.results(&block) if block_given? prepared_query.results end |
#count(constraints = {}) ⇒ Interger
Creates a count request which is more performant when counting objects.
307 308 309 |
# File 'lib/parse/model/core/querying.rb', line 307 def count(constraints = {}) query(constraints).count end |
#count_distinct(field, constraints = {}) ⇒ Integer
Counts the number of distinct values for a specified field. Uses MongoDB aggregation pipeline to efficiently count unique values.
322 323 324 |
# File 'lib/parse/model/core/querying.rb', line 322 def count_distinct(field, constraints = {}) query(constraints).count_distinct(field) end |
#cursor(constraints = {}, limit: 100, order: nil) ⇒ Parse::Cursor
Create a cursor-based paginator for efficiently traversing large datasets. This is more efficient than skip/offset pagination for large result sets.
377 378 379 |
# File 'lib/parse/model/core/querying.rb', line 377 def cursor(constraints = {}, limit: 100, order: nil) query(constraints).cursor(limit: limit, order: order) end |
#distinct(field, constraints = {}) ⇒ Array
Finds the distinct values for a specified field across a single collection or view and returns the results in an array.
335 336 337 |
# File 'lib/parse/model/core/querying.rb', line 335 def distinct(field, constraints = {}) query(constraints).distinct(field) end |
#each(constraints = {}) { ... } ⇒ Parse::Object
You cannot use :created_at as a constraint.
This methods allow you to efficiently iterate over all the records in the collection (lower memory cost) at a minor cost of performance. This method utilizes the ‘created_at` field of Parse records to order and iterate over all matching records, therefore you should not use this method if you want to perform a query with constraints against the `created_at` field or need specific type of ordering. If you need to use `:created_at` in your constraints, consider using #all or Actions::ClassMethods#save_all
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/parse/model/core/querying.rb', line 151 def each(constraints = {}, &block) # verify we don't hvae created at as a constraint, otherwise this will not work invalid_constraints = constraints.keys.any? do |k| (k == :created_at || k == :createdAt) || (k.is_a?(Parse::Operation) && (k.operand == :created_at || k.operand == :createdAt)) end if invalid_constraints raise ArgumentError, "[#{self.class}.each] Special method each()" \ "cannot be used with a :created_at constraint." end batch_size = 250 start_cursor = first(order: :created_at.asc, keys: :created_at) constraints.merge! cache: false, limit: batch_size, order: :created_at.asc _all_query = query(constraints) # used for reference in loop below cursor = start_cursor # the exclusion set is a set of ids not to include the next query. exclusion_set = [] loop do _q = query(constraints.dup) _q.where(:created_at.on_or_after => cursor.created_at) # set of ids not to include in the next query. non-performant, but accurate. _q.where(:id.nin => exclusion_set) unless exclusion_set.empty? results = _q.results # get results break cursor if results.empty? # break if no results results.each(&block) next_cursor = results.last # break if we got less than the maximum requested break next_cursor if results.count < batch_size # break if the next object is the same as the current object. break next_cursor if cursor.id == next_cursor.id # The exclusion set is used in the case where multiple records have the exact # same created_at date (down to the microsecond). This prevents getting the same # record in the next query request. exclusion_set = results.select { |r| r.created_at == next_cursor.created_at }.map(&:id) results = nil cursor = next_cursor end end |
#find(*parse_ids, type: :parallel, compact: true, cache: nil) ⇒ Parse::Object+ Also known as: get
Find objects for a given objectId in this collection. The result is a list (or single item) of the objects that were successfully found. By default, bypasses the cache to ensure fresh data from the server.
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 |
# File 'lib/parse/model/core/querying.rb', line 433 def find(*parse_ids, type: :parallel, compact: true, cache: nil) # flatten the list of Object ids. parse_ids.flatten! parse_ids.compact! # determines if the result back to the call site is an array or a single result as_array = parse_ids.count > 1 results = [] # Default to write-only cache mode - find always gets fresh data # but updates cache for future cached reads. Controlled by feature flag. if cache.nil? cache = Parse.cache_write_on_fetch ? :write_only : false end # Extract cache option for client requests client_opts = { cache: cache } if type == :batch # use a .in query with the given id as a list query = self.class.query(:id.in => parse_ids) query.cache = cache results = query.results else # use Parallel to make multiple threaded requests for finding these objects. # The benefit of using this as default is that each request goes to a specific URL # which is better than Query request (table scan). This in turn allows for caching of # individual objects. results = parse_ids.threaded_map do |parse_id| next nil unless parse_id.present? response = client.fetch_object(parse_class, parse_id, **client_opts) next nil if response.error? Parse::Object.build response.result, parse_class end end # removes any nil items in the array results.compact! if compact as_array ? results : results.first end |
#find_cached(*parse_ids, type: :parallel, compact: true) ⇒ Parse::Object+
Find objects with caching enabled. This is a convenience method that calls find with cache: true.
486 487 488 |
# File 'lib/parse/model/core/querying.rb', line 486 def find_cached(*parse_ids, type: :parallel, compact: true) find(*parse_ids, type: type, compact: compact, cache: true) end |
#first(count = 1) ⇒ Parse::Object+ #first(constraints = {}) ⇒ Parse::Object
Returns the first item matching the constraint.
230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/parse/model/core/querying.rb', line 230 def first(constraints = {}) fetch_count = 1 if constraints.is_a?(Numeric) fetch_count = constraints.to_i constraints = {} end constraints.merge!({ limit: fetch_count }) res = query(constraints).results return res.first if fetch_count == 1 return res.first fetch_count end |
#last_updated(count = 1) ⇒ Parse::Object+ #last_updated(constraints = {}) ⇒ Parse::Object
Returns the most recently updated object (ordered by updated_at descending).
285 286 287 288 289 290 291 292 293 294 295 296 297 298 |
# File 'lib/parse/model/core/querying.rb', line 285 def last_updated(constraints = {}) fetch_count = 1 if constraints.is_a?(Numeric) fetch_count = constraints.to_i constraints = {} else # Allow limit to be specified in constraints hash fetch_count = constraints.delete(:limit) || 1 end constraints.merge!({ limit: fetch_count, order: :updated_at.desc }) res = query(constraints).results return res.first if fetch_count == 1 return res.first fetch_count end |
#latest(count = 1) ⇒ Parse::Object+ #latest(constraints = {}) ⇒ Parse::Object
Returns the most recently created object (ordered by created_at descending).
256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
# File 'lib/parse/model/core/querying.rb', line 256 def latest(constraints = {}) fetch_count = 1 if constraints.is_a?(Numeric) fetch_count = constraints.to_i constraints = {} else # Allow limit to be specified in constraints hash fetch_count = constraints.delete(:limit) || 1 end constraints.merge!({ limit: fetch_count, order: :created_at.desc }) res = query(constraints).results return res.first if fetch_count == 1 return res.first fetch_count end |
#literal_where(conditions = {}) ⇒ self
126 127 128 |
# File 'lib/parse/model/core/querying.rb', line 126 def literal_where(conditions = {}) query.where(conditions) end |
#newest(constraints = {}) ⇒ Array<Parse::Object>
Find objects matching the constraint ordered by the descending created_at date.
342 343 344 345 346 347 |
# File 'lib/parse/model/core/querying.rb', line 342 def newest(constraints = {}) constraints.merge!(order: :created_at.desc) _q = query(constraints) _q.define_singleton_method(:method_missing) { |m, *args, &block| self.results.send(m, *args, &block) } _q end |
#oldest(constraints = {}) ⇒ Array<Parse::Object>
Find objects matching the constraint ordered by the ascending created_at date.
352 353 354 355 356 357 |
# File 'lib/parse/model/core/querying.rb', line 352 def oldest(constraints = {}) constraints.merge!(order: :created_at.asc) _q = query(constraints) _q.define_singleton_method(:method_missing) { |m, *args, &block| self.results.send(m, *args, &block) } _q end |
#query(constraints = {}) ⇒ Parse::Query Also known as: where
Creates a new Query with the given constraints for this class.
117 118 119 |
# File 'lib/parse/model/core/querying.rb', line 117 def query(constraints = {}) Parse::Query.new self.parse_class, constraints end |
#scope(name, body) ⇒ Symbol
This feature is a small subset of the ActiveRecord named scopes feature. Scoping allows you to specify commonly-used queries which can be referenced as class method calls and are chainable with other scopes. You can use every Query method previously covered such as ‘where`, `includes` and `limit`.
class Article < Parse::Object
property :published, :boolean
scope :published, -> { query(published: true) }
end
This is the same as defining your own class method for the query.
class Article < Parse::Object
def self.published
query(published: true)
end
end
You can also chain scopes and pass parameters. In addition, boolean and enumerated properties have automatically generated scopes for you to use.
class Article < Parse::Object
scope :published, -> { query(published: true) }
property :comment_count, :integer
property :category
property :approved, :boolean
scope :published_and_commented, -> { published.where :comment_count.gt => 0 }
scope :popular_topics, ->(name) { published_and_commented.where category: name }
end
# simple scope
Article.published # => where published is true
# chained scope
Article.published_and_commented # published is true and comment_count > 0
# scope with parameters
Article.popular_topic("music") # => popular music articles
# equivalent: where(published: true, :comment_count.gt => 0, category: name)
# automatically generated scope
Article.approved(category: "tour") # => where approved: true, category: 'tour'
If you would like to turn off automatic scope generation for property types, set the option ‘:scope` to false when declaring the property.
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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/parse/model/core/querying.rb', line 64 def scope(name, body) unless body.respond_to?(:call) raise ArgumentError, "The scope body needs to be callable." end name = name.to_sym if respond_to?(name, true) puts "Creating scope :#{name}. Will overwrite existing method #{self}.#{name}." end define_singleton_method(name) do |*args, &block| if body.arity.zero? res = body.call res.conditions(*args) if args.present? else res = body.call(*args) end _q = res || query if _q.is_a?(Parse::Query) klass = self _q.define_singleton_method(:method_missing) do |m, *args, &chained_block| if klass.respond_to?(m, true) # must be a scope klass_scope = klass.send(m, *args) if klass_scope.is_a?(Parse::Query) # merge constraints add_constraints(klass_scope.constraints) # if a block was passed, execute the query, otherwise return the query return chained_block.present? ? results(&chained_block) : self end # if klass = nil # help clean up ruby gc return klass_scope end klass = nil # help clean up ruby gc return results.send(m, *args, &chained_block) end end Parse::Query.apply_auto_introspection!(_q) return _q if block.nil? _q.results(&block) end end |
#subscribe(where: {}, fields: nil, session_token: nil, client: nil) ⇒ Parse::LiveQuery::Subscription
Subscribe to real-time updates for objects in this collection. Uses Parse LiveQuery WebSocket connection to receive push notifications when objects are created, updated, deleted, or enter/leave the query results.
409 410 411 |
# File 'lib/parse/model/core/querying.rb', line 409 def subscribe(where: {}, fields: nil, session_token: nil, client: nil) query(where).subscribe(fields: fields, session_token: session_token, client: client) end |