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 |
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