Docs Menu
Docs Home
/ / /
Ruby MongoDB Driver
/

CRUD Operations

On this page

  • Key-value Pair Notation
  • Creating Documents
  • Query Cache
  • Reading
  • Updating
  • Deleting
  • Write Concern
  • Field Names with Dots/Periods (.) and Dollar Signs ($)
  • A Note about the BSON Symbol type

CRUD operations are those which deal with creating, reading, updating, and deleting documents.

Key-value pairs appear in many different contexts in the MongoDB Ruby driver, and there are some quirks of syntax with regard to how they can be notated which depend on which version of Ruby you're using.

When constructing a document, the following syntax is acceptable and correct for Ruby version 1.9 and later:

document = { name: "Harriet", age: 36 }

If you're using Ruby version 2.2 or greater, you can optionally enclose your keys in quotes.

document = { "name": "Harriet", "age": 36 }

If you need to use any MongoDB operator which begins with $, such as $set, $gte, or $near, you must enclose it in quotes. If you're using Ruby version 2.2 or greater, you can notate it as follows:

collection.update_one({ name: "Harriet" }, { "$set": { age: 42 } })

If you're using an earlier version of Ruby, use the hashrocket symbol:

collection.update_one({ name: "Harriet" }, { "$set" => { age: 42 } })

Quoted strings and hashrockets for key-value pairs will work with any version of Ruby:

collection.update_one({ "name" => "Harriet" }, { "$set" => { age: 42 } })

To insert documents into a collection, select a collection on the client and call insert_one or insert_many.

Insert operations return a Mongo::Operation::Result object which gives you information about the insert itself.

On MongoDB 2.6 and later, if the insert fails, an exception is raised, because write commands are used.

On MongoDB 2.4, an exception is only raised if the insert fails and the write concern is 1 or higher.

client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music')
result = client[:artists].insert_one( { :name => 'FKA Twigs' } )
result.n # returns 1, because 1 document was inserted.
result = client[:artists].insert_many([
{ :name => 'Flying Lotus' },
{ :name => 'Aphex Twin' }
])
result.inserted_count # returns 2, because 2 documents were inserted.

New in version 3.4.

Decimal128 is a BSON datatype that employs 128-bit decimal-based floating-point values capable of emulating decimal rounding with exact precision. This functionality is intended for applications that handle monetary data, such as financial and tax computations.

The following example inserts a value of type Decimal128 into the price field of a collection named inventory:

client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test')
price = BSON::Decimal128.new("428.79")
client[:inventory].insert_one({ "_id" => 1,
"item" => "26 inch monitor",
"price" => price })

The above operation produces the following document:

{ "_id" : 1, "item" : "26 inch monitor", "price" : NumberDecimal("428.79") }

You can also create a Decimal128 object from a Ruby BigDecimal object, or with Decimal128.from_string().

big_decimal = BigDecimal.new(428.79, 5)
price = BSON::Decimal128.new(big_decimal)
# => BSON::Decimal128('428.79')
price = BSON::Decimal128.from_string("428.79")
# => BSON::Decimal128('428.79')

The Ruby driver provides a query cache. When enabled, the query cache will save the results of find and aggregation queries and return those saved results when the same queries are performed again.

To read more about the query cache, visit the query cache tutorial.

The Ruby driver provides a fluent interface for queries using the find method on the collection. Various options are available to the find method.

The query is lazily executed against the server only when iterating the results - at that point the query is dispatched and a Mongo::Cursor is returned.

To find all documents for a given filter, call find with the query:

client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music')
client[:artists].find(:name => 'Flying Lotus').each do |document|
#=> Yields a BSON::Document.
end

To query nested documents, specify the keys in nested order using dot notation.

client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music')
client[:artists].find("records.releaseYear": 2008).each do |document|
#=> Yields a BSON::Document.
end

This usage is deprecated.

The find method allows providing the query and the options using the legacy $query syntax in the first parameter:

