lib/parse/query.rb
# encoding: utf-8
module Parse
# Query objects
# https://parseplatform.github.io/docs/rest/guide/#queries
class Query
attr_accessor :where
attr_accessor :class_name
attr_accessor :order_by
attr_accessor :order
attr_accessor :limit
attr_accessor :skip
attr_accessor :count
attr_accessor :include
attr_accessor :client
attr_accessor :keys
def initialize(cls_name, client = nil)
@class_name = cls_name
@where = {}
@order = :ascending
@ors = []
@client = client || Parse.client
end
def add_constraint(field, constraint)
raise ArgumentError, 'cannot add constraint to an $or query' unless @ors.empty?
current = where[field]
if current && current.is_a?(Hash) && constraint.is_a?(Hash)
current.merge! constraint
else
where[field] = constraint
end
end
def or(query)
raise ArgumentError, "you must pass an entire #{self.class} to \#or" unless query.is_a?(self.class)
@ors << query
self
end
def eq(hash_or_field, value = nil)
return eq_pair(hash_or_field, value) unless hash_or_field.is_a?(Hash)
hash_or_field.each_pair { |k, v| eq_pair k, v }
self
end
def not_eq(field, value)
add_constraint field, '$ne' => Parse.pointerize_value(value)
self
end
def regex(field, expression)
add_constraint field, '$regex' => expression
self
end
def less_than(field, value)
add_constraint field, '$lt' => Parse.pointerize_value(value)
self
end
def less_eq(field, value)
add_constraint field, '$lte' => Parse.pointerize_value(value)
self
end
def greater_than(field, value)
add_constraint field, '$gt' => Parse.pointerize_value(value)
self
end
def greater_eq(field, value)
add_constraint field, '$gte' => Parse.pointerize_value(value)
self
end
def value_in(field, values)
add_constraint(
field, '$in' => values.map { |v| Parse.pointerize_value(v) })
self
end
def value_not_in(field, values)
add_constraint(
field, '$nin' => values.map { |v| Parse.pointerize_value(v) })
self
end
def contains_all(field, values)
add_constraint(
field, '$all' => values.map { |v| Parse.pointerize_value(v) })
self
end
def related_to(field, value)
h = { 'object' => Parse.pointerize_value(value), 'key' => field }
add_constraint('$relatedTo', h)
end
def exists(field, value = true)
add_constraint field, '$exists' => value
self
end
def in_query(field, query = nil)
query_hash = {
Parse::Protocol::KEY_CLASS_NAME => query.class_name,
'where' => query.where }
add_constraint(field, '$inQuery' => query_hash)
self
end
def count
@count = true
self
end
def where_as_json
if @ors.empty?
@where
else
{ '$or' => [where] + @ors.map(&:where_as_json) }
end
end
def first
self.limit = 1
get.first
end
def get
uri = if @class_name == Parse::Protocol::CLASS_USER
Protocol.user_uri
elsif @class_name == Parse::Protocol::CLASS_INSTALLATION
Protocol.installation_uri
else
Protocol.class_uri @class_name
end
query = { 'where' => where_as_json.to_json }
ordering(query)
[:count, :limit, :skip, :include, :keys].each do |a|
merge_attribute(a, query)
end
@client.logger.info { "Parse query for #{uri} #{query.inspect}" } unless @client.quiet
response = @client.request uri, :get, nil, query
if response.is_a?(Hash) && response.key?(Protocol::KEY_RESULTS) && response[Protocol::KEY_RESULTS].is_a?(Array)
parsed_results = response[Protocol::KEY_RESULTS].map do |result|
result = Parse.parse_json(class_name, result)
Parse.copy_client(@client, result)
end
if response.keys.size == 1
parsed_results
else
response.dup.merge(Protocol::KEY_RESULTS => parsed_results)
end
else
raise ParseError, "query response not a Hash with #{Protocol::KEY_RESULTS} key: #{response.class} #{response.inspect}"
end
end
private
def eq_pair(field, value)
add_constraint field, Parse.pointerize_value(value)
self
end
def ordering(query)
return unless @order_by
order_string = @order_by
order_string = "-#{order_string}" if @order == :descending
query[:order] = order_string
end
def merge_attribute(attribute, query, query_field = nil)
value = instance_variable_get("@#{attribute}")
return if value.nil?
to_merge = query_field || attribute
query[to_merge] = value
end
end
end