Skip to content

DOCSP-45187: Update #98

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion snooty.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ name = "ruby-driver"
title = "Ruby MongoDB Driver"
toc_landing_pages = [
"/get-started",
"/connect"
"/connect",
"/write"
]

intersphinx = ["https://www.mongodb.com/docs/manual/objects.inv"]
Expand Down
47 changes: 47 additions & 0 deletions source/includes/write/update.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'mongo'
end
Comment on lines +1 to +5
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: have we gotten any guidance from the ruby team about bundler vs. just require 'mongo'?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure – I'm going off the Quick Start (which shows the use of bundler), which I assumed had been tech reviewed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using bundler definitely describes a more complete picture, but for example scripts I think it's sufficient to just require 'mongo', personally.


uri = "<connection string URI>"

Mongo::Client.new(uri) do |client|
# start-db-coll
database = client.use('sample_restaurants')
collection = database[:restaurants]
# end-db-coll

# Updates a single document
# start-update-one
filter = { name: 'Happy Garden' }

update = { '$set' => { name: 'Mountain House' } }

single_result = collection.update_one(filter, update)

puts "#{single_result.modified_count} document(s) updated."
# end-update-one

# Updates multiple documents
# start-update-many
filter = { name: 'Starbucks' }

update = { '$rename' => { address: 'location' } }

many_result = collection.update_many(filter, update)

puts "#{many_result.modified_count} document(s) updated."
# end-update-many

# Performs an update operation with the upsert option enabled
# start-update-options
filter = { 'name' => 'Sunrise Pizzeria' }

update = { '$set' => { borough: 'Queens', cuisine: 'Italian' } }

upsert_result = collection.update_one(filter, update, upsert: true)

puts "#{upsert_result.modified_count} document(s) updated."
# end-update-options
end
1 change: 1 addition & 0 deletions source/index.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

Get Started </get-started>
Connect </connect>
Write Data </write>
Read Data </read>
View the Source <https://github.com/mongodb/mongo-ruby-driver>
API Documentation <{+api-root+}>
Expand Down
33 changes: 33 additions & 0 deletions source/write.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.. _ruby-write:

=====================
Write Data to MongoDB
=====================

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

.. facet::
:name: genre
:values: reference

.. meta::
:description: Learn how to use the Ruby driver to write data to MongoDB.
:keywords: usage examples, save, crud, create, code example

.. toctree::
:titlesonly:
:maxdepth: 1

/write/insert
/write/replace
/write/update
/write/delete
/write/bulk-write
/write/transactions
/write/gridfs

.. TODO
247 changes: 247 additions & 0 deletions source/write/update.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
.. _ruby-write-update:

================
Update Documents
================

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

.. facet::
:name: genre
:values: reference

.. meta::
:keywords: modify, change, operator, code example

Overview
--------

In this guide, you can learn how to use the {+driver-short+} to update
documents in a MongoDB collection by using the ``update_one`` and
``update_many`` methods.
Comment on lines +24 to +25
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: most of our docs include parentheses after a method name; is there something different about ruby?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I asked Nora about this – it seems like current Ruby documentation avoids using parentheses. The language in general seems more relaxed about using parentheses in code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parentheses are largely optional in Ruby, as long as the meaning is unambiguous. By convention, parentheses are almost always omitted when a method call has no parameters (e.g file.close). Otherwise, Rubyists often omit parentheses when there is only a single literal argument (e.g. puts "hello") or with keyword arguments (e.g. foo a: 1, b: "two"). It becomes a stylistic concern at that point.

Asking MongoGPT about it, the all-knowing AI suggests the following:

In Ruby, omitting parentheses for method arguments is common, especially in certain contexts where it enhances readability and maintains a more natural syntax. Here are some conventions and guidelines that developers often follow when deciding whether to omit parentheses:

  1. DSLs (Domain-Specific Languages): When designing or using DSLs, parentheses are often omitted to create a syntax that reads more like a natural language. This is common in frameworks like Rails, particularly in routing and validation declarations.

  2. Control Structures: For built-in Ruby control structures such as if, while, until, and case, parentheses are typically omitted around their conditions to improve readability and maintain a cleaner syntax.

  3. Short Methods: When calling methods that take no arguments or only a single simple argument, it is common to omit parentheses to streamline the code. For example:

    puts "Hello, world"
  4. Internal DSLs or Configure Blocks: In configurations or setup blocks (e.g., Rake tasks, ActiveRecord scopes), omitting parentheses can make the code easier to follow and more domain-centric.

  5. Chaining Methods: When method calls are chained, parentheses are usually omitted for the first method call if it takes a single argument, leading to a more fluid syntax.

  6. Readability: Parentheses can be omitted when they enhance readability by removing unnecessary clutter, especially in expressions that are clearly understood without them.

However, there are also scenarios where including parentheses is preferred:

  • Ambiguity: If omitting parentheses leads to ambiguity or confusion about the order of operations, it is better to include them.
  • Multi-Argument Methods: When a method takes multiple arguments, especially with complex expressions, using parentheses can make the argument list clearer.
  • Precedence Issues: In expressions with complex operator precedence, including parentheses can clarify the intended order of operations.
  • Method Definitions: When defining methods, it's common to use parentheses for the argument list to clearly specify the parameters, even though it is optional.

Ultimately, the decision to omit parentheses often comes down to personal or team styling preferences, but developers should aim for consistency and clarity in their code.


Sample Data
~~~~~~~~~~~

The examples in this guide use the ``restaurants`` collection in the ``sample_restaurants``
database from the :atlas:`Atlas sample datasets </sample-data>`. To access this collection
from your {+language+} application, create a ``Mongo::Client`` object that connects to an Atlas cluster
and assign the following values to your ``database`` and ``collection`` variables:

.. literalinclude:: /includes/write/update.rb
:language: ruby
:dedent:
:start-after: start-db-coll
:end-before: end-db-coll

To learn how to create a free MongoDB Atlas cluster and load the sample datasets, see the
:atlas:`Get Started with Atlas </getting-started>` guide.

Update Operations
-----------------

You can update documents in MongoDB by using the following methods:

- ``update_one``: Updates *the first document* that matches the search criteria
- ``update_many``: Updates *all documents* that match the search criteria

Each update method requires the following parameters:

- **Query filter**, which matches the documents you want to update. To learn
more about query filters, see the :ref:`ruby-specify-query`
guide.

- **Update document**, which specifies the update operator and the fields and values to be
updated. The update operator specifies the type of update to perform. To view a list of
update operators and learn about their usages, see the
:manual:`Field Update Operators guide page</reference/operator/update-field/>` in the
{+mdb-server+} manual.

Update One Document Example
~~~~~~~~~~~~~~~~~~~~~~~~~~~

The following example uses the ``update_one`` method to find the first document
where the value of the ``name`` field is ``"Happy Garden"``. It then uses the ``$set``
operator to update the ``name`` field value to ``"Mountain House"``.

.. io-code-block::
:copyable: true

.. input:: /includes/write/update.rb
:start-after: start-update-one
:end-before: end-update-one
:language: ruby
:dedent:

.. output::
:language: console
:visible: false

1 document(s) updated

Update Many Documents Example
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The following example uses the ``update_many`` method to update all documents
where the value of the ``name`` field is ``"Starbucks"``. The update document uses the
``$rename`` operator to change the name of the ``address`` field to ``location``.

.. io-code-block::
:copyable: true

.. input:: /includes/write/update.rb
:start-after: start-update-many
:end-before: end-update-many
:language: ruby
:dedent:

.. output::
:language: console
:visible: false

11 document(s) updated

Customize the Update Operation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The ``update_one`` and ``update_many`` methods accept options to configure the update
operation. You can pass these options individually as parameters, or you can create a
``Hash`` object that contains the options and pass the object as a parameter.
If you don't specify any options, the driver performs the update
operation with default settings.

