mongoid_fulltext 0.6.1 → 0.7.0

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