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.
- #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) ⇒ Parse::Object+ (also: #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.
- #first(constraints = {}) ⇒ Object
Returns the first item matching the constraint.
- #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.
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.
210 211 212 213 214 215 | # File 'lib/parse/model/core/querying.rb', line 210 def all(constraints = { limit: :max }) constraints = constraints.reverse_merge({ limit: :max }) prepared_query = query(constraints) return prepared_query.results(&Proc.new) if block_given? prepared_query.results end |
#count(constraints = {}) ⇒ Interger
Creates a count request which is more performant when counting objects.
248 249 250 | # File 'lib/parse/model/core/querying.rb', line 248 def count(constraints = {}) query(constraints).count 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.
261 262 263 | # File 'lib/parse/model/core/querying.rb', line 261 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
150 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 | # File 'lib/parse/model/core/querying.rb', line 150 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) 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) ⇒ 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.
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 | # File 'lib/parse/model/core/querying.rb', line 299 def find(*parse_ids, type: :parallel, compact: true) # 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 = [] if type == :batch # use a .in query with the given id as a list results = self.class.all(:id.in => parse_ids) 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) 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 |
#first(count = 1) ⇒ Parse::Object+ #first(constraints = {}) ⇒ Parse::Object
Returns the first item matching the constraint.
229 230 231 232 233 234 235 236 237 238 239 | # File 'lib/parse/model/core/querying.rb', line 229 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 |
#literal_where(conditions = {}) ⇒ self
125 126 127 | # File 'lib/parse/model/core/querying.rb', line 125 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.
268 269 270 271 272 273 | # File 'lib/parse/model/core/querying.rb', line 268 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.
278 279 280 281 282 283 | # File 'lib/parse/model/core/querying.rb', line 278 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 |