collection.find(:'$query' => {name: 'Mr. Smith'})
# Equivalent to:
collection.find(name: 'Mr. Smith')
collection.find(:'$query' => {name: 'Mr. Smith'}, :'$sort' => {age: 1})
# Equivalent to:
collection.find(name: 'Mr. Smith').sort(age: 1)

When the query is executed against MongoDB 3.2 or newer, the driver will use the protocol appropriate for the server version in question, automatically converting the query as needed to either a find command or an OP_MSG payload.

To add options to a query, chain the appropriate methods after the find method. Note that the underlying object, the Mongo::Collection::View, is immutable and a new object will be returned after each method call.

client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music')
documents = client[:artists].find(:name => 'Flying Lotus').skip(10).limit(10)
documents.each do |document|
#=> Yields a BSON::Document.
end

The following is a full list of the available options that can be added when querying and their corresponding methods as examples.

Option
Description

allow_disk_use

When set to true, the server can write temporary data to disk while executing the find operation. This option is only available on MongoDB server versions 4.4 and newer.

allow_partial_results

For use with sharded clusters. If a shard is down, allows the query to return results from the shards that are up, potentially only getting a portion of the results.

batch_size(Integer)

Specifies the size of each batch of documents the cursor will return on each GETMORE operation.

comment(String)

Adds a comment to the query.

explain(**opts)

Returns the query plan for the query. Pass the explain options via the keyword arguments using symbol keys.

# All server versions - default explain behavior
client[:artists].find.explain
# MongoDB 3.0 and newer
client[:artists].find.explain(verbosity: :query_planner)
client[:artists].find.explain(verbosity: :execution_stats)
client[:artists].find.explain(verbosity: :all_plans_execution)
# Alternative syntax using camel case
client[:artists].find.explain(verbosity: "queryPlanner")
client[:artists].find.explain(verbosity: "executionStats")
client[:artists].find.explain(verbosity: "allPlansExecution")
# MongoDB 2.6
client[:artists].find.explain(verbose: true)

The explain operation supports :session and :read (for read preference) options. To specify these options for a single explain operation, they must be given to the find method as follows:

client[:artists].find({}, session: session).explain
client[:artists].find({}, read: {mode: :secondary_preferred}).explain

If the read preference option is specified on the client or on the collection, it will be passed to the explain operation:

client[:artists, read: {mode: :secondary_preferred}].find.explain

Note that the session option is not accepted when creating a collection object.

The explain command does not support passing the read concern option. If the read concern is specifed on the client or collection level, or if the read concern is specified as a find option, it will NOT be passed by the driver to the explain command.

The information returned by the server for the explain command varies with server version and deployment topology. The driver's explain method returns whatever the server provided.

The return value of the ``explain`` method is not part of the driver's public API and depends on the server version and deployment topology.

hint(Hash)

Provides the query with an index hint to use.

let(Hash)

Mapping of variables to use in the query.

limit(Integer)

Limits the number of returned documents to the provided value.

max_scan(Integer)

Sets the maximum number of documents to scan if a full collection scan would be performed. Deprecated as of MongoDB server version 4.0.

max_time_ms(Integer)

The maximum amount of time to allow the query to run, in milliseconds.

no_cursor_timeout

MongoDB automatically closes inactive cursors after a period of 10 minutes. Call this for cursors to remain open indefinitely on the server.

projection(Hash)

Specifies the fields to include or exclude from the results.

client[:artists].find.projection(:name => 1)

read(Hash)

Changes the read preference for this query only.

client[:artists].find.read(:mode => :secondary_preferred)

session(Session)

The session to use.

show_disk_loc(Boolean)

Tells the results to also include the location of the documents on disk.

skip(Integer)

Skip the provided number of documents in the results.

snapshot

Execute the query in snapshot mode. Deprecated as of MongoDB server version 4.0.

sort(Hash)

Specifies sort criteria for the query.

