mongoid_search 0.2.8 → 0.3.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.
- data/README.md +86 -43
- data/lib/mongoid_search/log.rb +1 -1
- data/lib/mongoid_search/mongoid_search.rb +82 -62
- data/lib/mongoid_search/util.rb +13 -11
- data/lib/mongoid_search.rb +60 -2
- data/spec/models/category.rb +1 -1
- data/spec/models/product.rb +3 -3
- data/spec/models/subproduct.rb +1 -1
- data/spec/models/tag.rb +1 -1
- data/spec/mongoid_search_spec.rb +74 -40
- data/spec/spec_helper.rb +1 -2
- data/spec/util_spec.rb +22 -14
- metadata +14 -25
    
        data/README.md
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            Mongoid Search
         | 
| 2 2 | 
             
            ============
         | 
| 3 3 |  | 
| 4 | 
            -
            Mongoid Search is a simple full text search implementation for Mongoid ORM.
         | 
| 4 | 
            +
            Mongoid Search is a simple full text search implementation for Mongoid ORM. It performs well for small data sets. If your searchable model is big (i.e. 1.000.000+ records), solr or sphinx may suit you better.
         | 
| 5 5 |  | 
| 6 6 | 
             
            Installation
         | 
| 7 7 | 
             
            --------
         | 
| @@ -10,6 +10,10 @@ In your Gemfile: | |
| 10 10 |  | 
| 11 11 | 
             
                gem 'mongoid_search'
         | 
| 12 12 |  | 
| 13 | 
            +
            If your project is still using mongoid 2.x.x, stick to mongoid_search 0.2.x:
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                gem 'mongoid_search', '~> 0.2.8'
         | 
| 16 | 
            +
             | 
| 13 17 | 
             
            Then:
         | 
| 14 18 |  | 
| 15 19 | 
             
                bundle install
         | 
| @@ -23,8 +27,8 @@ Examples | |
| 23 27 | 
             
                  field :brand
         | 
| 24 28 | 
             
                  field :name
         | 
| 25 29 |  | 
| 26 | 
            -
                   | 
| 27 | 
            -
                   | 
| 30 | 
            +
                  has_many   :tags
         | 
| 31 | 
            +
                  belongs_to :category
         | 
| 28 32 |  | 
| 29 33 | 
             
                  search_in :brand, :name, :tags => :name, :category => :name
         | 
| 30 34 | 
             
                end
         | 
| @@ -33,14 +37,14 @@ Examples | |
| 33 37 | 
             
                  include Mongoid::Document
         | 
| 34 38 | 
             
                  field :name
         | 
| 35 39 |  | 
| 36 | 
            -
                   | 
| 40 | 
            +
                  belongs_to :product
         | 
| 37 41 | 
             
                end
         | 
| 38 42 |  | 
| 39 43 | 
             
                class Category
         | 
| 40 44 | 
             
                  include Mongoid::Document
         | 
| 41 45 | 
             
                  field :name
         | 
| 42 46 |  | 
| 43 | 
            -
                   | 
| 47 | 
            +
                  has_many :products
         | 
| 44 48 | 
             
                end
         | 
| 45 49 |  | 
| 46 50 | 
             
            Now when you save a product, you get a _keywords field automatically:
         | 
| @@ -52,70 +56,109 @@ Now when you save a product, you get a _keywords field automatically: | |
| 52 56 | 
             
                p.save
         | 
| 53 57 | 
             
                => true
         | 
| 54 58 | 
             
                p._keywords
         | 
| 59 | 
            +
                => ["amazing", "apple", "awesome", "iphone", "superb"]
         | 
| 55 60 |  | 
| 56 61 | 
             
            Now you can run search, which will look in the _keywords field and return all matching results:
         | 
| 57 62 |  | 
| 58 | 
            -
                Product. | 
| 63 | 
            +
                Product.full_text_search("apple iphone").size
         | 
| 59 64 | 
             
                => 1
         | 
| 60 65 |  | 
| 61 66 | 
             
            Note that the search is case insensitive, and accept partial searching too:
         | 
| 62 67 |  | 
| 63 | 
            -
                Product. | 
| 68 | 
            +
                Product.full_text_search("ipho").size
         | 
| 64 69 | 
             
                => 1
         | 
| 65 | 
            -
                
         | 
| 66 | 
            -
            Assuming you have a category with multiple products you can now use the following
         | 
| 67 | 
            -
            code to search for 'iphone' in products cheaper than $499
         | 
| 68 70 |  | 
| 69 | 
            -
             | 
| 71 | 
            +
            Assuming you have a category with multiple products you can use the following
         | 
| 72 | 
            +
            code to search for 'iphone' in products cheaper than $499
         | 
| 70 73 |  | 
| 71 | 
            -
             | 
| 72 | 
            -
            Mongoid defines it's own Criteria.search method.
         | 
| 74 | 
            +
                @category.products.where(:price.lt => 499).full_text_search('iphone').asc(:price)
         | 
| 73 75 |  | 
| 74 76 |  | 
| 75 77 | 
             
            Options
         | 
| 76 78 | 
             
            -------
         | 
| 77 79 |  | 
| 78 80 | 
             
            match:
         | 
| 81 | 
            +
             | 
| 79 82 | 
             
              _:any_ - match any occurrence
         | 
| 83 | 
            +
             | 
| 80 84 | 
             
              _:all_ - match all ocurrences
         | 
| 85 | 
            +
             | 
| 81 86 | 
             
              Default is _:any_.
         | 
| 82 87 |  | 
| 83 | 
            -
                 | 
| 88 | 
            +
                Product.full_text_search("apple motorola", match: :any).size
         | 
| 89 | 
            +
                => 1
         | 
| 84 90 |  | 
| 85 | 
            -
                Product. | 
| 91 | 
            +
                Product.full_text_search("apple motorola", match: :all).size
         | 
| 92 | 
            +
                => 0
         | 
| 93 | 
            +
             | 
| 94 | 
            +
            allow\_empty\_search:
         | 
| 95 | 
            +
             | 
| 96 | 
            +
              _true_ - will return Model.all
         | 
| 97 | 
            +
             | 
| 98 | 
            +
              _false_ - will return []
         | 
| 99 | 
            +
             | 
| 100 | 
            +
              Default is _false_.
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                Product.full_text_search("", allow_empty_search: true).size
         | 
| 86 103 | 
             
                => 1
         | 
| 87 104 |  | 
| 88 | 
            -
             | 
| 105 | 
            +
            relevant_search:
         | 
| 89 106 |  | 
| 90 | 
            -
             | 
| 91 | 
            -
             | 
| 107 | 
            +
              _true_ - Adds relevance information to the results
         | 
| 108 | 
            +
             | 
| 109 | 
            +
              _false_ - No relevance information
         | 
| 92 110 |  | 
| 93 | 
            -
            allow_empty_search:
         | 
