oedipus 0.0.1.pre1 → 0.0.1.pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.gitignore +2 -0
  2. data/README.md +235 -44
  3. data/Rakefile +25 -0
  4. data/ext/oedipus/extconf.rb +72 -0
  5. data/ext/oedipus/oedipus.c +239 -0
  6. data/ext/oedipus/oedipus.h +50 -0
  7. data/lib/oedipus/comparison/between.rb +26 -0
  8. data/lib/oedipus/comparison/equal.rb +21 -0
  9. data/lib/oedipus/comparison/gt.rb +21 -0
  10. data/lib/oedipus/comparison/gte.rb +21 -0
  11. data/lib/oedipus/comparison/in.rb +21 -0
  12. data/lib/oedipus/comparison/lt.rb +21 -0
  13. data/lib/oedipus/comparison/lte.rb +21 -0
  14. data/lib/oedipus/comparison/not.rb +25 -0
  15. data/lib/oedipus/comparison/not_equal.rb +21 -0
  16. data/lib/oedipus/comparison/not_in.rb +21 -0
  17. data/lib/oedipus/comparison/outside.rb +26 -0
  18. data/lib/oedipus/comparison/shortcuts.rb +144 -0
  19. data/lib/oedipus/comparison.rb +88 -0
  20. data/lib/oedipus/connection.rb +91 -13
  21. data/lib/oedipus/connection_error.rb +14 -0
  22. data/lib/oedipus/index.rb +189 -46
  23. data/lib/oedipus/query_builder.rb +97 -4
  24. data/lib/oedipus/version.rb +1 -1
  25. data/lib/oedipus.rb +24 -7
  26. data/oedipus.gemspec +4 -5
  27. data/spec/integration/connection_spec.rb +58 -0
  28. data/spec/integration/index_spec.rb +353 -0
  29. data/spec/spec_helper.rb +2 -23
  30. data/spec/support/test_harness.rb +30 -9
  31. data/spec/unit/comparison/between_spec.rb +36 -0
  32. data/spec/unit/comparison/equal_spec.rb +22 -0
  33. data/spec/unit/comparison/gt_spec.rb +22 -0
  34. data/spec/unit/comparison/gte_spec.rb +22 -0
  35. data/spec/unit/comparison/in_spec.rb +22 -0
  36. data/spec/unit/comparison/lt_spec.rb +22 -0
  37. data/spec/unit/comparison/lte_spec.rb +22 -0
  38. data/spec/unit/comparison/not_equal_spec.rb +22 -0
  39. data/spec/unit/comparison/not_in_spec.rb +22 -0
  40. data/spec/unit/comparison/not_spec.rb +37 -0
  41. data/spec/unit/comparison/outside_spec.rb +36 -0
  42. data/spec/unit/comparison/shortcuts_spec.rb +125 -0
  43. data/spec/unit/comparison_spec.rb +109 -0
  44. data/spec/unit/query_builder_spec.rb +150 -0
  45. metadata +68 -19
  46. data/lib/oedipus/mysql/client.rb +0 -136
  47. data/spec/unit/connection_spec.rb +0 -36
  48. data/spec/unit/index_spec.rb +0 -85
