mongoid_fulltext 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +47 -0
  3. data/.rspec +1 -1
  4. data/.rubocop.yml +6 -0
  5. data/.rubocop_todo.yml +101 -0
  6. data/.travis.yml +11 -3
  7. data/CHANGELOG.md +9 -2
  8. data/Gemfile +19 -9
  9. data/LICENSE +1 -1
  10. data/README.md +12 -9
  11. data/Rakefile +9 -29
  12. data/lib/mongoid/full_text_search/version.rb +5 -0
  13. data/lib/mongoid/full_text_search.rb +372 -0
  14. data/lib/mongoid/indexable.rb +13 -0
  15. data/lib/mongoid/indexes.rb +13 -0
  16. data/lib/mongoid_fulltext.rb +1 -341
  17. data/mongoid_fulltext.gemspec +16 -82
  18. data/spec/models/accentless_artwork.rb +1 -1
  19. data/spec/models/advanced_artwork.rb +1 -1
  20. data/spec/models/basic_artwork.rb +0 -1
  21. data/spec/models/delayed_artwork.rb +1 -2
  22. data/spec/models/external_artist.rb +1 -2
  23. data/spec/models/external_artwork.rb +1 -2
  24. data/spec/models/external_artwork_no_fields_supplied.rb +2 -2
  25. data/spec/models/filtered_artist.rb +4 -4
  26. data/spec/models/filtered_artwork.rb +7 -7
  27. data/spec/models/filtered_other.rb +3 -3
  28. data/spec/models/hidden_dragon.rb +0 -1
  29. data/spec/models/multi_external_artwork.rb +3 -3
  30. data/spec/models/multi_field_artist.rb +1 -1
  31. data/spec/models/multi_field_artwork.rb +1 -1
  32. data/spec/models/partitioned_artist.rb +8 -9
  33. data/spec/models/russian_artwork.rb +2 -2
  34. data/spec/models/short_prefixes_artwork.rb +3 -4
  35. data/spec/models/stopwords_artwork.rb +3 -4
  36. data/spec/mongoid/full_text_search_spec.rb +752 -0
  37. data/spec/spec_helper.rb +11 -7
  38. metadata +27 -68
  39. data/VERSION +0 -1
  40. data/lib/mongoid_indexes.rb +0 -12
  41. data/spec/config/mongoid.yml +0 -6
  42. data/spec/mongoid/fulltext_spec.rb +0 -799
data/spec/spec_helper.rb CHANGED
@@ -6,18 +6,22 @@ require 'mongoid'
6
6
 
7
7
  ENV['MONGOID_ENV'] = 'test'
8
8
 
9
- Mongoid.load!("#{File.dirname(__FILE__)}/config/mongoid.yml")
10
- Mongoid.logger = nil
11
-
12
- require File.expand_path("../../lib/mongoid_fulltext", __FILE__)
9
+ require File.expand_path('../../lib/mongoid_fulltext', __FILE__)
13
10
  Dir["#{File.dirname(__FILE__)}/models/**/*.rb"].each { |f| require f }
14
11
 
12
+ Mongoid.configure do |config|
13
+ config.connect_to('mongoid_fulltext_test')
14
+ end
15
+
15
16
  RSpec.configure do |c|
16
- c.before(:each) do
17
+ c.before :each do
17
18
  Mongoid.purge!
18
19
  end
19
- c.after(:all) do
20
+ c.after :all do
20
21
  Mongoid.purge!
21
22
  end
23
+ c.before :all do
24
+ Mongoid.logger.level = Logger::INFO
25
+ Mongo::Logger.logger.level = Logger::INFO if Mongoid::Compatibility::Version.mongoid5?
26
+ end
22
27
  end
23
-
metadata CHANGED
@@ -1,117 +1,80 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid_fulltext
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
5
- prerelease:
4
+ version: 0.7.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Aaron Windsor
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-04-03 00:00:00.000000000 Z
11
+ date: 2015-09-18 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: mongoid
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ~>
17
+ - - ! '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '3.0'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ~>
24
+ - - ! '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '3.0'
30
27
  - !ruby/object:Gem::Dependency
31
- name: unicode_utils
28
+ name: mongoid-compatibility
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ~>
31
+ - - ! '>='
36
32
  - !ruby/object:Gem::Version
37
- version: 1.0.0
33
+ version: '0'
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ~>
38
+ - - ! '>='
44
39
  - !ruby/object:Gem::Version
45
- version: 1.0.0
40
+ version: '0'
46
41
  - !ruby/object:Gem::Dependency
47
- name: bundler
42
+ name: unicode_utils
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
45
  - - ! '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
- type: :development
48
+ type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
52
  - - ! '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: '0'
62
- - !ruby/object:Gem::Dependency
63
- name: rspec
64
- requirement: !ruby/object:Gem::Requirement
65
- none: false
66
- requirements:
67
- - - ~>
68
- - !ruby/object:Gem::Version
69
- version: 2.10.0
70
- type: :development
71
- prerelease: false
72
- version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
- requirements:
75
- - - ~>
76
- - !ruby/object:Gem::Version
77
- version: 2.10.0
78
- - !ruby/object:Gem::Dependency
79
- name: jeweler
80
- requirement: !ruby/object:Gem::Requirement
81
- none: false
82
- requirements:
83
- - - ~>
84
- - !ruby/object:Gem::Version
85
- version: 1.8.3
86
- type: :development
87
- prerelease: false
88
- version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
- requirements:
91
- - - ~>
92
- - !ruby/object:Gem::Version
93
- version: 1.8.3
94
- description: Full-text search for the Mongoid ORM, using n-grams extracted from text
55
+ description:
95
56
  email: aaron.windsor@gmail.com
96
57
  executables: []
97
58
  extensions: []
98
- extra_rdoc_files:
99
- - LICENSE
100
- - README.md
59
+ extra_rdoc_files: []
101
60
  files:
102
61
  - .document
62
+ - .gitignore
103
63
  - .rspec
64
+ - .rubocop.yml
65
+ - .rubocop_todo.yml
104
66
  - .travis.yml
105
67
  - CHANGELOG.md
106
68
  - Gemfile
107
69
  - LICENSE
108
70
  - README.md
109
71
  - Rakefile
110
- - VERSION
72
+ - lib/mongoid/full_text_search.rb
73
+ - lib/mongoid/full_text_search/version.rb
74
+ - lib/mongoid/indexable.rb
75
+ - lib/mongoid/indexes.rb
111
76
  - lib/mongoid_fulltext.rb
112
- - lib/mongoid_indexes.rb
113
77
  - mongoid_fulltext.gemspec
