mongodb/mongo-ruby-driver

View on GitHub
docs/reference/crud-operations.txt

Summary

Maintainability
Test Coverage
***************
CRUD Operations
***************

.. default-domain:: mongodb

.. contents:: On this page
   :local:
   :backlinks: none
   :depth: 1
   :class: singlecol

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

Key-value Pair Notation
=======================

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:

.. code-block:: javascript

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

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

.. code-block:: javascript

  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:

.. code-block:: ruby

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

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

.. code-block:: ruby

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

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

.. code-block:: ruby

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


Creating Documents
==================

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
:manual:`write concern</reference/write-concern/>` is 1 or higher.

.. code-block:: ruby

  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.

.. _specify-decimal128:

Specify a ``Decimal128`` number
-------------------------------

.. versionadded:: 3.4

:manual:`Decimal128</tutorial/model-monetary-data>` is a
:doc:`BSON datatype </tutorials/bson>`
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
:manual:`monetary data </tutorial/model-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``:

.. code-block:: ruby

   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:

.. code-block:: javascript

   { "_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()``.

.. code-block:: ruby

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

Query Cache
===========

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
:ref:`query cache tutorial <query-cache>`.

Reading
=======

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:

.. code-block:: ruby

  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.

.. code-block:: ruby

  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


Legacy ``$query`` Syntax
------------------------

*This usage is deprecated.*

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

.. code-block:: ruby

  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.


.. _query-options:

Query Options
-------------

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.

.. code-block:: ruby

  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.

.. list-table::
   :header-rows: 1
   :widths: 40 80

   * - 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 :manual:`explain options
       </reference/command/explain/>` via the keyword arguments using symbol
       keys.

       .. code-block:: ruby

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

       .. code-block:: ruby

         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:

       .. code-block:: ruby

         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.

       .. note::

         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 ``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
       :manual:`index hint</reference/method/cursor.hint/>` to use.
   * - ``let(Hash)``
     - Mapping of :manual:`variables</reference/command/find/#std-label-find-let-syntax>`
       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.

       .. code-block:: ruby

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

   * - ``read(Hash)``
     - Changes the read preference for this query only.

       .. code-block:: ruby

         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.

       .. code-block:: ruby

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


Additional Query Operations
---------------------------

``count_documents``
  Get the total number of documents matching a filter, or the total number
  of documents in a collection.

.. code-block:: ruby

  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 <https://www.mongodb.com/docs/manual/reference/command/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.

.. code-block:: ruby

  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.

.. code-block:: ruby

  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.

.. code-block:: ruby

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

  client[:artists].find.distinct(:name )

Tailable Cursors
----------------

For capped collections you may use a :manual:`tailable cursor
</core/tailable-cursors/>` 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:

.. code-block:: ruby

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

Read concern can be :ref:`set on the client <client-options>`
or on the collection:

.. code-block:: ruby

  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 :ref:`starting a transaction
<using-transactions>`. When a transaction is active, :manual:`any read concern
specified on the client or on the collection is ignored
</core/transactions/#transactions-and-read-concern>`.

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

.. code-block:: ruby

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


.. _read-preference:

Read Preference
---------------

Read preference determines the candidate :manual:`replica set</replication/>`
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
<https://github.com/mongodb/specifications/blob/master/source/server-sel
ection/server-selection.rst>`_.

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

.. code-block:: ruby

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

.. code-block:: ruby

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

Mode
----

There are five possible read preference modes: ``:primary``, ``:secondary``,
``:primary_preferred``, ``:secondary_preferred`` and``:nearest``.
Please see the :manual:`read preference documentation in the MongoDB Manual
</core/read-preference/>` 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.

Tag sets
--------

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 :manual:`read preference documentation in
the MongoDB Manual </core/read-preference/>` 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.

Hedge
-----

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

.. code-block:: ruby

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

See the :manual:`MongoDB Manual </core/read-preference-hedge-option>` 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:

Updating
========

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

``update_one``

.. code-block:: ruby

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

.. code-block:: ruby

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

.. code-block:: ruby

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

.. code-block:: ruby

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

.. code-block:: ruby

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

.. code-block:: ruby

  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.

Update Options
--------------

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

.. code-block:: ruby

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

.. list-table::
   :header-rows: 1
   :widths: 40 80

   * - 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 :manual:`variables</reference/command/update/#std-label-update-let-syntax>`
       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:

- :manual:`update </reference/command/update>`
- :manual:`findAndModify </reference/command/findAndModify>`

Deleting
========

``delete_one``

.. code-block:: ruby

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

.. code-block:: ruby

  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.

Delete Options
--------------

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

.. code-block:: ruby

  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.

.. list-table::
   :header-rows: 1
   :widths: 40 80

   * - 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 :manual:`variables</reference/command/delete/#std-label-delete-let-syntax>`
       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 :manual:`delete command. </reference/command/delete>`

.. _write-concern:

Write Concern
=============

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 <https://mongodb.com/docs/manual/reference/write-concern/>`_.

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 :ref:`GridFS <gridfs>` page.

.. code-block:: ruby

  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:

.. code-block:: ruby

  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:

.. code-block:: ruby

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

.. code-block:: ruby

  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
<https://github.com/mongodb/specifications/blob/master/source/transactions/transactions.rst#writeconcern>`_.
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.

.. code-block:: ruby

  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:

.. code-block:: ruby

  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:

.. code-block:: ruby

  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:

.. code-block:: ruby

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

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

.. code-block:: ruby

    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.

.. _dots-dollars-in-field-names:

Field Names with Dots/Periods (.) and Dollar Signs ($)
======================================================

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 ($) <https://www.mongodb.com/docs/manual/core/dot-dollar-considerations/>`_
for more information on working with these types of fields.

A Note about the BSON Symbol type
=================================

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:

.. code-block:: ruby

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