@@ -0,0 +1,353 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Index do
13
+ include Oedipus::TestHarness
14
+
15
+ let(:conn) { Oedipus::Connection.new(searchd_host) }
16
+ let(:index) { Oedipus::Index.new(:posts_rt, conn) }
17
+
18
+ describe "#insert" do
19
+ context "with valid data" do
20
+ it "returns the number of rows inserted" do
21
+ index.insert(
22
+ 10,
23
+ title: "Badgers",
24
+ body: "They live in setts, do badgers.",
25
+ views: 721,
26
+ user_id: 7
27
+ ).should == 1
28
+ end
29
+ end
30
+
31
+ context "with invalid data" do
32
+ it "raises an error" do
33
+ expect {
34
+ index.insert(
35
+ 10,
36
+ bad_field: "Invalid",
37
+ body: "They live in setts, do badgers.",
38
+ views: 721,
39
+ user_id: 7
40
+ )
41
+ }.to raise_error(Oedipus::ConnectionError)
42
+ end
43
+ end
44
+ end
45
+
46
+ describe "#fetch" do
47
+ before(:each) do
48
+ index.insert(1, title: "Badgers and foxes", views: 150)
49
+ index.insert(2, title: "Rabbits and hares", views: 73)
50
+ index.insert(3, title: "Clowns and cannon girls", views: 1)
51
+ end
52
+
53
+ context "with a valid document ID" do
54
+ it "returns the matched document" do
55
+ index.fetch(2).should == { id: 2, views: 73, user_id: 0, status: "" }
56
+ end
57
+ end
58
+
59
+ context "with a bad document ID" do
60
+ it "returns nil" do
61
+ index.fetch(7).should be_nil
62
+ end
63
+ end
64
+ end
65
+
66
+ describe "#update" do
67
+ before(:each) do
68
+ index.insert(1, title: "Badgers and foxes", views: 150, user_id: 7)
69
+ end
70
+
71
+ context "with valid data" do
72
+ it "returns the number of rows modified" do
73
+ index.update(1, views: 721).should == 1
74
+ end
75
+
76
+ it "modifies the data" do
77
+ index.update(1, views: 721)
78
+ index.fetch(1).should == { id: 1, views: 721, user_id: 7, status: "" }
79
+ end
80
+ end
81
+
82
+ context "with unmatched data" do
83
+ it "returns 0" do
84
+ index.update(3, views: 721).should == 0
85
+ end
86
+ end
87
+
88
+ context "with invalid data" do
89
+ it "raises an error" do
90
+ expect {
91
+ index.update(1, bad_field: "Invalid", views: 721)
92
+ }.to raise_error(Oedipus::ConnectionError)
93
+ end
94
+ end
95
+ end
96
+
97
+ describe "#replace" do
98
+ before(:each) do
99
+ index.insert(
100
+ 1,
101
+ title: "Badgers",
102
+ body: "They live in setts, do badgers.",
103
+ views: 721,
104
+ user_id: 7
105
+ )
106
+ end
107
+
108
+ context "with valid existing data" do
109
+ it "returns the number of rows inserted" do
110
+ index.replace(1, title: "Badgers and foxes", views: 150).should == 1
111
+ end
112
+
113
+ it "entirely replaces the record" do
114
+ index.replace(1, title: "Badgers and foxes", views: 150)
115
+ index.fetch(1).should == { id: 1, views: 150, user_id: 0, status: "" }
116
+ end
117
+ end
118
+
119
+ context "with valid new data" do
120
+ it "returns the number of rows inserted" do
121
+ index.replace(2, title: "Beer and wine", views: 15).should == 1
122
+ end
123
+
124
+ it "entirely replaces the record" do
125
+ index.replace(2, title: "Beer and wine", views: 15)
126
+ index.fetch(2).should == { id: 2, views: 15, user_id: 0, status: "" }
127
+ end
128
+ end
129
+
130
+ context "with invalid data" do
131
+ it "raises an error" do
132
+ expect {
133
+ index.replace(1, bad_field: "Badgers and foxes", views: 150)
134
+ }.to raise_error(Oedipus::ConnectionError)
135
+ end
136
+ end
137
+ end
138
+
139
+ describe "#search" do
140
+ before(:each) do
141
+ index.insert(1, title: "Badgers and foxes", views: 150)
142
+ index.insert(2, title: "Rabbits and hares", views: 87)
143
+ index.insert(3, title: "Badgers in the wild", views: 41)
144
+ index.insert(4, title: "Badgers for all!", views: 3003)
145
+ end
146
+
147
+ context "by fulltext matching" do
148
+ it "indicates the number of records found" do
149
+ index.search("badgers")[:total_found].should == 3
150
+ end
151
+
152
+ it "includes the matches records" do
153
+ index.search("badgers")[:records].should == [
154
+ { id: 1, views: 150, user_id: 0, status: "" },
155
+ { id: 3, views: 41, user_id: 0, status: "" },
156
+ { id: 4, views: 3003, user_id: 0, status: "" }
157
+ ]
158
+ end
159
+ end
160
+
161
+ context "by attribute filtering" do
162
+ it "indicates the number of records found" do
163
+ index.search(views: 40..90)[:total_found].should == 2
164
+ end
165
+
166
+ it "includes the matches records" do
167
+ index.search(views: 40..90)[:records].should == [
168
+ { id: 2, views: 87, user_id: 0, status: "" },
169
+ { id: 3, views: 41, user_id: 0, status: "" }
170
+ ]
171
+ end
172
+ end
173
+
174
+ context "by fulltext with attribute filtering" do
175
+ it "indicates the number of records found" do
176
+ index.search("badgers", views: Oedipus.gt(100))[:total_found].should == 2
177
+ end
178
+
179
+ it "includes the matches records" do
180
+ index.search("badgers", views: Oedipus.gt(100))[:records].should == [
181
+ { id: 1, views: 150, user_id: 0, status: "" },
182
+ { id: 4, views: 3003, user_id: 0, status: "" }
183
+ ]
184
+ end
185
+ end
186
+
187
+ context "with limits" do
188
+ it "still indicates the number of records found" do
189
+ index.search("badgers", limit: 2)[:total_found].should == 3
190
+ end
191
+
192
+ it "returns the limited subset of the results" do
193
+ index.search("badgers", limit: 2)[:records].should == [
194
+ { id: 1, views: 150, user_id: 0, status: "" },
195
+ { id: 3, views: 41, user_id: 0, status: "" }
196
+ ]
197
+ end
198
+
199
+ it "can use an offset" do
200
+ index.search("badgers", limit: 1, offset: 1)[:records].should == [
201
+ { id: 3, views: 41, user_id: 0, status: "" }
202
+ ]
203
+ end
204
+ end
205
+
206
+ context "with ordering" do
207
+ it "returns the results ordered accordingly" do
208
+ index.search("badgers", order: {views: :desc})[:records].should == [
209
+ { id: 4, views: 3003, user_id: 0, status: "" },
210
+ { id: 1, views: 150, user_id: 0, status: "" },
211
+ { id: 3, views: 41, user_id: 0, status: "" },
212
+ ]
213
+ end
214
+ end
215
+ end
216
+
217
+ describe "#multi_search" do
218
+ before(:each) do
219
+ index.insert(1, title: "Badgers and foxes", views: 150, user_id: 1)
220
+ index.insert(2, title: "Rabbits and hares", views: 87, user_id: 1)
221
+ index.insert(3, title: "Badgers in the wild", views: 41, user_id: 2)
222
+ index.insert(4, title: "Badgers for all!", views: 3003, user_id: 1)
223
+ end
224
+
225
+ context "by fulltext querying" do
226
+ it "indicates the number of results for each query" do
227
+ results = index.multi_search(
228
+ badgers: "badgers",
229
+ rabbits: "rabbits"
230
+ )
231
+ results[:badgers][:total_found].should == 3
232
+ results[:rabbits][:total_found].should == 1
233
+ end
234
+
235
+ it "returns the records for each search" do
236
+ results = index.multi_search(
237
+ badgers: "badgers",
238
+ rabbits: "rabbits"
239
+ )
240
+ results[:badgers][:records].should == [
241
+ { id: 1, views: 150, user_id: 1, status: "" },
242
+ { id: 3, views: 41, user_id: 2, status: "" },
243
+ { id: 4, views: 3003, user_id: 1, status: "" }
244
+ ]
245
+ results[:rabbits][:records].should == [
246
+ { id: 2, views: 87, user_id: 1, status: "" }
247
+ ]
248
+ end
249
+ end
250
+
251
+ context "by attribute filtering" do
252
+ it "indicates the number of results for each query" do
253
+ results = index.multi_search(
254
+ shiela: {user_id: 1},
255
+ barry: {user_id: 2}
256
+ )
257
+ results[:shiela][:total_found].should == 3
258
+ results[:barry][:total_found].should == 1
259
+ end
260
+ end
261
+ end
262
+
263
+ describe "#faceted_search" do
264
+ before(:each) do
265
+ index.insert(1, title: "Badgers and foxes", body: "Badgers", views: 150, user_id: 1)
266
+ index.insert(2, title: "Rabbits and hares", body: "Rabbits", views: 87, user_id: 1)
267
+ index.insert(3, title: "Badgers in the wild", body: "Test", views: 41, user_id: 2)
268
+ index.insert(4, title: "Badgers for all!", body: "For all", views: 3003, user_id: 1)
269
+ end
270
+
271
+ context "with additional attribute filters" do
272
+ let(:results) do
273
+ index.faceted_search(
274
+ "badgers",
275
+ facets: {
276
+ popular: {views: Oedipus.gte(50)},
277
+ di_carla: {user_id: 2}
278
+ }
279
+ )
280
+ end
281
+
282
+ it "returns the main results in the top-level" do
283
+ results[:records].should == [
284
+ { id: 1, views: 150, user_id: 1, status: "" },
285
+ { id: 3, views: 41, user_id: 2, status: "" },
286
+ { id: 4, views: 3003, user_id: 1, status: "" }
287
+ ]
288
+ end
289
+
290
+ it "applies the filters on top of the base query" do
291
+ results[:facets][:popular][:records].should == [
292
+ { id: 1, views: 150, user_id: 1, status: "" },
293
+ { id: 4, views: 3003, user_id: 1, status: "" }
294
+ ]
295
+ results[:facets][:di_carla][:records].should == [
296
+ { id: 3, views: 41, user_id: 2, status: "" }
297
+ ]
298
+ end
299
+ end
300
+
301
+ context "with overriding attribute filters" do
302
+ let(:results) do
303
+ index.faceted_search(
304
+ "badgers",
305
+ user_id: 1,
306
+ facets: {
307
+ di_carla: {user_id: 2}
308
+ }
309
+ )
310
+ end
311
+
312
+ it "applies the filters on top of the base query" do
313
+ results[:facets][:di_carla][:records].should == [
314
+ { id: 3, views: 41, user_id: 2, status: "" }
315
+ ]
316
+ end
317
+ end
318
+
319
+ context "with overriding overriding fulltext queries" do
320
+ let(:results) do
321
+ index.faceted_search(
322
+ "badgers",
323
+ facets: {
324
+ rabbits: "rabbits"
325
+ }
326
+ )
327
+ end
328
+
329
+ it "entirely replaces the base query" do
330
+ results[:facets][:rabbits][:records].should == [
331
+ { id: 2, views: 87, user_id: 1, status: "" }
332
+ ]
333
+ end
334
+ end
335
+
336
+ context "with overriding refined fulltext queries" do
337
+ let(:results) do
338
+ index.faceted_search(
339
+ "badgers",
340
+ facets: {
341
+ in_body: "@body (%{query})"
342
+ }
343
+ )
344
+ end
345
+
346
+ it "merges the queries" do
347
+ results[:facets][:in_body][:records].should == [
348
+ { id: 1, views: 150, user_id: 1, status: "" },
349
+ ]
350
+ end
351
+ end
352
+ end
353
+ end
data/spec/spec_helper.rb CHANGED
@@ -7,33 +7,12 @@
7
7
  # See LICENSE file for details.
