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

Instance Method Details

#all(constraints = { limit: :max }) { ... } ⇒ Array<Parse::Object>

Note:

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.

Examples:


songs = Song.all( ... expressions ...) # => array of Parse::Objects
# memory efficient for large amounts of records.
Song.all( ... expressions ...) do |song|
    # ... do something with song..
end

Parameters:

  • constraints (Hash) (defaults to: { limit: :max })

    a set of Query constraints.

Yields:

  • a block to iterate with each matching object.

Returns:

  • (Array<Parse::Object>)

    an array of matching objects. If a block is passed, an empty array is returned.



281
282
283
284
285
286
# File 'lib/parse/model/core/querying.rb', line 281

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

#all_as(token, constraints = { limit: :max }, &block) ⇒ Array<Parse::Object>

Convenience wrapper around #all that runs the query under a caller-supplied session token. Equivalent to passing session_token: in the constraints hash, surfaced as a named kwarg so client-mode callers don't have to remember the constraint-key form. Returns nil if token is blank.

Parameters:

Returns:



297
298
299
300
301
# File 'lib/parse/model/core/querying.rb', line 297

def all_as(token, constraints = { limit: :max }, &block)
  tok = token.respond_to?(:session_token) ? token.session_token : token
  return nil if tok.nil? || tok.to_s.empty?
  all(constraints.merge(session_token: tok), &block)
end

#count(constraints = {}) ⇒ Interger

Creates a count request which is more performant when counting objects.

Examples:

# number of songs with a like count greater than 20.
count = Song.count( :like_count.gt => 20 )

Parameters:

  • constraints (Hash) (defaults to: {})

    a set of Query constraints.

Returns:

  • (Interger)

    the number of records matching the query.

See Also:



409
410
411
# File 'lib/parse/model/core/querying.rb', line 409

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.

Examples:

# get count of unique genres for songs with play_count > 100
distinct_genres_count = Song.count_distinct(:genre, :play_count.gt => 100)
# get total number of unique users
unique_users = User.count_distinct(:objectId)

Parameters:

  • field (Symbol|String)

    The name of the field to count distinct values for.

  • constraints (Hash) (defaults to: {})

    a set of Query constraints.

Returns:

  • (Integer)

    the number of distinct values

See Also:



424
425
426
# File 'lib/parse/model/core/querying.rb', line 424

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.

Examples:

Basic usage

cursor = Song.cursor(limit: 100, order: :created_at.desc)
cursor.each_page do |page|
  process(page)
end

With constraints

cursor = Song.cursor(artist: "Artist Name", limit: 50)
cursor.each { |song| puts song.title }

Parameters:

  • constraints (Hash) (defaults to: {})

    query constraints to apply

  • limit (Integer) (defaults to: 100)

    number of items per page (default: 100)

  • order (Symbol, Parse::Order) (defaults to: nil)

    the ordering for pagination

Returns:

See Also:



508
509
510
# File 'lib/parse/model/core/querying.rb', line 508

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.

Examples:

# get a list of unique city names for users who are older than 21.
cities = User.distinct(:city, :age.gt => 21 )

Parameters:

  • field

    The name of the field to use for unique aggregation.

  • constraints (Hash) (defaults to: {})

    a set of Query constraints.

Returns:

  • (Array)

    a list of distinct values

See Also:



437
438
439
# File 'lib/parse/model/core/querying.rb', line 437

def distinct(field, constraints = {})
  query(constraints).distinct(field)
end

#each(constraints = {}) { ... } ⇒ Parse::Object

Note:

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

Examples:


post = Post.first
# iterate over all comments matching conditions
Comment.each(post: post) do |comment|
   # ...
end

Parameters:

  • constraints (Hash) (defaults to: {})

    a set of query constraints.

Yields:

  • a block which will iterate through each matching record.

Returns:

Raises:

  • ArgumentError if :created_at is detected in the constraints argument.

See Also:

  • all
  • Actions.save_all


221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/parse/model/core/querying.rb', line 221

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, session_token: nil, use_master_key: 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.

Examples:

Object.find "<objectId>"
Object.find "<objectId>", "<objectId>"....
Object.find ["<objectId>", "<objectId>"]
Object.find "<objectId>", cache: true  # opt-in to cache

