mongoid_fulltext 0.3.6 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+
data/README.md CHANGED
@@ -207,6 +207,10 @@ Additional indexing/query options can be used as parameters to `fulltext_search_
207
207
  * `index_full_words`: index full words, which improves exact matches, default is `true`
208
208
  * `apply_prefix_scoring_to_all_words`: score n-grams at beginning of words higher, default is `true`
209
209
  * `max_ngrams_to_search`: maximum number of ngrams to query at any given time, default is `6`
210
+ * `max_candidate_set_size`: maximum number of candidate ngrams to examine for a given query.
211
+ Defaults to 1000. If you're seeing poor results, you can try increasing this value to consider
212
+ more ngrams per query (changing this parameter does not require a re-index.) The amount of time
213
+ a search takes is directly proportional to this parameter's value.
210
214
 
211
215
  Array filters
212
216
  -------------
@@ -243,14 +247,19 @@ Building the index
243
247
 
244
248
  The fulltext index is built and maintained incrementally by hooking into `before_save` and
245
249
  `before_destroy` callbacks on each model that's being indexed. If you want to build an index
246
- on existing models, you can call the `update_ngram_index` method on each instance:
250
+ on existing models, you can call the `update_ngram_index` method on the class or each instance:
247
251
 
248
- Artwork.all.each { |artwork| artwork.update_ngram_index }
252
+ Artwork.update_ngram_index
253
+ Artwork.find(id).update_ngram_index
249
254
 
250
- You can also remove instances in bulk from the index with the `remove_from_ngram_index`
255
+ You can remove all or individual instances from the index with the `remove_from_ngram_index`
251
256
  method:
252
257
 
253
- Artwork.all.each { |artwork| artwork.remove_from_ngram_index }
258
+ Artwork.remove_from_ngram_index
259
+ Artwork.find(id).remove_from_ngram_index
260
+
261
+ The methods on the model level perform bulk removal operations and are therefore faster that
262
+ updating or removing records individually.
254
263
 
255
264
  Running the specs
256
265
  -----------------
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.6
1
+ 0.4.0
@@ -8,7 +8,7 @@ module Mongoid::FullTextSearch
8
8
  class UnspecifiedIndexError < StandardError; end
9
9
 
10
10
  module ClassMethods
11
-
11
+
12
12
  def fulltext_search_in(*args)
13
13
  self.mongoid_fulltext_config = {} if self.mongoid_fulltext_config.nil?
14
14
  options = args.last.is_a?(Hash) ? args.pop : {}
@@ -24,7 +24,8 @@ module Mongoid::FullTextSearch
24
24
  :ngram_width => 3,
25
25
  :max_ngrams_to_search => 6,
26
26
  :apply_prefix_scoring_to_all_words => true,
27
- :index_full_words => true
27
+ :index_full_words => true,
28
+ :max_candidate_set_size => 1000
28
29
  }
29
30
 
30
31
  config.update(options)
@@ -35,14 +36,41 @@ module Mongoid::FullTextSearch
35
36
  config[:word_separators] = Hash[config[:word_separators].split('').map{ |ch| [ch,ch] }]
36
37
  self.mongoid_fulltext_config[index_name] = config
37
38
 
38
- coll = collection.db.collection(index_name)
39
- coll.ensure_index([['ngram', Mongo::ASCENDING]])
40
- coll.ensure_index([['document_id', Mongo::ASCENDING]])
39
+ ensure_indexes(index_name, config)
41
40
 
42
41
  before_save :update_ngram_index
43
42
  before_destroy :remove_from_ngram_index
44
43
  end
45
44
 
45
+ def ensure_indexes(index_name, config)
46
+ db = collection.db
47
+ coll = db.collection(index_name)
48
+
49
+ filter_indexes = (config[:filters] || []).map do |key,value|
50
+ ["filter_values.#{key}", Mongo::ASCENDING]
51
+ end
52
+ index_definition = [['ngram', Mongo::ASCENDING], ['score', Mongo::DESCENDING]].concat(filter_indexes)
53
+
54
+ # Since the definition of the index could have changed, we'll clean up by
55
+ # removing any indexes that aren't on the exact
56
+ correct_keys = index_definition.map{ |field_def| field_def[0] }
57
+ all_filter_keys = filter_indexes.map{ |field_def| field_def[0] }
58
+ coll.index_information.each do |name, definition|
59
+ keys = definition['key'].keys
60
+ next if !keys.member?('ngram')
61
+ all_filter_keys |= keys.find_all{ |key| key.starts_with?('filter_values.') }
62
+ coll.drop_index(name) if keys & correct_keys != correct_keys
63
+ end
64
+
65
+ if all_filter_keys.length > filter_indexes.length
66
+ filter_indexes = all_filter_keys.map { |key| [key, Mongo::ASCENDING] }
67
+ index_definition = [['ngram', Mongo::ASCENDING], ['score', Mongo::DESCENDING]].concat(filter_indexes)
68
+ end
69
+
70
+ coll.ensure_index(index_definition, name: 'fts_index')
71
+ coll.ensure_index([['document_id', Mongo::ASCENDING]]) # to make removes fast
72
+ end
73
+
46
74
  def fulltext_search(query_string, options={})
47
75
  max_results = options.has_key?(:max_results) ? options.delete(:max_results) : 10
48
76
  return_scores = options.has_key?(:return_scores) ? options.delete(:return_scores) : false
@@ -51,61 +79,84 @@ module Mongoid::FullTextSearch
51
79
  raise UnspecifiedIndexError, error_message % self.name, caller
52
80
  end
53
81
  index_name = options.has_key?(:index) ? options.delete(:index) : self.mongoid_fulltext_config.keys.first
54
-
55
- # options hash should only contain filters after this point
82
+
83
+ # Options hash should only contain filters after this point
84
+
56
85
  ngrams = all_ngrams(query_string, self.mongoid_fulltext_config[index_name])
57
86
  return [] if ngrams.empty?
58
-
59
- query = {'ngram' => {'$in' => ngrams.keys}}
60
- query.update(Hash[options.map { |key,value| [ 'filter_values.%s' % key, { '$all' => [ value ].flatten } ] }])
61
- map = <<-EOS
62
- function() {
63
- emit(this['document_id'], {'class': this['class'], 'score': this['score']*ngrams[this['ngram']] })
64
- }
65
- EOS
66
- reduce = <<-EOS
67
- function(key, values) {
68
- score = 0.0
69
- for (i in values) {
70
- score += values[i]['score']
71
- }
72
- return({'class': values[0]['class'], 'score': score})
73
- }
74
- EOS
75
- mr_options = {:scope => {:ngrams => ngrams }, :query => query, :raw => true}
76
- rc_options = { :return_scores => return_scores }
87
+
88
+ # For each ngram, construct the query we'll use to pull index documents and
89
+ # get a count of the number of index documents containing that n-gram
90
+ ordering = [['score', Mongo::DESCENDING]]
91
+ limit = self.mongoid_fulltext_config[index_name][:max_candidate_set_size]
77
92
  coll = collection.db.collection(index_name)
78
- if collection.db.connection.server_version >= '1.7.4'
79
- mr_options[:out] = {:inline => 1}
80
- results = coll.map_reduce(map, reduce, mr_options)['results'].sort_by{ |x| -x['value']['score'] }
81
- max_results = results.count if max_results.nil?
82
- instantiate_mapreduce_results(results.first(max_results), rc_options)
83
- else
84
- result_collection = coll.map_reduce(map, reduce, mr_options)['result']
85
- results = collection.db.collection(result_collection).find.sort(['value.score',-1])
86
- results = results.limit(max_results) if !max_results.nil?
87
- models = instantiate_mapreduce_results(results, rc_options)
88
- collection.db.collection(result_collection).drop
89
- models
93
+ cursors = ngrams.map do |ngram|
94
+ query = {'ngram' => ngram[0]}
95
+ query.update(Hash[options.map { |key,value| [ 'filter_values.%s' % key, { '$all' => [ value ].flatten } ] }])
96
+ count = coll.find(query).count
97
+ {:ngram => ngram, :count => count, :query => query}
98
+ end.sort_by!{ |record| record[:count] }
99
+
100
+ # Using the queries we just constructed and the n-gram frequency counts we
101
+ # just computed, pull in about *:max_candidate_set_size* candidates by
102
+ # considering the n-grams in order of increasing frequency. When we've
103
+ # spent all *:max_candidate_set_size* candidates, pull the top-scoring
104
+ # *max_results* candidates for each remaining n-gram.
105
+ results_so_far = 0
106
+ candidates_list = cursors.map do |doc|
107
+ next if doc[:count] == 0
108
+ query_options = {}
109
+ if results_so_far >= limit
110
+ query_options = {:sort => ordering, :limit => max_results}
111
+ elsif doc[:count] > limit - results_so_far
112
+ query_options = {:sort => ordering, :limit => limit - results_so_far}
113
+ end
114
+ results_so_far += doc[:count]
115
+ ngram_score = ngrams[doc[:ngram][0]]
116
+ Hash[coll.find(doc[:query], query_options).map do |candidate|
117
+ [candidate['document_id'],
118
+ {clazz: candidate['class'], score: candidate['score'] * ngram_score}]
119
+ end]
120
+ end.compact
121
+
122
+ # Finally, score all candidates by matching them up with other candidates that are
123
+ # associated with the same document. This is similar to how you might process a
124
+ # boolean AND query, except that with an AND query, you'd stop after considering
125
+ # the first candidate list and matching its candidates up with candidates from other
126
+ # lists, whereas here we want the search to be a little fuzzier so we'll run through
127
+ # all candidate lists, removing candidates as we match them up.
128
+ all_scores = []
129
+ while !candidates_list.empty?
130
+ candidates = candidates_list.pop
131
+ scores = candidates.map do |candidate_id, data|
132
+ {:id => candidate_id,
133
+ :clazz => data[:clazz],
134
+ :score => data[:score] + candidates_list.map{ |others| (others.delete(candidate_id) || {score: 0})[:score] }.sum
135
+ }
136
+ end
137
+ all_scores.concat(scores)
90
138
  end
139
+ all_scores.sort_by!{ |document| -document[:score] }
140
+
141
+ instantiate_mapreduce_results(all_scores[0..max_results-1], { :return_scores => return_scores })
91
142
  end
92
143
 
93
144
  def instantiate_mapreduce_result(result)
94
- Object::const_get(result['value']['class']).find(:first, :conditions => {:id => result['_id']})
145
+ result[:clazz].constantize.find(:first, :conditions => {'_id' => result[:id]})
95
146
  end
96
147
 
97
148
  def instantiate_mapreduce_results(results, options)
98
149
  if (options[:return_scores])
99
- results.map { |result| [ instantiate_mapreduce_result(result), result['value']['score'] ] }.find_all { |result| ! result[0].nil? }
150
+ results.map { |result| [ instantiate_mapreduce_result(result), result[:score] ] }.find_all { |result| ! result[0].nil? }
100
151
  else
101
- results.map { |result| instantiate_mapreduce_result(result) }.find_all { |result| ! result.nil? }
152
+ results.map { |result| instantiate_mapreduce_result(result) }.compact
102
153
  end
103
154
  end
104
155
 
105
156
  # returns an [ngram, score] [ngram, position] pair
106
157
  def all_ngrams(str, config, bound_number_returned = true)
107
158
  return {} if str.nil? or str.length < config[:ngram_width]
108
- filtered_str = str.downcase.split('').map{ |ch| config[:alphabet][ch] }.find_all{ |ch| !ch.nil? }.join('')
159
+ filtered_str = str.downcase.split('').map{ |ch| config[:alphabet][ch] }.compact.join('')
109
160
 
110
161
  if bound_number_returned
111
162
  step_size = [((filtered_str.length - config[:ngram_width]).to_f / config[:max_ngrams_to_search]).ceil, 1].max
@@ -141,7 +192,20 @@ module Mongoid::FullTextSearch
141
192
 
142
193
  ngram_hash
143
194
  end
144
-
195
+
196
+ def remove_from_ngram_index
197
+ self.mongoid_fulltext_config.each_pair do |index_name, fulltext_config|
198
+ coll = collection.db.collection(index_name)
199
+ coll.remove({'class' => self.name})
200
+ end
201
+ end
202
+
203
+ def update_ngram_index
204
+ self.all.each do |model|
205
+ model.update_ngram_index
206
+ end
207
+ end
208
+
145
209
  end
146
210
 
147
211
  def update_ngram_index
@@ -162,7 +226,7 @@ module Mongoid::FullTextSearch
162
226
  rescue
163
227
  # Suppress any exceptions caused by filters
164
228
  end
165
- end.find_all{ |x| !x.nil? }]
229
+ end.compact]
166
230
  end
167
231
  # insert new ngrams in external index
168
232
  ngrams.each_pair do |ngram, score|
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{mongoid_fulltext}
8
- s.version = "0.3.6"
8
+ s.version = "0.4.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 = %q{2011-05-27}
12
+ s.date = %q{2011-07-19}
13
13
  s.description = %q{Full-text search for the Mongoid ORM, using n-grams extracted from text}
14
14
  s.email = %q{aaron.windsor@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -18,6 +18,7 @@ Gem::Specification.new do |s|
18
18
  ]
19
19
  s.files = [
20
20
  ".document",
21
+ ".rspec",
21
22
  "Gemfile",
22
23
  "LICENSE",
23
24
  "README.md",
@@ -32,6 +33,8 @@ Gem::Specification.new do |s|
32
33
  "spec/models/external_artwork_no_fields_supplied.rb",
33
34
  "spec/models/filtered_artist.rb",
34
35
  "spec/models/filtered_artwork.rb",
36
+ "spec/models/filtered_other.rb",
37
+ "spec/models/gallery/basic_artwork.rb",
35
38
  "spec/models/multi_external_artwork.rb",
36
39
  "spec/models/multi_field_artist.rb",
37
40
  "spec/models/multi_field_artwork.rb",
@@ -42,7 +45,7 @@ Gem::Specification.new do |s|
42
45
  s.homepage = %q{http://github.com/aaw/mongoid_fulltext}
43
46
  s.licenses = ["MIT"]
44
47
  s.require_paths = ["lib"]
45
- s.rubygems_version = %q{1.3.7}
48
+ s.rubygems_version = %q{1.6.2}
46
49
  s.summary = %q{Full-text search for the Mongoid ORM}
47
50
  s.test_files = [
48
51
  "spec/models/advanced_artwork.rb",
@@ -52,6 +55,8 @@ Gem::Specification.new do |s|
52
55
  "spec/models/external_artwork_no_fields_supplied.rb",
53
56
  "spec/models/filtered_artist.rb",
54
57
  "spec/models/filtered_artwork.rb",
58
+ "spec/models/filtered_other.rb",
59
+ "spec/models/gallery/basic_artwork.rb",
55
60
  "spec/models/multi_external_artwork.rb",
56
61
  "spec/models/multi_field_artist.rb",
57
62
  "spec/models/multi_field_artwork.rb",
@@ -61,7 +66,6 @@ Gem::Specification.new do |s|
61
66
  ]
62
67
 
63
68
  if s.respond_to? :specification_version then
64
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
65
69
  s.specification_version = 3
66
70
 
67
71
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
@@ -0,0 +1,11 @@
1
+ # This is some other model that lives in the same index with FilteredArtist and FilteredArtwork,
2
+ # to make sure different filters can co-exist in the same index and are indexed properly.
3
+ class FilteredOther
4
+ include Mongoid::Document
5
+ include Mongoid::FullTextSearch
6
+ field :name
7
+ fulltext_search_in :name, :index_name => 'mongoid_fulltext.artworks_and_artists',
8
+ :filters => { :is_fuzzy => lambda { |x| true },
9
+ :is_awesome => lambda { |x| false }
10
+ }
11
+ end
@@ -0,0 +1,9 @@
1
+ module Gallery
2
+ class BasicArtwork
3
+ include Mongoid::Document
4
+ include Mongoid::FullTextSearch
5
+
6
+ field :title
7
+ fulltext_search_in :title
8
+ end
9
+ end
@@ -62,6 +62,23 @@ module Mongoid
62
62
  end
63
63
 
64
64
  end
65
+
66
+ context "with default settings" do
67
+
68
+ let!(:flower_myth) { Gallery::BasicArtwork.create(:title => 'Flower Myth') }
69
+ let!(:flowers) { Gallery::BasicArtwork.create(:title => 'Flowers') }
70
+ let!(:lowered) { Gallery::BasicArtwork.create(:title => 'Lowered') }
71
+ let!(:cookies) { Gallery::BasicArtwork.create(:title => 'Cookies') }
72
+ let!(:empty) { Gallery::BasicArtwork.create(:title => '') }
73
+
74
+ it "returns exact matches for model within a module" do
75
+ Gallery::BasicArtwork.fulltext_search('Flower Myth', :max_results => 1).first.should == flower_myth
76
+ Gallery::BasicArtwork.fulltext_search('Flowers', :max_results => 1).first.should == flowers
77
+ Gallery::BasicArtwork.fulltext_search('Cookies', :max_results => 1).first.should == cookies
78
+ Gallery::BasicArtwork.fulltext_search('Lowered', :max_results => 1).first.should == lowered
79
+ end
80
+
81
+ end
65
82
 
66
83
  context "with default settings" do
67
84
 
@@ -310,6 +327,26 @@ module Mongoid
310
327
  end
311
328
 
312
329
  end
330
+
331
+ context "with different filters applied to multiple models" do
332
+ let!(:foo_artwork) { FilteredArtwork.create(:title => 'foo') }
333
+ let!(:bar_artist) { FilteredArtist.create(:full_name => 'bar') }
334
+ let!(:baz_other) { FilteredOther.create(:name => 'baz') }
335
+
336
+ # These three models are all indexed by the same mongoid_fulltext index, but have different filters
337
+ # applied. The index created on the mongoid_fulltext collection should include the ngram and score
338
+ # fields as well as the union of all the filter fields to allow for efficient lookups.
339
+
340
+ it "creates a proper index for searching efficiently" do
341
+ index_collection = FilteredArtwork.collection.db.collection('mongoid_fulltext.artworks_and_artists')
342
+ ngram_indexes = index_collection.index_information.find_all{ |name, definition| definition['key'].has_key?('ngram') }
343
+ ngram_indexes.length.should == 1
344
+ keys = ngram_indexes.first[1]['key'].keys
345
+ expected_keys = ['ngram','score', 'filter_values.is_fuzzy', 'filter_values.is_awesome',
346
+ 'filter_values.is_foobar', 'filter_values.is_artwork', 'filter_values.is_artist'].sort
347
+ keys.sort.should == expected_keys
348
+ end
349
+ end
313
350
 
314
351
  context "with partitions applied to a model" do
315
352
 
@@ -318,9 +355,11 @@ module Mongoid
318
355
  let!(:artist_0) { PartitionedArtist.create(:full_name => 'foobar', :exhibitions => [ ]) }
319
356
 
320
357
  it "allows partitioned searches" do
321
- PartitionedArtist.fulltext_search('foobar').should == [ artist_2, artist_1, artist_0 ]
358
+ artists_by_exhibition_length = [ artist_0, artist_1, artist_2 ].sort_by{ |x| x.exhibitions.length }
359
+ PartitionedArtist.fulltext_search('foobar').sort_by{ |x| x.exhibitions.length }.should == artists_by_exhibition_length
322
360
  PartitionedArtist.fulltext_search('foobar', :exhibitions => [ "Armory NY" ]).should == [ artist_2 ]
323
- PartitionedArtist.fulltext_search('foobar', :exhibitions => [ "Art Basel 2011" ]).should == [ artist_2, artist_1 ]
361
+ art_basel_only = PartitionedArtist.fulltext_search('foobar', :exhibitions => [ "Art Basel 2011" ]).sort_by{ |x| x.exhibitions.length }
362
+ art_basel_only.should == [ artist_1, artist_2 ].sort_by{ |x| x.exhibitions.length }
324
363
  PartitionedArtist.fulltext_search('foobar', :exhibitions => [ "Art Basel 2011", "Armory NY" ]).should == [ artist_2 ]
325
364
  end
326
365
 
@@ -343,6 +382,56 @@ module Mongoid
343
382
  first_result[1].is_a?(Float).should be_true
344
383
  end
345
384
  end
385
+
386
+ context "remove_from_ngram_index" do
387
+ let!(:flowers1) { BasicArtwork.create(:title => 'Flowers 1') }
388
+ let!(:flowers2) { BasicArtwork.create(:title => 'Flowers 1') }
389
+
390
+ it "removes all records from the index" do
391
+ BasicArtwork.remove_from_ngram_index
392
+ BasicArtwork.fulltext_search('flower').length.should == 0
393
+ end
394
+
395
+ it "removes a single record from the index" do
396
+ flowers1.remove_from_ngram_index
397
+ BasicArtwork.fulltext_search('flower').length.should == 1
398
+ end
399
+ end
400
+
401
+ context "update_ngram_index" do
402
+ let!(:flowers1) { BasicArtwork.create(:title => 'Flowers 1') }
403
+ let!(:flowers2) { BasicArtwork.create(:title => 'Flowers 2') }
404
+
405
+ context "from scratch" do
406
+
407
+ before(:each) do
408
+ Mongoid.master["mongoid_fulltext.index_basicartwork_0"].remove
409
+ end
410
+
411
+ it "updates index on a single record" do
412
+ flowers1.update_ngram_index
413
+ BasicArtwork.fulltext_search('flower').length.should == 1
414
+ end
415
+
416
+ it "updates index on all records" do
417
+ BasicArtwork.update_ngram_index
418
+ BasicArtwork.fulltext_search('flower').length.should == 2
419
+ end
420
+
421
+ end
422
+
423
+ context "incremental" do
424
+
425
+ it "removes an existing record" do
426
+ coll = Mongoid.master["mongoid_fulltext.index_basicartwork_0"]
427
+ Mongoid.master.stub(:collection).with("mongoid_fulltext.index_basicartwork_0").and_return { coll }
428
+ coll.should_receive(:remove).once.with({'document_id' => flowers1._id})
429
+ flowers1.update_ngram_index
430
+ end
431
+
432
+ end
433
+
434
+ end
346
435
 
347
436
  end
348
437
  end
data/spec/spec_helper.rb CHANGED
@@ -11,10 +11,11 @@ Mongoid.configure do |config|
11
11
  end
12
12
 
13
13
  require File.expand_path("../../lib/mongoid_fulltext", __FILE__)
14
- Dir["#{File.dirname(__FILE__)}/models/*.rb"].each { |f| require f }
14
+ Dir["#{File.dirname(__FILE__)}/models/**/*.rb"].each { |f| require f }
15
15
 
16
16
  Rspec.configure do |c|
17
17
  c.before(:all) { DatabaseCleaner.strategy = :truncation }
18
18
  c.before(:each) { DatabaseCleaner.clean }
19
+ c.after(:all) { Mongoid.master.command({'repairDatabase' => 1}) }
19
20
  end
20
21
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid_fulltext
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.6
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,12 +9,12 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-05-27 00:00:00.000000000 -04:00
12
+ date: 2011-07-19 00:00:00.000000000 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: mongoid
17
- requirement: &82366030 !ruby/object:Gem::Requirement
17
+ requirement: &70446910 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ~>
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: 2.0.0
23
23
  type: :development
24
24
  prerelease: false
25
- version_requirements: *82366030
25
+ version_requirements: *70446910
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: database_cleaner
28
- requirement: &82365790 !ruby/object:Gem::Requirement
28
+ requirement: &70444760 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ~>
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: 0.6.0
34
34
  type: :development
35
35
  prerelease: false
36
- version_requirements: *82365790
36
+ version_requirements: *70444760
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: rspec
39
- requirement: &82365550 !ruby/object:Gem::Requirement
39
+ requirement: &70397170 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ~>
@@ -44,10 +44,10 @@ dependencies:
44
44
  version: 2.5.0
45
45
  type: :development
46
46
  prerelease: false
47
- version_requirements: *82365550
47
+ version_requirements: *70397170
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: jeweler
50
- requirement: &82365310 !ruby/object:Gem::Requirement
50
+ requirement: &70386800 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
53
  - - ~>
@@ -55,7 +55,7 @@ dependencies:
55
55
  version: 1.5.2
56
56
  type: :development
57
57
  prerelease: false
58
- version_requirements: *82365310
58
+ version_requirements: *70386800
59
59
  description: Full-text search for the Mongoid ORM, using n-grams extracted from text
60
60
  email: aaron.windsor@gmail.com
61
61
  executables: []
@@ -65,6 +65,7 @@ extra_rdoc_files:
65
65
  - README.md
66
66
  files:
67
67
  - .document
68
+ - .rspec
68
69
  - Gemfile
69
70
  - LICENSE
70
71
  - README.md
@@ -79,6 +80,8 @@ files:
79
80
  - spec/models/external_artwork_no_fields_supplied.rb
80
81
  - spec/models/filtered_artist.rb
81
82
  - spec/models/filtered_artwork.rb
83
+ - spec/models/filtered_other.rb
84
+ - spec/models/gallery/basic_artwork.rb
82
85
  - spec/models/multi_external_artwork.rb
83
86
  - spec/models/multi_field_artist.rb
84
87
  - spec/models/multi_field_artwork.rb
@@ -99,6 +102,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
99
102
  - - ! '>='
100
103
  - !ruby/object:Gem::Version
101
104
  version: '0'
105
+ segments:
106
+ - 0
107
+ hash: 858859311
102
108
  required_rubygems_version: !ruby/object:Gem::Requirement
103
109
  none: false
104
110
  requirements:
@@ -119,6 +125,8 @@ test_files:
119
125
  - spec/models/external_artwork_no_fields_supplied.rb
120
126
  - spec/models/filtered_artist.rb
121
127
  - spec/models/filtered_artwork.rb
128
+ - spec/models/filtered_other.rb
129
+ - spec/models/gallery/basic_artwork.rb
122
130
  - spec/models/multi_external_artwork.rb
123
131
  - spec/models/multi_field_artist.rb
124
132
  - spec/models/multi_field_artwork.rb