114
- - spec/config/mongoid.yml
115
78
  - spec/models/accentless_artwork.rb
116
79
  - spec/models/advanced_artwork.rb
117
80
  - spec/models/basic_artwork.rb
@@ -131,34 +94,30 @@ files:
131
94
  - spec/models/russian_artwork.rb
132
95
  - spec/models/short_prefixes_artwork.rb
133
96
  - spec/models/stopwords_artwork.rb
134
- - spec/mongoid/fulltext_spec.rb
97
+ - spec/mongoid/full_text_search_spec.rb
135
98
  - spec/spec_helper.rb
136
- homepage: http://github.com/aaw/mongoid_fulltext
99
+ homepage: https://github.com/artsy/mongoid_fulltext
137
100
  licenses:
138
101
  - MIT
102
+ metadata: {}
139
103
  post_install_message:
140
104
  rdoc_options: []
141
105
  require_paths:
142
106
  - lib
143
107
  required_ruby_version: !ruby/object:Gem::Requirement
144
- none: false
145
108
  requirements:
146
109
  - - ! '>='
147
110
  - !ruby/object:Gem::Version
148
111
  version: '0'
149
- segments:
150
- - 0
151
- hash: 2382360387082033861
152
112
  required_rubygems_version: !ruby/object:Gem::Requirement
153
- none: false
154
113
  requirements:
155
114
  - - ! '>='
156
115
  - !ruby/object:Gem::Version
157
- version: '0'
116
+ version: 1.3.6
158
117
  requirements: []
159
118
  rubyforge_project:
160
- rubygems_version: 1.8.25
119
+ rubygems_version: 2.4.5
161
120
  signing_key:
