scoped_search 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,158 @@
1
+ require "#{File.dirname(__FILE__)}/../spec_helper"
2
+
3
+ # These specs will run on all databases that are defined in the spec/database.yml file.
4
+ # Comment out any databases that you do not have available for testing purposes if needed.
5
+ ScopedSearch::Spec::Database.test_databases.each do |db|
6
+
7
+ describe ScopedSearch, "using a #{db} database" do
8
+
9
+ before(:all) do
10
+ ScopedSearch::Spec::Database.establish_named_connection(db)
11
+
12
+ @class = ScopedSearch::Spec::Database.create_model(:int => :integer, :timestamp => :datetime, :date => :date, :unindexed => :integer) do |klass|
13
+ klass.scoped_search :on => [:int, :timestamp]
14
+ klass.scoped_search :on => :date, :only_explicit => true
15
+ end
16
+ end
17
+
18
+ after(:all) do
19
+ ScopedSearch::Spec::Database.drop_model(@class)
20
+ ScopedSearch::Spec::Database.close_connection
21
+ end
22
+
23
+ context 'quering numerical fields' do
24
+
25
+ before(:all) do
26
+ @record = @class.create!(:int => 9)
27
+ end
28
+
29
+ after(:all) do
30
+ @record.destroy
31
+ end
32
+
33
+ it "should find the record with an exact integer match" do
34
+ @class.search_for('9').should have(1).item
35
+ end
36
+
37
+ it "should find the record with an exact integer match with an explicit operator" do
38
+ @class.search_for('= 9').should have(1).item
39
+ end
40
+
41
+ it "should find the record with an exact integer match with an explicit field name" do
42
+ @class.search_for('int = 9').should have(1).item
43
+ end
44
+
45
+ it "should find the record with an exact integer match with an explicit field name" do
46
+ @class.search_for('int > 8').should have(1).item
47
+ end
48
+
49
+ it "should find the record with a grater than operator and explicit field" do
50
+ @class.search_for('int > 9').should have(0).item
51
+ end
52
+
53
+ it "should find the record with an >= operator with an implicit field name" do
54
+ @class.search_for('>= 9').should have(1).item
55
+ end
56
+
57
+ it "should not return the record if only one predicate is true and AND is used (by default)" do
58
+ @class.search_for('int <= 8, int > 8').should have(0).item
59
+ end
60
+
61
+ it "should return the record in only one predicate is true and OR is used as operator" do
62
+ @class.search_for('int <= 8 || int > 8').should have(1).item
63
+ end
64
+ end
65
+
66
+ context 'querying unindexed fields' do
67
+
68
+ before(:all) do
69
+ @record = @class.create!(:int => 9, :unindexed => 10)
70
+ end
71
+
72
+ after(:all) do
73
+ @record.destroy
74
+ end
75
+
76
+ it "should raise an error when explicitly searching in the non-indexed column" do
77
+ lambda { @class.search_for('unindexed = 10') }.should raise_error(ScopedSearch::Exception)
78
+ end
79
+
80
+ it "should not return records for which the query matches unindex records" do
81
+ @class.search_for('= 10').should have(0).item
82
+ end
83
+ end
84
+
85
+ context 'querying date and time fields' do
86
+
87
+ before(:all) do
88
+ @record = @class.create!(:timestamp => Time.parse('2009-01-02 14:51:44'), :date => Date.parse('2009-01-02'))
89
+ @nil_record = @class.create!(:timestamp => nil, :date => nil)
90
+ end
91
+
92
+ after(:all) do
93
+ @record.destroy
94
+ @nil_record.destroy
95
+ end
96
+
97
+ it "should accept YYYY-MM-DD as date format" do
98
+ @class.search_for('date = 2009-01-02').should have(1).item
99
+ end
100
+
101
+ it "should accept YY-MM-DD as date format" do
102
+ @class.search_for('date = 09-01-02').should have(1).item
103
+ end
104
+
105
+ it "should accept MM/DD/YY as date format" do
106
+ @class.search_for('date = 01/02/09').should have(1).item
107
+ end
108
+
109
+ it "should accept YYYY/MM/DD as date format" do
110
+ @class.search_for('date = 2009/01/02').should have(1).item
111
+ end
112
+
113
+ it "should accept MM/DD/YYYY as date format" do
114
+ @class.search_for('date = 01/02/2009').should have(1).item
115
+ end
116
+
117
+ it "should ignore an invalid date and thus return all records" do
118
+ @class.search_for('>= 2009-14-57').should have(2).items
119
+ end
120
+
121
+ it "should find the records with a timestamp set some point on the provided date" do
122
+ @class.search_for('>= 2009-01-02').should have(1).item
123
+ end
124
+
125
+ it "should support full timestamps" do
126
+ @class.search_for('> "2009-01-02 02:02:02"').should have(1).item
127
+ end
128
+
129
+ it "should find no record with a timestamp in the past" do
130
+ @class.search_for('< 2009-01-02').should have(0).item
131
+ end
132
+
133
+ it "should find all timestamps on a date if no time is given using the = operator" do
134
+ @class.search_for('= 2009-01-02').should have(1).item
135
+ end
136
+
137
+ it "should find all timestamps on a date if no time is when no operator is given" do
138
+ @class.search_for('2009-01-02').should have(1).item
139
+ end
140
+
141
+ it "should find all timestamps not on a date if no time is given using the != operator" do
142
+ @class.search_for('!= 2009-01-02').should have(0).item
143
+ end
144
+
145
+ it "should find the records when the date part of a timestamp matches a date" do
146
+ @class.search_for('>= 2009-01-02').should have(1).item
147
+ end
148
+
149
+ it "should find the record with the timestamp today or in the past" do
150
+ @class.search_for('<= 2009-01-02').should have(1).item
151
+ end
152
+
153
+ it "should find no record with a timestamp later than today" do
154
+ @class.search_for('> 2009-01-02').should have(0).item
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,262 @@
1
+ require "#{File.dirname(__FILE__)}/../spec_helper"
2
+
3
+ # These specs will run on all databases that are defined in the spec/database.yml file.
4
+ # Comment out any databases that you do not have available for testing purposes if needed.
5
+ ScopedSearch::Spec::Database.test_databases.each do |db|
6
+
7
+ describe ScopedSearch, "using a #{db} database" do
8
+
9
+ before(:all) do
10
+ ScopedSearch::Spec::Database.establish_named_connection(db)
11
+ end
12
+
13
+ after(:all) do
14
+ ScopedSearch::Spec::Database.close_connection
15
+ end
16
+
17
+ context 'querying a :belongs_to relation' do
18
+
19
+ before(:all) do
20
+
21
+ # The related class
22
+ ActiveRecord::Migration.create_table(:bars) { |t| t.string :related }
23
+ class Bar < ActiveRecord::Base; has_many :foos; end
24
+
25
+ # The class on which to call search_for
26
+ Foo = ScopedSearch::Spec::Database.create_model(:foo => :string, :bar_id => :integer) do |klass|
27
+ klass.belongs_to :bar
28
+ klass.scoped_search :in => :bar, :on => :related
29
+ end
30
+
31
+ @bar_record = Bar.create!(:related => 'bar')
32
+
33
+ Foo.create!(:foo => 'foo', :bar => @bar_record)
34
+ Foo.create!(:foo => 'foo too', :bar => @bar_record)
35
+ Foo.create!(:foo => 'foo three', :bar => Bar.create!(:related => 'another bar'))
36
+ Foo.create!(:foo => 'foo four')
37
+ end
38
+
39
+ after(:all) do
40
+ ScopedSearch::Spec::Database.drop_model(Bar)
41
+ ScopedSearch::Spec::Database.drop_model(Foo)
42
+ Object.send :remove_const, :Foo
43
+ Object.send :remove_const, :Bar
44
+ end
45
+
46
+ it "should find all records with a related bar record containing bar" do
47
+ Foo.search_for('bar').should have(3).items
48
+ end
49
+
50
+ it "should find all records with a related bar record having an exact value of bar" do
51
+ Foo.search_for('= bar').should have(2).items
52
+ end
53
+
54
+ it "should find all records with a related bar record having an exact value of bar with an explicit field" do
55
+ Foo.search_for('related = bar').should have(2).items
56
+ end
57
+
58
+ it "should find records for which the bar relation is not set using null?" do
59
+ Foo.search_for('null? related').should have(1).items
60
+ end
61
+ end
62
+
63
+ context 'querying a :has_many relation' do
64
+
65
+ before(:all) do
66
+
67
+ # The related class
68
+ ActiveRecord::Migration.create_table(:bars) { |t| t.string :related; t.integer :foo_id }
69
+ class Bar < ActiveRecord::Base; belongs_to :foo; end
70
+
71
+ # The class on which to call search_for
72
+ Foo = ScopedSearch::Spec::Database.create_model(:foo => :string, :bar_id => :integer) do |klass|
73
+ klass.has_many :bars
74
+ klass.scoped_search :in => :bars, :on => :related
75
+ end
76
+
77
+ @foo_1 = Foo.create!(:foo => 'foo')
78
+ @foo_2 = Foo.create!(:foo => 'foo too')
79
+ @foo_3 = Foo.create!(:foo => 'foo three')
80
+
81
+ Bar.create!(:related => 'bar', :foo => @foo_1)
82
+ Bar.create!(:related => 'another bar', :foo => @foo_1)
83
+ Bar.create!(:related => 'other bar', :foo => @foo_2)
84
+ end
85
+
86
+ after(:all) do
87
+ ScopedSearch::Spec::Database.drop_model(Bar)
88
+ ScopedSearch::Spec::Database.drop_model(Foo)
89
+ Object.send :remove_const, :Foo
90
+ Object.send :remove_const, :Bar
91
+ end
92
+
93
+ it "should find all records with at least one bar record containing 'bar'" do
94
+ Foo.search_for('bar').should have(2).items
95
+ end
96
+
97
+ it "should find the only record with at least one bar record having the exact value 'bar'" do
98
+ Foo.search_for('= bar').should have(1).item
99
+ end
100
+
101
+ it "should find all records for which at least one related bar record exists" do
102
+ Foo.search_for('set? related').should have(2).items
103
+ end
104
+
105
+ it "should find all records for which none related bar records exist" do
106
+ Foo.search_for('null? related').should have(1).items
107
+ end
108
+
109
+ end
110
+
111
+ context 'querying a :has_one relation' do
112
+
113
+ before(:all) do
114
+
115
+ # The related class
116
+ ActiveRecord::Migration.create_table(:bars) { |t| t.string :related; t.integer :foo_id }
117
+ class Bar < ActiveRecord::Base; belongs_to :foo; end
118
+
119
+ # The class on which to call search_for
120
+ Foo = ScopedSearch::Spec::Database.create_model(:foo => :string) do |klass|
121
+ klass.has_one :bar
122
+ klass.scoped_search :in => :bar, :on => :related
123
+ end
124
+
125
+ @foo_1 = Foo.create!(:foo => 'foo')
126
+ @foo_2 = Foo.create!(:foo => 'foo too')
127
+ @foo_3 = Foo.create!(:foo => 'foo three')
128
+
129
+ Bar.create!(:related => 'bar', :foo => @foo_1)
130
+ Bar.create!(:related => 'other bar', :foo => @foo_2)
131
+ end
132
+
133
+ after(:all) do
134
+ ScopedSearch::Spec::Database.drop_model(Bar)
135
+ ScopedSearch::Spec::Database.drop_model(Foo)
136
+ Object.send :remove_const, :Foo
137
+ Object.send :remove_const, :Bar
138
+ end
139
+
140
+ it "should find all records with a bar record containing 'bar" do
141
+ Foo.search_for('bar').should have(2).items
142
+ end
143
+
144
+ it "should find the only record with the bar record has the exact value 'bar" do
145
+ Foo.search_for('= bar').should have(1).item
146
+ end
147
+
148
+ it "should find all records for which the related bar record exists" do
149
+ Foo.search_for('set? related').should have(2).items
150
+ end
151
+
152
+ it "should find all records for which the related bar record does not exist" do
153
+ Foo.search_for('null? related').should have(1).items
154
+ end
155
+ end
156
+
157
+ context 'querying a :has_and_belongs_to_many relation' do
158
+
159
+ before(:all) do
160
+
161
+ # Create some tables
162
+ ActiveRecord::Migration.create_table(:bars) { |t| t.string :related }
163
+ ActiveRecord::Migration.create_table(:bars_foos, :id => false) { |t| t.integer :foo_id; t.integer :bar_id }
164
+ ActiveRecord::Migration.create_table(:foos) { |t| t.string :foo }
165
+
166
+ # The related class
167
+ class Bar < ActiveRecord::Base; end
168
+
169
+ # The class on which to call search_for
170
+ class Foo < ActiveRecord::Base
171
+ has_and_belongs_to_many :bars
172
+ scoped_search :in => :bars, :on => :related
173
+ end
174
+
175
+ @foo_1 = Foo.create!(:foo => 'foo')
176
+ @foo_2 = Foo.create!(:foo => 'foo too')
177
+ @foo_3 = Foo.create!(:foo => 'foo three')
178
+
179
+ @bar_1 = Bar.create!(:related => 'bar')
180
+ @bar_2 = Bar.create!(:related => 'other bar')
181
+ @bar_3 = Bar.create!(:related => 'last bar')
182
+
183
+ @foo_1.bars << @bar_1 << @bar_2
184
+ @foo_2.bars << @bar_2 << @bar_3
185
+ end
186
+
187
+ after(:all) do
188
+ ActiveRecord::Migration.drop_table(:bars_foos)
189
+ ActiveRecord::Migration.drop_table(:bars)
190
+ ActiveRecord::Migration.drop_table(:foos)
191
+ Object.send :remove_const, :Foo
192
+ Object.send :remove_const, :Bar
193
+ end
194
+
195
+ it "should find all records with at least one associated bar record containing 'bar'" do
196
+ Foo.search_for('bar').should have(2).items
197
+ end
198
+
199
+ it "should find record which is related to @bar_1" do
200
+ Foo.search_for('= bar').should have(1).items
201
+ end
202
+
203
+ it "should find the only record related to @bar_3" do
204
+ Foo.search_for('last').should have(1).items
205
+ end
206
+
207
+ it "should find all records that are related to @bar_2" do
208
+ Foo.search_for('other').should have(2).items
209
+ end
210
+ end
211
+
212
+ context 'querying a :has_many => :through relation' do
213
+
214
+ before(:all) do
215
+
216
+ # Create some tables
217
+ ActiveRecord::Migration.create_table(:bars) { |t| t.integer :foo_id; t.integer :baz_id }
218
+ ActiveRecord::Migration.create_table(:bazs) { |t| t.string :related }
219
+ ActiveRecord::Migration.create_table(:foos) { |t| t.string :foo }
220
+
221
+ # The related classes
222
+ class Bar < ActiveRecord::Base; belongs_to :baz; belongs_to :foo; end
223
+ class Baz < ActiveRecord::Base; has_many :bars; end
224
+
225
+ # The class on which to call search_for
226
+ class Foo < ActiveRecord::Base
227
+ has_many :bars
228
+ has_many :bazs, :through => :bars
229
+
230
+ scoped_search :in => :bazs, :on => :related
231
+ end
232
+
233
+ @foo_1 = Foo.create!(:foo => 'foo')
234
+ @foo_2 = Foo.create!(:foo => 'foo too')
235
+ @foo_3 = Foo.create!(:foo => 'foo three')
236
+
237
+ @baz_1 = Baz.create(:related => 'baz')
238
+ @baz_2 = Baz.create(:related => 'baz too!')
239
+
240
+ @bar_1 = Bar.create!(:foo => @foo_1, :baz => @baz_1)
241
+ @bar_2 = Bar.create!(:foo => @foo_1)
242
+ @bar_3 = Bar.create!(:foo => @foo_2, :baz => @baz_1)
243
+ @bar_3 = Bar.create!(:foo => @foo_2, :baz => @baz_2)
244
+ @bar_3 = Bar.create!(:foo => @foo_2, :baz => @baz_2)
245
+ @bar_4 = Bar.create!(:foo => @foo_3)
246
+ end
247
+
248
+ after(:all) do
249
+ ActiveRecord::Migration.drop_table(:bazs)
250
+ ActiveRecord::Migration.drop_table(:bars)
251
+ ActiveRecord::Migration.drop_table(:foos)
252
+ Object.send :remove_const, :Foo
253
+ Object.send :remove_const, :Bar
254
+ Object.send :remove_const, :Baz
255
+ end
256
+
257
+ it "should find the two records that are related to a baz record" do
258
+ Foo.search_for('baz').should have(2).items
259
+ end
260
+ end
261
+ end
262
+ end
@@ -0,0 +1,192 @@
1
+ require "#{File.dirname(__FILE__)}/../spec_helper"
2
+
3
+ # These specs will run on all databases that are defined in the spec/database.yml file.
4
+ # Comment out any databases that you do not have available for testing purposes if needed.
5
+ ScopedSearch::Spec::Database.test_databases.each do |db|
6
+
7
+ describe ScopedSearch, "using a #{db} database" do
8
+
9
+ before(:all) do
10
+ ScopedSearch::Spec::Database.establish_named_connection(db)
11
+
12
+ @class = ScopedSearch::Spec::Database.create_model(:string => :string, :another => :string, :explicit => :string) do |klass|
13
+ klass.scoped_search :on => :string
14
+ klass.scoped_search :on => :another, :default_operator => :eq, :alias => :alias
15
+ klass.scoped_search :on => :explicit, :only_explicit => true
16
+ end
17
+
18
+ @class.create!(:string => 'foo', :another => 'temp 1', :explicit => 'baz')
19
+ @class.create!(:string => 'bar', :another => 'temp 2', :explicit => 'baz')
20
+ @class.create!(:string => 'baz', :another => 'temp 3', :explicit => nil)
21
+ end
22
+
23
+ after(:all) do
24
+ ScopedSearch::Spec::Database.drop_model(@class)
25
+ ScopedSearch::Spec::Database.close_connection
26
+ end
27
+
28
+ context 'in an implicit string field' do
29
+ it "should find the record with an exact string match" do
30
+ @class.search_for('foo').should have(1).item
31
+ end
32
+
33
+ it "should find the opther two records using NOT with an exact string match" do
34
+ @class.search_for('-foo').should have(2).item
35
+ end
36
+
37
+ it "should find the record with an exact string match and an explicit field operator" do
38
+ @class.search_for('string = foo').should have(1).item
39
+ end
40
+
41
+ it "should find the record with an exact string match and an explicit field operator" do
42
+ @class.search_for('another = foo').should have(0).items
43
+ end
44
+
45
+ it "should find the record with an partial string match" do
46
+ @class.search_for('fo').should have(1).item
47
+ end
48
+
49
+ it "should find the other two records using NOT with an partial string match" do
50
+ @class.search_for('-fo').should have(2).item
51
+ end
52
+
53
+ it "should not find the record with an explicit equals operator and a partial match" do
54
+ @class.search_for('= fo').should have(0).items
55
+ end
56
+
57
+ it "should find the record with an explicit LIKE operator and a partial match" do
58
+ @class.search_for('~ fo').should have(1).items
59
+ end
60
+
61
+ it "should find the all other record with an explicit NOT LIKE operator and a partial match" do
62
+ @class.search_for('string !~ fo').should have(@class.count - 1).items
63
+ end
64
+
65
+ it "should not find a record with a non-match" do
66
+ @class.search_for('nonsense').should have(0).items
67
+ end
68
+
69
+ it "should find two records if it partially matches them" do
70
+ @class.search_for('ba').should have(2).item
71
+ end
72
+
73
+ it "should find no records starting with an a" do
74
+ @class.search_for('a%').should have(0).item
75
+ end
76
+
77
+ it "should find one records ending with an oo" do
78
+ @class.search_for('%oo').should have(1).item
79
+ end
80
+
81
+ it "should find records without case sensitivity when using the LIKE operator" do
82
+ @class.search_for('string ~ FOO').should have(1).item
83
+ end
84
+
85
+ it "should not find records without case sensitivity when using the = operator" do
86
+ @class.search_for('string = FOO').should have(0).items
87
+ end
88
+
89
+ it "should find records without case sensitivity when using the != operator" do
90
+ @class.search_for('string != FOO').should have(3).items
91
+ end
92
+
93
+ it "should find records without case sensitivity when using the NOT LIKE operator" do
94
+ @class.search_for('string !~ FOO').should have(2).items
95
+ end
96
+
97
+ it "should find the record if one of the query words match using OR" do
98
+ @class.search_for('foo OR nonsense').should have(1).item
99
+ end
100
+
101
+ it "should find no records in one of the AND conditions isn't met" do
102
+ @class.search_for('foo AND nonsense').should have(0).item
103
+ end
104
+
105
+ it "should find two records every single OR conditions matches one single record" do
106
+ @class.search_for('foo OR baz').should have(2).item
107
+ end
108
+
109
+ it "should find two records every single AND conditions matches one single record" do
110
+ @class.search_for('foo AND baz').should have(0).item
111
+ end
112
+ end
113
+
114
+ context 'in a field with a different default operator' do
115
+ it "should find an exact match" do
116
+ @class.search_for('"temp 1"').should have(1).item
117
+ end
118
+
119
+ it "should find the orther records using NOT and an exact match" do
120
+ @class.search_for('-"temp 1"').should have(2).item
121
+ end
122
+
123
+ it "should find an explicit match" do
124
+ @class.search_for('another = "temp 1"').should have(1).item
125
+ end
126
+
127
+ it "should not find a partial match" do
128
+ @class.search_for('temp').should have(0).item
129
+ end
130
+
131
+ it "should find all records using a NOT with a partial match on all records" do
132
+ @class.search_for('-temp"').should have(3).item
133
+ end
134
+
135
+ it "should find a partial match when the like operator is given" do
136
+ @class.search_for('~ temp').should have(3).item
137
+ end
138
+
139
+ it "should find a partial match when the like operator and the field name is given" do
140
+ @class.search_for('another ~ temp').should have(3).item
141
+ end
142
+ end
143
+
144
+ context 'using an aliased field' do
145
+ it "should find an explicit match using its alias" do
146
+ @class.search_for('alias = "temp 1"').should have(1).item
147
+ end
148
+ end
149
+
150
+ context 'in an explicit string field' do
151
+
152
+ it "should not find the records if the explicit field is not given in the query" do
153
+ @class.search_for('= baz').should have(1).item
154
+ end
155
+
156
+ it "should find all records when searching on the explicit field" do
157
+ @class.search_for('explicit = baz').should have(2).items
158
+ end
159
+
160
+ it "should find no records if the value in the explicit field is not an exact match" do
161
+ @class.search_for('explicit = ba').should have(0).item
162
+ end
163
+
164
+ it "should find all records when searching on the explicit field" do
165
+ @class.search_for('explicit ~ ba').should have(2).items
166
+ end
167
+
168
+ it "should only find the record with string = foo and explicit = baz" do
169
+ @class.search_for('foo, explicit = baz').should have(1).item
170
+ end
171
+ end
172
+
173
+ context 'using null? and set? queries' do
174
+
175
+ it "should return all records if the string field is being checked with set?" do
176
+ @class.search_for('set? string').should have(3).items
177
+ end
178
+
179
+ it "should return no records if the string field is being checked with null?" do
180
+ @class.search_for('null? string').should have(0).items
181
+ end
182
+
183
+ it "should return all records with a value if the string field is being checked with set?" do
184
+ @class.search_for('set? explicit').should have(2).items
185
+ end
186
+
187
+ it "should return all records without a value if the string field is being checked with null?" do
188
+ @class.search_for('null? explicit').should have(1).items
189
+ end
190
+ end
191
+ end
192
+ end