Kenneth Falck's Blog

Querying by exact array value in MongoMapper

Posted on 2011-08-29 by Kenneth Falck

MongoMapper is a great MongoDB ORM for Ruby on Rails. It basically allows you to use MongoDB as a drop-in replacement for MySQL/PostgreSQL.

However, it has one particular weakness: It is impossible to filter MongoDB queries by exact array values. MongoMapper converts array queries into $in operations, which means that all documents that contain any of the specified array values will be returned.

For example, assume you have two MongoDB documents:

{ "name": "A", "items": [1, 2, 3] }

{ "name": "B", "items": [1, 2] }

The following MongoMapper query will return both documents, even if you might have expected only the first one:

MyModel.where(:items => [1, 2, 3])

My $exact fork on GitHub

I've created a fork of plucky, the query engine used by MongoMapper, which adds a new pseudo operator called $exact. My fork can be found here:

kennu/plucky (master, based on v0.4.1) kennu/plucky/tree/backport_exact_v3 (backported to v0.3.8)

I've also submitted my change set as a pull request to the author of plucky.

You can use the fork in your Rails Gemfile like this (you'll need the v0.3.8 version with current mongo_mapper):

gem 'plucky', :git => '', :branch => 'backport_exact_v3'
gem 'mongo_mapper'

After running bundle install, you can specify queries in the this format:

MyModel.where(:items => { :$exact => [1, 2, 3] })

This query will return only the first document of the previous example above.

Remember, though, that you must keep your arrays in a specific order for these queries to work. An array of [1, 2, 3] won't match if you query it as [2, 3, 1].