client[:artists].find.sort(:name => -1)
count_documents
Get the total number of documents matching a filter, or the total number of documents in a collection.
client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music')
client[:artists].find(:name => 'Flying Lotus').count_documents
estimated_document_count

Get an approximate number of documents in the collection.

Note that unlike count_documents, estimated_document_count does not accept a filter.

The count server command is used to implement estimated_document_count. More information can be found via Count: Behavior.

Due to an oversight in MongoDB versions 5.0.0-5.0.7, the count command, which estimated_document_count uses in its implementation, was not included in v1 of the Stable API. Therefore, users of the Stable API with estimated_document_count are recommended to upgrade their server version to 5.0.8+ or set api_strict: false to avoid encountering errors.

client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music')
client[:artists].estimated_document_count
count

Get an approximate number of documents matching a filter, or an approximate number of documents in the collection.

Deprecated: The count method is deprecated and does not work in transactions. Please use count_documents to obtain an exact count of documents potentially matching a filter or estimated_document_count to obtain an approximate number of documents in the collection.

client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music')
client[:artists].find(:name => 'Flying Lotus').count
distinct
Filters out documents with duplicate values. Equivalent to the SQL distinct clause.
client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music')
client[:artists].find.distinct(:name )

For capped collections you may use a tailable cursor that remains open after the client exhausts the results in the initial cursor. The following code example shows how a tailable cursor might be used:

client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music')
client[:artists].drop
client[:artists, capped: true, size: 512].create
result = client[:artists].insert_many([
{ :name => 'Flying Lotus' },
{ :name => 'Aphex Twin' }
])
enum = client[:artists].find({}, cursor_type: :tailable_await).to_enum
while true
doc = enum.next
# do something
sleep(1)
end

Read concern can be set on the client or on the collection:

client = Mongo::Client.new(['localhost:14420'], database: 'music',
read_concern: {level: :local})
client['collection'].find.to_a
collection = client['collection', read_concern: {level: :majority}]
collection.find.to_a

The driver does not currently support setting read concern on an individual query.

Read concern can be specified when starting a transaction. When a transaction is active, any read concern specified on the client or on the collection is ignored.

When using the generic command helper, the read concern can be specified as part of the command:

client.database.command(dbStats: 1, readConcern: {level: :majority})

Read preference determines the candidate replica set members to which a query or command can be sent. They consist of a mode specified as a symbol, an array of hashes known as tag_sets, the hedge option, which is a Hash specifying hedged read behavior, and two timing options: local_threshold and server_selection_timeout.

local_threshold
Defines the upper limit in seconds of the latency window between the nearest server and suitable servers to which an operation may be sent. The default is 15 milliseconds, or 0.015 seconds.
server_selection_timeout
Defines how long to block for server selection before throwing an exception. The default is 30,000 milliseconds, or 30 seconds.

Note

Read preference does not apply to Standalone deployments. When a client is connected to a Standalone deployment, any application-specified read preference is ignored.

For more information on the algorithm used to select a server, please refer to the Server Selection documentation, available on GitHub.

Read preference can be set as an option on the client or passed an option when a command is run on a database:

# Set read preference on a client, used for all operations
client = Mongo::Client.new([ '127.0.0.1:27017' ],
read: { mode: :secondary,
tag_sets: [ { 'dc' => 'nyc' } ]
} )
# Set read preference for a given command
client.database.command( { dbStats: 1 }, read: { mode: secondary,
tag_sets: [ { 'dc' => 'nyc' } ] } )

Read preference can also be set for specific operations on a collection using the with method:

client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music')
artists = client[:artists]
artists.with(:read => { :mode => :primary_preferred }).find.to_a

There are five possible read preference modes: :primary, :secondary, :primary_preferred, :secondary_preferred and``:nearest``. Please see the read preference documentation in the MongoDB Manual for an explanation of the modes.

Note

