/
query.rb
182 lines (146 loc) · 4.75 KB
/
query.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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
class Query
def initialize(klass)
@klass = klass
end
def criteria
@criteria ||= { :conditions => {} }
end
def where(args)
criteria[:conditions].merge!(args)
self
end
def limit(limit)
# If > 1000, set chunking, because large queries over 1000 need it with Parse
chunk(1000) if limit > 1000
criteria[:limit] = limit
self
end
def include_object(parent)
criteria[:include] = parent
self
end
def order(attr)
orders = attr.split(" ")
if orders.count > 1
criteria[:order] = orders.last.downcase == "desc" ? "-#{orders.first}" : "#{orders.first}"
else
criteria[:order] = orders.first
end
self
end
def skip(skip)
criteria[:skip] = skip
self
end
def count(count=1)
criteria[:count] = count
all
end
# Divides the query into multiple chunks if you're running into RestClient::BadRequest errors.
def chunk(count=100)
criteria[:chunk] = count
self
end
def near(klass, geo_point, options)
if geo_point.is_a? Array
geo_point = ParseGeoPoint.new :latitude => geo_point[0], :longitude => geo_point[1]
end
query = { "$nearSphere" => geo_point.to_pointer }
if options[:maxDistanceInMiles]
query["$maxDistanceInMiles"] = options[:maxDistanceInMiles]
elsif options[:maxDistanceInRadians]
query["$maxDistanceInRadians"] = options[:maxDistanceInRadians]
elsif options[:maxDistanceInKilometers]
query["$maxDistanceInKilometers"] = options[:maxDistanceInKilometers]
end
criteria[:conditions].merge!({ klass => query })
self
end
def within_box(klass, geo_point_south, geo_point_north)
if geo_point_south.is_a? Array
geo_point_south = ParseGeoPoint.new :latitude => geo_point_south[0], :longitude => geo_point_south[1]
end
if geo_point_north.is_a? Array
geo_point_north = ParseGeoPoint.new :latitude => geo_point_north[0], :longitude => geo_point_north[1]
end
query = { "$within" => { "$box" => [geo_point_south.to_pointer, geo_point_north.to_pointer]}}
criteria[:conditions].merge!({ klass => query })
self
end
def related_to(obj, key)
query = { "$relatedTo" => { "object" => obj.to_pointer, "key" => key } }
criteria[:conditions].merge!(query)
self
end
def execute
params = {}
params.merge!({:where => criteria[:conditions].to_json}) if criteria[:conditions]
params.merge!({:limit => criteria[:limit].to_json}) if criteria[:limit]
params.merge!({:skip => criteria[:skip].to_json}) if criteria[:skip]
params.merge!({:count => criteria[:count].to_json}) if criteria[:count]
params.merge!({:include => criteria[:include]}) if criteria[:include]
params.merge!({:order => criteria[:order]}) if criteria[:order]
return chunk_results(params) if criteria[:chunk]
resp = @klass.resource.get(:params => params)
if criteria[:count] == 1
results = JSON.parse(resp)['count']
return results.to_i
else
results = JSON.parse(resp)['results']
return results.map {|r| @klass.model_name.to_s.constantize.new(r, false)}
end
end
def chunk_results(params={})
criteria[:limit] ||= 100
start_row = criteria[:skip].to_i
end_row = [criteria[:limit].to_i - start_row - 1, 1].max
result = []
# Start at start_row, go to end_row, get results in chunks
(start_row..end_row).each_slice(criteria[:chunk].to_i) do |slice|
params[:skip] = slice.first
params[:limit] = slice.length # Either the chunk size or the end of the limited results
resp = @klass.resource.get(:params => params)
results = JSON.parse(resp)['results']
result = result + results.map {|r| @klass.model_name.to_s.constantize.new(r, false)}
break if results.length < params[:limit] # Got back fewer than we asked for, so exit.
end
result
end
def first
limit(1)
execute.first
end
def all
execute
end
def method_missing(meth, *args, &block)
method_name = method_name.to_s
if method_name.start_with?("find_by_")
attrib = method_name.gsub(/^find_by_/,"")
finder_name = "find_all_by_#{attrib}"
define_singleton_method(finder_name) do |target_value|
where({attrib.to_sym => target_value}).first
end
send(finder_name, args[0])
elsif method_name.start_with?("find_all_by_")
attrib = method_name.gsub(/^find_all_by_/,"")
finder_name = "find_all_by_#{attrib}"
define_singleton_method(finder_name) do |target_value|
where({attrib.to_sym => target_value}).all
end
send(finder_name, args[0])
end
if Array.method_defined?(meth)
all.send(meth, *args, &block)
else
super
end
end
def respond_to?(meth)
if Array.method_defined?(meth)
true
else
super
end
end
end