diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index a0ed496..b31254d 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2018-05-08 14:54:25 +0200 using RuboCop version 0.55.0. +# on 2018-05-08 15:36:14 +0200 using RuboCop version 0.55.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -64,7 +64,7 @@ Metrics/MethodLength: # Offense count: 1 # Configuration parameters: CountComments. Metrics/ModuleLength: - Max: 223 + Max: 300 # Offense count: 4 Metrics/PerceivedComplexity: @@ -129,7 +129,7 @@ Style/NumericPredicate: - 'spec/**/*' - 'lib/mongoid/full_text_search.rb' -# Offense count: 262 +# Offense count: 265 # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. # URISchemes: http, https Metrics/LineLength: diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fd4122..15d0013 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,12 @@ -### 0.8.1 (Next) +### 0.8.3 (Next) -* [#35](https://github.com/mongoid/mongoid_fulltext/pull/35): Mongoid 7 compatibility - [@tomasc](https://github.com/tomasc). +* [#37](https://github.com/mongoid/mongoid_fulltext/pull/37): Sci & criteria support - [@tomasc](https://github.com/tomasc). * Your contribution here. +### 0.8.2 (8/5/2018) + +* [#35](https://github.com/mongoid/mongoid_fulltext/pull/35): Mongoid 7 compatibility - [@tomasc](https://github.com/tomasc). + ### 0.8.0 (1/19/2017) * [#28](https://github.com/mongoid/mongoid_fulltext/pull/28): Moved to the mongoid organization - [@dblock](https://github.com/dblock). diff --git a/README.md b/README.md index 195d417..1791363 100644 --- a/README.md +++ b/README.md @@ -249,6 +249,40 @@ the AND of all of the individual results for each of the fields. Finally, if a f but criteria for that filter aren't passed to `fulltext_search`, the result is as if the filter had never been defined - you see both models that both pass and fail the filter in the results. +SCI Support +----------- + +The search respects SCI. From the spec: + +```ruby +class MyDoc + include Mongoid::Document + include Mongoid::FullTextSearch + + field :title + fulltext_search_in :title +end + +class MyInheritedDoc < MyDoc +end +``` + +```ruby +MyDoc.fulltext_search(…) # => will return both MyDoc as well as MyInheritedDoc documents +MyInheritedDoc.fulltext_search(…) # => will return only MyInheritedDoc documents +``` + +Criteria Support +---------------- + +It is also possible to pre-empt the search with Monogid criteria: + +```ruby +MyDoc.where(value: 10).fulltext_search(…) +``` + +Please note that this will not work in case an index is shared by multiple classes (that are not connected through inheritance), since a criteria applies only to one class. + Indexing Options ---------------- diff --git a/lib/mongoid/full_text_search.rb b/lib/mongoid/full_text_search.rb index cf12703..1730e2c 100644 --- a/lib/mongoid/full_text_search.rb +++ b/lib/mongoid/full_text_search.rb @@ -143,6 +143,7 @@ def fulltext_search(query_string, options = {}) coll = collection.database[index_name] cursors = ngrams.map do |ngram| query = { 'ngram' => ngram[0] } + query.update(document_type_filters) query.update(map_query_filters(options)) count = coll.find(query).count { ngram: ngram, count: count, query: query } @@ -191,7 +192,11 @@ def fulltext_search(query_string, options = {}) end def instantiate_mapreduce_result(result) - result[:clazz].constantize.find(result[:id]) + if criteria.selector.empty? + result[:clazz].constantize.find(result[:id]) + else + criteria.where(_id: result[:id]).first + end end def instantiate_mapreduce_results(results, options) @@ -295,6 +300,13 @@ def update_ngram_index private + # add filter by type according to SCI classes + def document_type_filters + return {} unless fields['_type'].present? + kls = ([self] + descendants).map(&:to_s) + { 'class' => { '$in' => kls } } + end + # Take a list of filters to be mapped so they can update the query # used upon the fulltext search of the ngrams def map_query_filters(filters) diff --git a/spec/models/my_doc.rb b/spec/models/my_doc.rb new file mode 100644 index 0000000..0a87892 --- /dev/null +++ b/spec/models/my_doc.rb @@ -0,0 +1,9 @@ +class MyDoc + include Mongoid::Document + include Mongoid::FullTextSearch + + field :title + field :value, type: Integer + + fulltext_search_in :title +end diff --git a/spec/models/my_further_inherited_doc.rb b/spec/models/my_further_inherited_doc.rb new file mode 100644 index 0000000..42f14e7 --- /dev/null +++ b/spec/models/my_further_inherited_doc.rb @@ -0,0 +1,4 @@ +require 'models/my_inherited_doc.rb' + +class MyFurtherInheritedDoc < MyInheritedDoc +end diff --git a/spec/models/my_inherited_doc.rb b/spec/models/my_inherited_doc.rb new file mode 100644 index 0000000..3cd0109 --- /dev/null +++ b/spec/models/my_inherited_doc.rb @@ -0,0 +1,2 @@ +class MyInheritedDoc < MyDoc +end diff --git a/spec/mongoid/criteria_search_spec.rb b/spec/mongoid/criteria_search_spec.rb new file mode 100644 index 0000000..2e5b8ff --- /dev/null +++ b/spec/mongoid/criteria_search_spec.rb @@ -0,0 +1,14 @@ + +require 'spec_helper' + +describe Mongoid::FullTextSearch do + context 'Criteria' do + let!(:my_doc_1) { MyDoc.create!(title: 'My Doc 1') } + let!(:my_doc_2) { MyDoc.create!(title: 'My Doc 2', value: 10) } + + let(:result) { MyDoc.where(value: 10).fulltext_search('doc') } + + it { expect(result).not_to include my_doc_1 } + it { expect(result).to include my_doc_2 } + end +end diff --git a/spec/mongoid/sci_search_spec.rb b/spec/mongoid/sci_search_spec.rb new file mode 100644 index 0000000..2b40e66 --- /dev/null +++ b/spec/mongoid/sci_search_spec.rb @@ -0,0 +1,31 @@ + +require 'spec_helper' + +describe Mongoid::FullTextSearch do + context 'SCI' do + let!(:my_doc) { MyDoc.create!(title: 'My Doc') } + let!(:my_inherited_doc) { MyInheritedDoc.create!(title: 'My Inherited Doc') } + let!(:my_further_inherited_doc) { MyFurtherInheritedDoc.create!(title: 'My Inherited Doc') } + + context 'root class returns results for subclasses' do + let(:result) { MyDoc.fulltext_search('doc') } + it { expect(result).to include my_doc } + it { expect(result).to include my_inherited_doc } + it { expect(result).to include my_further_inherited_doc } + end + + context 'child class does not return superclass' do + let(:result) { MyInheritedDoc.fulltext_search('doc') } + it { expect(result).not_to include my_doc } + it { expect(result).to include my_inherited_doc } + it { expect(result).to include my_further_inherited_doc } + end + + context 'child class does not return superclass' do + let(:result) { MyFurtherInheritedDoc.fulltext_search('doc') } + it { expect(result).not_to include my_doc } + it { expect(result).not_to include my_inherited_doc } + it { expect(result).to include my_further_inherited_doc } + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a01760d..7b86747 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -22,6 +22,9 @@ config.connect_to('mongoid_fulltext_test') end +Mongoid.logger.level = Logger::INFO +Mongo::Logger.logger.level = Logger::INFO if Mongoid::Compatibility::Version.mongoid5_or_newer? + RSpec.configure do |c| c.before :each do DatabaseCleaner.clean