| 94 | 
            -
              _true_ - match any occurrence
         | 
| 95 | 
            -
              _false_ - match all ocurrences
         | 
| 96 111 | 
             
              Default is _false_.
         | 
| 97 112 |  | 
| 98 | 
            -
                 | 
| 113 | 
            +
                Product.full_text_search('amazing apple', relevant_search: true)
         | 
| 114 | 
            +
                => [#<Product _id: 5016e7d16af54efe1c000001, _type: nil, brand: "Apple", name: "iPhone", attrs: nil, info: nil, category_id: nil, _keywords: ["amazing", "apple", "awesome", "iphone", "superb"], relevance: 2.0>]
         | 
| 99 115 |  | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 104 | 
            -
             | 
| 105 | 
            -
             | 
| 106 | 
            -
             | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 111 | 
            -
                   | 
| 112 | 
            -
                  
         | 
| 113 | 
            -
             | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 116 | 
            -
             | 
| 117 | 
            -
             | 
| 118 | 
            -
             | 
| 119 | 
            -
             | 
| 120 | 
            -
             | 
| 116 | 
            +
             Please note that relevant_search will return an Array and not a Criteria object. The search method shoud always be called in the end of the method chain.
         | 
| 117 | 
            +
             | 
| 118 | 
            +
            Initializer
         | 
| 119 | 
            +
            -----------
         | 
| 120 | 
            +
             | 
| 121 | 
            +
            Alternatively, you can create an initializer to setup those options:
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                Mongoid::Search.setup do |config|
         | 
| 124 | 
            +
                  ## Default matching type. Match :any or :all searched keywords
         | 
| 125 | 
            +
                  config.match = :any
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                  ## If true, an empty search will return all objects
         | 
| 128 | 
            +
                  config.allow_empty_search = false
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                  ## If true, will search with relevance information
         | 
| 131 | 
            +
                  config.relevant_search = false
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                  ## Stem keywords
         | 
| 134 | 
            +
                  config.stem_keywords = false
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                  ## Words to ignore
         | 
| 137 | 
            +
                  config.ignore_list = []
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                  ## An array of words
         | 
| 140 | 
            +
                  # config.ignore_list = %w{ a an to from as }
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                  ## Or from a file
         | 
| 143 | 
            +
                  # config.ignore_list = YAML.load(File.open(File.dirname(__FILE__) + '/config/ignorelist.yml'))["ignorelist"]
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                  ## Search using regex (slower)
         | 
| 146 | 
            +
                  config.regex_search = true
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                  ## Regex to search
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                  ## Match partial words on both sides (slower)
         | 
| 151 | 
            +
                  config.regex = Proc.new { |query| /#{query}/ }
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                  ## Match partial words on the beginning or in the end (slightly faster)
         | 
| 154 | 
            +
                  # config.regex = Proc.new { |query| /ˆ#{query}/ }
         | 
| 155 | 
            +
                  # config.regex = Proc.new { |query| /#{query}$/ }
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                  # Ligatures to be replaced
         | 
| 158 | 
            +
                  # http://en.wikipedia.org/wiki/Typographic_ligature
         | 
| 159 | 
            +
                  config.ligatures = { "œ"=>"oe", "æ"=>"ae" }
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                  # Minimum word size. Words smaller than it won't be indexed
         | 
| 162 | 
            +
                  config.minimum_word_size = 2
         | 
| 163 | 
            +
                end
         | 
| 121 164 |  | 
    
        data/lib/mongoid_search/log.rb
    CHANGED
    
    
| @@ -1,100 +1,120 @@ | |
| 1 1 | 
             
            module Mongoid::Search
         | 
| 2 | 
            -
               | 
| 2 | 
            +
              def self.included(base)
         | 
| 3 | 
            +
                base.send(:cattr_accessor, :search_fields)
         | 
| 3 4 |  | 
| 4 | 
            -
             | 
| 5 | 
            -
                cattr_accessor :search_fields, :match, :allow_empty_search, :relevant_search, :stem_keywords, :ignore_list
         | 
| 6 | 
            -
              end
         | 
| 5 | 
            +
                base.extend ClassMethods
         | 
| 7 6 |  | 
| 8 | 
            -
             | 
| 9 | 
            -
                 | 
| 10 | 
            -
                @classes << base
         | 
| 7 | 
            +
                @@classes ||= []
         | 
| 8 | 
            +
                @@classes << base
         | 
| 11 9 | 
             
              end
         | 
| 12 10 |  | 
| 13 11 | 
             
              def self.classes
         | 
| 14 | 
            -
                 | 
| 12 | 
            +
                @@classes
         | 
| 15 13 | 
             
              end
         | 
| 16 14 |  | 
| 17 15 | 
             
              module ClassMethods #:nodoc:
         | 
| 18 16 | 
             
                # Set a field or a number of fields as sources for search
         | 
| 19 17 | 
             
                def search_in(*args)
         | 
| 20 | 
            -
                   | 
| 21 | 
            -
                  self. | 
| 22 | 
            -
                  self.allow_empty_search = [true, false].include?(options[:allow_empty_search]) ? options[:allow_empty_search] : false
         | 
| 23 | 
            -
                  self.relevant_search    = [true, false].include?(options[:relevant_search]) ? options[:allow_empty_search] : false
         | 
| 24 | 
            -
                  self.stem_keywords      = [true, false].include?(options[:stem_keywords]) ? options[:allow_empty_search] : false
         | 
| 25 | 
            -
                  self.ignore_list        = YAML.load(File.open(options[:ignore_list]))["ignorelist"] if options[:ignore_list].present?
         | 
| 26 | 
            -
                  self.search_fields      = (self.search_fields || []).concat args
         | 
| 18 | 
            +
                  args, options = args_and_options(args)
         | 
| 19 | 
            +
                  self.search_fields = (self.search_fields || []).concat args
         | 
| 27 20 |  | 
| 28 21 | 
             
                  field :_keywords, :type => Array
         | 
| 29 | 
            -
             | 
| 30 | 
            -
                   | 
| 22 | 
            +
             | 
| 23 | 
            +
                  index({ :_keywords => 1 }, { :background => true })
         | 
| 31 24 |  | 
| 32 25 | 
             
                  before_save :set_keywords
         | 
| 33 26 | 
             
                end
         | 
| 34 27 |  | 
| 35 | 
            -
                def  | 
| 36 | 
            -
                   | 
| 28 | 
            +
                def full_text_search(query, options={})
         | 
| 29 | 
            +
                  options = extract_options(options)
         | 
| 30 | 
            +
                  return (options[:allow_empty_search] ? criteria.all : []) if query.blank?
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  if options[:relevant_search]
         | 
| 37 33 | 
             
                    search_relevant(query, options)
         | 
| 38 34 | 
             
                  else
         | 
| 39 35 | 
             
                    search_without_relevance(query, options)
         | 
| 40 36 | 
             
                  end
         | 
| 41 37 | 
             
                end
         | 
| 42 38 |  | 
| 43 | 
            -
                #  | 
| 44 | 
            -
                 | 
| 45 | 
            -
                alias  | 
| 39 | 
            +
                # Keeping these aliases for compatibility purposes
         | 
| 40 | 
            +
                alias csearch full_text_search
         | 
| 41 | 
            +
                alias search full_text_search
         | 
| 46 42 |  | 
| 47 | 
            -
                 | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 43 | 
            +
                # Goes through all documents in the class that includes Mongoid::Search
         | 
| 44 | 
            +
                # and indexes the keywords.
         | 
| 45 | 
            +
                def index_keywords!
         | 
| 46 | 
            +
                  all.each { |d| d.index_keywords! ? Log.green(".") : Log.red("F") }
         | 
| 50 47 | 
             
                end
         | 
| 51 48 |  | 
| 52 | 
            -
                 | 
| 53 | 
            -
             | 
| 49 | 
            +
                private
         | 
| 50 | 
            +
                def query(keywords, options)
         | 
| 51 | 
            +
                  keywords_hash = keywords.map do |kw|
         | 
| 52 | 
            +
                    kw = Mongoid::Search.regex.call(kw) if Mongoid::Search.regex_search
         | 
| 53 | 
            +
                    { :_keywords => kw }
         | 
| 54 | 
            +
                  end
         | 
| 54 55 |  | 
| 55 | 
            -
                   | 
| 56 | 
            +
                  criteria.send("#{(options[:match]).to_s}_of", *keywords_hash)
         | 
| 57 | 
            +
                end
         | 
| 56 58 |  | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
                          if(this._keywords[j] == keywords[i])
         | 
| 63 | 
            -
                            entries++
         | 
| 64 | 
            -
                        }
         | 
| 65 | 
            -
                      if(entries > 0)
         | 
| 66 | 
            -
                        emit(this._id, entries)
         | 
| 67 | 
            -
                    }
         | 