162
- specification_version: 3
163
- summary: Full-text search for the Mongoid ORM
121
+ specification_version: 4
122
+ summary: Full-text search for the Mongoid ORM, using n-grams extracted from text.
164
123
  test_files: []
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.6.1
@@ -1,12 +0,0 @@
1
- # hook onto model index creation to create related FT indexes
2
- module Mongoid::Indexes::ClassMethods
3
-
4
- alias_method :create_fulltext_indexes_hook, :create_indexes
5
-
6
- def create_indexes
7
- create_fulltext_indexes if respond_to?(:create_fulltext_indexes)
8
- create_fulltext_indexes_hook
9
- end
10
-
11
- end
12
-
@@ -1,6 +0,0 @@
1
- test:
2
- sessions:
3
- default:
4
- database: mongoid_fulltext_test
5
- hosts:
6
- - localhost:27017
@@ -1,799 +0,0 @@
1
- # coding: utf-8
2
- require 'spec_helper'
3
-
4
- module Mongoid
5
- describe FullTextSearch do
6
-
7
- context "with several config options defined" do
8
-
9
- let!(:abcdef) { AdvancedArtwork.create(:title => 'abcdefg hijklmn') }
10
- let!(:cesar) { AccentlessArtwork.create(:title => "C\u00e9sar Galicia") }
11
- let!(:julio) { AccentlessArtwork.create(:title => "Julio Cesar Morales") }
12
-
13
- it "should recognize all options" do
14
- # AdvancedArtwork is defined with an ngram_width of 4 and a different alphabet (abcdefg)
15
- AdvancedArtwork.fulltext_search('abc').should == []
16
- AdvancedArtwork.fulltext_search('abcd').first.should == abcdef
17
- AdvancedArtwork.fulltext_search('defg').first.should == abcdef
18
- AdvancedArtwork.fulltext_search('hijklmn').should == []
19
- # AccentlessArtwork is just like BasicArtwork, except that we set :remove_accents to false,
20
- # so this behaves like the ``old'' version of fulltext_search
21
- AccentlessArtwork.fulltext_search("cesar").first.should == julio
22
- end
23
-
24
- end
25
-
26
- context "with default settings" do
27
-
28
- let!(:flower_myth) { BasicArtwork.create(:title => 'Flower Myth') }
29
- let!(:flowers) { BasicArtwork.create(:title => 'Flowers') }
30
- let!(:lowered) { BasicArtwork.create(:title => 'Lowered') }
31
- let!(:cookies) { BasicArtwork.create(:title => 'Cookies') }
32
- let!(:empty) { BasicArtwork.create(:title => '') }
33
- let!(:cesar) { BasicArtwork.create(:title => "C\u00e9sar Galicia") }
34
- let!(:julio) { BasicArtwork.create(:title => "Julio Cesar Morales") }
35
- let!(:csar) { BasicArtwork.create(:title => "Csar") }
36
- let!(:percent) { BasicArtwork.create(:title => "Untitled (cal%desert)") }
37
-
38
- it "returns empty for empties" do
39
- BasicArtwork.fulltext_search(nil, :max_results => 1).should == []
40
- BasicArtwork.fulltext_search("", :max_results => 1).should == []
41
- end
42
-
43
- it "finds percents" do
44
- BasicArtwork.fulltext_search("cal%desert".force_encoding("ASCII-8BIT"), :max_results => 1).first.should == percent
45
- BasicArtwork.fulltext_search("cal%desert".force_encoding("UTF-8"), :max_results => 1).first.should == percent
46
- end
47
-
48
- it "forgets accents" do
49
- BasicArtwork.fulltext_search('cesar', :max_results => 1).first.should == cesar
50
- BasicArtwork.fulltext_search('cesar g', :max_results => 1).first.should == cesar
51
- BasicArtwork.fulltext_search("C\u00e9sar", :max_results => 1).first.should == cesar
52
- BasicArtwork.fulltext_search("C\303\251sar".force_encoding("UTF-8"), :max_results => 1).first.should == cesar
53
- BasicArtwork.fulltext_search(CGI.unescape("c%C3%A9sar"), :max_results => 1).first.should == cesar
54
- BasicArtwork.fulltext_search(CGI.unescape("c%C3%A9sar".encode("ASCII-8BIT")), :max_results => 1).first.should == cesar
55
- end
56
-
57
- it "returns exact matches" do
58
- BasicArtwork.fulltext_search('Flower Myth', :max_results => 1).first.should == flower_myth
59
- BasicArtwork.fulltext_search('Flowers', :max_results => 1).first.should == flowers
60
- BasicArtwork.fulltext_search('Cookies', :max_results => 1).first.should == cookies
61
- BasicArtwork.fulltext_search('Lowered', :max_results => 1).first.should == lowered
62
- end
63
-
64
- it "returns exact matches regardless of case" do
65
- BasicArtwork.fulltext_search('fLOWER mYTH', :max_results => 1).first.should == flower_myth
66
- BasicArtwork.fulltext_search('FLOWERS', :max_results => 1).first.should == flowers
67
- BasicArtwork.fulltext_search('cOOkies', :max_results => 1).first.should == cookies
68
- BasicArtwork.fulltext_search('lOWERED', :max_results => 1).first.should == lowered
69
- end
70
-
71
- it "returns all relevant results, sorted by relevance" do
72
- BasicArtwork.fulltext_search('Flowers').should == [flowers, flower_myth, lowered]
73
- end
74
-
75
- it "prefers prefix matches" do
76
- [flowers, flower_myth].should include(BasicArtwork.fulltext_search('Floweockies').first)
77
- BasicArtwork.fulltext_search('Lowers').first.should == lowered
78
- BasicArtwork.fulltext_search('Cookilowers').first.should == cookies
79
- end
80
-
81
- it "returns an empty result set for an empty query" do
82
- BasicArtwork.fulltext_search('').empty?.should be_true
83
- end
84
-
85
- it "returns an empty result set for a query that doesn't contain any characters in the alphabet" do
86
- BasicArtwork.fulltext_search('_+=--@!##%#$%%').empty?.should be_true
87
- end
88
-
89
- it "returns results for a query that contains only a single ngram" do
90
- BasicArtwork.fulltext_search('coo').first.should == cookies
91
- BasicArtwork.fulltext_search('c!!!oo').first.should == cookies
92
- end
93
-
94
- end
95
-
96
- context "with default settings" do
97
-
98
- let!(:flower_myth) { Gallery::BasicArtwork.create(:title => 'Flower Myth') }
99
- let!(:flowers) { Gallery::BasicArtwork.create(:title => 'Flowers') }
100
- let!(:lowered) { Gallery::BasicArtwork.create(:title => 'Lowered') }
101
- let!(:cookies) { Gallery::BasicArtwork.create(:title => 'Cookies') }
102
- let!(:empty) { Gallery::BasicArtwork.create(:title => '') }
103
-
104
- it "returns exact matches for model within a module" do
105
- Gallery::BasicArtwork.fulltext_search('Flower Myth', :max_results => 1).first.should == flower_myth
106
- Gallery::BasicArtwork.fulltext_search('Flowers', :max_results => 1).first.should == flowers
107
- Gallery::BasicArtwork.fulltext_search('Cookies', :max_results => 1).first.should == cookies
108
- Gallery::BasicArtwork.fulltext_search('Lowered', :max_results => 1).first.should == lowered
109
- end
110
-
111
- end
112
-
113
- context "with default settings" do
114
-
115
- let!(:yellow) { BasicArtwork.create(:title => 'Yellow') }
116
- let!(:yellow_leaves_2) { BasicArtwork.create(:title => 'Yellow Leaves 2') }
117
- let!(:yellow_leaves_3) { BasicArtwork.create(:title => 'Yellow Leaves 3') }
118
- let!(:yellow_leaves_20) { BasicArtwork.create(:title => 'Yellow Leaves 20') }
119
- let!(:yellow_cup) { BasicArtwork.create(:title => 'Yellow Cup') }
120
-
121
- it "prefers the best prefix that matches a given string" do
122
- BasicArtwork.fulltext_search('yellow').first.should == yellow
123
- BasicArtwork.fulltext_search('yellow leaves', :max_results => 3).sort_by{ |x| x.title }.should == \
124
- [yellow_leaves_2, yellow_leaves_3, yellow_leaves_20].sort_by{ |x| x.title }
125
- BasicArtwork.fulltext_search('yellow cup').first.should == yellow_cup
126
- end
127
-
128
- end
129
-
130
- context "with default settings" do
131
- let!(:monet) { BasicArtwork.create(:title => 'claude monet') }
132
- let!(:one_month_weather_permitting) { BasicArtwork.create(:title => 'one month weather permitting monday') }
133
-
134
- it "finds better matches within exact strings" do
135
- BasicArtwork.fulltext_search('monet').first.should == monet
136
- end
137
- end
138
-
139
- context "with default settings" do
140
-
141
- let!(:abc) { BasicArtwork.create(:title => "abc") }
142
- let!(:abcd) { BasicArtwork.create(:title => "abcd") }
143
- let!(:abcde) { BasicArtwork.create(:title => "abcde") }
144
- let!(:abcdef) { BasicArtwork.create(:title => "abcdef") }
145
- let!(:abcdefg) { BasicArtwork.create(:title => "abcdefg") }
146
- let!(:abcdefgh) { BasicArtwork.create(:title => "abcdefgh") }
147
-
148
- it "returns exact matches from a list of similar prefixes" do
149
- BasicArtwork.fulltext_search('abc').first.should == abc
150
- BasicArtwork.fulltext_search('abcd').first.should == abcd
151
- BasicArtwork.fulltext_search('abcde').first.should == abcde
152
- BasicArtwork.fulltext_search('abcdef').first.should == abcdef
153
- BasicArtwork.fulltext_search('abcdefg').first.should == abcdefg
154
- BasicArtwork.fulltext_search('abcdefgh').first.should == abcdefgh
155
- end
156
-
157
- end
158
-
159
- context "with an index name specified" do
160
- let!(:pablo_picasso) { ExternalArtist.create(:full_name => 'Pablo Picasso') }
161
- let!(:portrait_of_picasso) { ExternalArtwork.create(:title => 'Portrait of Picasso') }
162
- let!(:andy_warhol) { ExternalArtist.create(:full_name => 'Andy Warhol') }
163
- let!(:warhol) { ExternalArtwork.create(:title => 'Warhol') }
164
- let!(:empty) { ExternalArtwork.create(:title => '') }
165
-
166
- it "returns results of different types from the same query" do
167
- results = ExternalArtwork.fulltext_search('picasso', :max_results => 2).map{ |result| result }
168
- results.member?(portrait_of_picasso).should be_true
169
- results.member?(pablo_picasso).should be_true
170
- results = ExternalArtist.fulltext_search('picasso', :max_results => 2).map{ |result| result }
171
- results.member?(portrait_of_picasso).should be_true
172
- results.member?(pablo_picasso).should be_true
173
- end
174
-
175
- it "returns exact matches" do
176
- ExternalArtwork.fulltext_search('Pablo Picasso', :max_results => 1).first.should == pablo_picasso
177
- ExternalArtwork.fulltext_search('Portrait of Picasso', :max_results => 1).first.should == portrait_of_picasso
178
- ExternalArtwork.fulltext_search('Andy Warhol', :max_results => 1).first.should == andy_warhol
179
- ExternalArtwork.fulltext_search('Warhol', :max_results => 1).first.should == warhol
180
- ExternalArtist.fulltext_search('Pablo Picasso', :max_results => 1).first.should == pablo_picasso
181
- ExternalArtist.fulltext_search('Portrait of Picasso', :max_results => 1).first.should == portrait_of_picasso
182
- ExternalArtist.fulltext_search('Andy Warhol', :max_results => 1).first.should == andy_warhol
183
- ExternalArtist.fulltext_search('Warhol', :max_results => 1).first.should == warhol
184
- end
185
-
186
- it "returns exact matches regardless of case" do
187
- ExternalArtwork.fulltext_search('pABLO pICASSO', :max_results => 1).first.should == pablo_picasso
188
- ExternalArtist.fulltext_search('PORTRAIT OF PICASSO', :max_results => 1).first.should == portrait_of_picasso
189
- ExternalArtwork.fulltext_search('andy warhol', :max_results => 1).first.should == andy_warhol
190
- ExternalArtwork.fulltext_search('wArHoL', :max_results => 1).first.should == warhol
191
- end
192
-
193
- it "returns all relevant results, sorted by relevance" do
194
- ExternalArtist.fulltext_search('Pablo Picasso').should == [pablo_picasso, portrait_of_picasso]
195
- ExternalArtwork.fulltext_search('Pablo Picasso').should == [pablo_picasso, portrait_of_picasso]
196
- ExternalArtist.fulltext_search('Portrait of Picasso').should == [portrait_of_picasso, pablo_picasso]
197
- ExternalArtwork.fulltext_search('Portrait of Picasso').should == [portrait_of_picasso, pablo_picasso]
198
- ExternalArtist.fulltext_search('Andy Warhol').should == [andy_warhol, warhol]
199
- ExternalArtwork.fulltext_search('Andy Warhol').should == [andy_warhol, warhol]
200
- ExternalArtist.fulltext_search('Warhol').should == [warhol, andy_warhol]
201
- ExternalArtwork.fulltext_search('Warhol').should == [warhol, andy_warhol]
202
- end
203
-
204
- it "prefers prefix matches" do
205
- ExternalArtist.fulltext_search('PabloWarhol').first.should == pablo_picasso
206
- ExternalArtist.fulltext_search('AndyPicasso').first.should == andy_warhol
207
- end
208
-
209
- it "returns an empty result set for an empty query" do
210
- ExternalArtist.fulltext_search('').empty?.should be_true
211
- end
212
-
213
- it "returns an empty result set for a query that doesn't contain any characters in the alphabet" do
214
- ExternalArtwork.fulltext_search('#$%!$#*%*').empty?.should be_true
215
- end
216
-
217
- it "returns results for a query that contains only a single ngram" do
218
- ExternalArtist.fulltext_search('and').first.should == andy_warhol
219
- end
220
-
221
- end
222
-
223
- context "with an index name specified" do
224
-
225
- let!(:andy_warhol) { ExternalArtist.create(:full_name => 'Andy Warhol') }
226
- let!(:warhol) { ExternalArtwork.create(:title => 'Warhol') }
227
-
228
- it "doesn't blow up if garbage is in the index collection" do
229
- ExternalArtist.fulltext_search('warhol').should == [warhol, andy_warhol]
230
- index_collection = ExternalArtist.collection.database[ExternalArtist.mongoid_fulltext_config.keys.first]
231
- index_collection.find('document_id' => warhol.id).each do |idef|
232
- index_collection.find('_id' => idef['_id']).update('document_id' => Moped::BSON::ObjectId.new)
233
- end
234
- # We should no longer be able to find warhol, but that shouldn't keep it from returning results
235
- ExternalArtist.fulltext_search('warhol').should == [andy_warhol]
236
- end
237
-
238
- end
239
-
240
- context "with an index name specified" do
241
-
242
- let!(:pop) { ExternalArtwork.create(:title => 'Pop') }
243
- let!(:pop_culture) { ExternalArtwork.create(:title => 'Pop Culture') }
244
- let!(:contemporary_pop) { ExternalArtwork.create(:title => 'Contemporary Pop') }
245
- let!(:david_poppie) { ExternalArtist.create(:full_name => 'David Poppie') }
246
- let!(:kung_fu_lollipop) { ExternalArtwork.create(:title => 'Kung-Fu Lollipop') }
247
-
248
- it "prefers the best prefix that matches a given string" do
249
- ExternalArtwork.fulltext_search('pop').first.should == pop
250
- ExternalArtwork.fulltext_search('poppie').first.should == david_poppie
251
- ExternalArtwork.fulltext_search('pop cult').first.should == pop_culture
252
- ExternalArtwork.fulltext_search('pop', :max_results => 5)[4].should == kung_fu_lollipop
253
- end
254
-
255
- end
256
- context "with an index name specified" do
257
-
258
- let!(:abc) { ExternalArtwork.create(:title => "abc") }
259
- let!(:abcd) { ExternalArtwork.create(:title => "abcd") }
260
- let!(:abcde) { ExternalArtwork.create(:title => "abcde") }
261
- let!(:abcdef) { ExternalArtwork.create(:title => "abcdef") }
262
- let!(:abcdefg) { ExternalArtwork.create(:title => "abcdefg") }
263
- let!(:abcdefgh) { ExternalArtwork.create(:title => "abcdefgh") }
264
-
265
- it "returns exact matches from a list of similar prefixes" do
266
- ExternalArtwork.fulltext_search('abc').first.should == abc
267
- ExternalArtwork.fulltext_search('abcd').first.should == abcd
268
- ExternalArtwork.fulltext_search('abcde').first.should == abcde
269
- ExternalArtwork.fulltext_search('abcdef').first.should == abcdef
270
- ExternalArtwork.fulltext_search('abcdefg').first.should == abcdefg
271
- ExternalArtwork.fulltext_search('abcdefgh').first.should == abcdefgh
272
- end
273
-
274
- end
275
-
276
- context "with an index name specified" do
277
-
278
- it "cleans up item from the index after they're destroyed" do
279
- foobar = ExternalArtwork.create(:title => "foobar")
280
- barfoo = ExternalArtwork.create(:title => "barfoo")
281
- ExternalArtwork.fulltext_search('foobar').should == [foobar, barfoo]
282
- foobar.destroy
283
- ExternalArtwork.fulltext_search('foobar').should == [barfoo]
284
- barfoo.destroy
285
- ExternalArtwork.fulltext_search('foobar').should == []
286
- end
287
-
288
- end
289
-
290
- context "with an index name specified and no fields provided to index" do
291
-
292
- let!(:big_bang) { ExternalArtworkNoFieldsSupplied.create(:title => 'Big Bang', :artist => 'David Poppie', :year => '2009') }
293
-
294
- it "indexes the string returned by to_s" do
295
- ExternalArtworkNoFieldsSupplied.fulltext_search('big bang').first.should == big_bang
296
- ExternalArtworkNoFieldsSupplied.fulltext_search('poppie').first.should == big_bang
297
- ExternalArtworkNoFieldsSupplied.fulltext_search('2009').first.should == big_bang
298
- end
299
-
300
- end
301
-
302
- context "with multiple indexes defined" do
303
-
304
- let!(:pop) { MultiExternalArtwork.create(:title => 'Pop', :year => '1970', :artist => 'Joe Schmoe') }
305
- let!(:pop_culture) { MultiExternalArtwork.create(:title => 'Pop Culture', :year => '1977', :artist => 'Jim Schmoe') }
306
- let!(:contemporary_pop) { MultiExternalArtwork.create(:title => 'Contemporary Pop', :year => '1800', :artist => 'Bill Schmoe') }
307
- let!(:kung_fu_lollipop) { MultiExternalArtwork.create(:title => 'Kung-Fu Lollipop', :year => '2006', :artist => 'Michael Anderson') }
308
-
309
- it "allows searches to hit a particular index" do
310
- title_results = MultiExternalArtwork.fulltext_search('pop', :index => 'mongoid_fulltext.titles').sort_by{ |x| x.title }
311
- title_results.should == [pop, pop_culture, contemporary_pop, kung_fu_lollipop].sort_by{ |x| x.title }
312
- year_results = MultiExternalArtwork.fulltext_search('197', :index => 'mongoid_fulltext.years').sort_by{ |x| x.title }
313
- year_results.should == [pop, pop_culture].sort_by{ |x| x.title }
314
- all_results = MultiExternalArtwork.fulltext_search('1800 and', :index => 'mongoid_fulltext.all').sort_by{ |x| x.title }
315
- all_results.should == [contemporary_pop, kung_fu_lollipop].sort_by{ |x| x.title }
316
- end
317
-
318
- it "should raise an error if you don't specify which index to search with" do
319
- lambda { MultiExternalArtwork.fulltext_search('foobar') }.should raise_error(Mongoid::FullTextSearch::UnspecifiedIndexError)
320
- end
321
-
322
- end
323
-
324
- context "with multiple fields indexed and the same index used by multiple models" do
325
-
326
- let!(:andy_warhol) { MultiFieldArtist.create(:full_name => 'Andy Warhol', :birth_year => '1928') }
327
- let!(:warhol) { MultiFieldArtwork.create(:title => 'Warhol', :year => '2010') }
328
- let!(:pablo_picasso) { MultiFieldArtist.create(:full_name => 'Pablo Picasso', :birth_year => '1881') }
329
- let!(:portrait_of_picasso) { MultiFieldArtwork.create(:title => 'Portrait of Picasso', :year => '1912') }
330
-
331
- it "allows searches across all models on both fields indexed" do
332
- MultiFieldArtist.fulltext_search('2010').first.should == warhol
333
- MultiFieldArtist.fulltext_search('andy').first.should == andy_warhol
334
- MultiFieldArtist.fulltext_search('pablo').first.should == pablo_picasso
335
- MultiFieldArtist.fulltext_search('1881').first.should == pablo_picasso
336
- MultiFieldArtist.fulltext_search('portrait 1912').first.should == portrait_of_picasso
337
-
338
- MultiFieldArtwork.fulltext_search('2010').first.should == warhol
339
- MultiFieldArtwork.fulltext_search('andy').first.should == andy_warhol
340
- MultiFieldArtwork.fulltext_search('pablo').first.should == pablo_picasso
341
- MultiFieldArtwork.fulltext_search('1881').first.should == pablo_picasso
342
- MultiFieldArtwork.fulltext_search('portrait 1912').first.should == portrait_of_picasso
343
- end
344
-
345
- end
346
- context "with filters applied to multiple models" do
347
-
348
- let!(:foobar_artwork) { FilteredArtwork.create(:title => 'foobar') }
349
- let!(:barfoo_artwork) { FilteredArtwork.create(:title => 'barfoo') }
350
- let!(:foobar_artist) { FilteredArtist.create(:full_name => 'foobar') }
351
- let!(:barfoo_artist) { FilteredArtist.create(:full_name => 'barfoo') }
352
-
353
- it "allows filtered searches" do
354
- FilteredArtwork.fulltext_search('foobar', :is_artwork => true).should == [foobar_artwork, barfoo_artwork]
355
- FilteredArtist.fulltext_search('foobar', :is_artwork => true).should == [foobar_artwork, barfoo_artwork]
356
-
357
- FilteredArtwork.fulltext_search('foobar', :is_artwork => true, :is_foobar => true).should == [foobar_artwork]
358
- FilteredArtwork.fulltext_search('foobar', :is_artwork => true, :is_foobar => false).should == [barfoo_artwork]
359
- FilteredArtwork.fulltext_search('foobar', :is_artwork => false, :is_foobar => true).should == [foobar_artist]
360
- FilteredArtwork.fulltext_search('foobar', :is_artwork => false, :is_foobar => false).should == [barfoo_artist]
361
-
362
- FilteredArtist.fulltext_search('foobar', :is_artwork => true, :is_foobar => true).should == [foobar_artwork]
363
- FilteredArtist.fulltext_search('foobar', :is_artwork => true, :is_foobar => false).should == [barfoo_artwork]
364
- FilteredArtist.fulltext_search('foobar', :is_artwork => false, :is_foobar => true).should == [foobar_artist]
365
- FilteredArtist.fulltext_search('foobar', :is_artwork => false, :is_foobar => false).should == [barfoo_artist]
366
- end
367
-
368
- end
369
-
370
- context "with different filters applied to multiple models" do
371
- let!(:foo_artwork) { FilteredArtwork.create(:title => 'foo') }
372
- let!(:bar_artist) { FilteredArtist.create(:full_name => 'bar') }
373
- let!(:baz_other) { FilteredOther.create(:name => 'baz') }
374
-
375
- # These three models are all indexed by the same mongoid_fulltext index, but have different filters
376
- # applied. The index created on the mongoid_fulltext collection should include the ngram and score
377
- # fields as well as the union of all the filter fields to allow for efficient lookups.
378
-
379
- it "creates a proper index for searching efficiently" do
380
- [ FilteredArtwork, FilteredArtist, FilteredOther].each do |klass|
381
- klass.create_indexes
382
- end
383
- index_collection = FilteredArtwork.collection.database['mongoid_fulltext.artworks_and_artists']
384
- ngram_indexes = []
385
- index_collection.indexes.each {|idef| ngram_indexes << idef if idef['key'].has_key?('ngram') }
386
- ngram_indexes.length.should == 1
387
- keys = ngram_indexes.first['key'].keys
388
- expected_keys = ['ngram','score', 'filter_values.is_fuzzy', 'filter_values.is_awesome',
389
- 'filter_values.is_foobar', 'filter_values.is_artwork', 'filter_values.is_artist', 'filter_values.colors?'].sort
390
- keys.sort.should == expected_keys
391
- end
392
-
393
- end
394
-
395
- context "with partitions applied to a model" do
396
-
397
- let!(:artist_2) { PartitionedArtist.create(:full_name => 'foobar', :exhibitions => [ "Art Basel 2011", "Armory NY" ]) }
398
- let!(:artist_1) { PartitionedArtist.create(:full_name => 'foobar', :exhibitions => [ "Art Basel 2011", ]) }
399
- let!(:artist_0) { PartitionedArtist.create(:full_name => 'foobar', :exhibitions => [ ]) }
400
-
401
- it "allows partitioned searches" do
402
- artists_by_exhibition_length = [ artist_0, artist_1, artist_2 ].sort_by{ |x| x.exhibitions.length }
403
- PartitionedArtist.fulltext_search('foobar').sort_by{ |x| x.exhibitions.length }.should == artists_by_exhibition_length
404
- PartitionedArtist.fulltext_search('foobar', :exhibitions => [ "Armory NY" ]).should == [ artist_2 ]
405
- art_basel_only = PartitionedArtist.fulltext_search('foobar', :exhibitions => [ "Art Basel 2011" ]).sort_by{ |x| x.exhibitions.length }
406
- art_basel_only.should == [ artist_1, artist_2 ].sort_by{ |x| x.exhibitions.length }
407
- PartitionedArtist.fulltext_search('foobar', :exhibitions => [ "Art Basel 2011", "Armory NY" ]).should == [ artist_2 ]
408
- end
409
-
410
- end
411
-
412
- context "using search options" do
413
- let!(:patterns) { BasicArtwork.create(:title => 'Flower Patterns') }
414
- let!(:flowers) { BasicArtwork.create(:title => 'Flowers') }
415
-
416
- it "returns max_results" do
417
- BasicArtwork.fulltext_search('flower', { :max_results => 1 }).length.should == 1
418
- end
419
-
420
- it "returns scored results" do
421
- results = BasicArtwork.fulltext_search('flowers', { :return_scores => true })
422
- first_result = results[0]
423
- first_result.is_a?(Array).should be_true
424
- first_result.size.should == 2
425
- first_result[0].should == flowers
426
- first_result[1].is_a?(Float).should be_true
427
- end
428
- end
429
-
430
- context "with various word separators" do
431
- let!(:hard_edged_painting) { BasicArtwork.create(:title => "Hard-edged painting") }
432
- let!(:edgy_painting) { BasicArtwork.create(:title => "Edgy painting") }
433
- let!(:hard_to_find_ledge) { BasicArtwork.create(:title => "Hard to find ledge") }
434
-
435
- it "should treat dashes as word separators, giving a score boost to each dash-separated word" do
436
- BasicArtwork.fulltext_search('hard-edged').first.should == hard_edged_painting
437
- BasicArtwork.fulltext_search('hard edge').first.should == hard_edged_painting
438
- BasicArtwork.fulltext_search('hard edged').first.should == hard_edged_painting
439
- end
440
- end
441
-
442
- context "returning scores" do
443
- # Since we return scores, let's make some weak guarantees about what they actually mean
444
-
445
- let!(:mao_yan) { ExternalArtist.create(:full_name => "Mao Yan") }
446
- let!(:mao) { ExternalArtwork.create(:title => "Mao by Andy Warhol") }
447
- let!(:maox) { ExternalArtwork.create(:title => "Maox by Randy Morehall") }
448
- let!(:somao) { ExternalArtwork.create(:title => "Somao by Randy Morehall") }
449
-
450
- it "returns basic matches that don't match a whole word and aren't prefixes with score < 1" do
451
- ['paox', 'porehall'].each do |query|
452
- results = ExternalArtist.fulltext_search(query, { :return_scores => true })
453
- results.length.should > 0
454
- results.map{ |result| result[-1] }.inject(true){ |accum, item| accum &= (item < 1) }.should be_true
455
- end
456
- end
457
-
458
- it "returns prefix matches with a score >= 1 but < 2" do
459
- ['warho', 'rand'].each do |query|
460
- results = ExternalArtist.fulltext_search(query, { :return_scores => true })
461
- results.length.should > 0
462
- results.map{ |result| result[-1] if result[0].to_s.starts_with?(query)}.compact.inject(true){ |accum, item| accum &= (item >= 1 and item < 2) }.should be_true
463
- end
464
- end
465
-
466
- it "returns full-word matches with a score >= 2" do
467
- ['andy', 'warhol', 'mao'].each do |query|
468
- results = ExternalArtist.fulltext_search(query, { :return_scores => true })
469
- results.length.should > 0
470
- results.map{ |result| result[-1] if result[0].to_s.split(' ').member?(query) }.compact.inject(true){ |accum, item| accum &= (item >= 2) }.should be_true
471
- end
472
- end
473
-
474
- end
475
-
476
- context "with stop words defined" do
477
- let!(:flowers) { StopwordsArtwork.create(:title => "Flowers by Andy Warhol") }
478
- let!(:many_ands) { StopwordsArtwork.create(:title => "Foo and bar and baz and foobar") }
479
- let!(:harry) { StopwordsArtwork.create(:title => "Harry in repose by JK Rowling") }
480
-
481
- it "doesn't give a full-word score boost to stopwords" do
482
- StopwordsArtwork.fulltext_search("andy").map{ |a| a.title }.should == [flowers.title, many_ands.title]
483
- StopwordsArtwork.fulltext_search("warhol and other stuff").map{ |a| a.title }.should == [flowers.title, many_ands.title]
484
- end
485
-
486
- it "allows searching on words that are more than one letter, less than the ngram length and not stopwords" do
487
- StopwordsArtwork.fulltext_search("jk").map{ |a| a.title }.should == [harry.title]
488
- StopwordsArtwork.fulltext_search("by").map{ |a| a.title }.should == []
489
- end
490
-
491
- end
492
-
493
- context "indexing short prefixes" do
494
- let!(:dimethyl_mercury) { ShortPrefixesArtwork.create(:title => "Dimethyl Mercury by Damien Hirst") }
495
- let!(:volume) { ShortPrefixesArtwork.create(:title => "Volume by Dadamaino") }
496
- let!(:damaged) { ShortPrefixesArtwork.create(:title => "Damaged: Photographs from the Chicago Daily News 1902-1933 (Governor) by Lisa Oppenheim") }
497
- let!(:frozen) { ShortPrefixesArtwork.create(:title => "Frozen Fountain XXX by Evelyn Rosenberg") }
498
- let!(:skull) { ShortPrefixesArtwork.create(:title => "Skull by Andy Warhol") }
499
-
500
- it "finds the most relevant items with prefix indexing" do
501
- ShortPrefixesArtwork.fulltext_search("damien").first.should == dimethyl_mercury
502
- ShortPrefixesArtwork.fulltext_search("dami").first.should == dimethyl_mercury
503
- ShortPrefixesArtwork.fulltext_search("dama").first.should == damaged
504
- ShortPrefixesArtwork.fulltext_search("dam").first.should_not == volume
505
- ShortPrefixesArtwork.fulltext_search("dadamaino").first.should == volume
506
- ShortPrefixesArtwork.fulltext_search("kull").first.should == skull
507
- end
508
-
509
- it "doesn't index prefixes of stopwords" do
510
- # damaged has the word "from" in it, which shouldn't get indexed.
511
- ShortPrefixesArtwork.fulltext_search("fro").should == [frozen]
512
- end
513
-
514
- it "does index prefixes that would be stopwords taken alone" do
515
- # skull has the word "andy" in it, which should get indexed as "and" even though "and" is a stopword
516
- ShortPrefixesArtwork.fulltext_search("and").should == [skull]
517
- end
518
- end
519
-
520
- context "remove_from_ngram_index" do
521
- let!(:flowers1) { BasicArtwork.create(:title => 'Flowers 1') }
522
- let!(:flowers2) { BasicArtwork.create(:title => 'Flowers 1') }
523
-
524
- it "removes all records from the index" do
525
- BasicArtwork.remove_from_ngram_index
526
- BasicArtwork.fulltext_search('flower').length.should == 0
527
- end
528
-
529
- it "removes a single record from the index" do
530
- flowers1.remove_from_ngram_index
531
- BasicArtwork.fulltext_search('flower').length.should == 1
532
- end
533
- end
534
-
535
- context "update_ngram_index" do
536
- let!(:flowers1) { BasicArtwork.create(:title => 'Flowers 1') }
537
- let!(:flowers2) { BasicArtwork.create(:title => 'Flowers 2') }
538
-
539
- context "when config[:update_if] exists" do
540
-
541
- let(:painting) { BasicArtwork.new :title => 'Painting' }
542
- let(:conditional_index) { BasicArtwork.mongoid_fulltext_config["mongoid_fulltext.index_conditional"] }
543
-
544
- before(:all) do
545
- BasicArtwork.class_eval do
546
- fulltext_search_in :title, :index_name => "mongoid_fulltext.index_conditional"
547
- end
548
- end
549
-
550
- after(:all) do
551
- # Moped 1.0.0rc raises an error when removing a collection that does not exist
552
- # Will be fixed soon.
553
- begin
554
- Mongoid.default_session["mongoid_fulltext.index_conditional"].drop
555
- rescue Moped::Errors::OperationFailure => e
556
- end
557
- BasicArtwork.mongoid_fulltext_config.delete "mongoid_fulltext.index_conditional"
558
- end
559
-
560
- context "and is a symbol" do
561
-
562
- before(:all) do
563
- conditional_index[:update_if] = :persisted?
564
- end
565
-
566
- context "when sending the symbol to the document evaluates to false" do
567
-
568
- it "doesn't update the index for the document" do
569
- painting.update_ngram_index
570
- BasicArtwork.fulltext_search('painting', :index => "mongoid_fulltext.index_conditional").length.should be 0
571
- end
572
-
573
- end
574
- end
575
-
576
- context "and is a string" do
577
-
578
- before(:all) do
579
- conditional_index[:update_if] = 'false'
580
- end
581
-
582
- context "when evaluating the string within the document's instance evaluates to false" do
583
-
584
- it "doesn't update the index for the document" do
585
- painting.update_ngram_index
586
- BasicArtwork.fulltext_search('painting', :index => "mongoid_fulltext.index_conditional").length.should be 0
587
- end
588
-
589
- end
590
- end
591
-
592
- context "and is a proc" do
593
-
594
- before(:all) do
595
- conditional_index[:update_if] = Proc.new { false }
596
- end
597
-
598
- context "when evaluating the string within the document's instance evaluates to false" do
599
-
600
- it "doesn't update the index for the document" do
601
- painting.update_ngram_index
602
- BasicArtwork.fulltext_search('painting', :index => "mongoid_fulltext.index_conditional").length.should be 0
603
- end
604
-
605
- end
606
- end
607
-
608
- context "and is not a symbol, string, or proc" do
609
-
610
- before(:all) do
611
- conditional_index[:update_if] = %w(this isn't a symbol, string, or proc)
612
- end
613
-
614
- it "doesn't update the index for the document" do
615
- painting.update_ngram_index
616
- BasicArtwork.fulltext_search('painting', :index => "mongoid_fulltext.index_conditional").length.should be 0
617
- end
618
-
619
- end
620
- end
621
-
622
- context "from scratch" do
623
-
624
- before(:each) do
625
- Mongoid.default_session["mongoid_fulltext.index_basicartwork_0"].drop
626
- end
627
-
628
- it "updates index on a single record" do
629
- flowers1.update_ngram_index
630
- BasicArtwork.fulltext_search('flower').length.should == 1
631
- end
632
-
633
- it "updates index on all records" do
634
- BasicArtwork.update_ngram_index
635
- BasicArtwork.fulltext_search('flower').length.should == 2
636
- end
637
-
638
- end
639
-
640
- context "incremental" do
641
-
642
- it "removes an existing record" do
643
- coll = Mongoid.default_session["mongoid_fulltext.index_basicartwork_0"]
644
- coll.find('document_id' => flowers1._id).remove_all
645
- coll.find('document_id' => flowers1._id).one.should == nil
646
- flowers1.update_ngram_index
647
- end
648
-
649
- end
650
-
651
- context "mongoid indexes" do
652
-
653
- it "can re-create dropped indexes" do
654
- # there're no indexes by default as Mongoid.autocreate_indexes is set to false
655
- # but mongo will automatically attempt to index _id in the background
656
- Mongoid.default_session["mongoid_fulltext.index_basicartwork_0"].indexes.count.should <= 1
657
- BasicArtwork.create_indexes
658
- expected_indexes = ['_id_', 'fts_index', 'document_id_1'].sort
659
- current_indexes = []
660
- Mongoid.default_session["mongoid_fulltext.index_basicartwork_0"].indexes.each do |idef|
661
- current_indexes << idef['name']
662
- end
663
- current_indexes.sort.should == expected_indexes
664
- end
665
-
666
- it "doesn't fail on models that don't have a fulltext index" do
667
- lambda { HiddenDragon.create_indexes }.should_not raise_error
668
- end
669
-
670
- it "doesn't blow up when the Mongoid.logger is set to false" do
671
- Mongoid.logger = false
672
- BasicArtwork.create_indexes
673
- end
674
-
675
- end
676
-
677
- end
678
-
679
- context "batched reindexing" do
680
- let!(:flowers1) { DelayedArtwork.create(:title => 'Flowers 1') }
681
-
682
- it "should not rebuild index until explicitly invoked" do
683
- DelayedArtwork.fulltext_search("flowers").length.should == 0
684
- DelayedArtwork.update_ngram_index
685
- DelayedArtwork.fulltext_search("flowers").length.should == 1
686
- end
687
- end
688
-
689
- # For =~ operator documentation
690
- # https://github.com/dchelimsky/rspec/blob/master/lib/spec/matchers/match_array.rb#L53
691
-
692
- context "with artwork that returns an array of colors as a filter" do
693
- let!(:title) {"title"}
694
- let!(:nomatch) {"nomatch"}
695
- let!(:red) {"red"}
696
- let!(:green) {"green"}
697
- let!(:blue) {"blue"}
698
- let!(:yellow) {"yellow"}
699
- let!(:brown) {"brown"}
700
-
701
- let!(:rgb_artwork) {FilteredArtwork.create(:title => "#{title} rgb", :colors => [red,green,blue])}
702
- let!(:holiday_artwork) {FilteredArtwork.create(:title => "#{title} holiday", :colors => [red,green])}
703
- let!(:aqua_artwork) {FilteredArtwork.create(:title => "#{title} aqua", :colors => [green,blue])}
704
-
705
- context "with a fulltext search passing red, green, and blue to the colors filter" do
706
- it "should return the rgb artwork" do
707
- FilteredArtwork.fulltext_search(title, :colors? => [red,green,blue]).should == [rgb_artwork]
708
- end
709
- end
710
-
711
- context "with a fulltext search passing blue and red to the colors filter" do
712
- it "should return the rgb artwork" do
713
- FilteredArtwork.fulltext_search(title, :colors? => [blue,red]).should == [rgb_artwork]
714
- end
715
- end
716
-
717
- context "with a fulltext search passing green to the colors filter" do
718
- it "should return all artwork" do
719
- FilteredArtwork.fulltext_search(title, :colors? => [green]).should =~ [rgb_artwork,holiday_artwork,aqua_artwork]
720
- end
721
- end
722
-
723
- context "with a fulltext search passing no colors to the filter" do
724
- it "should return all artwork" do
725
- FilteredArtwork.fulltext_search(title).should =~ [rgb_artwork,holiday_artwork,aqua_artwork]
726
- end
727
- end
728
-
729
- context "with a fulltext search passing green and yellow to the colors filter" do
730
- it "should return no artwork" do
731
- FilteredArtwork.fulltext_search(title, :colors? => [green,yellow]).should == []
732
- end
733
- end
734
-
735
- context "with the query operator overridden to use $in instead of the default $all" do
736
- context "with a fulltext search passing green and yellow to the colors filter" do
737
- it "should return all of the artwork" do
738
- FilteredArtwork.fulltext_search(title, :colors? => {:any => [green,yellow]}).should =~ [rgb_artwork,holiday_artwork,aqua_artwork]
739
- end
740
- end
741
-
742
- context "with a fulltext search passing brown and yellow to the colors filter" do
743
- it "should return none of the artwork" do
744
- FilteredArtwork.fulltext_search(title, :colors? => {:any => [brown,yellow]}).should == []
745
- end
746
- end
747
-
748
- context "with a fulltext search passing blue to the colors filter" do
749
- it "should return the rgb and aqua artwork" do
750
- FilteredArtwork.fulltext_search(title, :colors? => {:any => [blue]}).should == [rgb_artwork,aqua_artwork]
751
- end
752
- end
753
-
754
- context "with a fulltext search term that won't match" do
755
- it "should return none of the artwork" do
756
- FilteredArtwork.fulltext_search(nomatch, :colors? => {:any => [green,yellow]}).should == []
757
- end
758
- end
759
- end
760
-
761
- context "with the query operator overridden to use $all" do
762
- context "with a fulltext search passing red, green, and blue to the colors filter" do
763
- it "should return the rgb artwork" do
764
- FilteredArtwork.fulltext_search(title, :colors? => {:all => [red,green,blue]}).should == [rgb_artwork]
765
- end
766
- end
767
-
768
- context "with a fulltext search passing green to the colors filter" do
769
- it "should return all artwork" do
770
- FilteredArtwork.fulltext_search(title, :colors? => {:all => [green]}).should =~ [rgb_artwork,holiday_artwork,aqua_artwork]
771
- end
772
- end
773
- end
774
-
775
- context "with an unknown query operator used to override the default $all" do
776
- context "with a fulltext search passing red, green, and blue to the colors filter" do
777
- it "should raise an error" do
778
- lambda {
779
- FilteredArtwork.fulltext_search(title, :colors? => {:unknown => [red,green,blue]})
780
- }.should raise_error(Mongoid::FullTextSearch::UnknownFilterQueryOperator)
781
- end
782
- end
783
- end
784
-
785
- context "should properly work with non-latin strings (i.e. cyrillic)" do
786
- let!(:morning) { RussianArtwork.create(:title => "Утро в сосновом лесу Шишкин Morning in a Pine Forest Shishkin") }
787
-
788
- it "should find a match if query is non-latin string" do
789
- # RussianArtwork is just like BasicArtwork, except that we set :alphabet to
790
- # 'abcdefghijklmnopqrstuvwxyz0123456789абвгдежзиклмнопрстуфхцчшщъыьэюя'
791
- RussianArtwork.fulltext_search("shishkin").first.should == morning
792
- RussianArtwork.fulltext_search("шишкин").first.should == morning
793
- end
794
-
795
- end
796
- end
797
-
798
- end
799
- end