8
8
  ##
9
9
 
10
- require "rspec"
11
10
  require "bundler/setup"
11
+
12
+ require "rspec"
12
13
  require "oedipus"
13
14
 
14
15
  Dir[File.expand_path("../support/**/*rb", __FILE__)].each { |f| require f }
15
16
 
16
17
  RSpec.configure do |config|
17
- include Oedipus::TestHarness
18
-
19
- config.before(:suite) do
20
- unless ENV.key?("SEARCHD")
21
- raise "You must specify a path to the Sphinx 'searchd' executable (>= 2.0.2)"
22
- end
23
-
24
- set_data_dir File.expand_path("../data", __FILE__)
25
- set_searchd ENV["SEARCHD"]
26
-
27
- prepare_data_dirs
28
- write_sphinx_conf
29
- start_searchd
30
- end
31
-
32
- config.before(:each) do
33
- empty_indexes
34
- end
35
-
36
- config.after(:suite) do
37
- stop_searchd
38
- end
39
18
  end
@@ -8,8 +8,33 @@
8
8
  ##
9
9
 
10
10
  module Oedipus
11
- # Mixed into RSpec suites to manage starting/stopping Sphinx.
11
+ # Mixed into RSpec suites to manage starting/stopping Sphinx and writing indexes.
12
12
  module TestHarness
