Skip to content

Instantly share code, notes, and snippets.

@fcheung
Created Nov 27, 2017
Embed
What would you like to do?
changes observed from ruby mongo 1.x to 2.x

#Updating from mongo ruby drive 1.x to 2.x

We've recently been belatedly migrating to the 2.x series of the mongo ruby driver. At least in part this is because there's very little documentation out there on what has changed, except that there is no backwards compatibility. This post is a list of issues that we came across - I expect it is by no means exhaustive

Dependencies

Before you get started, make sure that any dependencies you have are also comaptible with mongo 2.x. For example database_cleaner needs to be 1.5 or higher

Class name changes

A lot of the classes / constants in the Mongo:: hierarchy have moved around. For us the lions share were exceptions we were rescuing, for example

Mongo::GridFileNotFound => Mongo::Error::FileNotFound
BSON::InvalidObjectId   => BSON::ObjectId::Invalid
Mongo::OperationFailure => Mongo::Error::OperationFailure

CRUD changes

Several of the methods here have been renamed or had their signature change slightly.

Read

Previously Collection#find took a block and yielded a cursor, with the block used to determine the lifetime of the cursor. find now returns a view that can be iterated over. If you don't want the cursor to timeout, this option has been renamed from timeout: false to no_cursor_timeout: true

insert

The old Mongo::Collection had an insert method that took either a single document or an array of documents and inserted them into the collection. You now need to use insert_one for the single document case and insert_many for the second case

update

Previously one of the options the update method took was a boolean multi: option that controlled whether mongodb should update all matching documents or only the first one. Instead call update_one or update_many

delete

Deleting documents used to be via the remove method, with a limit option to control whether the first matching document was removed or all of them (the default). In 2.x, call delete_one or delete_many

count

In 1.x, Mongo::Collection#count took a single hash of options, which could include a query. In 2.x, the first argument is instead a query. For example

collection.count(query: {active: true}, {read: :secondary})

becomes

collection.count({active: true}, {read: {mode: :secondary}})

GridFS

This used to look like

grid = Mongo::Grid.new(database)
id = grid.put(data, opts) #data is string or has #read method
grid.get(id) #returns a gridio

In 2.x you would do

grid = database.fs

id = grid.upload_from_stream(filename, io)

grid.open_download_stream(id) do |stream|
  stream.each do |chunk| #you can also use stream.get
    # do something with data
  end
end

Read preferences

Instead of {read: secondary}, use {read: {mode: :secondary}}

To execute a read against a secondary you can do

collection.with(read: {mode: :secondary}).find(...)

MapReduce

Previously you would do

collection.map_reduce(map_function, reduce_function, {query: {active: true}, ...})

This executes the map reduce operation and, for the inline result types, returns the data

If you wanted to perform that over a subset of the collection, then you added query: to the options.

In 2.x, map_reduce is a method on a view and doesn't accept a query option (or rather, it ignores it). The above code would look like

collection.find(active: true).map_reduce(map_function, reduce_function)

This in itself doesn't do anything than create a Mongo::View::MapReduce object. If you try to iterate over it, that will trigger execution of the map reduce operation. Node that

collection.find(active: true).map_reduce(map_function, reduce_function).map do |value|
...
end

returns the source of the map function, rather than executing Enumerable#map against the result set (use collect if you want this)

Aggregation pipeline

Like map reduce this now executes lazily. Some options have changed:

  • allowDiskUse is now allow_disk_use
  • cursor is now use_cursor

The read preference is now set on the collection/view rather than as an option to aggregate, ie

collection.with(:read => { :mode => :secondary }).aggregate(...)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment