Driver

The driver is the core of Moped that is used to interact with MongoDB. It is based of almost entirely four classes: Session, Collection, Indexes, and Query.

Behaviour

Session

The session is the main entry point to communicating with the database, similar to a database connection. The session is special though in that it can be a session with a single database server, a multi-node replica set, or a mongos server for sharded scenarios.

Once a new session is created you can interact with it in a similar fashion that you would with the mongo console.

session = Moped::Session.new([ "127.0.0.1:27017", "127.0.0.1:27018", "127.0.0.1:27019" ])
session.use :moped_test

session[:users].insert({ name: "Syd" })

session.with(safe: { w: 2, wtimeout: 5 }) do |_session|
  _session[:users].find.remove_all
end

session.with(database: "important_db", consistency: :strong) do |_session|
  _session[:users].find.first
end

The following is a table of the most common session API usage. For a complete reference please see the API docs.

Operation Syntax
Session#use

Set the current database to operate on. All subsequent commands will be on this database until switched.

session.use(:my_app_test)
Session#with

Return or yield a copy of the session with different options set on it.

session.with(safe: true) do |_session|
  ...
end
session.with(database: "admin").command(...)
Session#[]

Get a collection in the current database.

session[:users]
Session#drop

Drop the current database.

session.drop
Session#command

Run a command on the current database.

session.command(ping: 1)
session.command(
  mapreduce: "users",
  map: "...",
  reduce: "...",
  query: { created_at: { "$gte" => Time.now }},
  out: { inline: 1 }
)
Session#login

Authenticate with the current database.

session.login("name", "password")
Session#logout

Logout of the current database.

session.logout

Collection

The collection is the main entry point for dealing with collection operations, such as finding and inserting documents.

users = session[:users]
users.drop
users.find.count #=> 0.0

users.indexes.create({name: 1}, {unique: true})

users.insert(name: "John")
users.find.count #=> 1.0

users.insert(name: "John")
users.find.count #=> 1.0

session.with(safe: true) do |session|
  session[:users].insert(name: "John")
end # raises Moped::Errors::OperationFailure

The following is a table of the most common collection API usage. For a complete reference please see the API docs.

Operation Syntax
Collection#drop

Drop the collection

collection.drop
Collection#find

Build a query on the collection.

collection.find(name: "Syd")
Collection#indexes

Access information about the collection's indexes.

collection.indexes
Collection#insert

Insert one or multiple documents into the collection.

collection.insert(name: "Syd")
colleciton.insert([{ name: "Syd" }, { name: "Nancy" }])

Indexes

The indexes object is the main entry point for dealing with indexes on a single collection.

session[:users].indexes.create(name: 1)
session[:users].indexes.create(
  { name: 1, location: "2d" },
  { unique: true }
)
session[:users].indexes[name: 1]
# => {"v"=>1, "key"=>{"name"=>1}, "ns"=>"moped_test.users", "name"=>"name_1" }

session[:users].indexes.drop(name: 1)
session[:users].indexes[name: 1] # => nil

The following is a table of the most common indexes API usage. For a complete reference please see the API docs.

Operation Syntax
Indexes#[]

Get an index by its spec.

indexes[_id: 1]
Indexes#create

Create a new index.

indexes.create({ name: 1 }, { unique: true })
Indexes#drop

Drop one or all indexes.

indexes.drop(name: 1)
indexes.drop
Indexes#each

Iterate over and yield each index.

indexes.each do |index|
  ...
end

Query

The query is an object that is used to either return documents that match a criterion, or perform some other operation on the documents that match a criterion. This includes updates and deletes.

users = session[:users]

users.insert(name: "John")
users.find.count # => 1

users.find(name: "Mary").upsert(name: "Mary")
users.find.count # => 2

users.find.skip(1).limit(1).sort(name: -1).one
# => {"_id" => <...>, "name" => "John" }

scope = users.find(name: "Mary").select(_id: 0, name: 1)
scope.one # => {"name" => "Mary" }
scope.remove
scope.one # nil

The following is a table of the most common query API usage. For a complete reference please see the API docs.

Operation Syntax
Query#limit

Limit the number of returned documents.

query.limit(20)
Query#skip

Skip the provided number of documents.

query.skip(20)
Query#sort

Sort the returned results.

query.sort(name: 1, created_at: -1)
Query#distinct

Get an array of distinct values for a field.

query.distinct(:name)
Query#select

Select the fields you want returned.

query.select(name: 1, dob: 1)
Query#first

Return a single document for the query. Also one.

query.first
Query#each

Iterate through the results of a query.

query.each do |doc|
  ...
end
Query#count

Return the number of documents that match the query.

query.count
Query#update

Update the first document matching the query.

query.update('$set' => {name: "Tool"})
Query#update_all

Update all documents matching the query.

query.update_all('$set' => {pending: true})
Query#upsert

Create or update a document using query's selector.

query.upsert(name: "Depeche Mode")
Query#remove

Remove a single document matching the query.

query.remove
Query#remove_all

Remove all documents matching the query.

query.remove_all

Wire Protocol

Moped::Protocol is the namespace for Moped's implementation of the Mongo Wire Protocol. Its public API consists of classes representing each type of message in the protocol: Delete, GetMore, Insert, KillCursors, Query, Reply, Update, and a convenience class Command.

You should never have to worry about protocol objects, but more details can be found in the API docs if you're interested.

Errors

Here's a list of the exceptions generated by Moped.

Error Description
Moped::Errors::ConnectionFailure Raised when a node cannot be reached or a connection is lost. Note: this exception is only raised if Moped could not reconnect, so you shouldn't attempt to rescue this.
Moped::Errors::OperationFailure Raised when a command fails or is invalid, such as when an insert fails in safe mode.
Moped::Errors::QueryFailure Raised when an invalid query was sent to the database.
Moped::Errors::AuthenticationFailure Raised when invalid credentials were passed to session.login.
Moped::Errors::SocketError Not a real exception, but a module used to tag unhandled exceptions inside of a node's networking code. Allows you to rescue Moped::SocketError while preserving the real exception.

Other exceptions are possible while running commands, such as IO Errors around failed connections. Moped tries to be smart about managing its connections, such as checking if they're dead before executing a command; but those checks aren't foolproof, and Moped is conservative about handling unexpected errors on its connections. Namely, Moped will *not* retry a command if an unexpected exception is raised. Why? Because it's impossible to know whether the command was actually received by the remote Mongo instance, and without domain knowledge it cannot be safely retried.

Take for example this case:

session.with(safe: true)["users"].insert(name: "John")

It's entirely possible that the insert command will be sent to Mongo, but the connection gets closed before we read the result for getLastError. In this case, there's no way to know whether the insert was actually successful!

If, however, you want to gracefully handle this in your own application, you could do something like:

document = { _id: Moped::BSON::ObjectId.new, name: "John" }

begin
  session["users"].insert(document)
rescue Moped::Errors::SocketError
  session["users"].find(_id: document[:_id]).upsert(document)
end