13
+ class << self
14
+ def included(base)
15
+ base.before(:all) do
16
+ unless ENV.key?("SEARCHD")
17
+ raise "You must specify a path to the Sphinx 'searchd' executable (>= 2.0.2)"
18
+ end
19
+
20
+ set_data_dir File.expand_path("../../data", __FILE__)
21
+ set_searchd ENV["SEARCHD"]
22
+
23
+ prepare_data_dirs
24
+ write_sphinx_conf
25
+ start_searchd
26
+ end
27
+
28
+ base.before(:each) do
29
+ empty_indexes
30
+ end
31
+
32
+ base.after(:all) do
33
+ stop_searchd
34
+ end
35
+ end
36
+ end
37
+
13
38
  # Set the path to the searchd executable.
14
39
  #
15
40
  # The version of Sphinx must be >= 2.0.2.
@@ -37,15 +62,11 @@ module Oedipus
37
62
  end
38
63
 
39
64
  def empty_indexes
40
- require "mysql"
41
- @conn ||= ::Mysql.new(searchd_host[:host], nil, nil, nil, searchd_host[:port])
42
-
43
- idxs = @conn.query("SHOW TABLES")
65
+ @conn ||= Oedipus::Connection.new(host: searchd_host[:host], port: searchd_host[:port])
44
66
 
45
- while idx = idxs.fetch_hash
46
- docs = @conn.query("SELECT id FROM #{idx['Index']}")
47
- while hash = docs.fetch_hash
48
- @conn.query("DELETE FROM #{idx['Index']} WHERE id = #{hash['id']}")
67
+ @conn.query("SHOW TABLES").each do |idx|
68
+ @conn.query("SELECT id FROM #{idx['Index']}").each do |hash|
69
+ @conn.execute("DELETE FROM #{idx['Index']} WHERE id = #{hash['id']}")
49
70
  end
50
71
  end
51
72
  end
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Comparison::Between do
13
+ context "with an inclusive range" do
14
+ let(:comparison) { Oedipus::Comparison::Between.new(42..100) }
15
+
16
+ it "draws as BETWEEN x AND y" do
17
+ comparison.to_s.should == "BETWEEN 42 AND 100"
18
+ end
19
+
20
+ it "inverses as NOT BETWEEN x AND y" do
21
+ comparison.inverse.to_s.should == "NOT BETWEEN 42 AND 100"
22
+ end
23
+ end
24
+
25
+ context "with an exclusive range" do
26
+ let(:comparison) { Oedipus::Comparison::Between.new(42...100) }
27
+
28
+ it "draws as BETWEEN x AND y-1" do
29
+ comparison.to_s.should == "BETWEEN 42 AND 99"
30
+ end
31
+
32
+ it "inverses as NOT BETWEEN x AND y-1" do
33
+ comparison.inverse.to_s.should == "NOT BETWEEN 42 AND 99"
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Comparison::Equal do
13
+ let(:comparison) { Oedipus::Comparison::Equal.new('test') }
14
+
15
+ it "draws as = v" do
16
+ comparison.to_s.should == "= 'test'"
17
+ end
18
+
19
+ it "inverses as != v" do
20
+ comparison.inverse.to_s.should == "!= 'test'"
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Comparison::GT do
13
+ let(:comparison) { Oedipus::Comparison::GT.new(42) }
14
+
15
+ it "draws as > v" do
16
+ comparison.to_s.should == "> 42"
17
+ end
18
+
19
+ it "inverses as <= v" do
20
+ comparison.inverse.to_s.should == "<= 42"
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Comparison::GTE do
13
+ let(:comparison) { Oedipus::Comparison::GTE.new(42) }
14
+
15
+ it "draws as >= v" do
16
+ comparison.to_s.should == ">= 42"
17
+ end
18
+
19
+ it "inverses as < v" do
20
+ comparison.inverse.to_s.should == "< 42"
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Comparison::In do
13
+ let(:comparison) { Oedipus::Comparison::In.new([1, 2, 3]) }
14
+
15
+ it "draws as IN (x, y, z)" do
16
+ comparison.to_s.should == "IN (1, 2, 3)"
17
+ end
18
+
19
+ it "inverses as NOT IN (x, y, z)" do
20
+ comparison.inverse.to_s.should == "NOT IN (1, 2, 3)"
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Comparison::LT do
13
+ let(:comparison) { Oedipus::Comparison::LT.new(42) }
14
+
15
+ it "draws as < v" do
16
+ comparison.to_s.should == "< 42"
17
+ end
18
+
19
+ it "inverses as >= v" do
20
+ comparison.inverse.to_s.should == ">= 42"
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Comparison::LTE do
13
+ let(:comparison) { Oedipus::Comparison::LTE.new(42) }
14
+
15
+ it "draws as <= v" do
16
+ comparison.to_s.should == "<= 42"
17
+ end
18
+
19
+ it "inverses as > v" do
20
+ comparison.inverse.to_s.should == "> 42"
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Comparison::NotEqual do
13
+ let(:comparison) { Oedipus::Comparison::NotEqual.new('test') }
14
+
15
+ it "draws as != v" do
16
+ comparison.to_s.should == "!= 'test'"
17
+ end
18
+
19
+ it "inverses as = v" do
20
+ comparison.inverse.to_s.should == "= 'test'"
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Comparison::NotIn do
13
+ let(:comparison) { Oedipus::Comparison::NotIn.new([1, 2, 3]) }
14
+
15
+ it "draws as NOT IN (x, y, z)" do
16
+ comparison.to_s.should == "NOT IN (1, 2, 3)"
17
+ end
18
+
19
+ it "inverses as IN (x, y, z)" do
20
+ comparison.inverse.to_s.should == "IN (1, 2, 3)"
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+
3
+ ##
4
+ # Oedipus Sphinx 2 Search.
5
+ # Copyright © 2012 Chris Corbyn.
6
+ #
7
+ # See LICENSE file for details.
8
+ ##
9
+
10
+ require "spec_helper"
11
+
12
+ describe Oedipus::Comparison::In do
13
+ context "with a non-comparison" do
14
+ let(:comparison) { Oedipus::Comparison::Not.new(0..10) }
15
+
16
+ it "converts to a comparison" do
17
+ comparison.v.should be_a_kind_of(Oedipus::Comparison)
18
+ end
19
+
20
+ it "returns the comparison as its inverse" do
21
+ comparison.inverse.should == comparison.v
22
+ end
23
+ end
24
+
25
+ context "with a comparison" do
26
+ let(:original) { Oedipus::Comparison::GTE.new(7) }
27
+ let(:comparison) { Oedipus::Comparison::Not.new(original) }
28
+
29
+ it "draws as the inverse of the comparison" do
30
+ comparison.to_s.should == original.inverse.to_s
31
+ end
32
+
33
+ it "inverses as the original" do
34
+ comparison.inverse.to_s == original.to_s
35
+ end
36
+ end
37
+ end