| 68 | 
            -
                  EOS
         | 
| 69 | 
            -
                  reduce = <<-EOS
         | 
| 70 | 
            -
                    function(key, values) {
         | 
| 71 | 
            -
                      return(values[0])
         | 
| 72 | 
            -
                    }
         | 
| 73 | 
            -
                  EOS
         | 
| 59 | 
            +
                def args_and_options(args)
         | 
| 60 | 
            +
                  options = args.last.is_a?(Hash) &&
         | 
| 61 | 
            +
                            [:match,
         | 
| 62 | 
            +
                             :allow_empty_search,
         | 
| 63 | 
            +
                             :relevant_search].include?(args.last.keys.first) ? args.pop : {}
         | 
| 74 64 |  | 
| 75 | 
            -
                   | 
| 65 | 
            +
                  [args, extract_options(options)]
         | 
| 66 | 
            +
                end
         | 
| 76 67 |  | 
| 77 | 
            -
             | 
| 78 | 
            -
             | 
| 79 | 
            -
             | 
| 68 | 
            +
                def extract_options(options)
         | 
| 69 | 
            +
                  {
         | 
| 70 | 
            +
                    :match              => options[:match]              || Mongoid::Search.match,
         | 
| 71 | 
            +
                    :allow_empty_search => options[:allow_empty_search] || Mongoid::Search.allow_empty_search,
         | 
| 72 | 
            +
                    :relevant_search    => options[:relevant_search]    || Mongoid::Search.relevant_search
         | 
| 73 | 
            +
                  }
         | 
| 74 | 
            +
                end
         | 
| 80 75 |  | 
| 81 | 
            -
             | 
| 76 | 
            +
                def search_without_relevance(query, options)
         | 
| 77 | 
            +
                  query(Util.normalize_keywords(query), options)
         | 
| 78 | 
            +
                end
         | 
| 82 79 |  | 
| 83 | 
            -
             | 
| 80 | 
            +
                def search_relevant(query, options)
         | 
| 81 | 
            +
                  results_with_relevance(query, options).sort { |o| o['value'] }.map do |r|
         | 
| 84 82 |  | 
| 85 | 
            -
             | 
| 86 | 
            -
             | 
| 87 | 
            -
             | 
| 83 | 
            +
                    new(r['_id'].merge(:relevance => r['value'])) do |o|
         | 
| 84 | 
            +
                      # Need to match the actual object
         | 
| 85 | 
            +
                      o.instance_variable_set('@new_record', false)
         | 
| 86 | 
            +
                      o._id = r['_id']['_id']
         | 
| 87 | 
            +
                    end
         | 
| 88 88 |  | 
| 89 | 
            -
                   | 
| 90 | 
            -
                  # res.find.sort(['value', -1]) # Cursor
         | 
| 91 | 
            -
                  collection.map_reduce(map, reduce, options)
         | 
| 89 | 
            +
                  end
         | 
| 92 90 | 
             
                end
         | 
| 93 91 |  | 
| 94 | 
            -
                 | 
| 95 | 
            -
             | 
| 96 | 
            -
             | 
| 97 | 
            -
                   | 
| 92 | 
            +
                def results_with_relevance(query, options)
         | 
| 93 | 
            +
                  keywords = Mongoid::Search::Util.normalize_keywords(query)
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                  map = %Q{
         | 
| 96 | 
            +
                    function() {
         | 
| 97 | 
            +
                      var entries = 0;
         | 
| 98 | 
            +
                      for(i in keywords) {
         | 
| 99 | 
            +
                        for(j in this._keywords) {
         | 
| 100 | 
            +
                          if(this._keywords[j] == keywords[i]) {
         | 
| 101 | 
            +
                            entries++;
         | 
| 102 | 
            +
                          }
         | 
| 103 | 
            +
                        }
         | 
| 104 | 
            +
                      }
         | 
| 105 | 
            +
                      if(entries > 0) {
         | 
| 106 | 
            +
                        emit(this, entries);
         | 
| 107 | 
            +
                      }
         | 
| 108 | 
            +
                    }
         | 
| 109 | 
            +
                  }
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                  reduce = %Q{
         | 
| 112 | 
            +
                    function(key, values) {
         | 
| 113 | 
            +
                      return(values);
         | 
| 114 | 
            +
                    }
         | 
| 115 | 
            +
                  }
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                  query(keywords, options).map_reduce(map, reduce).scope(:keywords => keywords).out(:inline => 1)
         | 
| 98 118 | 
             
                end
         | 
| 99 119 | 
             
              end
         | 
| 100 120 |  | 
| @@ -105,7 +125,7 @@ module Mongoid::Search | |
| 105 125 | 
             
              private
         | 
| 106 126 | 
             
              def set_keywords
         | 