The following table describes the options that you can use to
configure the update operation:

.. list-table::
:widths: 30 70
:header-rows: 1

* - Option
- Description

* - ``upsert``
- | Whether the update operation performs an upsert operation if no
documents match the query filter. For more information, see the :manual:`upsert
statement </reference/command/update/#std-label-update-command-upsert>`
in the {+mdb-server+} manual.
| Default: ``false``

* - ``bypass_document_validation``
- | Whether the update operation bypasses document validation. This lets you
update documents that don't meet the schema validation requirements, if any
exist. For more information about schema validation, see :manual:`Schema
Validation </core/schema-validation/#schema-validation>` in the MongoDB
Server manual.
| Default: ``false``

* - ``collation``
- | Language collation to use when sorting
results. For more information, see :manual:`Collation </reference/collation/#std-label-collation>`
in the {+mdb-server+} manual.

* - ``array_filters``
- | List of filters that you specify to select which
array elements the update applies to.

* - ``hint``
- | Index to use when matching documents.
For more information, see the :manual:`hint statement </reference/command/update/#std-label-update-command-hint>`
in the {+mdb-server+} manual.

* - ``let``
- | Map of parameter names and values to set top-level
variables for the operation. Values must be constant or closed
expressions that don't reference document fields. For more information,
see the :manual:`let statement
</reference/command/update/#std-label-update-let-syntax>` in the
{+mdb-server+} manual.

Modify Update Example
`````````````````````

The example uses the ``$equal`` operator to match documents
where the value of the ``name`` field is ``"Sunrise Pizzeria"``. It then uses the ``$set``
operator to set the ``borough`` field value in the first matching document to
``"Queens"`` and the ``cuisine`` field value to ``"Italian"``.

Because the ``upsert`` option is set to ``true``, if the query filter
doesn't match any existing documents, the driver inserts a new document that
contains the fields and values in the filter and update documents.

.. io-code-block::
:copyable: true

.. input:: /includes/write/update.rb
:start-after: start-update-options
:end-before: end-update-options
:language: ruby
:dedent:

.. output::
:language: console
:visible: false

1 document(s) updated

Return Value
~~~~~~~~~~~~

The ``update_one`` and ``update_many`` methods each return a ``Result``
object. You can access the following methods from a ``Result`` instance:

.. list-table::
:widths: 30 70
:header-rows: 1

* - Method
- Description

* - ``matched_count``
- | Number of documents that matched the query filter, regardless of
how many updates were performed.

* - ``modified_count``
- | Number of documents modified by the update operation. If an updated
document is identical to the original, it is not included in this
count.

* - ``acknowledged?``
- | Returns ``true`` if the server acknowledged the result.

* - ``upserted_count``
- | Returns the number of documents that were upserted in the database, if the driver
performed an upsert.

* - ``upserted_ids``
- | Returns the ``_id`` value of the document that was upserted
in the database, if the driver performed an upsert.

.. tip::

Check the value of the ``acknowledged?`` method before you try
to call any other ``Result`` methods. If the ``acknowledged?``
method returns ``false``, the driver throws an ``InvalidOperation``
exception if you try to call any other method on the ``Result`` object.
The driver cannot determine these values if the server does not acknowledge the write
operation.

Additional Information
----------------------

To view runnable code examples that demonstrate how to update documents by
using the {+driver-short+}, see :ref:`ruby-write`.

API Documentation
~~~~~~~~~~~~~~~~~

To learn more about any of the methods or types discussed in this
guide, see the following API documentation:

- `update_one <{+api-root+}/Mongo/Collection.html#update_one-instance_method>`_
- `update_many <{+api-root+}/Mongo/Collection.html#update_many-instance_method>`_
- `Result <{+api-root+}/Mongo/Operation/Update/Result.html>`_
Loading