rad_core_rails 0.7.4 → 0.8.3
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/lib/rad_core_rails/query_generator.rb +175 -69
- data/lib/rad_core_rails/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: '014856ac16f8d40063feb80c47e97120f02ddcf2f8a6717fad3472aefb6c0e4b'
|
4
|
+
data.tar.gz: d0ec03250fb3b8b868ce3694b3d029b86469563d9fede11fb8b34b690ea2fef5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 410ed03d6ddd007f220ce1ceeeea8cedc1a7df95b044d8417ed7d11a00579b8eb980befc9fc2fc81dde7131502dcba08bb1d462bb50a0fe34cf577be9e606a27
|
7
|
+
data.tar.gz: 4a1fe8b16e4bda9f0631624cd769d67610a67efbd6c59e012c0c68b13f8b50496fbc8e5e08a3a6c69a5280537bf30f77a0cd55064537253224950e577c587a02
|
@@ -24,15 +24,16 @@ module RadCoreRails
|
|
24
24
|
@filter_manifest || {}.with_indifferent_access
|
25
25
|
end
|
26
26
|
|
27
|
-
def create_filters(
|
27
|
+
def create_filters(filters)
|
28
28
|
query = []
|
29
29
|
args = []
|
30
|
-
clause, arguments = generate_search_clause(search)
|
31
|
-
query << clause
|
32
|
-
arguments.each do |arg|
|
33
|
-
|
34
|
-
end
|
35
|
-
filters.map do |
|
30
|
+
# clause, arguments = generate_search_clause(search)
|
31
|
+
# query << clause
|
32
|
+
# arguments.each do |arg|
|
33
|
+
# args << arg
|
34
|
+
# end
|
35
|
+
filters.map do |raw_filter|
|
36
|
+
filter = raw_filter.with_indifferent_access
|
36
37
|
begin
|
37
38
|
clause, arguments = filter_manifest[filter[:key]].call(filter)
|
38
39
|
query << clause
|
@@ -46,92 +47,126 @@ module RadCoreRails
|
|
46
47
|
[query.reject(&:blank?).join(' AND '), args]
|
47
48
|
end
|
48
49
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
# # search_term_size = search.split(' ').length
|
54
|
-
# and_terms = search.split(' ').select { |term| !term.include?('+') }
|
55
|
-
# or_terms = search.split(' ').select { |term| term.include?('+') }
|
56
|
-
# and_columns = []
|
57
|
-
# or_columns = []
|
58
|
-
# # just_ors = search_term_size == or_term_size
|
59
|
-
# # operand = just_ors ? 'OR' : 'AND'
|
60
|
-
# # search_terms = just_ors == true ? search.split(' ') : search.split(' ').select { |term| !term.include?('+') }
|
61
|
-
# and_terms.each do |term|
|
62
|
-
# columns = []
|
63
|
-
# and_terms.each do |col|
|
64
|
-
# and_columns.push("(LOWER(#{col}) ILIKE ?)")
|
65
|
-
# and_args.push '%' + term.downcase.strip + '%'
|
66
|
-
# end
|
67
|
-
# or_terms.each do |col|
|
68
|
-
# columns.push("(LOWER(#{col}) ILIKE ?)")
|
69
|
-
# or_args.push '%' + term[1, term.length].downcase.strip + '%'
|
70
|
-
# end
|
71
|
-
# clause = if or_columns.empty?
|
72
|
-
# '(' + and_columns.join(' OR ') + ')'
|
73
|
-
# else
|
74
|
-
# '(' + '(' + and_columns.join(' OR ') + ')' + 'AND' + '(' + or_columns.join(' OR ') + ')' + ')'
|
75
|
-
# end
|
76
|
-
# end
|
77
|
-
# [clause, and_args + or_args]
|
78
|
-
# else
|
79
|
-
# ['', []]
|
80
|
-
# end
|
81
|
-
# end
|
82
|
-
|
83
|
-
# @searchable_columns, is an array of column names that you can compare to your terms.
|
84
|
-
#
|
85
|
-
def generate_search_clause(search)
|
50
|
+
def generate_search_clause(filter)
|
51
|
+
search = filter[:values][0]
|
52
|
+
search_clause = ['', []]
|
53
|
+
|
86
54
|
if search.present? && searchable_columns.is_a?(Array)
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
#
|
55
|
+
and_args = [] # SQL from sanitized_and_terms
|
56
|
+
excluded_args = [] # SQL from sanitized_excluded_terms
|
57
|
+
and_clause = [] # table columns SQL for all the AND terms
|
58
|
+
excluded_clause = [] # table columns SQL for all the AND NOT terms
|
59
|
+
or_args = [] # SQL from sanitized_or_terms
|
60
|
+
or_clause = [] # table columns SQL for all the OR terms
|
61
|
+
|
62
|
+
phrases_or = search.scan(/\+"([^"]*)"/) # e.g. +"Phrase for OR"
|
63
|
+
# remove 'or' phrases from search if any
|
64
|
+
phrases_or.each { |phrase| search.gsub!('+"' + phrase.first.to_s + '"', '') } if phrases_or.any?
|
65
|
+
|
66
|
+
phrases_to_exclude = search.scan(/-"([^"]*)"/) # e.g. -"Phrase to exclude"
|
67
|
+
# remove excluded phrases from search if any
|
68
|
+
if phrases_to_exclude.any?
|
69
|
+
phrases_to_exclude.each { |phrase| search.gsub!('-"' + phrase.first.to_s + '"', '') }
|
70
|
+
end
|
71
|
+
|
72
|
+
phrases_and = search.scan(/"([^"]*)"/) # e.g. "Phrase for AND"
|
73
|
+
# remove 'and' phrases from search if any
|
74
|
+
phrases_and.each { |phrase| search.gsub!('"' + phrase.first.to_s + '"', '') } if phrases_and.any?
|
75
|
+
|
76
|
+
# extract 'and' terms, remove casing, and add ILIKE '%' comparisons
|
96
77
|
sanitized_and_terms = search.split(' ')
|
97
|
-
.reject { |term| term.include?('+') }
|
78
|
+
.reject { |term| term.include?('+') || term.include?('-') }
|
98
79
|
.map { |term| '%' + term.downcase.strip + '%' }
|
99
|
-
#
|
80
|
+
# add 'and' phrase terms, remove casing, and add ILIKE '%' comparisons
|
81
|
+
if phrases_and.any?
|
82
|
+
sanitized_and_terms += phrases_and.flatten.map { |phrase| '%' + phrase.downcase.strip + '%' }
|
83
|
+
end
|
84
|
+
|
85
|
+
# extract 'or' terms, remove casing, and add ILIKE '%' comparisons
|
100
86
|
sanitized_or_terms = search.split(' ')
|
101
87
|
.select { |term| term.include?('+') }
|
102
88
|
.map { |term| '%' + term[1, term.length].downcase.strip + '%' }
|
89
|
+
|
90
|
+
# add 'or' phrase terms, remove casing, and add ILIKE '%' comparisons
|
91
|
+
sanitized_or_terms += phrases_or.flatten.map { |phrase| '%' + phrase.downcase.strip + '%' } if phrases_or.any?
|
92
|
+
|
93
|
+
# extract excluded terms, remove casing, and add NOT ILIKE '%' comparisons
|
94
|
+
sanitized_excluded_terms = search.split(' ')
|
95
|
+
.select { |term| term.include?('-') }
|
96
|
+
.map { |term| '%' + term[1, term.length].downcase.strip + '%' }
|
97
|
+
|
98
|
+
# add excluded phrase terms, remove casing, and add NOT ILIKE '%' comparisons
|
99
|
+
if phrases_to_exclude.any?
|
100
|
+
sanitized_excluded_terms += phrases_to_exclude.flatten
|
101
|
+
.map { |phrase| '%' + phrase.downcase.strip + '%' }
|
102
|
+
end
|
103
|
+
|
103
104
|
# loop through sanitized_and_terms to find all possible columns.
|
104
105
|
sanitized_and_terms.each do |sanitized_term|
|
105
106
|
columns = []
|
106
107
|
# all possible columns where this should be searched
|
107
108
|
searchable_columns.each do |col|
|
108
|
-
columns.push("(LOWER(#{col}) ILIKE ?)")
|
109
|
+
columns.push("(COALESCE(LOWER(#{col}), '') ILIKE ?)")
|
109
110
|
and_args.push sanitized_term
|
110
111
|
end
|
111
112
|
and_clause.push '(' + columns.join(' OR ') + ')'
|
112
113
|
end
|
114
|
+
# loop through sanitized_excluded_terms to find all possible columns.
|
115
|
+
sanitized_excluded_terms.each do |sanitized_term|
|
116
|
+
columns = []
|
117
|
+
# all possible columns where this should be searched
|
118
|
+
searchable_columns.each do |col|
|
119
|
+
columns.push("(COALESCE(LOWER(#{col}), '') NOT ILIKE ?)")
|
120
|
+
excluded_args.push sanitized_term
|
121
|
+
end
|
122
|
+
excluded_clause.push '(' + columns.join(' AND ') + ')'
|
123
|
+
end
|
113
124
|
# loop through sanitized_or_terms to find all possible columns.
|
114
125
|
sanitized_or_terms.each do |sanitized_term|
|
115
126
|
columns = []
|
116
127
|
# all possible columns where this should be searched
|
117
128
|
searchable_columns.each do |col|
|
118
|
-
columns.push("(LOWER(#{col}) ILIKE ?)")
|
129
|
+
columns.push("(COALESCE(LOWER(#{col}), '') ILIKE ?)")
|
119
130
|
or_args.push sanitized_term
|
120
131
|
end
|
121
132
|
or_clause.push '(' + columns.join(' OR ') + ')'
|
122
133
|
end
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
134
|
+
|
135
|
+
return ['', []] if or_clause.empty? && and_clause.empty? && excluded_clause.empty?
|
136
|
+
|
137
|
+
return ['(' + and_clause.join(' AND ') + ')', and_args] if or_clause.empty? && excluded_clause.empty?
|
138
|
+
|
139
|
+
return ['(' + or_clause.join(' OR ') + ')', or_args] if and_clause.empty? && excluded_clause.empty?
|
140
|
+
|
141
|
+
return ['(' + excluded_clause.join(' AND ') + ')', excluded_args] if or_clause.empty? && and_clause.empty?
|
142
|
+
|
143
|
+
if or_clause.empty?
|
144
|
+
return ['((' + and_clause.join(' AND ') + ') AND (' + excluded_clause.join(' AND ') + '))',
|
145
|
+
and_args + excluded_args]
|
131
146
|
end
|
132
|
-
|
133
|
-
|
147
|
+
|
148
|
+
if and_clause.empty?
|
149
|
+
return ['((' + or_clause.join(' OR ') + ') AND (' + excluded_clause.join(' AND ') + '))',
|
150
|
+
or_args + excluded_args]
|
151
|
+
end
|
152
|
+
|
153
|
+
if excluded_clause.empty?
|
154
|
+
return ['((' + and_clause.join(' AND ') + ') AND (' + or_clause.join(' OR ') + '))', and_args + or_args]
|
155
|
+
end
|
156
|
+
|
157
|
+
search_clause = [
|
158
|
+
'((' +
|
159
|
+
and_clause.join(' AND ') +
|
160
|
+
') AND (' +
|
161
|
+
or_clause.join(' OR ') +
|
162
|
+
') AND (' +
|
163
|
+
excluded_clause.join(' AND ') +
|
164
|
+
'))',
|
165
|
+
and_args + or_args + excluded_args
|
166
|
+
]
|
134
167
|
end
|
168
|
+
|
169
|
+
search_clause
|
135
170
|
end
|
136
171
|
|
137
172
|
# Generate Boolean clause
|
@@ -156,6 +191,77 @@ module RadCoreRails
|
|
156
191
|
[str, args]
|
157
192
|
end
|
158
193
|
|
194
|
+
def sanitize_vector_string(search)
|
195
|
+
phrases_or = search.scan(/\+"([^"]*)"/) # e.g. +"Phrase for OR"
|
196
|
+
# remove 'or' phrases from search if any
|
197
|
+
phrases_or.each { |phrase| search.gsub!('+"' + phrase.first.to_s + '"', '') } if phrases_or.any?
|
198
|
+
|
199
|
+
phrases_to_exclude = search.scan(/-"([^"]*)"/) # e.g. -"Phrase to exclude"
|
200
|
+
# remove excluded phrases from search if any
|
201
|
+
phrases_to_exclude.each { |phrase| search.gsub!('-"' + phrase.first.to_s + '"', '') } if phrases_to_exclude.any?
|
202
|
+
|
203
|
+
phrases_and = search.scan(/"([^"]*)"/) # e.g. "Phrase for AND"
|
204
|
+
# remove 'and' phrases from search if any
|
205
|
+
phrases_and.each { |phrase| search.gsub!('"' + phrase.first.to_s + '"', '') } if phrases_and.any?
|
206
|
+
|
207
|
+
sanitized_and_terms = search.split(' ')
|
208
|
+
.reject { |term| term.include?('+') || term.include?('-') }
|
209
|
+
.map { |term| term.downcase.strip + ':*' }
|
210
|
+
.reject { |t| t.blank? }
|
211
|
+
|
212
|
+
sanitized_or_terms = search.split(' ')
|
213
|
+
.select { |term| term.include?('+') }
|
214
|
+
.map { |term| term[1, term.length].downcase + ':*' }
|
215
|
+
.reject { |t| t.blank? }
|
216
|
+
|
217
|
+
sanitized_excluded_terms = search.split(' ')
|
218
|
+
.select { |term| term.include?('-') }
|
219
|
+
.map { |term| term[1, term.length].downcase.strip + ':*' }
|
220
|
+
.reject { |t| t.blank? }
|
221
|
+
|
222
|
+
and_clause = sanitized_and_terms.join(' & ')
|
223
|
+
|
224
|
+
or_clause = sanitized_or_terms.join(' | ')
|
225
|
+
|
226
|
+
excluded_clause = sanitized_excluded_terms.map { |t| "!#{t}" }.join(' & ')
|
227
|
+
|
228
|
+
# puts '------'
|
229
|
+
# puts '------'
|
230
|
+
# puts '------'
|
231
|
+
# puts and_clause.inspect
|
232
|
+
# puts or_clause.inspect
|
233
|
+
# puts excluded_clause.inspect
|
234
|
+
# puts '------'
|
235
|
+
# puts '------'
|
236
|
+
|
237
|
+
if and_clause.present? && or_clause.empty? && excluded_clause.empty?
|
238
|
+
and_clause
|
239
|
+
elsif and_clause.empty? && or_clause.present? && excluded_clause.empty?
|
240
|
+
or_clause
|
241
|
+
elsif and_clause.empty? && or_clause.empty? && excluded_clause.present?
|
242
|
+
excluded_clause
|
243
|
+
elsif and_clause.present? && or_clause.present? && excluded_clause.empty?
|
244
|
+
"(#{and_clause}) & (#{or_clause})"
|
245
|
+
elsif and_clause.present? && or_clause.empty? && excluded_clause.present?
|
246
|
+
"(#{and_clause}) & (#{excluded_clause})"
|
247
|
+
elsif and_clause.empty? && or_clause.present? && excluded_clause.present?
|
248
|
+
"(#{or_clause}) & (#{excluded_clause})"
|
249
|
+
elsif and_clause.present? && or_clause.present? && excluded_clause.present?
|
250
|
+
"(#{and_clause}) & (#{or_clause}) & (#{excluded_clause})"
|
251
|
+
else
|
252
|
+
raise StandardError, 'Combination not available.'
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def generate_vector_clause(column_name, filter)
|
257
|
+
return ['', []] if filter[:values][0].blank?
|
258
|
+
|
259
|
+
vector_string = sanitize_vector_string(filter[:values][0])
|
260
|
+
|
261
|
+
query = "(#{column_name} @@ to_tsquery('simple', ?))"
|
262
|
+
[query, [vector_string]]
|
263
|
+
end
|
264
|
+
|
159
265
|
def generate_zip_codes_clause(column_name, filter)
|
160
266
|
str = "(#{column_name} = ANY(#{zip_code_class.distance_query}))"
|
161
267
|
args = zip_code_class.distance_args(filter)
|
@@ -165,10 +271,10 @@ module RadCoreRails
|
|
165
271
|
|
166
272
|
def generate_array_clause(column_name, filter)
|
167
273
|
if filter[:option] == '!='
|
168
|
-
str = "(SELECT NOT(#{column_name}::text[] && ARRAY[?]))"
|
274
|
+
str = "(SELECT NOT(#{column_name}::text[] && ARRAY[?]::text[]))"
|
169
275
|
args = [filter[:values]]
|
170
276
|
else
|
171
|
-
str = "(SELECT (#{column_name}::text[] && ARRAY[?]))"
|
277
|
+
str = "(SELECT (#{column_name}::text[] && ARRAY[?]::text[]))"
|
172
278
|
args = [filter[:values]]
|
173
279
|
end
|
174
280
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rad_core_rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oleksandr Poltavets, James Marrs
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-03-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|