Parameters:

  • parse_ids (String)

    the objectId to find.

  • type (Symbol) (defaults to: :parallel)

    the fetching methodology to use if more than one id was passed.

    • :parallel : Utilizes parrallel HTTP requests to fetch all objects requested.
    • :batch : This uses a batch fetch request using a contained_in clause.
  • compact (Boolean) (defaults to: true)

    whether to remove nil items from the returned array for objects that were not found.

  • cache (Boolean, Symbol) (defaults to: nil)

    caching mode. Defaults to :write_only when Parse.cache_write_on_fetch is true.

    • :write_only (default) - skip cache read, but update cache with fresh data
    • true - read from and write to cache
    • false - completely bypass cache (no read or write)

Returns:



595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
# File 'lib/parse/model/core/querying.rb', line 595

def find(*parse_ids, type: :parallel, compact: true, cache: nil, session_token: nil, use_master_key: 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 }
  # Forward session-token / use_master_key when supplied so client-mode
  # callers can scope a `.find` to a logged-in user without dropping
  # down to the raw `client.fetch_object` form.
  client_opts[:session_token]  = session_token  unless session_token.nil?
  client_opts[:use_master_key] = use_master_key unless use_master_key.nil?
  # The parallel path spawns worker threads via `Parallel.map`. Worker
  # threads don't inherit fiber-local storage from the calling thread,
  # so `Parse.current_session_token` resolved inside the worker would
  # be nil even when the caller is inside a `Parse.with_session(...)`
  # block. Snapshot the ambient here (in the calling thread) and pass
  # it explicitly so each worker sends the right auth.
  if !client_opts.key?(:session_token)
    ambient = Parse.current_session_token
    client_opts[:session_token] = ambient if ambient.is_a?(String) && !ambient.empty?
  end

  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
    query.session_token = session_token unless session_token.nil?
    query.use_master_key = use_master_key unless use_master_key.nil?
    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.

Examples:

Object.find_cached "<objectId>"
Object.find_cached "<objectId>", "<objectId>"....

Parameters:

  • parse_ids (String)

    the objectId(s) to find.

  • type (Symbol) (defaults to: :parallel)

    the fetching methodology (:parallel or :batch).

  • compact (Boolean) (defaults to: true)

    whether to remove nil items from the returned array.

Returns:

See Also:



665
666
667
# File 'lib/parse/model/core/querying.rb', line 665

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.

Overloads:

  • #first(count = 1) ⇒ Parse::Object+

    Examples:

    Object.first(2) # => an array of the first 2 objects in the collection.

    Parameters:

    • count (Interger) (defaults to: 1)

      The number of items to return.

    Returns:

  • #first(constraints = {}) ⇒ Parse::Object

    Returns the first matching object.

    Examples:

    Object.first( :name => "Anthony" )

    Parameters:

    • constraints (Hash) (defaults to: {})

      a set of Query constraints.

    Returns:



315
316
317
318
319
320
321
322
323
324
325
# File 'lib/parse/model/core/querying.rb', line 315

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

#first_as(token, constraints = {}) ⇒ Parse::Object, ...

Convenience wrapper around #first that runs the query under a caller-supplied session token.

Parameters:

Returns:



333
334
335
336
337
338
339
340
341
342
# File 'lib/parse/model/core/querying.rb', line 333

def first_as(token, constraints = {})
  tok = token.respond_to?(:session_token) ? token.session_token : token
  return nil if tok.nil? || tok.to_s.empty?
  if constraints.is_a?(Numeric)
    # `first(2)` shape — surface kwarg via a synthetic constraints hash
    first({ limit: constraints.to_i, session_token: tok })
  else
    first(constraints.merge(session_token: tok))
  end
end

#group_by(field, **opts) ⇒ Parse::GroupBy, Parse::SortableGroupBy

Groups records by a field and returns a GroupBy (or SortableGroupBy) aggregation object you can call .count, .sum, .average, etc. on.

Examples:

Post.group_by(:category).count
Post.group_by(:category, sortable: true).count.sort_by_value_desc

Parameters:

  • field (Symbol, String)

    the field to group by.

  • opts

    keyword arguments forwarded to Parse::Query#group_by (flatten_arrays:, sortable:, return_pointers:, mongo_direct:).

Returns:

See Also:



451
452
453
# File 'lib/parse/model/core/querying.rb', line 451

def group_by(field, **opts)
  query.group_by(field, **opts)
end

#group_by_date(field, interval, **opts) ⇒ Parse::GroupByDate, Parse::SortableGroupByDate

Groups records by a date field truncated to the given interval and returns a GroupByDate (or SortableGroupByDate) aggregation object.

Examples:

Post.group_by_date(:created_at, :month).count
Post.group_by_date(:created_at, :day, sortable: true).count.sort_by_value_desc

