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.
- checksums.yaml +15 -0
- data/.gitignore +47 -0
- data/.rspec +1 -1
- data/.rubocop.yml +6 -0
- data/.rubocop_todo.yml +101 -0
- data/.travis.yml +11 -3
- data/CHANGELOG.md +9 -2
- data/Gemfile +19 -9
- data/LICENSE +1 -1
- data/README.md +12 -9
- data/Rakefile +9 -29
- data/lib/mongoid/full_text_search/version.rb +5 -0
- data/lib/mongoid/full_text_search.rb +372 -0
- data/lib/mongoid/indexable.rb +13 -0
- data/lib/mongoid/indexes.rb +13 -0
- data/lib/mongoid_fulltext.rb +1 -341
- data/mongoid_fulltext.gemspec +16 -82
- data/spec/models/accentless_artwork.rb +1 -1
- data/spec/models/advanced_artwork.rb +1 -1
- data/spec/models/basic_artwork.rb +0 -1
- data/spec/models/delayed_artwork.rb +1 -2
- data/spec/models/external_artist.rb +1 -2
- data/spec/models/external_artwork.rb +1 -2
- data/spec/models/external_artwork_no_fields_supplied.rb +2 -2
- data/spec/models/filtered_artist.rb +4 -4
- data/spec/models/filtered_artwork.rb +7 -7
- data/spec/models/filtered_other.rb +3 -3
- data/spec/models/hidden_dragon.rb +0 -1
- data/spec/models/multi_external_artwork.rb +3 -3
- data/spec/models/multi_field_artist.rb +1 -1
- data/spec/models/multi_field_artwork.rb +1 -1
- data/spec/models/partitioned_artist.rb +8 -9
- data/spec/models/russian_artwork.rb +2 -2
- data/spec/models/short_prefixes_artwork.rb +3 -4
- data/spec/models/stopwords_artwork.rb +3 -4
- data/spec/mongoid/full_text_search_spec.rb +752 -0
- data/spec/spec_helper.rb +11 -7
- metadata +27 -68
- data/VERSION +0 -1
- data/lib/mongoid_indexes.rb +0 -12
- data/spec/config/mongoid.yml +0 -6
- 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
|
-
|
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
|
17
|
+
c.before :each do
|
17
18
|
Mongoid.purge!
|
18
19
|
end
|
19
|
-
c.after
|
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.
|
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:
|
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:
|
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:
|
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:
|
40
|
+
version: '0'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
|
-
name:
|
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: :
|
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
|
-
|
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
|
-
-
|
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/
|
97
|
+
- spec/mongoid/full_text_search_spec.rb
|
135
98
|
- spec/spec_helper.rb
|
136
|
-
homepage:
|
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:
|
116
|
+
version: 1.3.6
|
158
117
|
requirements: []
|
159
118
|
rubyforge_project:
|
160
|
-
rubygems_version:
|
119
|
+
rubygems_version: 2.4.5
|
161
120
|
signing_key:
|
162
|
-
specification_version:
|
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
|
data/lib/mongoid_indexes.rb
DELETED
@@ -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
|
-
|
data/spec/config/mongoid.yml
DELETED
@@ -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
|