When a client is directly connected to a server using the :direct_connection Ruby option or the directConnection URI option, read preference mode is automatically set to :primary_preferred to permit read operations against secondaries. If the application specified a :primary read preference mode, the mode is automatically converted to :primary_preferred. If another read preference mode is specified, it is passed to the server unchanged.

The tag_sets parameter is an ordered list of tag sets used to restrict the eligibility of servers for selection, such as for data center awareness. Please see the read preference documentation in the MongoDB Manual for an explanation of tag sets.

A read preference tag set (T) matches a server tag set (S) – or equivalently a server tag set (S) matches a read preference tag set (T) — if T is a subset of S.

For example, the read preference tag set { dc: 'ny', rack: 2 } matches a secondary server with tag set { dc: 'ny', rack: 2, size: 'large' }.

A tag set that is an empty document matches any server, because the empty tag set is a subset of any tag set. This means the default tag_sets parameter [{}] matches all servers.

The hedge parameter is a Hash that specifies whether the server should use hedged reads. With hedged reads, sharded clusters can route read operations to two replica set members and return results from the first respondent.

The hedge option may only be specified on non-primary read preferences. It must be provided as Hash with the key enabled set to true or false.

client = Mongo::Client.new(
[ '127.0.0.1:27017' ],
read: { mode: :secondary, hedge: { enabled: true } },
)

See the MongoDB Manual for more information about hedged reads.

Note

The hedge option is only available on MongoDB server versions 4.4 and newer. Attempting to use this option on older server versions will result in an error.

Updating documents is possible by executing a single or multiple update, or by using the $findAndModify command.

update_one

client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music')
artists = client[:artists]
result = artists.find(:name => 'Goldie').update_one("$inc" => { :plays => 1 } )
result.n # Returns 1.
result = artists.update_one( { :name => 'Goldie' }, { "$inc" => { :plays => 1 } } )
result.n # Returns 1.

update_many

result = artists.find(:label => 'Hospital').update_many( "$inc" => { :plays => 1 } )
result.modified_count # Returns the number of documents that were updated.
result = artists.update_many( { :label => 'Hospital' }, { "$inc" => { :plays => 1 } } )
result.modified_count # Returns the number of documents that were updated.

replace_one

result = artists.find(:name => 'Aphex Twin').replace_one(:name => 'Richard James')
result.modified_count # Returns 1.
result = artists.replace_one( { :name => 'Aphex Twin' }, { :name => 'Richard James' } )
result.modified_count # Returns 1.

To update documents and return a document via $findAndModify, use one of the three provided helpers: find_one_and_delete, find_one_and_replace, or find_one_and_update. You can opt to return the document before or after the modification occurs.

find_one_and_delete

client = Mongo::Client.new( [ '127.0.0.1:27017' ], :database => 'music')
artists = client[:artists]
artists.find(:name => 'José James').find_one_and_delete # Returns the document.

find_one_and_replace

doc = artists.find(:name => 'José James').find_one_and_replace(:name => 'José')
doc # Return the document before the update.
doc = artists.find_one_and_replace({ :name => 'José James' }, { :name => 'José' })
doc # Return the document before the update.
doc = artists.find(:name => 'José James').
find_one_and_replace( { :name => 'José' }, :return_document => :after )
doc # Return the document after the update.

find_one_and_update

doc = artists.find(:name => 'José James').
find_one_and_update( '$set' => { :name => 'José' } )
doc # Return the document before the update.
doc = artists.find_one_and_update( { :name => 'José James' }, { '$set' => { :name => 'José' } } )
doc # Return the document before the update.

To add options to an update command, specify them as key-value pairs in the options Hash argument.

client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music')
artists = client[:artists]
artists.indexes.create_one(name: 1)
# Force the server to use the name index to perform this operation
result = artists.update_one(
{ :name => 'Goldie' },
{ "$inc" => { :plays => 1 } },
{ hint: { name: 1 } }
)
result.n # Returns 1.

The following is a list of the options that can be added to update operations, including update_one, update_many, replace_one, find_one_and_delete, find_one_and_update, and find_one_and_replace.

