-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patharel_predications_unit_spec.rb
More file actions
334 lines (316 loc) · 14.3 KB
/
Copy patharel_predications_unit_spec.rb
File metadata and controls
334 lines (316 loc) · 14.3 KB
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
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
327
328
329
330
331
332
333
334
# frozen_string_literal: true
require "spec_helper"
RSpec.describe "ArelPredicationsUnitTest" do
before do
@t = ::Arel::Table.new("products")
end
def sql(node)
ParadeDB::Arel.to_sql(node)
end
# ---- pdb_match ----
it "pdb_match single term" do
node = @t[:description].pdb_match("shoes")
assert_equal %("products"."description" &&& 'shoes'), sql(node)
end
it "pdb_match query" do
node = @t[:description].pdb_match("running shoes lightweight")
assert_equal %("products"."description" &&& 'running shoes lightweight'), sql(node)
end
it "pdb_match accepts sql function term" do
term = Arel::Nodes::NamedFunction.new("lower", [Arel::Nodes.build_quoted("SHOES")])
node = @t[:description].pdb_match(term)
assert_equal %("products"."description" &&& lower('SHOES')), sql(node)
end
it "pdb_match with boost" do
node = @t[:description].pdb_match("shoes", boost: 2.5)
assert_equal %("products"."description" &&& 'shoes'::pdb.boost(2.5)), sql(node)
end
it "pdb_match without boost" do
node = @t[:description].pdb_match("shoes", boost: nil)
assert_equal %("products"."description" &&& 'shoes'), sql(node)
end
it "pdb_match raises with no terms" do
assert_raises(ArgumentError) { @t[:description].pdb_match }
end
# ---- pdb_match_any ----
it "pdb_match_any single term" do
node = @t[:description].pdb_match_any("wireless")
assert_equal %("products"."description" ||| 'wireless'), sql(node)
end
it "pdb_match_any query" do
node = @t[:description].pdb_match_any("wireless bluetooth earbuds")
assert_equal %("products"."description" ||| 'wireless bluetooth earbuds'), sql(node)
end
it "pdb_match_any accepts sql function term" do
term = Arel.sql("trim(' shoes ')")
node = @t[:description].pdb_match_any(term)
assert_equal %("products"."description" ||| trim(' shoes ')), sql(node)
end
it "pdb_match with tokenizer override" do
node = @t[:description].pdb_match("running shoes", tokenizer: "whitespace")
assert_equal %("products"."description" &&& 'running shoes'::pdb.whitespace), sql(node)
end
it "pdb_match with tokenizer args" do
node = @t[:description].pdb_match("running shoes", tokenizer: "whitespace('lowercase=false')")
assert_equal %("products"."description" &&& 'running shoes'::pdb.whitespace('lowercase=false')), sql(node)
end
# ---- pdb_phrase ----
it "pdb_phrase without slop" do
node = @t[:description].pdb_phrase("running shoes")
assert_equal %("products"."description" ### 'running shoes'), sql(node)
end
it "pdb_phrase with slop zero" do
node = @t[:description].pdb_phrase("running shoes", slop: 0)
assert_equal %("products"."description" ### 'running shoes'::pdb.slop(0)), sql(node)
end
it "pdb_phrase with slop large" do
node = @t[:description].pdb_phrase("running shoes", slop: 10)
assert_equal %("products"."description" ### 'running shoes'::pdb.slop(10)), sql(node)
end
it "pdb_phrase with tokenizer" do
node = @t[:description].pdb_phrase("running shoes", tokenizer: ParadeDB::Tokenizer.whitespace())
assert_equal %("products"."description" ### 'running shoes'::pdb.whitespace), sql(node)
end
it "pdb_phrase with pretokenized array" do
node = @t[:description].pdb_phrase(%w[running shoes])
assert_equal %("products"."description" ### ARRAY['running', 'shoes']), sql(node)
end
# ---- pdb_term ----
it "pdb_term without boost" do
node = @t[:category].pdb_term("footwear")
assert_equal %("products"."category" === 'footwear'), sql(node)
end
it "pdb_term with float boost" do
node = @t[:category].pdb_term("footwear", boost: 1.5)
assert_equal %("products"."category" === 'footwear'::pdb.boost(1.5)), sql(node)
end
it "pdb_term with boolean value" do
node = @t[:in_stock].pdb_term(true)
assert_equal %("products"."in_stock" === TRUE), sql(node)
end
it "pdb_term with integer value" do
node = @t[:rating].pdb_term(5)
assert_equal %("products"."rating" === 5), sql(node)
end
it "pdb_term accepts sql function value" do
term = Arel::Nodes::NamedFunction.new("lower", [Arel::Nodes.build_quoted("SHOES")])
node = @t[:description].pdb_term(term)
assert_equal %("products"."description" === lower('SHOES')), sql(node)
end
it "pdb_term_set" do
node = @t[:category].pdb_term_set(%w[audio footwear])
assert_equal %("products"."category" @@@ pdb.term_set(ARRAY['audio', 'footwear'])), sql(node)
end
# ---- fuzzy options on pdb_term ----
it "pdb_term with distance" do
node = @t[:description].pdb_term("shose", distance: 2)
assert_equal %("products"."description" === 'shose'::pdb.fuzzy(2)), sql(node)
end
it "pdb_term with prefix true" do
node = @t[:description].pdb_term("runn", distance: 1, prefix: true)
assert_equal %("products"."description" === 'runn'::pdb.fuzzy(1, "true")), sql(node)
end
it "pdb_term with prefix false" do
node = @t[:description].pdb_term("shose", distance: 2, prefix: false)
assert_equal %("products"."description" === 'shose'::pdb.fuzzy(2)), sql(node)
end
it "pdb_term with transposition cost one" do
node = @t[:description].pdb_term("shose", distance: 1, transposition_cost_one: true)
assert_equal %("products"."description" === 'shose'::pdb.fuzzy(1, "false", "true")), sql(node)
end
it "pdb_term with fuzzy boost" do
node = @t[:description].pdb_term("shose", distance: 2, boost: 1.5)
assert_equal %("products"."description" === 'shose'::pdb.fuzzy(2)::pdb.boost(1.5)), sql(node)
end
# ---- pdb_regex ----
it "pdb_regex simple" do
node = @t[:description].pdb_regex("run.*")
assert_equal %("products"."description" @@@ pdb.regex('run.*')), sql(node)
end
it "pdb_regex complex pattern" do
node = @t[:description].pdb_regex("(wireless|bluetooth).*earbuds")
assert_equal %("products"."description" @@@ pdb.regex('(wireless|bluetooth).*earbuds')), sql(node)
end
it "pdb_regex accepts sql function pattern" do
node = @t[:description].pdb_regex(Arel.sql("lower('RUN.*')"))
assert_equal %("products"."description" @@@ pdb.regex(lower('RUN.*'))), sql(node)
end
it "pdb_regex_phrase" do
node = @t[:description].pdb_regex_phrase("run.*", "sho.*", slop: 2, max_expansions: 100)
assert_equal %("products"."description" @@@ pdb.regex_phrase(ARRAY['run.*', 'sho.*'], slop => 2, max_expansions => 100)), sql(node)
end
# ---- pdb_near ----
it "pdb_near distance 1" do
node = @t[:description].pdb_near(ParadeDB.proximity("running").within(1, "shoes"))
assert_equal %("products"."description" @@@ ('running' ## 1 ## 'shoes')), sql(node)
end
it "pdb_near ordered" do
node = @t[:description].pdb_near(ParadeDB.proximity("running").within(1, "shoes", ordered: true))
assert_equal %("products"."description" @@@ ('running' ##> 1 ##> 'shoes')), sql(node)
end
it "pdb_near large distance" do
node = @t[:description].pdb_near(ParadeDB.proximity("running").within(5, "shoes"))
assert_equal %("products"."description" @@@ ('running' ## 5 ## 'shoes')), sql(node)
end
it "pdb_near with array left operand" do
node = @t[:description].pdb_near(ParadeDB.proximity("sleek", "white").within(1, "shoes"))
assert_equal %("products"."description" @@@ (pdb.prox_array('sleek', 'white') ## 1 ## 'shoes')), sql(node)
end
it "pdb_near with regex wrapper" do
node = @t[:description].pdb_near(ParadeDB.proximity(ParadeDB.regex_term("sl.*"), "white").within(1, "shoes"))
assert_equal %("products"."description" @@@ (pdb.prox_array(pdb.prox_regex('sl.*'), 'white') ## 1 ## 'shoes')), sql(node)
end
it "pdb_near with array right operand" do
node = @t[:description].pdb_near(ParadeDB.proximity("sleek").within(1, "white", "shoes"))
assert_equal %("products"."description" @@@ ('sleek' ## 1 ## pdb.prox_array('white', 'shoes'))), sql(node)
end
it "pdb_near with boost" do
node = @t[:description].pdb_near(ParadeDB.proximity("running").within(1, "shoes"), boost: 2.0)
assert_equal %("products"."description" @@@ ('running' ## 1 ## 'shoes')::pdb.boost(2.0)), sql(node)
end
# ---- pdb_phrase_prefix ----
it "pdb_phrase_prefix single term" do
node = @t[:description].pdb_phrase_prefix("run")
assert_equal %("products"."description" @@@ pdb.phrase_prefix(ARRAY['run'])), sql(node)
end
it "pdb_phrase_prefix multiple terms" do
node = @t[:description].pdb_phrase_prefix("running", "sh")
assert_equal %("products"."description" @@@ pdb.phrase_prefix(ARRAY['running', 'sh'])), sql(node)
end
it "pdb_phrase_prefix with max expansion" do
node = @t[:description].pdb_phrase_prefix("running", "sh", max_expansion: 100)
assert_equal %("products"."description" @@@ pdb.phrase_prefix(ARRAY['running', 'sh'], 100)), sql(node)
end
it "pdb_phrase_prefix raises with no terms" do
assert_raises(ArgumentError) { @t[:description].pdb_phrase_prefix }
end
# ---- pdb_parse ----
it "pdb_parse basic" do
node = @t[:description].pdb_parse("shoes OR boots")
assert_equal %("products"."description" @@@ pdb.parse('shoes OR boots')), sql(node)
end
it "pdb_parse with lenient true" do
node = @t[:description].pdb_parse("shoes OR", lenient: true)
assert_equal %("products"."description" @@@ pdb.parse('shoes OR', lenient => true)), sql(node)
end
it "pdb_parse with lenient false" do
node = @t[:description].pdb_parse("shoes", lenient: false)
assert_equal %("products"."description" @@@ pdb.parse('shoes', lenient => false)), sql(node)
end
it "pdb_parse with conjunction_mode true" do
node = @t[:description].pdb_parse("running shoes", conjunction_mode: true)
assert_equal %("products"."description" @@@ pdb.parse('running shoes', conjunction_mode => true)), sql(node)
end
# ---- pdb_all ----
it "pdb_all" do
node = @t[:id].pdb_all
assert_equal %("products"."id" @@@ pdb.all()), sql(node)
end
it "pdb_exists" do
node = @t[:id].pdb_exists
assert_equal %("products"."id" @@@ pdb.exists()), sql(node)
end
it "pdb_range with Ruby range" do
node = @t[:rating].pdb_range(3..5)
assert_equal %("products"."rating" @@@ pdb.range(int8range(3, 5, '[]'))), sql(node)
end
it "pdb_range with bound options" do
node = @t[:rating].pdb_range(gte: 3, lt: 5)
assert_equal %q{"products"."rating" @@@ pdb.range(int8range(3, 5, '[)'))}, sql(node)
end
it "pdb_range_term" do
node = @t[:weight_range].pdb_range_term("(10, 12]", relation: "Intersects", range_type: "int4range")
assert_equal %q{"products"."weight_range" @@@ pdb.range_term('(10, 12]'::int4range, 'Intersects')}, sql(node)
end
# ---- pdb_more_like_this ----
it "pdb_more_like_this with integer key" do
node = @t[:id].pdb_more_like_this(3, fields: [:description])
assert_equal %("products"."id" @@@ pdb.more_like_this(3, ARRAY['description'])), sql(node)
end
it "pdb_more_like_this without fields" do
node = @t[:id].pdb_more_like_this(3)
assert_equal %("products"."id" @@@ pdb.more_like_this(3)), sql(node)
end
it "pdb_more_like_this with multiple fields" do
node = @t[:id].pdb_more_like_this(5, fields: [:description, :category])
assert_equal %("products"."id" @@@ pdb.more_like_this(5, ARRAY['description', 'category'])), sql(node)
end
it "pdb_more_like_this with options" do
node = @t[:id].pdb_more_like_this(
5,
fields: [:description],
options: { min_term_frequency: 2, max_query_terms: 10, stopwords: %w[the a] }
)
assert_equal %("products"."id" @@@ pdb.more_like_this(5, ARRAY['description'], min_term_frequency => 2, max_query_terms => 10, stopwords => ARRAY['the', 'a'])), sql(node)
end
# ---- pdb_full_text ----
it "pdb_full_text with string expression" do
node = @t[:description].pdb_full_text("pdb.all()")
assert_equal %("products"."description" @@@ pdb.all()), sql(node)
end
# ---- pdb_score / pdb_snippet ----
it "pdb_score renders pdb function" do
node = @t[:id].pdb_score
assert_equal %(pdb.score("products"."id")), sql(node)
end
it "pdb_snippet no extra args" do
node = @t[:description].pdb_snippet
assert_equal %(pdb.snippet("products"."description")), sql(node)
end
it "pdb_snippet with tags" do
node = @t[:description].pdb_snippet("<b>", "</b>")
assert_equal %(pdb.snippet("products"."description", '<b>', '</b>')), sql(node)
end
it "pdb_snippets with named args" do
node = @t[:description].pdb_snippets(
start_tag: "<em>",
end_tag: "</em>",
max_num_chars: 15,
limit: 1,
offset: 0,
sort_by: "position"
)
assert_equal %(pdb.snippets("products"."description", start_tag => '<em>', end_tag => '</em>', max_num_chars => 15, "limit" => 1, "offset" => 0, sort_by => 'position')), sql(node)
end
it "pdb_snippet_positions" do
node = @t[:description].pdb_snippet_positions
assert_equal %(pdb.snippet_positions("products"."description")), sql(node)
end
# ---- Boolean composition with standard AR predicates ----
it "pdb_match and standard eq compose" do
node = @t[:description].pdb_match("shoes").and(@t[:in_stock].eq(true))
rendered = sql(node)
assert_includes rendered, "&&&"
assert_includes rendered, "AND"
assert_includes rendered, "in_stock"
end
it "pdb_match or composition" do
a = @t[:description].pdb_match("shoes")
b = @t[:description].pdb_match("boots")
rendered = sql(a.or(b))
assert_includes rendered, " OR "
assert_includes rendered, "'shoes'"
assert_includes rendered, "'boots'"
end
it "pdb_match not composition" do
node = @t[:description].pdb_match("cheap").not
assert_equal %(NOT ("products"."description" &&& 'cheap')), sql(node)
end
# ---- Validation ----
it "pdb_term raises on non-numeric distance" do
error = assert_raises(ArgumentError) { @t[:description].pdb_term("shoes", distance: "far") }
assert_match(/distance must be numeric/, error.message)
end
it "pdb_term raises on out-of-range distance" do
error = assert_raises(ArgumentError) { @t[:description].pdb_term("shoes", distance: 5) }
assert_match(/between 0 and 2/, error.message)
end
it "pdb_match raises on non-numeric boost" do
error = assert_raises(ArgumentError) { @t[:description].pdb_match("shoes", boost: "high") }
assert_match(/boost must be numeric/, error.message)
end
it "pdb_match raises on invalid tokenizer" do
error = assert_raises(ArgumentError) { @t[:description].pdb_match("shoes", tokenizer: "bad;tokenizer") }
assert_match(/invalid tokenizer expression/, error.message)
end
end