| 107 127 | 
             
                self._keywords = self.search_fields.map do |field|
         | 
| 108 | 
            -
                  Util.keywords(self, field | 
| 128 | 
            +
                  Mongoid::Search::Util.keywords(self, field)
         | 
| 109 129 | 
             
                end.flatten.reject{|k| k.nil? || k.empty?}.uniq.sort
         | 
| 110 130 | 
             
              end
         | 
| 111 131 | 
             
            end
         | 
    
        data/lib/mongoid_search/util.rb
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            # encoding: utf-8
         | 
| 2 | 
            -
            module Util
         | 
| 2 | 
            +
            module Mongoid::Search::Util
         | 
| 3 3 |  | 
| 4 | 
            -
              def self.keywords(klass, field | 
| 4 | 
            +
              def self.keywords(klass, field)
         | 
| 5 5 | 
             
                if field.is_a?(Hash)
         | 
| 6 6 | 
             
                  field.keys.map do |key|
         | 
| 7 7 | 
             
                    attribute = klass.send(key)
         | 
| @@ -9,30 +9,32 @@ module Util | |
| 9 9 | 
             
                      method = field[key]
         | 
| 10 10 | 
             
                      if attribute.is_a?(Array)
         | 
| 11 11 | 
             
                        if method.is_a?(Array)
         | 
| 12 | 
            -
                          method.map {|m| attribute.map { |a|  | 
| 12 | 
            +
                          method.map {|m| attribute.map { |a| normalize_keywords a.send(m) } }
         | 
| 13 13 | 
             
                        else
         | 
| 14 | 
            -
                          attribute.map(&method).map { |t|  | 
| 14 | 
            +
                          attribute.map(&method).map { |t| normalize_keywords t }
         | 
| 15 15 | 
             
                        end
         | 
| 16 16 | 
             
                      elsif attribute.is_a?(Hash)
         | 
| 17 17 | 
             
                        if method.is_a?(Array)
         | 
| 18 | 
            -
                          method.map {|m|  | 
| 18 | 
            +
                          method.map {|m| normalize_keywords attribute[m.to_sym] }
         | 
| 19 19 | 
             
                        else
         | 
| 20 | 
            -
                           | 
| 20 | 
            +
                          normalize_keywords(attribute[method.to_sym])
         | 
| 21 21 | 
             
                        end
         | 
| 22 22 | 
             
                      else
         | 
| 23 | 
            -
                         | 
| 23 | 
            +
                        normalize_keywords(attribute.send(method))
         | 
| 24 24 | 
             
                      end
         | 
| 25 25 | 
             
                    end
         | 
| 26 26 | 
             
                  end
         | 
| 27 27 | 
             
                else
         | 
| 28 28 | 
             
                  value = klass[field]
         | 
| 29 29 | 
             
                  value = value.join(' ') if value.respond_to?(:join)
         | 
| 30 | 
            -
                   | 
| 30 | 
            +
                  normalize_keywords(value) if value
         | 
| 31 31 | 
             
                end
         | 
| 32 32 | 
             
              end
         | 
| 33 33 |  | 
| 34 | 
            -
              def self.normalize_keywords(text | 
| 35 | 
            -
                ligatures | 
| 34 | 
            +
              def self.normalize_keywords(text)
         | 
| 35 | 
            +
                ligatures     = Mongoid::Search.ligatures
         | 
| 36 | 
            +
                ignore_list   = Mongoid::Search.ignore_list
         | 
| 37 | 
            +
                stem_keywords = Mongoid::Search.stem_keywords
         | 
| 36 38 |  | 
| 37 39 | 
             
                return [] if text.blank?
         | 
| 38 40 | 
             
                text = text.to_s.
         | 
| @@ -44,7 +46,7 @@ module Util | |
| 44 46 | 
             
                  gsub(/[^[:alnum:]\s]/,'').   # strip accents
         | 
| 45 47 | 
             
                  gsub(/[#{ligatures.keys.join("")}]/) {|c| ligatures[c]}.
         | 
| 46 48 | 
             
                  split(' ').
         | 
| 47 | 
            -
                  reject { |word| word.size <  | 
| 49 | 
            +
                  reject { |word| word.size < Mongoid::Search.minimum_word_size }
         | 
| 48 50 | 
             
                text = text.reject { |word| ignore_list.include?(word) } unless ignore_list.blank?
         | 
| 49 51 | 
             
                text = text.map(&:stem) if stem_keywords
         | 
| 50 52 | 
             
                text
         | 
    
        data/lib/mongoid_search.rb
    CHANGED
    
    | @@ -1,4 +1,62 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require 'mongoid_search/railtie' if defined?(Rails)
         | 
| 2 | 
            -
            require 'mongoid_search/util'
         | 
| 3 | 
            -
            require 'mongoid_search/log'
         | 
| 4 4 | 
             
            require 'mongoid_search/mongoid_search'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Mongoid::Search
         | 
| 7 | 
            +
              ## Default matching type. Match :any or :all searched keywords
         | 
| 8 | 
            +
              mattr_accessor :match
         | 
| 9 | 
            +
              @@match = :any
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              ## If true, an empty search will return all objects
         | 
| 12 | 
            +
              mattr_accessor :allow_empty_search
         | 
| 13 | 
            +
              @@allow_empty_search = false
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              ## If true, will search with relevance information
         | 
| 16 | 
            +
              mattr_accessor :relevant_search
         | 
| 17 | 
            +
              @@relevant_search = false
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              ## Stem keywords
         | 
| 20 | 
            +
              mattr_accessor :stem_keywords
         | 
| 21 | 
            +
              @@stem_keywords = false
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              ## Words to ignore
         | 
| 24 | 
            +
              mattr_accessor :ignore_list
         | 
| 25 | 
            +
              @@ignore_list = []
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              ## An array of words
         | 
| 28 | 
            +
              # @@ignore_list = %w{ a an to from as }
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              ## Or from a file
         | 
| 31 | 
            +
              # @@ignore_list = YAML.load(File.open(File.dirname(__FILE__) + '/config/ignorelist.yml'))["ignorelist"]
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              ## Search using regex (slower)
         | 
| 34 | 
            +
              mattr_accessor :regex_search
         | 
| 35 | 
            +
              @@regex_search = true
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              ## Regex to search
         | 
| 38 | 
            +
              mattr_accessor :regex
         | 
| 39 | 
            +
             | 
| 40 | 
            +
              ## Match partial words on both sides (slower)
         | 
| 41 | 
            +
              @@regex = Proc.new { |query| /#{query}/ }
         | 
| 42 | 
            +
             | 
| 43 | 
            +
              ## Match partial words on the beginning or in the end (slightly faster)
         | 
| 44 | 
            +
              # @@regex = Proc.new { |query| /ˆ#{query}/ }
         | 
| 45 | 
            +
              # @@regex = Proc.new { |query| /#{query}$/ }
         | 
| 46 | 
            +
             | 
| 47 | 
            +
              # Ligatures to be replaced
         | 
| 48 | 
            +
              # http://en.wikipedia.org/wiki/Typographic_ligature
         | 
| 49 | 
            +
              mattr_accessor :ligatures
         | 
| 50 | 
            +
              @@ligatures = { "œ"=>"oe", "æ"=>"ae" }
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              # Minimum word size. Words smaller than it won't be indexed
         | 
| 53 | 
            +
              mattr_accessor :minimum_word_size
         | 
| 54 | 
            +
              @@minimum_word_size = 2
         | 
| 55 | 
            +
             | 
| 56 | 
            +
              def self.setup
         | 
| 57 | 
            +
                yield self
         | 
| 58 | 
            +
              end
         | 
| 59 | 
            +
            end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            require 'mongoid_search/util'
         | 
| 62 | 
            +
            require 'mongoid_search/log'
         | 
    
        data/spec/models/category.rb
    CHANGED
    
    
    
        data/spec/models/product.rb
    CHANGED
    
    | @@ -6,9 +6,9 @@ class Product | |
| 6 6 | 
             
              field :attrs, :type => Array
         | 
| 7 7 | 
             
              field :info, :type => Hash
         | 
| 8 8 |  | 
| 9 | 
            -
               | 
| 10 | 
            -
               | 
| 11 | 
            -
              embeds_many | 
| 9 | 
            +
              has_many    :tags
         | 
| 10 | 
            +
              belongs_to  :category
         | 
| 11 | 
            +
              embeds_many :subproducts
         | 
| 12 12 |  | 
| 13 13 | 
             
              search_in :brand, :name, :outlet, :attrs, :tags => :name, :category => :name,
         | 
| 14 14 | 
             
                        :subproducts => [:brand, :name], :info => [ :summary, :description ]
         | 
    
        data/spec/models/subproduct.rb
    CHANGED
    
    
    
        data/spec/models/tag.rb
    CHANGED
    
    
    
        data/spec/mongoid_search_spec.rb
    CHANGED
    
    | @@ -5,8 +5,9 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper') | |
| 5 5 | 
             
            describe Mongoid::Search do
         | 
| 6 6 |  | 
| 7 7 | 
             
              before(:each) do
         | 
| 8 | 
            -
                 | 
| 9 | 
            -
                 | 
| 8 | 
            +
                Mongoid::Search.match         = :any
         | 
| 9 | 
            +
                Mongoid::Search.stem_keywords = false
         | 
| 10 | 
            +
                Mongoid::Search.ignore_list   = nil
         | 
| 10 11 | 
             
                @product = Product.create :brand => "Apple",
         | 
| 11 12 | 
             
                                          :name => "iPhone",
         | 
| 12 13 | 
             
                                          :tags => ["Amazing", "Awesome", "Olé"].map { |tag| Tag.new(:name => tag) },
         | 
| @@ -19,8 +20,8 @@ describe Mongoid::Search do | |
| 19 20 | 
             
              describe "Serialized hash fields" do
         | 
| 20 21 | 
             
                context "when the hash is populated" do
         | 
| 21 22 | 
             
                  it "should return the product" do
         | 
| 22 | 
            -
                    Product. | 
| 23 | 
            -
                    Product. | 
| 23 | 
            +
                    Product.full_text_search("Info-summary").first.should eq @product
         | 
| 24 | 
            +
                    Product.full_text_search("Info-description").first.should eq @product
         | 
| 24 25 | 
             
                  end
         | 
| 25 26 | 
             
                end
         | 
| 26 27 |  | 
| @@ -31,29 +32,29 @@ describe Mongoid::Search do | |
| 31 32 | 
             
                  end
         | 
| 32 33 |  | 
| 33 34 | 
             
                  it "should not return the product" do
         | 
| 34 | 
            -
                    Product. | 
| 35 | 
            -
                    Product. | 
| 35 | 
            +
                    Product.full_text_search("Info-description").size.should eq 0
         | 
| 36 | 
            +
                    Product.full_text_search("Info-summary").size.should eq 0
         | 
| 36 37 | 
             
                  end
         | 
| 37 38 | 
             
                end
         | 
| 38 39 | 
             
              end
         | 
| 39 40 |  | 
| 40 41 | 
             
              context "utf-8 characters" do
         | 
| 41 | 
            -
                before | 
| 42 | 
            -
                   | 
| 43 | 
            -
                   | 
| 42 | 
            +
                before do
         | 
| 43 | 
            +
                  Mongoid::Search.stem_keywords = false
         | 
| 44 | 
            +
                  Mongoid::Search.ignore_list   = nil
         | 
| 44 45 | 
             
                  @product = Product.create :brand => "Эльбрус",
         | 
| 45 46 | 
             
                                            :name => "Процессор",
         | 
| 46 47 | 
             
                                            :tags => ["Amazing", "Awesome", "Olé"].map { |tag| Tag.new(:name => tag) },
         | 
| 47 48 | 
             
                                            :category => Category.new(:name => "процессоры"),
         | 
| 48 49 | 
             
                                            :subproducts => []
         | 
| 49 | 
            -
                 | 
| 50 | 
            +
                end
         | 
| 50 51 |  | 
| 51 52 | 
             
                it "should leave utf8 characters" do
         | 
| 52 53 | 
             
                  @product._keywords.should == ["amazing", "awesome", "ole", "процессор", "процессоры", "эльбрус"]
         | 
| 53 54 | 
             
                end
         | 
| 54 55 |  | 
| 55 56 | 
             
                it "should return results in search when case doesn't match" do
         | 
| 56 | 
            -
                  Product. | 
| 57 | 
            +
                  Product.full_text_search("ЭЛЬБРУС").size.should == 1
         | 
| 57 58 | 
             
                end
         | 
| 58 59 | 
             
              end
         | 
| 59 60 |  | 
| @@ -83,7 +84,7 @@ describe Mongoid::Search do | |
| 83 84 | 
             
                                          :subproducts => [Subproduct.new(:brand => "Apple", :name => "Craddle")],
         | 
| 84 85 | 
             
                                          :color => :white
         | 
| 85 86 | 
             
                variant._keywords.should include 'white'
         | 
| 86 | 
            -
                Variant. | 
| 87 | 
            +
                Variant.full_text_search(:name => 'Apple', :color => :white).should eq [variant]
         | 
| 87 88 | 
             
              end
         | 
| 88 89 |  | 
| 89 90 | 
             
              it "should expand the ligature to ease searching" do
         | 
| @@ -91,19 +92,20 @@ describe Mongoid::Search do | |
| 91 92 | 
             
                variant1 = Variant.create :tags => ["œuvre"].map {|tag| Tag.new(:name => tag)}
         | 
| 92 93 | 
             
                variant2 = Variant.create :tags => ["æquo"].map {|tag| Tag.new(:name => tag)}
         | 
| 93 94 |  | 
| 94 | 
            -
                Variant. | 
| 95 | 
            -
                Variant. | 
| 96 | 
            -
                Variant. | 
| 97 | 
            -
                Variant. | 
| 95 | 
            +
                Variant.full_text_search("œuvre").should eq [variant1]
         | 
| 96 | 
            +
                Variant.full_text_search("oeuvre").should eq [variant1]
         | 
| 97 | 
            +
                Variant.full_text_search("æquo").should eq [variant2]
         | 
| 98 | 
            +
                Variant.full_text_search("aequo").should eq [variant2]
         | 
| 98 99 | 
             
              end
         | 
| 100 | 
            +
             | 
| 99 101 | 
             
              it "should set the _keywords field with stemmed words if stem is enabled" do
         | 
| 100 | 
            -
                 | 
| 102 | 
            +
                Mongoid::Search.stem_keywords = true
         | 
| 101 103 | 
             
                @product.save!
         | 
| 102 104 | 
             
                @product._keywords.sort.should == ["amaz", "appl", "awesom", "craddl", "iphon", "mobil", "ol", "info", "descript", "summari"].sort
         | 
| 103 105 | 
             
              end
         | 
| 104 106 |  | 
| 105 107 | 
             
              it "should ignore keywords in an ignore list" do
         | 
| 106 | 
            -
                 | 
| 108 | 
            +
                Mongoid::Search.ignore_list = YAML.load(File.open(File.dirname(__FILE__) + '/config/ignorelist.yml'))["ignorelist"]
         | 
| 107 109 | 
             
                @product.save!
         | 
| 108 110 | 
             
                @product._keywords.sort.should == ["apple", "craddle", "iphone", "mobile", "ole", "info", "description", "summary"].sort
         | 
| 109 111 | 
             
              end
         | 
| @@ -115,67 +117,68 @@ describe Mongoid::Search do | |
| 115 117 | 
             
                                          :category => Category.new(:name => "Vehicle")
         | 
| 116 118 |  | 
| 117 119 | 
             
                  @product.save!
         | 
| 118 | 
            -
                  @product._keywords.should == ["1908","amazing", "car", "first", "ford",  "vehicle"]
         | 
| 120 | 
            +
                  @product._keywords.should == ["1908", "amazing", "car", "first", "ford",  "vehicle"]
         | 
| 119 121 | 
             
               end
         | 
| 120 122 |  | 
| 121 123 |  | 
| 122 124 | 
             
              it "should return results in search" do
         | 
| 123 | 
            -
                Product. | 
| 125 | 
            +
                Product.full_text_search("apple").size.should == 1
         | 
| 124 126 | 
             
              end
         | 
| 125 127 |  | 
| 126 128 | 
             
              it "should return results in search for dynamic attribute" do
         | 
| 127 129 | 
             
                @product[:outlet] = "online shop"
         | 
| 128 130 | 
             
                @product.save!
         | 
| 129 | 
            -
                Product. | 
| 131 | 
            +
                Product.full_text_search("online").size.should == 1
         | 
| 130 132 | 
             
              end
         | 
| 131 133 |  | 
| 132 134 | 
             
              it "should return results in search even searching a accented word" do
         | 
| 133 | 
            -
                Product. | 
| 134 | 
            -
                Product. | 
| 135 | 
            +
                Product.full_text_search("Ole").size.should == 1
         | 
| 136 | 
            +
                Product.full_text_search("Olé").size.should == 1
         | 
| 135 137 | 
             
              end
         | 
| 136 138 |  | 
| 137 139 | 
             
              it "should return results in search even if the case doesn't match" do
         | 
| 138 | 
            -
                Product. | 
| 140 | 
            +
                Product.full_text_search("oLe").size.should == 1
         | 
| 139 141 | 
             
              end
         | 
| 140 142 |  | 
| 141 | 
            -
              it "should return results in search with a partial word" do
         | 
| 142 | 
            -
                Product. | 
| 143 | 
            +
              it "should return results in search with a partial word by default" do
         | 
| 144 | 
            +
                Product.full_text_search("iph").size.should == 1
         | 
| 143 145 | 
             
              end
         | 
| 144 146 |  | 
| 145 147 | 
             
              it "should return results for any matching word with default search" do
         | 
| 146 | 
            -
                Product. | 
| 148 | 
            +
                Product.full_text_search("apple motorola").size.should == 1
         | 
| 147 149 | 
             
              end
         | 
| 148 150 |  | 
| 149 151 | 
             
              it "should not return results when all words do not match, if using :match => :all" do
         | 
| 150 | 
            -
                 | 
| 151 | 
            -
                Product. | 
| 152 | 
            +
                Mongoid::Search.match = :all
         | 
| 153 | 
            +
                Product.full_text_search("apple motorola").size.should == 0
         | 
| 152 154 | 
             
              end
         | 
| 153 155 |  | 
| 154 | 
            -
              it "should return results for any matching word, using :match => :all, passing :match => :any to . | 
| 155 | 
            -
                 | 
| 156 | 
            -
                Product. | 
| 156 | 
            +
              it "should return results for any matching word, using :match => :all, passing :match => :any to .full_text_search" do
         | 
| 157 | 
            +
                Mongoid::Search.match = :all
         | 
| 158 | 
            +
                Product.full_text_search("apple motorola", :match => :any).size.should == 1
         | 
| 157 159 | 
             
              end
         | 
| 158 160 |  | 
| 159 | 
            -
              it "should not return results when all words do not match, passing :match => :all to . | 
| 160 | 
            -
                Product. | 
| 161 | 
            +
              it "should not return results when all words do not match, passing :match => :all to .full_text_search" do
         | 
| 162 | 
            +
                Product.full_text_search("apple motorola", :match => :all).size.should == 0
         | 
| 161 163 | 
             
              end
         | 
| 162 164 |  | 
| 163 165 | 
             
              it "should return no results when a blank search is made" do
         | 
| 164 | 
            -
                 | 
| 166 | 
            +
                Mongoid::Search.allow_empty_search = false
         | 
| 167 | 
            +
                Product.full_text_search("").size.should == 0
         | 
| 165 168 | 
             
              end
         | 
| 166 169 |  | 
| 167 170 | 
             
              it "should return results when a blank search is made when :allow_empty_search is true" do
         | 
| 168 | 
            -
                 | 
| 169 | 
            -
                Product. | 
| 171 | 
            +
                Mongoid::Search.allow_empty_search = true
         | 
| 172 | 
            +
                Product.full_text_search("").size.should == 1
         | 
| 170 173 | 
             
              end
         | 
| 171 174 |  | 
| 172 175 | 
             
              it "should search for embedded documents" do
         | 
| 173 | 
            -
                Product. | 
| 176 | 
            +
                Product.full_text_search("craddle").size.should == 1
         | 
| 174 177 | 
             
              end
         | 
| 175 178 |  | 
| 176 179 | 
             
              it 'should work in a chainable fashion' do
         | 
| 177 | 
            -
                @product.category.products.where(:brand => 'Apple'). | 
| 178 | 
            -
                @product.category.products. | 
| 180 | 
            +
                @product.category.products.where(:brand => 'Apple').full_text_search('apple').size.should == 1
         | 
| 181 | 
            +
                @product.category.products.full_text_search('craddle').where(:brand => 'Apple').size.should == 1
         | 
| 179 182 | 
             
              end
         | 
| 180 183 |  | 
| 181 184 | 
             
              it 'should return the classes that include the search module' do
         | 
| @@ -190,5 +193,36 @@ describe Mongoid::Search do | |
| 190 193 | 
             
                Product.index_keywords!.should_not include(false)
         | 
| 191 194 | 
             
              end
         | 
| 192 195 |  | 
| 196 | 
            +
              context "when regex search is false" do
         | 
| 197 | 
            +
                before do
         | 
| 198 | 
            +
                  Mongoid::Search.regex_search = false
         | 
| 199 | 
            +
                end
         | 
| 200 | 
            +
             | 
| 201 | 
            +
                it "should not return results in search with a partial word if not using regex search" do
         | 
| 202 | 
            +
                  Product.full_text_search("iph").size.should == 0
         | 
| 203 | 
            +
                end
         | 
| 204 | 
            +
             | 
| 205 | 
            +
                it "should return results in search with a full word if not using regex search" do
         | 
| 206 | 
            +
                  Product.full_text_search("iphone").size.should == 1
         | 
| 207 | 
            +
                end
         | 
| 208 | 
            +
              end
         | 
| 209 | 
            +
             | 
| 210 | 
            +
              context "relevant search" do
         | 
| 211 | 
            +
                before do
         | 
| 212 | 
            +
                  Mongoid::Search.relevant_search = true
         | 
| 213 | 
            +
                  @imac = Product.create :name => 'apple imac'
         | 
| 214 | 
            +
                end
         | 
| 215 | 
            +
             | 
| 216 | 
            +
                it "should return results ordered by relevance and with correct ids" do
         | 
| 217 | 
            +
                  Product.full_text_search('apple imac').map(&:_id).should == [@imac._id, @product._id]
         | 
| 218 | 
            +
                end
         | 
| 219 | 
            +
             | 
| 220 | 
            +
                it "results should be recognized as persisted objects" do
         | 
| 221 | 
            +
                  Product.full_text_search('apple imac').map(&:persisted?).should_not include false
         | 
| 222 | 
            +
                end
         | 
| 193 223 |  | 
| 224 | 
            +
                it "should include relevance information" do
         | 
| 225 | 
            +
                  Product.full_text_search('apple imac').map(&:relevance).should == [2, 1]
         | 
| 226 | 
            +
                end
         | 
| 227 | 
            +
              end
         | 
| 194 228 | 
             
            end
         | 
    
        data/spec/spec_helper.rb
    CHANGED
    
    | @@ -10,8 +10,7 @@ require 'yaml' | |
| 10 10 | 
             
            require 'mongoid_search'
         | 
| 11 11 |  | 
| 12 12 | 
             
            Mongoid.configure do |config|
         | 
| 13 | 
            -
               | 
| 14 | 
            -
              config.master = Mongo::Connection.new.db(name)
         | 
| 13 | 
            +
              config.connect_to "mongoid_search_test"
         | 
| 15 14 | 
             
            end
         | 
| 16 15 |  | 
| 17 16 | 
             
            Dir["#{File.dirname(__FILE__)}/models/*.rb"].each { |file| require file }
         | 
    
        data/spec/util_spec.rb
    CHANGED
    
    | @@ -1,48 +1,56 @@ | |
| 1 1 | 
             
            # encoding: utf-8
         | 
| 2 2 | 
             
            require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
         | 
| 3 3 |  | 
| 4 | 
            -
            describe Util do
         | 
| 4 | 
            +
            describe Mongoid::Search::Util do
         | 
| 5 | 
            +
              before do
         | 
| 6 | 
            +
                Mongoid::Search.stem_keywords = false
         | 
| 7 | 
            +
                Mongoid::Search.ignore_list = ""
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
             | 
| 5 10 | 
             
              it "should return an empty array if no text is passed" do
         | 
| 6 | 
            -
                Util.normalize_keywords("" | 
| 11 | 
            +
                Mongoid::Search::Util.normalize_keywords("").should == []
         | 
| 7 12 | 
             
              end
         | 
| 8 13 |  | 
| 9 14 | 
             
              it "should return an array of keywords" do
         | 
| 10 | 
            -
                Util.normalize_keywords("keyword" | 
| 15 | 
            +
                Mongoid::Search::Util.normalize_keywords("keyword").class.should == Array
         | 
| 11 16 | 
             
              end
         | 
| 12 17 |  | 
| 13 18 | 
             
              it "should return an array of strings" do
         | 
| 14 | 
            -
                Util.normalize_keywords("keyword" | 
| 19 | 
            +
                Mongoid::Search::Util.normalize_keywords("keyword").first.class.should == String
         | 
| 15 20 | 
             
              end
         | 
| 16 21 |  | 
| 17 22 | 
             
              it "should remove accents from the text passed" do
         | 
| 18 | 
            -
                Util.normalize_keywords("café" | 
| 23 | 
            +
                Mongoid::Search::Util.normalize_keywords("café").should == ["cafe"]
         | 
| 19 24 | 
             
              end
         | 
| 20 25 |  | 
| 21 26 | 
             
              it "should downcase the text passed" do
         | 
| 22 | 
            -
                Util.normalize_keywords("CaFé" | 
| 27 | 
            +
                Mongoid::Search::Util.normalize_keywords("CaFé").should == ["cafe"]
         | 
| 23 28 | 
             
              end
         | 
| 24 29 |  | 
| 25 30 | 
             
              it "should downcase utf-8 chars of the text passed" do
         | 
| 26 | 
            -
                Util.normalize_keywords("Кафе" | 
| 31 | 
            +
                Mongoid::Search::Util.normalize_keywords("Кафе").should == ["кафе"]
         | 
| 27 32 | 
             
              end
         | 
| 28 33 |  | 
| 29 34 | 
             
              it "should split whitespaces, hifens, dots, underlines, etc.." do
         | 
| 30 | 
            -
                Util.normalize_keywords("CaFé-express.com delicious;come visit, and 'win' an \"iPad\"" | 
| 35 | 
            +
                Mongoid::Search::Util.normalize_keywords("CaFé-express.com delicious;come visit, and 'win' an \"iPad\"").should == ["cafe", "express", "com", "delicious", "come", "visit", "and", "win", "an", "ipad"]
         | 
| 31 36 | 
             
              end
         | 
| 32 37 |  | 
| 33 38 | 
             
              it "should stem keywords" do
         | 
| 34 | 
            -
                 | 
| 39 | 
            +
                Mongoid::Search.stem_keywords = true
         | 
| 40 | 
            +
                Mongoid::Search::Util.normalize_keywords("A runner running and eating").should == ["runner", "run", "and", "eat"]
         | 
| 35 41 | 
             
              end
         | 
| 36 42 |  | 
| 37 43 | 
             
              it "should ignore keywords from ignore list" do
         | 
| 38 | 
            -
                 | 
| 44 | 
            +
                Mongoid::Search.stem_keywords = true
         | 
| 45 | 
            +
                Mongoid::Search.ignore_list = YAML.load(File.open(File.dirname(__FILE__) + '/config/ignorelist.yml'))["ignorelist"]
         | 
| 46 | 
            +
                Mongoid::Search::Util.normalize_keywords("An amazing awesome runner running and eating").should == ["an", "runner", "run", "and", "eat"]
         | 
| 39 47 | 
             
              end
         | 
| 40 48 |  | 
| 41 49 | 
             
              it "should ignore keywords with less than two words" do
         | 
| 42 | 
            -
                Util.normalize_keywords("A runner running" | 
| 50 | 
            +
                Mongoid::Search::Util.normalize_keywords("A runner running").should_not include "a"
         | 
| 43 51 | 
             
              end
         | 
| 44 52 |  | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 53 | 
            +
              it "should not ignore numbers" do
         | 
| 54 | 
            +
                Mongoid::Search::Util.normalize_keywords("Ford T 1908").should include "1908"
         | 
| 55 | 
            +
              end
         | 
| 48 56 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: mongoid_search
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.3.0
         | 
| 5 5 | 
             
              prerelease: 
         | 
| 6 6 | 
             
            platform: ruby
         | 
| 7 7 | 
             
            authors:
         | 
| @@ -13,29 +13,18 @@ date: 2012-07-30 00:00:00.000000000 Z | |
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: mongoid
         | 
| 16 | 
            -
              requirement: & | 
| 16 | 
            +
              requirement: &70200267450400 !ruby/object:Gem::Requirement
         | 
| 17 17 | 
             
                none: false
         | 
| 18 18 | 
             
                requirements:
         | 
| 19 19 | 
             
                - - ! '>='
         | 
| 20 20 | 
             
                  - !ruby/object:Gem::Version
         | 
| 21 | 
            -
                    version:  | 
| 21 | 
            +
                    version: 3.0.0
         | 
| 22 22 | 
             
              type: :runtime
         | 
| 23 23 | 
             
              prerelease: false
         | 
| 24 | 
            -
              version_requirements: * | 
| 25 | 
            -
            - !ruby/object:Gem::Dependency
         | 
| 26 | 
            -
              name: bson_ext
         | 
| 27 | 
            -
              requirement: &70364721930900 !ruby/object:Gem::Requirement
         | 
| 28 | 
            -
                none: false
         | 
| 29 | 
            -
                requirements:
         | 
| 30 | 
            -
                - - ! '>='
         | 
| 31 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 32 | 
            -
                    version: '1.2'
         | 
| 33 | 
            -
              type: :runtime
         | 
| 34 | 
            -
              prerelease: false
         | 
| 35 | 
            -
              version_requirements: *70364721930900
         | 
| 24 | 
            +
              version_requirements: *70200267450400
         | 
| 36 25 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 37 26 | 
             
              name: fast-stemmer
         | 
| 38 | 
            -
              requirement: & | 
| 27 | 
            +
              requirement: &70200267465820 !ruby/object:Gem::Requirement
         | 
| 39 28 | 
             
                none: false
         | 
| 40 29 | 
             
                requirements:
         | 
| 41 30 | 
             
                - - ~>
         | 
| @@ -43,21 +32,21 @@ dependencies: | |
| 43 32 | 
             
                    version: 1.0.0
         | 
| 44 33 | 
             
              type: :runtime
         | 
| 45 34 | 
             
              prerelease: false
         | 
| 46 | 
            -
              version_requirements: * | 
| 35 | 
            +
              version_requirements: *70200267465820
         | 
| 47 36 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 48 37 | 
             
              name: database_cleaner
         | 
| 49 | 
            -
              requirement: & | 
| 38 | 
            +
              requirement: &70200267464480 !ruby/object:Gem::Requirement
         | 
| 50 39 | 
             
                none: false
         | 
| 51 40 | 
             
                requirements:
         | 
| 52 | 
            -
                - -  | 
| 41 | 
            +
                - - ! '>='
         | 
| 53 42 | 
             
                  - !ruby/object:Gem::Version
         | 
| 54 | 
            -
                    version: 0. | 
| 43 | 
            +
                    version: 0.8.0
         | 
| 55 44 | 
             
              type: :development
         | 
| 56 45 | 
             
              prerelease: false
         | 
| 57 | 
            -
              version_requirements: * | 
| 46 | 
            +
              version_requirements: *70200267464480
         | 
| 58 47 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 59 48 | 
             
              name: rake
         | 
| 60 | 
            -
              requirement: & | 
| 49 | 
            +
              requirement: &70200267462600 !ruby/object:Gem::Requirement
         | 
| 61 50 | 
             
                none: false
         | 
| 62 51 | 
             
                requirements:
         | 
| 63 52 | 
             
                - - ~>
         | 
| @@ -65,10 +54,10 @@ dependencies: | |
| 65 54 | 
             
                    version: 0.8.7
         | 
| 66 55 | 
             
              type: :development
         | 
| 67 56 | 
             
              prerelease: false
         | 
| 68 | 
            -
              version_requirements: * | 
| 57 | 
            +
              version_requirements: *70200267462600
         | 
| 69 58 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 70 59 | 
             
              name: rspec
         | 
| 71 | 
            -
              requirement: & | 
| 60 | 
            +
              requirement: &70200267461180 !ruby/object:Gem::Requirement
         | 
| 72 61 | 
             
                none: false
         | 
| 73 62 | 
             
                requirements:
         | 
| 74 63 | 
             
                - - ~>
         | 
| @@ -76,7 +65,7 @@ dependencies: | |
| 76 65 | 
             
                    version: '2.4'
         | 
| 77 66 | 
             
              type: :development
         | 
| 78 67 | 
             
              prerelease: false
         | 
| 79 | 
            -
              version_requirements: * | 
| 68 | 
            +
              version_requirements: *70200267461180
         | 
| 80 69 | 
             
            description: Simple full text search implementation.
         | 
| 81 70 | 
             
            email:
         | 
| 82 71 | 
             
            - mauricio@papodenerd.net
         |