Option
Description

array_filters

An Array of filter documents that determine which array elements to modify for an update operation on an array field.

bypass_document_validation

Whether to skip document-level validation before writing the document.

collation

Specifies a set of rules to use when comparing strings complying with the conventions of a particular language.

hint

The index to use for this operation. May be specified as a Hash (e.g. { _id: 1 }) or as a String (e.g. "_id_"). Supported on MongoDB server versions 4.2 and newer for update_one, update_many, and replace_one commands, and on server versions 4.4 and newer for find_one_and_delete, find_one_and_update, and find_one_and_replace commands.

let(Hash)

Mapping of variables to use for this operation.

projection

The fields to exclude or include in the operation result (only available on find_one_and_delete, find_one_and_replace, and find_one_and_update commands).

return_document

A symbol specifying whether to return the updated document as it was before or after the update. Potential values are :before or :after. (Only available on find_one_and_update and find_one_and_replace commands).

sort

How to sort the results of a find and modify command. Specified as a Hash key-value pair, where the key is the name of the field to sort by, and the value is either 1 or -1, specifying a sort in ascending or descending order (only available on find_one_and_delete, find_one_and_replace, and find_one_and_update commands).

session

The session to use for this operation.

upsert

Whether to upsert if the document doesn't exist. Cannot be used on find_one_and_delete operation.

For more information about update options, see the MongoDB server documentation on the following commands:

  • update

  • findAndModify

delete_one

client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music')
artists = client[:artists]
result = artists.find(:name => 'Björk').delete_one
result.deleted_count # Returns 1.
result = artists.delete_one(:name => 'Björk')
result.deleted_count # Returns 1.

delete_many

result = artists.find(:label => 'Mute').delete_many
result.deleted_count # Returns the number deleted.
result = artists.delete_many(:label => 'Mute')
result.deleted_count # Returns the number deleted.

To add options to a delete command, specify them as key-value pairs in the options Hash argument.

client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'music')
artists = client[:artists]
artists.indexes.create_one(name: 1)
# Force the server to use the name index to perform this operation
result = artists.find(:name => 'Björk').delete_one(hint: { name: 1 })
result.deleted_count # Returns 1.

The following is a full list of the available options that can be added to delete_one and delete_many operations.

Option
Description

collation

Specifies a set of rules to use when comparing strings complying with the conventions of a particular language.

hint

The index to use for this operation. May be specified as a Hash (e.g. { _id: 1 }) or as a String (e.g. "_id_"). Supported on MongoDB server versions 4.4 and newer.

let(Hash)

Mapping of variables to use for this operation.

session

The session to use for this operation.

For more information about update options, see the MongoDB server documentation on the delete command.

All write operations in MongoDB are executed with a write concern which is the level of acknowledgment requested from MongoDB for the particular write. More information about write concerns in general is available in the MongoDB manual.

The Ruby driver supports specifying write concern on client, collection, session (for transactions on that session), transaction, GridFS bucket and write stream levels, as well as when manually issuing commands via Database#command.

As of driver version 2.10, all driver objects accepting write concerns do so through the :write_concern option, which should be given a hash with the write concern options. Usage of the :write option is deprecated. In driver versions 2.9 and below, client, collection and GridFS objects took write concern options in the :write option with session and transaction objects employing the :write_concern option.

Below are some examples of passing write concerns to client and collection objects. The :write_concern option can be provided when constructing new client and collection objects, or to the #with methods.

GridFS examples are provided on the GridFS page.

client = Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music',
write_concern: {w: 2})
alt_client = client.with(write_concern: {w: :majority})
collection = client[:artists, write_concern: {w: 3}]
alt_collection = collection.with(write_concern: {w: :majority})
# Uses w: 3
collection.insert_one({name: 'SUN Project'})
# Uses w: :majority
alt_collection.insert_one({name: 'SUN Project'})

Driver versions 2.9 and earlier accepted write concerns on client and collection level via the :write option. This usage continues to be supported for backwards compatibility, but is deprecated:

client = Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music',
write: {w: 2})
alt_client = client.with(write: {w: :majority})
collection = client[:artists, write: {w: 3}]
alt_collection = collection.with(write: {w: :majority})

If both :write and :write_concern options are provided, their values must be identical or an exception will be raised:

# OK
client = Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music',
write_concern: {w: 3}, write: {w: 3})
# Error
client = Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music',
write_concern: {w: 3}, write: {w: :majority})

When #with methods are used to alter the options on a client or collection, the last provided option wins in case of naming differences:

client = Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music',
write_concern: {w: 2})
alt_client = client.with(write: {w: 3})
alt_client.options[:write]
# => {"w"=>3}
alt_client.options[:write_concern]
# => nil

When using transactions, write concern is only sent to the server in commit_transaction and abort_transaction operations per the transactions specification. Write concern may be set via the :write_concern option in a with_transaction or start_transaction call, or via default_transaction_options option on a session object. If neither of these is set, write concern of the client is used; note that transactions ignore write concerns of collections that are involved in their operations. Note that when setting the write concern as a transaction option, the :write option is not recognized by any driver version.

client = Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music',
write_concern: {w: 2})
collection = client[:artists, write_concern: {w: :majority}]
session = client.start_session
session.with_transaction do
collection.insert_one({test: 1}, session: session)
# Uses w: 2 when committing
end
session = client.start_session(default_transaction_options:
{write_concern: {w: 3})
)
session.with_transaction do
collection.insert_one({test: 1}, session: session)
# Uses w: 3 when committing
end
session = client.start_session
session.with_transaction(write_concern: {w: 3}) do
collection.insert_one({test: 1}, session: session)
# Uses w: 3 when committing
end

When write concerns are inherited, inheritance applies to the entire write concern hash rather than individual elements. For example, j: true is not inherited in the following case:

client = Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music',
write_concern: {w: 1, j: true})
collection = client[:artists, write_concern: {w: 2}]
collection.write_concern.options
# => #<Mongo::WriteConcern::Acknowledged:0x47289650367880 options={:w=>2}>

Although CRUD operations accept an options hash, they currently do not recognize the :write_concern option:

client = Mongo::Client.new([ '127.0.0.1:27017' ], database: 'music',
write_concern: {w: 2})
collection = client[:artists, write_concern: {w: :majority}]
# Still uses w: :majority
collection.insert_one({name: 'SUN Project'}, write_concern: {w: 1})

The easiest workaround for this is to use #with to obtain a new collection instance with the desired write concern:

# Uses w: 1
collection.with(write_concern: {w: 1}).insert_one(name: 'SUN Project')

Write concern can also be manually specified in Database#command:

client.database.command(create: 'foo-collection', writeConcern: {w: :majority})

Note that writeConcern here is part of the operation rather than options, and the syntax is the camel case one that MongoDB server recognizes, not the underscore one that Ruby driver uses.

Starting in Mongo Ruby Driver version 2.18.0, the ability to work with fields that begin with dollar signs ($) and fields with dots/periods (.) in them is available. In Driver version 2.17.0 and earlier, any attempt to work with dotted or dollared fields would result in an IllegalKey error being raised. See the MongoDB docs on Field Names with Periods (.) and Dollar Signs ($) for more information on working with these types of fields.

Because the BSON specification deprecated the BSON symbol type, the bson gem will serialize Ruby symbols into BSON strings when used on its own. However, in order to maintain backwards compatibility with older datasets, the Ruby driver overrides this behavior to serialize Ruby symbols as BSON symbols. This is necessary to be able to specify queries for documents which contain BSON symbols as fields. Despite this, new documents with symbol type fields should not be stored in the database; instead, use string fields.

To override default behavior and configure the driver to encode symbol values as strings, include the following code snippet in your project:

class Symbol
def bson_type
BSON::String::BSON_TYPE
end
end

Back

Working With Data