scoped_search 2.0.1

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.
@@ -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