query_helper 0.2.7 → 0.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +27 -0
- data/lib/query_helper.rb +81 -26
- data/lib/query_helper/query_helper_concern.rb +19 -1
- data/lib/query_helper/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d785c033c2c9b9f1eaa01641b8e92b8690a1a54eceb1507e58efb4a4437655ab
|
4
|
+
data.tar.gz: de0529d9069f0c612b39873c69160eff8eeb52b35bcc05401026cc161b9914c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 39e7d8268cde0434747bfb2cc52e0f85c6d294ba18af86b5a415f5c92da2f1dfbe7822dbdc685c55f99520b15e205e2a75867db6e628b5d594730a1b472990ca
|
7
|
+
data.tar.gz: efcb54e2da3b21a3c454ff490b2c892336913dde41602aa07b31edf658f4644ebcc4f1a234bff8b72706ebd8fcfc7366de24c1a88891d1a3176d72686cdceb7e
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -111,6 +111,33 @@ null | is null *or* is not null
|
|
111
111
|
|
112
112
|
Note: For the null operator code, toggle *is null* operator with true and *is not null* operator with false
|
113
113
|
|
114
|
+
#### Search
|
115
|
+
|
116
|
+
QueryHelper supports searching across multiple fields. To implement pass an array of column aliases into the `search_fields` argument when creating or updating a `QueryHelper` object.
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
@query_helper.update(search_fields: ["column1", "column2"])
|
120
|
+
render json: @query_helper.results()
|
121
|
+
```
|
122
|
+
|
123
|
+
You can then take advantage of the `search_for` url param to do text matching in any of the columns included
|
124
|
+
|
125
|
+
Request: `http://www.example.com/resources?search_for=foo`
|
126
|
+
|
127
|
+
Results:
|
128
|
+
```json
|
129
|
+
[
|
130
|
+
{
|
131
|
+
"column1": "foobar",
|
132
|
+
"column2": "bar"
|
133
|
+
},
|
134
|
+
{
|
135
|
+
"column1": "bar",
|
136
|
+
"column2": "barfoo"
|
137
|
+
}
|
138
|
+
]
|
139
|
+
```
|
140
|
+
|
114
141
|
#### Associations
|
115
142
|
|
116
143
|
Include ActiveRecord associations in the payload. The association must be defined in the model.
|
data/lib/query_helper.rb
CHANGED
@@ -13,7 +13,7 @@ require "query_helper/invalid_query_error"
|
|
13
13
|
|
14
14
|
class QueryHelper
|
15
15
|
|
16
|
-
attr_accessor :model, :bind_variables, :sql_filter, :sql_sort, :page, :per_page, :single_record, :associations, :as_json_options, :executed_query, :api_payload, :preload
|
16
|
+
attr_accessor :model, :bind_variables, :sql_filter, :sql_sort, :page, :per_page, :single_record, :associations, :as_json_options, :executed_query, :api_payload, :preload, :search_field, :search_string, :metadata
|
17
17
|
attr_reader :query
|
18
18
|
|
19
19
|
def initialize(
|
@@ -29,7 +29,10 @@ class QueryHelper
|
|
29
29
|
as_json_options: nil, # a list of as_json options you'd like run before returning the payload
|
30
30
|
custom_mappings: {}, # custom keyword => sql_expression mappings
|
31
31
|
api_payload: false, # Return the paginated payload or simply return the result array
|
32
|
-
preload: [] # preload activerecord associations - used instead of `associations` when you don't want them included in the payload
|
32
|
+
preload: [], # preload activerecord associations - used instead of `associations` when you don't want them included in the payload
|
33
|
+
search_fields: [],
|
34
|
+
search_string: nil,
|
35
|
+
metadata: {}
|
33
36
|
)
|
34
37
|
@query = query.class < ActiveRecord::Relation ? query.to_sql : query
|
35
38
|
@model = query.class < ActiveRecord::Relation ? query.base_class : model
|
@@ -38,21 +41,16 @@ class QueryHelper
|
|
38
41
|
@sql_sort = sql_sort
|
39
42
|
@page = determine_page(page: page, per_page: per_page)
|
40
43
|
@per_page = determine_per_page(page: page, per_page: per_page)
|
44
|
+
set_limit_and_offset()
|
41
45
|
@single_record = single_record
|
42
46
|
@associations = associations
|
43
47
|
@as_json_options = as_json_options
|
44
48
|
@custom_mappings = custom_mappings
|
45
49
|
@api_payload = api_payload
|
46
50
|
@preload = preload
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
limit = @per_page
|
51
|
-
offset = (@page - 1) * @per_page
|
52
|
-
|
53
|
-
# Merge limit/offset variables into bind_variables
|
54
|
-
@bind_variables.merge!({limit: limit, offset: offset})
|
55
|
-
end
|
51
|
+
@search_fields = search_fields
|
52
|
+
@search_string = search_string
|
53
|
+
@metadata = metadata
|
56
54
|
end
|
57
55
|
|
58
56
|
def update(
|
@@ -64,7 +62,14 @@ class QueryHelper
|
|
64
62
|
as_json_options: nil,
|
65
63
|
single_record: nil,
|
66
64
|
custom_mappings: nil,
|
67
|
-
preload: []
|
65
|
+
preload: [],
|
66
|
+
search_fields: nil,
|
67
|
+
sql_filter: nil,
|
68
|
+
sql_sort: nil,
|
69
|
+
page: nil,
|
70
|
+
per_page: nil,
|
71
|
+
search_string: nil,
|
72
|
+
metadata: nil
|
68
73
|
)
|
69
74
|
@query = query.class < ActiveRecord::Relation ? query.to_sql : query if query
|
70
75
|
@model = query.class < ActiveRecord::Relation ? query.base_class : model if model || query
|
@@ -75,6 +80,14 @@ class QueryHelper
|
|
75
80
|
@as_json_options = as_json_options if as_json_options
|
76
81
|
@custom_mappings = custom_mappings if custom_mappings
|
77
82
|
@preload = preload if preload
|
83
|
+
@search_fields = search_fields if search_fields
|
84
|
+
@sql_filter = sql_filter if sql_filter
|
85
|
+
@sql_sort = sql_sort if sql_sort
|
86
|
+
@search_string = search_string if search_string
|
87
|
+
@page = determine_page(page: page, per_page: per_page) if page
|
88
|
+
@per_page = determine_per_page(page: page, per_page: per_page) if per_page
|
89
|
+
@metadata = metadata if metadata
|
90
|
+
set_limit_and_offset()
|
78
91
|
return self
|
79
92
|
end
|
80
93
|
|
@@ -102,14 +115,27 @@ class QueryHelper
|
|
102
115
|
# create the filters from the column maps
|
103
116
|
@sql_filter.create_filters()
|
104
117
|
|
118
|
+
having_clauses = @sql_filter.having_clauses
|
119
|
+
where_clauses = @sql_filter.where_clauses
|
120
|
+
|
121
|
+
if @search_string
|
122
|
+
search_filter = search_filter(column_maps)
|
123
|
+
if search_filter[:placement] == :where
|
124
|
+
where_clauses << search_filter[:filter]
|
125
|
+
else
|
126
|
+
having_clauses << search_filter[:filter]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
|
105
131
|
# merge the filter bind variables into the query bind variables
|
106
132
|
@bind_variables.merge!(@sql_filter.bind_variables)
|
107
|
-
|
133
|
+
|
108
134
|
# Execute Sql Query
|
109
135
|
manipulator = SqlManipulator.new(
|
110
136
|
sql: @query,
|
111
|
-
where_clauses:
|
112
|
-
having_clauses:
|
137
|
+
where_clauses: where_clauses,
|
138
|
+
having_clauses: having_clauses,
|
113
139
|
order_by_clauses: @sql_sort.parse_sort_string,
|
114
140
|
include_limit_clause: @page && @per_page ? true : false,
|
115
141
|
additional_select_clauses: @sql_sort.select_strings
|
@@ -167,9 +193,22 @@ class QueryHelper
|
|
167
193
|
return nil
|
168
194
|
end
|
169
195
|
|
196
|
+
def set_limit_and_offset
|
197
|
+
if @page && @per_page
|
198
|
+
# Determine limit and offset
|
199
|
+
limit = @per_page
|
200
|
+
offset = (@page - 1) * @per_page
|
201
|
+
|
202
|
+
# Merge limit/offset variables into bind_variables
|
203
|
+
@bind_variables[:limit] = limit
|
204
|
+
@bind_variables[:offset] = offset
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
170
208
|
def paginated_results
|
171
209
|
{ pagination: pagination_results(),
|
172
|
-
data: @results
|
210
|
+
data: @results,
|
211
|
+
metadata: @metadata }
|
173
212
|
end
|
174
213
|
|
175
214
|
def determine_count
|
@@ -202,22 +241,22 @@ class QueryHelper
|
|
202
241
|
|
203
242
|
def pagination_results
|
204
243
|
# Set pagination params if they aren't provided
|
205
|
-
@per_page
|
206
|
-
@page
|
244
|
+
results_per_page = @per_page || @count
|
245
|
+
results_page = @page || 1
|
207
246
|
|
208
|
-
total_pages = (@count/(
|
209
|
-
next_page =
|
210
|
-
previous_page =
|
211
|
-
first_page =
|
212
|
-
last_page =
|
213
|
-
out_of_range =
|
247
|
+
total_pages = (@count/(results_per_page.nonzero? || 1).to_f).ceil
|
248
|
+
next_page = results_page + 1 if results_page.between?(1, total_pages - 1)
|
249
|
+
previous_page = results_page - 1 if results_page.between?(2, total_pages)
|
250
|
+
first_page = results_page == 1
|
251
|
+
last_page = results_page == total_pages
|
252
|
+
out_of_range = !results_page.between?(1,total_pages)
|
214
253
|
|
215
254
|
{ count: @count,
|
216
|
-
current_page:
|
255
|
+
current_page: results_page,
|
217
256
|
next_page: next_page,
|
218
257
|
previous_page: previous_page,
|
219
258
|
total_pages: total_pages,
|
220
|
-
per_page:
|
259
|
+
per_page: results_per_page,
|
221
260
|
first_page: first_page,
|
222
261
|
last_page: last_page,
|
223
262
|
out_of_range: out_of_range }
|
@@ -230,4 +269,20 @@ class QueryHelper
|
|
230
269
|
model: @model
|
231
270
|
)
|
232
271
|
end
|
272
|
+
|
273
|
+
def search_filter(column_maps)
|
274
|
+
raise ArgumentError.new("search_fields not defined") unless @search_fields.length > 0
|
275
|
+
placement = :where
|
276
|
+
maps = column_maps.select do |cm|
|
277
|
+
placement = :having if cm.aggregate
|
278
|
+
@search_fields.include? cm.alias_name
|
279
|
+
end
|
280
|
+
bind_variable = ('a'..'z').to_a.shuffle[0,20].join.to_sym
|
281
|
+
@bind_variables[bind_variable] = "%#{@search_string}%"
|
282
|
+
filter = "#{maps.map{|m| "#{m.sql_expression}::varchar"}.join(" || ")} ilike :#{bind_variable}"
|
283
|
+
return {
|
284
|
+
filter: filter,
|
285
|
+
placement: placement
|
286
|
+
}
|
287
|
+
end
|
233
288
|
end
|
@@ -9,10 +9,22 @@ class QueryHelper
|
|
9
9
|
@query_helper
|
10
10
|
end
|
11
11
|
|
12
|
+
def query_helper_with_no_pagination
|
13
|
+
QueryHelper.new(**query_helper_params_no_pagination)
|
14
|
+
end
|
15
|
+
|
12
16
|
def create_query_helper
|
13
17
|
@query_helper = QueryHelper.new(**query_helper_params, api_payload: true)
|
14
18
|
end
|
15
19
|
|
20
|
+
def create_query_helper_with_no_pagination
|
21
|
+
@query_helper = query_helper_with_no_pagination()
|
22
|
+
end
|
23
|
+
|
24
|
+
def reload_query_params(query_helper=@query_helper)
|
25
|
+
query_helper.update(**query_helper_params)
|
26
|
+
end
|
27
|
+
|
16
28
|
def create_query_helper_filter
|
17
29
|
filter_values = params[:filter].permit!.to_h
|
18
30
|
QueryHelper::SqlFilter.new(filter_values: filter_values)
|
@@ -27,12 +39,18 @@ class QueryHelper
|
|
27
39
|
end
|
28
40
|
|
29
41
|
def query_helper_params
|
30
|
-
helpers =
|
42
|
+
helpers = query_helper_params_no_pagination
|
31
43
|
helpers[:page] = params[:page] if params[:page]
|
32
44
|
helpers[:per_page] = params[:per_page] if params[:per_page]
|
45
|
+
helpers
|
46
|
+
end
|
47
|
+
|
48
|
+
def query_helper_params_no_pagination
|
49
|
+
helpers = {}
|
33
50
|
helpers[:sql_filter] = create_query_helper_filter() if params[:filter]
|
34
51
|
helpers[:sql_sort] = create_query_helper_sort() if params[:sort]
|
35
52
|
helpers[:associations] = create_query_helper_associations() if params[:include]
|
53
|
+
helpers[:search_string] = params[:search_for] if params[:search_for]
|
36
54
|
helpers
|
37
55
|
end
|
38
56
|
end
|
data/lib/query_helper/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: query_helper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evan McDaniel
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-06-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|