Parameters:

  • field (Symbol, String)

    the date field to group by.

  • interval (Symbol)

    one of :year, :month, :week, :day, :hour, :minute, :second.

  • opts

    keyword arguments forwarded to Parse::Query#group_by_date (sortable:, return_pointers:, timezone:, mongo_direct:).

Returns:

See Also:



466
467
468
# File 'lib/parse/model/core/querying.rb', line 466

def group_by_date(field, interval, **opts)
  query.group_by_date(field, interval, **opts)
end

#last_updated(count = 1) ⇒ Parse::Object+ #last_updated(constraints = {}) ⇒ Parse::Object

Returns the most recently updated object (ordered by updated_at descending).

Overloads:

  • #last_updated(count = 1) ⇒ Parse::Object+

    Examples:

    Object.last_updated(5) # => an array of the 5 most recently updated objects.

    Parameters:

    • count (Integer) (defaults to: 1)

      The number of items to return.

    Returns:

  • #last_updated(constraints = {}) ⇒ Parse::Object

    Returns the most recently updated object matching constraints.

    Examples:

    Object.last_updated(status: "active") # => most recently updated active object
    Object.last_updated(:user.eq => user, limit: 3) # => 3 most recently updated for user

    Parameters:

    • constraints (Hash) (defaults to: {})

      a set of Query constraints. Supports a :limit key to override the default limit of 1.

    Returns:

    • (Parse::Object)

      the most recently updated object matching constraints.



387
388
389
390
391
392
393
394
395
396
397
398
399
400
# File 'lib/parse/model/core/querying.rb', line 387

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).

Overloads:

  • #latest(count = 1) ⇒ Parse::Object+

    Examples:

    Object.latest(3) # => an array of the 3 most recently created objects.

    Parameters:

    • count (Integer) (defaults to: 1)

      The number of items to return.

    Returns:

  • #latest(constraints = {}) ⇒ Parse::Object

    Returns the most recently created object matching constraints.

    Examples:

    Object.latest(category: "news") # => most recent object in news category
    Object.latest(:user.eq => user, limit: 5) # => 5 most recent for user

    Parameters:

    • constraints (Hash) (defaults to: {})

      a set of Query constraints. Supports a :limit key to override the default limit of 1.

    Returns:

    • (Parse::Object)

      the most recently created object matching constraints.



358
359
360
361
362
363
364
365
366
367
368
369
370
371
# File 'lib/parse/model/core/querying.rb', line 358

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

Parameters:

  • conditions (Hash) (defaults to: {})

    a set of constraints for this query.

Returns:

  • (self)

See Also:



196
197
198
# File 'lib/parse/model/core/querying.rb', line 196

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.

Parameters:

  • constraints (Hash) (defaults to: {})

    a set of Query constraints.

Returns:



473
474
475
476
477
478
# File 'lib/parse/model/core/querying.rb', line 473

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.

Parameters:

  • constraints (Hash) (defaults to: {})

    a set of Query constraints.

Returns:



483
484
485
486
487
488
# File 'lib/parse/model/core/querying.rb', line 483

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

#pluralized_alias!(constant_name = nil) ⇒ self?

Define a pluralized constant alias for this class so the plural form can be used as a query entry point — e.g. Posts.where(...).count for a class Post. The alias is the same class object, so every class method (where, query, count, find, all, scopes) works through it and Posts.parse_class still returns "Post".

This is the explicit counterpart to the automatic Parse.pluralized_aliases behavior. Use it when automatic aliasing is disabled, when the class name ends in s (which the automatic path skips), when you want a custom plural, or for namespaced models (the alias is defined on the enclosing module, not at top level).

Examples:

default plural

class Post < Parse::Object
  pluralized_alias!          # defines ::Posts => Post
end

custom plural for a name ending in s

class Status < Parse::Object
  pluralized_alias! :Statuses
end

namespaced model

module Blog
  class Post < Parse::Object
    pluralized_alias!        # defines Blog::Posts => Blog::Post
  end
end

Parameters:

  • constant_name (Symbol, String, nil) (defaults to: nil)

    the plural constant to define; defaults to the ActiveSupport pluralization of the class's demodulized name.

Returns:

  • (self, nil)

    self when an alias exists/was created; nil if the class is anonymous or the plural matches the singular name.

Raises:

  • (ArgumentError)

    if the target constant already exists and is not this class.



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
190
191
# File 'lib/parse/model/core/querying.rb', line 159

