0.0
No commit activity in last 3 years
No release in over 3 years
This gem hijacks the $or operator, doing one query per $or clause, merging the results with plain ruby.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

>= 0

Runtime

~> 5.1
 Project Readme

mongoid_ruby_or

This gem hijacks the $or operator, doing one query per $or clause, merging the results with plain ruby.

Background

WARNING: understand what this code does before using (it's only ~20 LOC!)

MongoDB is not the smartest when using indexes for the $or operator.

This is my case:

class Brand
  include Mongoid::Document

  field :name, type: String

  has_many :resources
end

class Resource
  include Mongoid::Document

  field :name,       type: String
  field :content_id, type: String

  index({ brand_id: 1, name: 1 })
  index({ brand_id: 1, content_id: 1 })

  belongs_to :brand
end

Given the above indexes, I do queries like:

brand.resources                           # will use index
brand.resources.where(name: 'test')       # will use index
brand.resources.where(content_id: 'test') # will use index

The problem comes when using the $or operator:

brand.resources.or({name: 'test'}, {content_id: 'test'}) # will do a full scan

Unfortunately, it will not use an index unless it starts with the field from the $or's. In order to have indexes that satisfies that query and the ones from above we will need something like:

index({ brand_id: 1 })
index({ name: 1, brand_id: 1 })
index({ content_id: 1, brand_id: 1 })

So in this simplified case, we will need to add an extra index.

Sometimes is not worth it, maybe that query is not use very often to justify the necessity to maintain the index, or you are trying to cut back on indexes for that collection.

This gem will add a #ruby_or method to the Mongoid::Criteria class, and do a query for each $or clause and then merge in ruby.

Installation

Add to your Gemfile:

gem :mongoid_ruby_or

For mongoid v3.x use:

gem :mongoid_ruby_or, '0.1.0'

Usage

Using the same example from above:

# problematic query, will be slow as it will do a full scan
brand.resources.or({name: /^test/}, {content_id: /^test/}).limit(10)
# this will do two fast queries and patch them together
brand.resources.or({name: /^test/}, {content_id: /^test/}).limit(10).ruby_or

Some observations

  • built for a system running ruby 1.9.3, rails 3.2, mongo 2.6.3, and mongoid 3.1.6, may not be an issue in the future
  • built for a system running ruby 2.0.0, rails 4.2.6, mongo 2.6.3, and mongoid 5.1.1, may not be an issue in the future
  • tested for simple $or clauses
  • can't do count for obvious reasons
  • ruby_or will cut the criteria chain and get results as soon as invoked, so use at the end