Simple full text search for Mongoid ORM
Latest commit 77bf98e Sep 8, 2016 @mauriciozaffari mauriciozaffari committed on GitHub Merge pull request #101 from alexkravets/master
Make rake index task available within rails app

Mongoid Search

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.


In your Gemfile:

gem 'mongoid_search'

If your project is still using mongoid 2.x.x, stick to mongoid_search 0.2.x:

gem 'mongoid_search', '~> 0.2.8'


bundle install


class Product
  include Mongoid::Document
  include Mongoid::Search
  field :brand
  field :name
  field :info, :type => Hash

  has_many   :tags
  belongs_to :category

  search_in :brand, :name, :tags => :name, :category => :name, :info => [:summary, :description]

class Tag
  include Mongoid::Document
  field :name

  belongs_to :product

class Category
  include Mongoid::Document
  field :name

  has_many :products

Now when you save a product, you get a _keywords field automatically:

p = :brand => "Apple", :name => "iPhone", :info => {:summary => "Info-summary", :description => "Info-description"}
p.tags << => "Amazing")
p.tags << => "Awesome")
p.tags << => "Superb")
=> true
=> ["amazing", "apple", "awesome", "iphone", "superb", "Info-summary", "Info-description"]

Now you can run search, which will look in the _keywords field and return all matching results:

Product.full_text_search("apple iphone").size
=> 1

Note that the search is case insensitive, and accept partial searching too:

=> 1

Assuming you have a category with multiple products you can use the following code to search for 'iphone' in products cheaper than $499

@category.products.where( => 499).full_text_search('iphone').asc(:price)

To index or reindex all existing records, run this rake task

$ rake mongoid_search:index



:any - match any occurrence

:all - match all ocurrences

Default is :any.

Product.full_text_search("apple motorola", match: :any).size
=> 1

Product.full_text_search("apple motorola", match: :all).size
=> 0


true - will return Model.all

false - will return []

Default is false.

Product.full_text_search("", allow_empty_search: true).size
=> 1


true - Adds relevance information to the results

false - No relevance information

Default is false.

Product.full_text_search('amazing apple', relevant_search: true)
=> [#<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>]

Please note that relevant_search will return an Array and not a Criteria object. The search method should always be called in the end of the method chain.


Alternatively, you can create an initializer to setup those options:

Mongoid::Search.setup do |config|
  ## Default matching type. Match :any or :all searched keywords
  config.match = :any

  ## If true, an empty search will return all objects
  config.allow_empty_search = false

  ## If true, will search with relevance information
  config.relevant_search = false

  ## Stem keywords
  config.stem_keywords = false

  ## Add a custom proc returning strings to replace the default stemmer
  # For example using ruby-stemmer:
  # config.stem_proc = { |word| Lingua.stemmer(word, :language => 'nl') }

  ## Words to ignore
  config.ignore_list = []

  ## An array of words
  # config.ignore_list = %w{ a an to from as }

  ## Or from a file
  # config.ignore_list = YAML.load( + '/config/ignorelist.yml'))["ignorelist"]

  ## Search using regex (slower)
  config.regex_search = true

  ## Regex to search

  ## Match partial words on both sides (slower)
  config.regex = { |query| /#{query}/ }

  ## Match partial words on the beginning or in the end (slightly faster)
  # config.regex = { |query| /^#{query}/ }
  # config.regex = { |query| /#{query}$/ }

  # Ligatures to be replaced
  config.ligatures = { "œ"=>"oe", "æ"=>"ae" }

  # Minimum word size. Words smaller than it won't be indexed
  config.minimum_word_size = 2