def pluralized_alias!(constant_name = nil)
  base = name
  return nil if base.nil?
  parts = base.split("::")
  short = parts.last
  plural = (constant_name && constant_name.to_s) || short.pluralize
  return nil if plural == short
  # NOTE: bare `Object` here would lexically resolve to `Parse::Object`
  # (we are inside module Parse::Core::Querying), so the alias must be
  # anchored at the true top level with `::Object`.
  parent = parts.length > 1 ? parts[0..-2].join("::").constantize : ::Object
  if parent.const_defined?(plural.to_sym, false)
    existing = parent.const_get(plural.to_sym)
    return self if existing.equal?(self)
    # A code reloader (Zeitwerk in development) swaps `self` for a fresh
    # class object but does not clean up the alias constant we set — it
    # owns no autoload entry for it. On re-run of the class body the
    # plural still points at the now-orphaned previous class. Re-point it
    # to the current class instead of raising on every reload. Only a
    # genuinely foreign constant (not a Parse model mapping to the same
    # remote class) is treated as a conflict.
    stale_reload = existing.is_a?(Class) && existing < Parse::Object &&
                   existing.parse_class == parse_class
    unless stale_reload
      raise ArgumentError,
            "Cannot define pluralized alias #{plural} for #{base}: " \
            "constant already defined as #{existing}."
    end
    parent.send(:remove_const, plural.to_sym)
  end
  parent.const_set(plural.to_sym, self)
  self
end

#query(constraints = {}) ⇒ Parse::Query Also known as: where

Creates a new Query with the given constraints for this class.

Examples:

# assume Post < Parse::Object
query = Post.query(:updated_at.before => DateTime.now)

Returns:

  • (Parse::Query)

    a new query with the given constraints for this Parse::Object subclass.



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.

Parameters:

  • name (Symbol)

    the name of the scope.

  • body (Proc)

    the proc related to the scope.

Returns:

  • (Symbol)

    the name of the singleton method created.

Raises:

  • ArgumentError if body parameter does not respond to call



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, use_master_key: false) {|subscription| ... } ⇒ 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.

Examples:

Basic subscription (all objects)

subscription = Song.subscribe
subscription.on(:create) { |song| puts "New song: #{song.title}" }
subscription.on(:update) { |song, original| puts "Updated!" }
subscription.on(:delete) { |song| puts "Deleted!" }

Subscribe with query constraints

subscription = Song.subscribe(where: { artist: "Beatles" })
subscription.on_create { |song| puts "New Beatles song!" }

With field filtering

subscription = User.subscribe(where: { status: "online" }, fields: ["name", "avatar"])
subscription.on_update { |user| puts "User changed: #{user.name}" }

With session token for ACL-aware subscriptions

subscription = PrivateData.subscribe(session_token: current_user.session_token)

block form (ergonomic, no race window)

Post.subscribe(where: { published: true }) do |sub|
  sub.on(:create) { |obj| puts "new: #{obj.id}" }
  sub.on(:update) { |obj, prev| puts "updated: #{obj.id}" }
end

capture-then-wire form (equivalent, but has a tiny

window between subscribe-frame send and the first .on call
where a server event would be dropped if it arrived first)
sub = Post.subscribe(where: { published: true })
sub.on(:create) { |obj| … }

Parameters:

  • where (Hash) (defaults to: {})

    query constraints for the subscription

  • fields (Array<String>) (defaults to: nil)

    specific fields to watch for changes (nil = all fields)

  • session_token (String) (defaults to: nil)

    session token for ACL-aware subscriptions

  • client (Parse::LiveQuery::Client) (defaults to: nil)

    custom LiveQuery client (optional)

  • use_master_key (Boolean) (defaults to: false)

    per-subscription master-key opt-in. See Query#subscribe for the full description.

Yields:

  • (subscription)

    runs the block with the freshly-constructed LiveQuery::Subscription BEFORE the subscribe frame is sent so caller-registered callbacks are wired before any server events can arrive. Optional — callers may still capture the returned subscription and register callbacks later.

Returns:

See Also:



557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
# File 'lib/parse/model/core/querying.rb', line 557

def subscribe(where: {}, fields: nil, session_token: nil, client: nil,
              use_master_key: false, &block)
  # Fall through to the ambient set by `Parse.with_session` / `Parse.login`
  # so a caller wrapping a region with `with_session(user) { Klass.subscribe ... }`
  # gets an ACL-aware subscription without re-threading the token.
  if session_token.nil?
    ambient = Parse.current_session_token
    session_token = ambient if ambient.is_a?(String) && !ambient.empty?
  end
  query(where).subscribe(
    fields: fields,
    session_token: session_token,
    client: client,
    use_master_key: use_master_key,
    &block
  )
end