mongoid_fulltext 0.5.8 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.8
1
+ 0.6.0
@@ -23,7 +23,7 @@ module Mongoid::FullTextSearch
23
23
  index_name = 'mongoid_fulltext.index_%s_%s' % [self.name.downcase, self.mongoid_fulltext_config.count]
24
24
  end
25
25
 
26
- config = {
26
+ config = {
27
27
  :alphabet => 'abcdefghijklmnopqrstuvwxyz0123456789 ',
28
28
  :word_separators => "-_ \n\t",
29
29
  :ngram_width => 3,
@@ -34,17 +34,17 @@ module Mongoid::FullTextSearch
34
34
  :max_candidate_set_size => 1000,
35
35
  :remove_accents => true,
36
36
  :reindex_immediately => true,
37
- :stop_words => Hash[['i', 'a', 's', 't', 'me', 'my', 'we', 'he', 'it', 'am', 'is', 'be', 'do', 'an', 'if',
38
- 'or', 'as', 'of', 'at', 'by', 'to', 'up', 'in', 'on', 'no', 'so', 'our', 'you', 'him',
39
- 'his', 'she', 'her', 'its', 'who', 'are', 'was', 'has', 'had', 'did', 'the', 'and',
40
- 'but', 'for', 'out', 'off', 'why', 'how', 'all', 'any', 'few', 'nor', 'not', 'own',
41
- 'too', 'can', 'don', 'now', 'ours', 'your', 'hers', 'they', 'them', 'what', 'whom',
42
- 'this', 'that', 'were', 'been', 'have', 'does', 'with', 'into', 'from', 'down', 'over',
43
- 'then', 'once', 'here', 'when', 'both', 'each', 'more', 'most', 'some', 'such', 'only',
44
- 'same', 'than', 'very', 'will', 'just', 'yours', 'their', 'which', 'these', 'those',
45
- 'being', 'doing', 'until', 'while', 'about', 'after', 'above', 'below', 'under',
46
- 'again', 'there', 'where', 'other', 'myself', 'itself', 'theirs', 'having', 'during',
47
- 'before', 'should', 'himself', 'herself', 'because', 'against', 'between', 'through',
37
+ :stop_words => Hash[['i', 'a', 's', 't', 'me', 'my', 'we', 'he', 'it', 'am', 'is', 'be', 'do', 'an', 'if',
38
+ 'or', 'as', 'of', 'at', 'by', 'to', 'up', 'in', 'on', 'no', 'so', 'our', 'you', 'him',
39
+ 'his', 'she', 'her', 'its', 'who', 'are', 'was', 'has', 'had', 'did', 'the', 'and',
40
+ 'but', 'for', 'out', 'off', 'why', 'how', 'all', 'any', 'few', 'nor', 'not', 'own',
41
+ 'too', 'can', 'don', 'now', 'ours', 'your', 'hers', 'they', 'them', 'what', 'whom',
42
+ 'this', 'that', 'were', 'been', 'have', 'does', 'with', 'into', 'from', 'down', 'over',
43
+ 'then', 'once', 'here', 'when', 'both', 'each', 'more', 'most', 'some', 'such', 'only',
44
+ 'same', 'than', 'very', 'will', 'just', 'yours', 'their', 'which', 'these', 'those',
45
+ 'being', 'doing', 'until', 'while', 'about', 'after', 'above', 'below', 'under',
46
+ 'again', 'there', 'where', 'other', 'myself', 'itself', 'theirs', 'having', 'during',
47
+ 'before', 'should', 'himself', 'herself', 'because', 'against', 'between', 'through',
48
48
  'further', 'yourself', 'ourselves', 'yourselves', 'themselves'].map{ |x| [x,true] }]
49
49
  }
50
50
 
@@ -68,45 +68,46 @@ module Mongoid::FullTextSearch
68
68
  end
69
69
 
70
70
  def fulltext_search_ensure_indexes(index_name, config)
71
- db = collection.db
72
- coll = db.collection(index_name)
71
+ db = collection.database
72
+ coll = db[index_name]
73
73
 
74
74
  # The order of filters matters when the same index is used from two or more collections.
75
75
  filter_indexes = (config[:filters] || []).map do |key,value|
76
- ["filter_values.#{key}", Mongo::ASCENDING]
76
+ ["filter_values.#{key}", 1]
77
77
  end.sort_by { |filter_index| filter_index[0] }
78
78
 
79
- index_definition = [['ngram', Mongo::ASCENDING], ['score', Mongo::DESCENDING]].concat(filter_indexes)
79
+ index_definition = [['ngram', 1], ['score', -1]].concat(filter_indexes)
80
80
 
81
81
  # Since the definition of the index could have changed, we'll clean up by
82
82
  # removing any indexes that aren't on the exact.
83
83
  correct_keys = index_definition.map{ |field_def| field_def[0] }
84
84
  all_filter_keys = filter_indexes.map{ |field_def| field_def[0] }
85
- coll.index_information.each do |name, definition|
86
- keys = definition['key'].keys
85
+ coll.indexes.each do |idef|
86
+ keys = idef['key'].keys
87
87
  next if !keys.member?('ngram')
88
88
  all_filter_keys |= keys.find_all{ |key| key.starts_with?('filter_values.') }
89
89
  if keys & correct_keys != correct_keys
90
- Mongoid.logger.info "Dropping #{name} [#{keys & correct_keys} <=> #{correct_keys}]" if Mongoid.logger
91
- coll.drop_index(name)
90
+ Mongoid.logger.info "Dropping #{idef['name']} [#{keys & correct_keys} <=> #{correct_keys}]" if Mongoid.logger
91
+ coll.indexes.drop(idef['key'])
92
92
  end
93
93
  end
94
94
 
95
95
  if all_filter_keys.length > filter_indexes.length
96
- filter_indexes = all_filter_keys.map { |key| [key, Mongo::ASCENDING] }.sort_by { |filter_index| filter_index[0] }
97
- index_definition = [['ngram', Mongo::ASCENDING], ['score', Mongo::DESCENDING]].concat(filter_indexes)
96
+ filter_indexes = all_filter_keys.map {|key| [key, 1] }.sort_by { |filter_index| filter_index[0] }
97
+ index_definition = [['ngram', 1], ['score', -1]].concat(filter_indexes)
98
98
  end
99
99
 
100
100
  Mongoid.logger.info "Ensuring fts_index on #{coll.name}: #{index_definition}" if Mongoid.logger
101
- coll.ensure_index(index_definition, { :name => 'fts_index' })
101
+ coll.indexes.create(Hash[index_definition], { :name => 'fts_index' })
102
+
102
103
  Mongoid.logger.info "Ensuring document_id index on #{coll.name}" if Mongoid.logger
103
- coll.ensure_index([['document_id', Mongo::ASCENDING]]) # to make removes fast
104
+ coll.indexes.create('document_id' => 1) # to make removes fast
104
105
  end
105
106
 
106
107
  def fulltext_search(query_string, options={})
107
108
  max_results = options.has_key?(:max_results) ? options.delete(:max_results) : 10
108
109
  return_scores = options.has_key?(:return_scores) ? options.delete(:return_scores) : false
109
- if self.mongoid_fulltext_config.count > 1 and !options.has_key?(:index)
110
+ if self.mongoid_fulltext_config.count > 1 and !options.has_key?(:index)
110
111
  error_message = '%s is indexed by multiple full-text indexes. You must specify one by passing an :index_name parameter'
111
112
  raise UnspecifiedIndexError, error_message % self.name, caller
112
113
  end
@@ -117,12 +118,12 @@ module Mongoid::FullTextSearch
117
118
  ngrams = all_ngrams(query_string, self.mongoid_fulltext_config[index_name])
118
119
  return [] if ngrams.empty?
119
120
 
120
- # For each ngram, construct the query we'll use to pull index documents and
121
+ # For each ngram, construct the query we'll use to pull index documents and
121
122
  # get a count of the number of index documents containing that n-gram
122
- ordering = [['score', Mongo::DESCENDING]]
123
+ ordering = {'score' => -1}
123
124
  limit = self.mongoid_fulltext_config[index_name][:max_candidate_set_size]
124
- coll = collection.db.collection(index_name)
125
- cursors = ngrams.map do |ngram|
125
+ coll = collection.database[index_name]
126
+ cursors = ngrams.map do |ngram|
126
127
  query = {'ngram' => ngram[0]}
127
128
  query.update(map_query_filters options)
128
129
  count = coll.find(query).count
@@ -130,23 +131,23 @@ module Mongoid::FullTextSearch
130
131
  end.sort!{ |record1, record2| record1[:count] <=> record2[:count] }
131
132
 
132
133
  # Using the queries we just constructed and the n-gram frequency counts we
133
- # just computed, pull in about *:max_candidate_set_size* candidates by
134
- # considering the n-grams in order of increasing frequency. When we've
135
- # spent all *:max_candidate_set_size* candidates, pull the top-scoring
134
+ # just computed, pull in about *:max_candidate_set_size* candidates by
135
+ # considering the n-grams in order of increasing frequency. When we've
136
+ # spent all *:max_candidate_set_size* candidates, pull the top-scoring
136
137
  # *max_results* candidates for each remaining n-gram.
137
138
  results_so_far = 0
138
139
  candidates_list = cursors.map do |doc|
139
140
  next if doc[:count] == 0
140
- query_options = {}
141
+ query_result = coll.find(doc[:query])
141
142
  if results_so_far >= limit
142
- query_options = {:sort => ordering, :limit => max_results}
143
+ query_result = query_result.sort(ordering).limit(max_results)
143
144
  elsif doc[:count] > limit - results_so_far
144
- query_options = {:sort => ordering, :limit => limit - results_so_far}
145
+ query_result = query_result.sort(ordering).limit(limit - results_so_far)
145
146
  end
146
147
  results_so_far += doc[:count]
147
148
  ngram_score = ngrams[doc[:ngram][0]]
148
- Hash[coll.find(doc[:query], query_options).map do |candidate|
149
- [candidate['document_id'],
149
+ Hash[query_result.map do |candidate|
150
+ [candidate['document_id'],
150
151
  {:clazz => candidate['class'], :score => candidate['score'] * ngram_score}]
151
152
  end]
152
153
  end.compact
@@ -161,8 +162,8 @@ module Mongoid::FullTextSearch
161
162
  while !candidates_list.empty?
162
163
  candidates = candidates_list.pop
163
164
  scores = candidates.map do |candidate_id, data|
164
- {:id => candidate_id,
165
- :clazz => data[:clazz],
165
+ {:id => candidate_id,
166
+ :clazz => data[:clazz],
166
167
  :score => data[:score] + candidates_list.map{ |others| (others.delete(candidate_id) || {:score => 0})[:score] }.sum
167
168
  }
168
169
  end
@@ -173,7 +174,7 @@ module Mongoid::FullTextSearch
173
174
  end
174
175
 
175
176
  def instantiate_mapreduce_result(result)
176
- result[:clazz].constantize.find(:first, :conditions => {'_id' => result[:id]})
177
+ result[:clazz].constantize.find(result[:id])
177
178
  end
178
179
 
179
180
  def instantiate_mapreduce_results(results, options)
@@ -188,11 +189,15 @@ module Mongoid::FullTextSearch
188
189
  return {} if str.nil?
189
190
 
190
191
  if config[:remove_accents]
191
- str = UnicodeUtils.nfkd(str).gsub(/[^\x00-\x7F]/,'')
192
+ if defined?(UnicodeUtils)
193
+ str = UnicodeUtils.nfkd(str)
194
+ elsif defined?(DiacriticsFu)
195
+ str = DiacriticsFu::escape(str)
196
+ end
192
197
  end
193
198
 
194
199
  # Remove any characters that aren't in the alphabet and aren't word separators
195
- filtered_str = str.mb_chars.to_s.downcase.split('').find_all{ |ch| config[:alphabet][ch] or config[:word_separators][ch] }.join('')
200
+ filtered_str = str.mb_chars.downcase.to_s.split('').find_all{ |ch| config[:alphabet][ch] or config[:word_separators][ch] }.join('')
196
201
 
197
202
  # Figure out how many ngrams to extract from the string. If we can't afford to extract all ngrams,
198
203
  # step over the string in evenly spaced strides to extract ngrams. For example, to extract 3 3-letter
@@ -203,7 +208,7 @@ module Mongoid::FullTextSearch
203
208
  step_size = 1
204
209
  end
205
210
 
206
- # Create an array of records of the form {:ngram => x, :score => y} for all ngrams that occur in the
211
+ # Create an array of records of the form {:ngram => x, :score => y} for all ngrams that occur in the
207
212
  # input string using the step size that we just computed. Let score(x,y) be the score of string x
208
213
  # compared with string y - assigning scores to ngrams with the square root-based scoring function
209
214
  # below and multiplying scores of matching ngrams together yields a score function that has the
@@ -258,8 +263,8 @@ module Mongoid::FullTextSearch
258
263
 
259
264
  def remove_from_ngram_index
260
265
  self.mongoid_fulltext_config.each_pair do |index_name, fulltext_config|
261
- coll = collection.db.collection(index_name)
262
- coll.remove({'class' => self.name})
266
+ coll = collection.database[index_name]
267
+ coll.find({'class' => self.name}).remove_all
263
268
  end
264
269
  end
265
270
 
@@ -300,8 +305,8 @@ module Mongoid::FullTextSearch
300
305
  end
301
306
 
302
307
  # remove existing ngrams from external index
303
- coll = collection.db.collection(index_name)
304
- coll.remove({'document_id' => self._id})
308
+ coll = collection.database[index_name.to_sym]
309
+ coll.find({'document_id' => self._id}).remove_all
305
310
  # extract ngrams from fields
306
311
  field_values = fulltext_config[:ngram_fields].map { |field| self.send(field) }
307
312
  ngrams = field_values.inject({}) { |accum, item| accum.update(self.class.all_ngrams(item, fulltext_config, false))}
@@ -310,9 +315,9 @@ module Mongoid::FullTextSearch
310
315
  filter_values = nil
311
316
  if fulltext_config.has_key?(:filters)
312
317
  filter_values = Hash[fulltext_config[:filters].map do |key,value|
313
- begin
314
- [key, value.call(self)]
315
- rescue
318
+ begin
319
+ [key, value.call(self)]
320
+ rescue
316
321
  # Suppress any exceptions caused by filters
317
322
  end
318
323
  end.compact]
@@ -328,8 +333,8 @@ module Mongoid::FullTextSearch
328
333
 
329
334
  def remove_from_ngram_index
330
335
  self.mongoid_fulltext_config.each_pair do |index_name, fulltext_config|
331
- coll = collection.db.collection(index_name)
332
- coll.remove({'document_id' => self._id})
336
+ coll = collection.database[index_name]
337
+ coll.find({'document_id' => self._id}).remove_all
333
338
  end
334
339
  end
335
340
 
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "mongoid_fulltext"
8
- s.version = "0.5.8"
8
+ s.version = "0.6.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Aaron Windsor"]
12
- s.date = "2012-03-08"
12
+ s.date = "2012-07-16"
13
13
  s.description = "Full-text search for the Mongoid ORM, using n-grams extracted from text"
14
14
  s.email = "aaron.windsor@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -19,6 +19,8 @@ Gem::Specification.new do |s|
19
19
  s.files = [
20
20
  ".document",
21
21
  ".rspec",
22
+ ".travis.yml",
23
+ "CHANGELOG.md",
22
24
  "Gemfile",
23
25
  "LICENSE",
24
26
  "README.md",
@@ -27,6 +29,7 @@ Gem::Specification.new do |s|
27
29
  "lib/mongoid_fulltext.rb",
28
30
  "lib/mongoid_indexes.rb",
29
31
  "mongoid_fulltext.gemspec",
32
+ "spec/config/mongoid.yml",
30
33
  "spec/models/accentless_artwork.rb",
31
34
  "spec/models/advanced_artwork.rb",
32
35
  "spec/models/basic_artwork.rb",
@@ -43,6 +46,7 @@ Gem::Specification.new do |s|
43
46
  "spec/models/multi_field_artist.rb",
44
47
  "spec/models/multi_field_artwork.rb",
45
48
  "spec/models/partitioned_artist.rb",
49
+ "spec/models/russian_artwork.rb",
46
50
  "spec/models/short_prefixes_artwork.rb",
47
51
  "spec/models/stopwords_artwork.rb",
48
52
  "spec/mongoid/fulltext_spec.rb",
@@ -51,53 +55,31 @@ Gem::Specification.new do |s|
51
55
  s.homepage = "http://github.com/aaw/mongoid_fulltext"
52
56
  s.licenses = ["MIT"]
53
57
  s.require_paths = ["lib"]
54
- s.rubygems_version = "1.8.10"
58
+ s.rubygems_version = "1.8.24"
55
59
  s.summary = "Full-text search for the Mongoid ORM"
56
- s.test_files = [
57
- "spec/models/accentless_artwork.rb",
58
- "spec/models/advanced_artwork.rb",
59
- "spec/models/basic_artwork.rb",
60
- "spec/models/delayed_artwork.rb",
61
- "spec/models/external_artist.rb",
62
- "spec/models/external_artwork.rb",
63
- "spec/models/external_artwork_no_fields_supplied.rb",
64
- "spec/models/filtered_artist.rb",
65
- "spec/models/filtered_artwork.rb",
66
- "spec/models/filtered_other.rb",
67
- "spec/models/gallery/basic_artwork.rb",
68
- "spec/models/hidden_dragon.rb",
69
- "spec/models/multi_external_artwork.rb",
70
- "spec/models/multi_field_artist.rb",
71
- "spec/models/multi_field_artwork.rb",
72
- "spec/models/partitioned_artist.rb",
73
- "spec/models/short_prefixes_artwork.rb",
74
- "spec/models/stopwords_artwork.rb",
75
- "spec/mongoid/fulltext_spec.rb",
76
- "spec/spec_helper.rb"
77
- ]
78
60
 
79
61
  if s.respond_to? :specification_version then
80
62
  s.specification_version = 3
81
63
 
82
64
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
65
+ s.add_runtime_dependency(%q<mongoid>, ["~> 3.0.1"])
83
66
  s.add_runtime_dependency(%q<unicode_utils>, ["~> 1.0.0"])
84
- s.add_development_dependency(%q<mongoid>, ["~> 2.0.0"])
85
- s.add_development_dependency(%q<bson_ext>, ["~> 1.5.2"])
86
- s.add_development_dependency(%q<rspec>, ["~> 2.5.0"])
87
- s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
67
+ s.add_development_dependency(%q<bundler>, [">= 0"])
68
+ s.add_development_dependency(%q<rspec>, ["~> 2.10.0"])
69
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
88
70
  else
71
+ s.add_dependency(%q<mongoid>, ["~> 3.0.1"])
89
72
  s.add_dependency(%q<unicode_utils>, ["~> 1.0.0"])
90
- s.add_dependency(%q<mongoid>, ["~> 2.0.0"])
91
- s.add_dependency(%q<bson_ext>, ["~> 1.5.2"])
92
- s.add_dependency(%q<rspec>, ["~> 2.5.0"])
93
- s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
73
+ s.add_dependency(%q<bundler>, [">= 0"])
74
+ s.add_dependency(%q<rspec>, ["~> 2.10.0"])
75
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
94
76
  end
95
77
  else
78
+ s.add_dependency(%q<mongoid>, ["~> 3.0.1"])
96
79
  s.add_dependency(%q<unicode_utils>, ["~> 1.0.0"])
97
- s.add_dependency(%q<mongoid>, ["~> 2.0.0"])
98
- s.add_dependency(%q<bson_ext>, ["~> 1.5.2"])
99
- s.add_dependency(%q<rspec>, ["~> 2.5.0"])
100
- s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
80
+ s.add_dependency(%q<bundler>, [">= 0"])
81
+ s.add_dependency(%q<rspec>, ["~> 2.10.0"])
82
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
101
83
  end
102
84
  end
103
85
 
@@ -0,0 +1,6 @@
1
+ test:
2
+ sessions:
3
+ default:
4
+ database: mongoid_fulltext_test
5
+ hosts:
6
+ - localhost:27017
@@ -1,8 +1,8 @@
1
1
  class FilteredArtwork
2
2
  include Mongoid::Document
3
3
  include Mongoid::FullTextSearch
4
- field :title, type: String
5
- field :colors, type: Array, default: []
4
+ field :title, :type => String
5
+ field :colors, :type => Array, :default => []
6
6
  fulltext_search_in :title, :index_name => 'mongoid_fulltext.artworks_and_artists',
7
7
  :filters => { :is_foobar => lambda { |x| x.title == 'foobar' },
8
8
  :is_artwork => lambda { |x| true },
@@ -0,0 +1,10 @@
1
+ # coding: utf-8
2
+ class RussianArtwork
3
+ include Mongoid::Document
4
+ include Mongoid::FullTextSearch
5
+
6
+ Alphabet = 'abcdefghijklmnopqrstuvwxyz0123456789абвгдежзиклмнопрстуфхцчшщъыьэюя'
7
+
8
+ field :title
9
+ fulltext_search_in :title, :alphabet => Alphabet
10
+ end
@@ -1,3 +1,4 @@
1
+ # coding: utf-8
1
2
  require 'spec_helper'
2
3
 
3
4
  module Mongoid
@@ -7,7 +8,7 @@ module Mongoid
7
8
 
8
9
  let!(:abcdef) { AdvancedArtwork.create(:title => 'abcdefg hijklmn') }
9
10
  let!(:cesar) { AccentlessArtwork.create(:title => "C\u00e9sar Galicia") }
10
- let!(:julio) { AccentlessArtwork.create(:title => "Julio Cesar Morales") }
11
+ let!(:julio) { AccentlessArtwork.create(:title => "Julio Cesar Morales") }
11
12
 
12
13
  it "should recognize all options" do
13
14
  # AdvancedArtwork is defined with an ngram_width of 4 and a different alphabet (abcdefg)
@@ -226,8 +227,10 @@ module Mongoid
226
227
 
227
228
  it "doesn't blow up if garbage is in the index collection" do
228
229
  ExternalArtist.fulltext_search('warhol').should == [warhol, andy_warhol]
229
- index_collection = ExternalArtist.collection.db.collection(ExternalArtist.mongoid_fulltext_config.keys.first)
230
- index_collection.update({'document_id' => warhol.id}, {'$set' => { 'document_id' => BSON::ObjectId.new }}, :multi => true)
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
231
234
  # We should no longer be able to find warhol, but that shouldn't keep it from returning results
232
235
  ExternalArtist.fulltext_search('warhol').should == [andy_warhol]
233
236
  end
@@ -374,14 +377,15 @@ module Mongoid
374
377
  # fields as well as the union of all the filter fields to allow for efficient lookups.
375
378
 
376
379
  it "creates a proper index for searching efficiently" do
377
- [ FilteredArtwork, FilteredArtist, FilteredOther].each do |klass|
380
+ [ FilteredArtwork, FilteredArtist, FilteredOther].each do |klass|
378
381
  klass.create_indexes
379
382
  end
380
- index_collection = FilteredArtwork.collection.db.collection('mongoid_fulltext.artworks_and_artists')
381
- ngram_indexes = index_collection.index_information.find_all{ |name, definition| definition['key'].has_key?('ngram') }
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') }
382
386
  ngram_indexes.length.should == 1
383
- keys = ngram_indexes.first[1]['key'].keys
384
- expected_keys = ['ngram','score', 'filter_values.is_fuzzy', 'filter_values.is_awesome',
387
+ keys = ngram_indexes.first['key'].keys
388
+ expected_keys = ['ngram','score', 'filter_values.is_fuzzy', 'filter_values.is_awesome',
385
389
  'filter_values.is_foobar', 'filter_values.is_artwork', 'filter_values.is_artist', 'filter_values.colors?'].sort
386
390
  keys.sort.should == expected_keys
387
391
  end
@@ -455,10 +459,7 @@ module Mongoid
455
459
  ['warho', 'rand'].each do |query|
456
460
  results = ExternalArtist.fulltext_search(query, { :return_scores => true })
457
461
  results.length.should > 0
458
- results.map{ |result| result[-1] if result[0].to_s.starts_with?(query)}
459
- .compact
460
- .inject(true){ |accum, item| accum &= (item >= 1 and item < 2) }
461
- .should be_true
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
462
463
  end
463
464
  end
464
465
 
@@ -466,10 +467,7 @@ module Mongoid
466
467
  ['andy', 'warhol', 'mao'].each do |query|
467
468
  results = ExternalArtist.fulltext_search(query, { :return_scores => true })
468
469
  results.length.should > 0
469
- results.map{ |result| result[-1] if result[0].to_s.split(' ').member?(query) }
470
- .compact
471
- .inject(true){ |accum, item| accum &= (item >= 2) }
472
- .should be_true
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
473
471
  end
474
472
  end
475
473
 
@@ -530,8 +528,8 @@ module Mongoid
530
528
 
531
529
  it "removes a single record from the index" do
532
530
  flowers1.remove_from_ngram_index
533
- BasicArtwork.fulltext_search('flower').length.should == 1
534
- end
531
+ BasicArtwork.fulltext_search('flower').length.should == 1
532
+ end
535
533
  end
536
534
 
537
535
  context "update_ngram_index" do
@@ -550,7 +548,12 @@ module Mongoid
550
548
  end
551
549
 
552
550
  after(:all) do
553
- Mongoid.master["mongoid_fulltext.index_conditional"].remove
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
554
557
  BasicArtwork.mongoid_fulltext_config.delete "mongoid_fulltext.index_conditional"
555
558
  end
556
559
 
@@ -619,7 +622,7 @@ module Mongoid
619
622
  context "from scratch" do
620
623
 
621
624
  before(:each) do
622
- Mongoid.master["mongoid_fulltext.index_basicartwork_0"].remove
625
+ Mongoid.default_session["mongoid_fulltext.index_basicartwork_0"].drop
623
626
  end
624
627
 
625
628
  it "updates index on a single record" do
@@ -637,9 +640,9 @@ module Mongoid
637
640
  context "incremental" do
638
641
 
639
642
  it "removes an existing record" do
640
- coll = Mongoid.master["mongoid_fulltext.index_basicartwork_0"]
641
- Mongoid.master.stub(:collection).with("mongoid_fulltext.index_basicartwork_0").and_return { coll }
642
- coll.should_receive(:remove).once.with({'document_id' => flowers1._id})
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
643
646
  flowers1.update_ngram_index
644
647
  end
645
648
 
@@ -650,10 +653,14 @@ module Mongoid
650
653
  it "can re-create dropped indexes" do
651
654
  # there're no indexes by default as Mongoid.autocreate_indexes is set to false
652
655
  # but mongo will automatically attempt to index _id in the background
653
- Mongoid.master["mongoid_fulltext.index_basicartwork_0"].index_information.size.should <= 1
656
+ Mongoid.default_session["mongoid_fulltext.index_basicartwork_0"].indexes.count.should <= 1
654
657
  BasicArtwork.create_indexes
655
658
  expected_indexes = ['_id_', 'fts_index', 'document_id_1'].sort
656
- Mongoid.master["mongoid_fulltext.index_basicartwork_0"].index_information.keys.sort.should == expected_indexes
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
657
664
  end
658
665
 
659
666
  it "doesn't fail on models that don't have a fulltext index" do
@@ -768,10 +775,24 @@ module Mongoid
768
775
  context "with an unknown query operator used to override the default $all" do
769
776
  context "with a fulltext search passing red, green, and blue to the colors filter" do
770
777
  it "should raise an error" do
771
- -> {FilteredArtwork.fulltext_search(title, :colors? => {:unknown => [red,green,blue]})}.should raise_error(Mongoid::FullTextSearch::UnknownFilterQueryOperator)
778
+ lambda {
779
+ FilteredArtwork.fulltext_search(title, :colors? => {:unknown => [red,green,blue]})
780
+ }.should raise_error(Mongoid::FullTextSearch::UnknownFilterQueryOperator)
772
781
  end
773
782
  end
774
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
775
796
  end
776
797
 
777
798
  end