From 0fdae59f844aaec458dfb054ed422285511c70ac Mon Sep 17 00:00:00 2001 From: Michael Morisi Date: Thu, 14 Nov 2024 15:46:04 -0500 Subject: [PATCH 1/3] DOCSP-45007: Distinct --- source/fundamentals/crud/read-operations.txt | 2 + .../crud/read-operations/distinct.txt | 252 ++++++++++++++++++ .../fundamentals/code-examples/Distinct.cs | 105 ++++++++ 3 files changed, 359 insertions(+) create mode 100644 source/fundamentals/crud/read-operations/distinct.txt create mode 100644 source/includes/fundamentals/code-examples/Distinct.cs diff --git a/source/fundamentals/crud/read-operations.txt b/source/fundamentals/crud/read-operations.txt index 60cf8425..82ee4e1e 100644 --- a/source/fundamentals/crud/read-operations.txt +++ b/source/fundamentals/crud/read-operations.txt @@ -12,8 +12,10 @@ Read Operations Retrieve Data Count Documents + /fundamentals/crud/read-operations/distinct Monitor Data Changes - :ref:`csharp-retrieve` - :ref:`csharp-count-documents` +- :ref:`csharp-distinct` - :ref:`csharp-change-streams` diff --git a/source/fundamentals/crud/read-operations/distinct.txt b/source/fundamentals/crud/read-operations/distinct.txt new file mode 100644 index 00000000..2a273f19 --- /dev/null +++ b/source/fundamentals/crud/read-operations/distinct.txt @@ -0,0 +1,252 @@ +.. _csharp-distinct: + +============================== +Retrieve Distinct Field Values +============================== + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: read, unique, code example + +Overview +-------- + +In this guide, you can learn how to use the {+driver-short+} to retrieve the +distinct values of a specified field across a collection. + +Within a collection, different documents might contain different values for a +single field. For example, one document in a ``restaurants`` collection has a +``borough`` value of ``"Manhattan"``, and another has a ``borough`` value of +``"Queens"``. By using the {+driver-short+}, you can retrieve all the unique values +that a field contains across multiple documents in a collection. + +Sample Data +~~~~~~~~~~~ + +The examples in this guide use the ``sample_restaurants.restaurants`` collection +from the :atlas:`Atlas sample datasets `. To learn how to create a +free MongoDB Atlas cluster and load the sample datasets, see the :ref:``. + +Retrieve Distinct Values +------------------------ + +To retrieve the distinct values for a specified field, call the ``IMongoCollection.Distinct()`` +or ``IMongoCollection.DistinctAsync()`` method and pass the name of the field +you want to find distinct values for. + +Retrieve Values Across a Collection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following example retrieves the distinct values of the ``borough`` field in +the ``restaurants`` collection. Select the :guilabel:`Asynchronous` or :guilabel:`Synchronous` +tab to see the corresponding code. + + +.. tabs:: + + .. tab:: Asynchronous + :tabid: distinct-async + + .. io-code-block:: + :copyable: + + .. input:: /includes/fundamentals/code-examples/Distinct.cs + :start-after: start-distinct-async + :end-before: end-distinct-async + :language: csharp + :dedent: + + .. output:: + :visible: false + + Bronx + Brooklyn + Manhattan + Missing + Queens + Staten Island + + .. tab:: Synchronous + :tabid: distinct-sync + + .. io-code-block:: + :copyable: + + .. input:: /includes/fundamentals/code-examples/Distinct.cs + :start-after: start-distinct + :end-before: end-distinct + :language: csharp + :dedent: + + .. output:: + :visible: false + + Bronx + Brooklyn + Manhattan + Missing + Queens + Staten Island + +The operation returns a cursor that you can iterate through to access each distinct ``borough`` +field value. Although several documents have the same value in the ``borough`` field, each value appears +in the results only once. + +Retrieve Values Across Specified Documents +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can provide a **query filter** to the ``Distinct()`` and ``DistinctAsync()`` methods +to find distinct field values within a subset of documents in a collection. A query filter +is an expression that specifies search criteria used to match documents in an operation. +For more information about creating a query filter, see the :ref:`csharp-specify-query` guide. + +The following example retrieves the distinct values of the ``borough`` field for +all documents that have a ``cuisine`` field value of ``"Italian"``. Select the +:guilabel:`Asynchronous` or :guilabel:`Synchronous` tab to see the corresponding code. + +.. tabs:: + + .. tab:: Asynchronous + :tabid: distinct-async + + .. io-code-block:: + :copyable: + + .. input:: /includes/fundamentals/code-examples/Distinct.cs + :start-after: start-distinct-with-query-async + :end-before: end-distinct-with-query-async + :language: csharp + :dedent: + + .. output:: + :visible: false + + Bronx + Brooklyn + Manhattan + Queens + Staten Island + + .. tab:: Synchronous + :tabid: distinct-sync + + .. io-code-block:: + :copyable: + + .. input:: /includes/fundamentals/code-examples/Distinct.cs + :start-after: start-distinct-with-query + :end-before: end-distinct-with-query + :language: csharp + :dedent: + + .. output:: + :visible: false + + Bronx + Brooklyn + Manhattan + Queens + Staten Island + +Modify Distinct Behavior +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can modify the behavior of the ``Distinct()`` and ``DistinctAsync()`` methods by +providing a ``DistinctOptions`` instance as an optional parameter. The following table +describes the properties you can set on a ``DistinctOptions`` instance: + +.. list-table:: + :widths: 30 70 + :header-rows: 1 + + * - Method + - Description + + * - ``Collation`` + - | Sets the collation to use for the operation. + | **Parameter Type**: ``Collation`` + + * - ``MaxTime`` + - | Sets the maximum amount of time that the operation can run. + | **Parameter Type**: ``TimeSpan`` + + * - ``Comment`` + - | Attaches a comment to the operation. + | **Parameter Type**: ``BsonValue`` or ``String`` + +The following example retrieves the distinct values of the ``name`` field for +all documents that have a ``borough`` field value of ``"Bronx"`` and a +``cuisine`` field value of ``"Pizza"``. Then, it adds a comment to the operation by +providing a ``DistinctOptions`` instance to the ``Distinct()`` method. + +Select the :guilabel:`Asynchronous` or :guilabel:`Synchronous` tab to see the +corresponding code. + +.. tabs:: + + .. tab:: Asynchronous + :tabid: distinct-async + + .. io-code-block:: + :copyable: + + .. input:: /includes/fundamentals/code-examples/Distinct.cs + :start-after: start-distinct-with-comment-async + :end-before: end-distinct-with-comment-async + :language: csharp + :dedent: + + .. output:: + :visible: false + + $1.25 Pizza + 18 East Gunhill Pizza + 2 Bros + Aenos Pizza + Alitalia Pizza Restaurant + Amici Pizza And Pasta + Angie'S Cafe Pizza + ... + + .. tab:: Synchronous + :tabid: distinct-sync + + .. io-code-block:: + :copyable: + + .. input:: /includes/fundamentals/code-examples/Distinct.cs + :start-after: start-distinct-with-comment + :end-before: end-distinct-with-comment + :language: csharp + :dedent: + + .. output:: + :visible: false + + $1.25 Pizza + 18 East Gunhill Pizza + 2 Bros + Aenos Pizza + Alitalia Pizza Restaurant + Amici Pizza And Pasta + Angie'S Cafe Pizza + ... + +API Documentation +----------------- + +To learn more about any of the methods or types discussed in this +guide, see the following API documentation: + +- `Distinct() <{+new-api-root+}/MongoDB.Driver/MongoDB.Driver.IMongoCollection-1.Distinct.html>`__ +- `DistinctAsync() <{+new-api-root+}/MongoDB.Driver/MongoDB.Driver.IMongoCollection-1.DistinctAsync.html>`__ +- `DistinctOptions <{+new-api-root+}/MongoDB.Driver/MongoDB.Driver.DistinctOptions.html>`__ \ No newline at end of file diff --git a/source/includes/fundamentals/code-examples/Distinct.cs b/source/includes/fundamentals/code-examples/Distinct.cs new file mode 100644 index 00000000..95883da4 --- /dev/null +++ b/source/includes/fundamentals/code-examples/Distinct.cs @@ -0,0 +1,105 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson.Serialization.Conventions; +using MongoDB.Driver; + +public class LimitSortSkip +{ + // Replace with your connection string + private const string MongoConnectionString = ">"; + + public static void Main(string[] args) + { + var mongoClient = new MongoClient(MongoConnectionString); + var database = mongoClient.GetDatabase("sample_restaurants"); + var collection = database.GetCollection("restaurants"); + + { + // start-distinct + var results = collection.Distinct("borough", new BsonDocument()).ToList(); + foreach (var result in results) + { + Console.WriteLine(result); + } + // end-distinct + } + + { + // start-distinct-with-query + var filter = Builders.Filter.Eq("cuisine", "Italian"); + var results = collection.Distinct("borough", filter).ToList(); + foreach (var result in results) + { + Console.WriteLine(result); + } + // end-distinct-with-query + } + + { + // start-distinct-with-comment + var cuisineFilter = Builders.Filter.Eq("cuisine", "Pizza"); + var boroughFilter = Builders.Filter.Eq("borough", "Bronx"); + var filter = Builders.Filter.And(cuisineFilter, boroughFilter); + + var options = new DistinctOptions { + Comment = "Find all Italian restaurants in the Bronx" + }; + + var results = collection.Distinct("name", filter).ToList(); + foreach (var result in results) + { + Console.WriteLine(result); + } + // end-distinct-with-comment + } + + } + + private static async void DistinctAsync (IMongoCollection collection) + { + // start-distinct-async + var results = await collection.DistinctAsync("borough", new BsonDocument()); + await results.ForEachAsync(result => Console.WriteLine(result)); + // end-distinct-async + } + + private static async void DistinctWithQueryAsync (IMongoCollection collection) + { + // start-distinct-with-query-async + var filter = Builders.Filter.Eq("cuisine", "Italian"); + var results = await collection.DistinctAsync("borough", filter); + await results.ForEachAsync(result => Console.WriteLine(result)); + // end-distinct-with-query-async + } + + private static async void DistinctWithCommentAsync (IMongoCollection collection) + { + // start-distinct-with-comment-async + var cuisineFilter = Builders.Filter.Eq("cuisine", "Pizza"); + var boroughFilter = Builders.Filter.Eq("borough", "Bronx"); + var filter = Builders.Filter.And(cuisineFilter, boroughFilter); + + var options = new DistinctOptions { + Comment = "Find all Italian restaurants in the Bronx" + }; + + var results = await collection.DistinctAsync("name", filter, options); + await results.ForEachAsync(result => Console.WriteLine(result)); + // end-distinct-with-comment-async + } +} + +// start-restaurant-class +public class Restaurant { + public ObjectId? Id { get; set; } + + [BsonElement("name")] + public string? Name { get; set; } + + [BsonElement("cuisine")] + public string? Cuisine { get; set; } + + [BsonElement("borough")] + public string? Borough { get; set; } +} +// end-restaurant-class \ No newline at end of file From ef3cf73cf23bdfeae0e651e1349abcb8b0789377 Mon Sep 17 00:00:00 2001 From: Michael Morisi Date: Thu, 14 Nov 2024 16:20:05 -0500 Subject: [PATCH 2/3] MW feedback --- .../fundamentals/crud/read-operations/distinct.txt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/source/fundamentals/crud/read-operations/distinct.txt b/source/fundamentals/crud/read-operations/distinct.txt index 2a273f19..9ab7b4b8 100644 --- a/source/fundamentals/crud/read-operations/distinct.txt +++ b/source/fundamentals/crud/read-operations/distinct.txt @@ -39,9 +39,9 @@ free MongoDB Atlas cluster and load the sample datasets, see the :ref:`.Distinct()`` -or ``IMongoCollection.DistinctAsync()`` method and pass the name of the field -you want to find distinct values for. +To retrieve the distinct values for a specified field, call the ``Distinct()`` or +``DistinctAsync()`` method of an ``IMongoCollection`` instance and pass the name +of the field you want to find distinct values for. Retrieve Values Across a Collection ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -50,7 +50,6 @@ The following example retrieves the distinct values of the ``borough`` field in the ``restaurants`` collection. Select the :guilabel:`Asynchronous` or :guilabel:`Synchronous` tab to see the corresponding code. - .. tabs:: .. tab:: Asynchronous @@ -173,15 +172,15 @@ describes the properties you can set on a ``DistinctOptions`` instance: * - ``Collation`` - | Sets the collation to use for the operation. - | **Parameter Type**: ``Collation`` + | **Data type**: `Collation <{+new-api-root+}/MongoDB.Driver/MongoDB.Driver.DistinctOptions.Collation.html>`__ * - ``MaxTime`` - | Sets the maximum amount of time that the operation can run. - | **Parameter Type**: ``TimeSpan`` + | **Data type**: ``TimeSpan`` * - ``Comment`` - | Attaches a comment to the operation. - | **Parameter Type**: ``BsonValue`` or ``String`` + | **Data type**: `BsonValue <{+new-api-root+}/MongoDB.Bson/MongoDB.Bson.BsonValue.html>`__ or ``string`` The following example retrieves the distinct values of the ``name`` field for all documents that have a ``borough`` field value of ``"Bronx"`` and a From 149b490554edd6b1f39ce62b36519df2044ed464 Mon Sep 17 00:00:00 2001 From: Michael Morisi Date: Fri, 15 Nov 2024 09:39:54 -0500 Subject: [PATCH 3/3] Address technical feedback --- .../crud/read-operations/distinct.txt | 9 ++++++ .../fundamentals/code-examples/Distinct.cs | 30 +++++++++---------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/source/fundamentals/crud/read-operations/distinct.txt b/source/fundamentals/crud/read-operations/distinct.txt index 9ab7b4b8..743609ae 100644 --- a/source/fundamentals/crud/read-operations/distinct.txt +++ b/source/fundamentals/crud/read-operations/distinct.txt @@ -36,6 +36,15 @@ The examples in this guide use the ``sample_restaurants.restaurants`` collection from the :atlas:`Atlas sample datasets `. To learn how to create a free MongoDB Atlas cluster and load the sample datasets, see the :ref:``. +The examples on this page uses the following ``Restaurant`` class to model +the documents in the collection: + +.. literalinclude:: /includes/fundamentals/code-examples/Distinct.cs + :start-after: start-model + :end-before: end-model + :language: csharp + + Retrieve Distinct Values ------------------------ diff --git a/source/includes/fundamentals/code-examples/Distinct.cs b/source/includes/fundamentals/code-examples/Distinct.cs index 95883da4..79d0e619 100644 --- a/source/includes/fundamentals/code-examples/Distinct.cs +++ b/source/includes/fundamentals/code-examples/Distinct.cs @@ -3,7 +3,7 @@ using MongoDB.Bson.Serialization.Conventions; using MongoDB.Driver; -public class LimitSortSkip +public class Distinct { // Replace with your connection string private const string MongoConnectionString = ">"; @@ -16,7 +16,7 @@ public static void Main(string[] args) { // start-distinct - var results = collection.Distinct("borough", new BsonDocument()).ToList(); + var results = collection.Distinct(r => r.Borough, Builders.Filters.Empty).ToList(); foreach (var result in results) { Console.WriteLine(result); @@ -26,8 +26,8 @@ public static void Main(string[] args) { // start-distinct-with-query - var filter = Builders.Filter.Eq("cuisine", "Italian"); - var results = collection.Distinct("borough", filter).ToList(); + var filter = Builders.Filter.Eq(r => r.Cuisine, "Italian"); + var results = collection.Distinct(r => r.Borough, filter).ToList(); foreach (var result in results) { Console.WriteLine(result); @@ -37,15 +37,15 @@ public static void Main(string[] args) { // start-distinct-with-comment - var cuisineFilter = Builders.Filter.Eq("cuisine", "Pizza"); - var boroughFilter = Builders.Filter.Eq("borough", "Bronx"); + var cuisineFilter = Builders.Filter.Eq(r => r.Cuisine, "Pizza"); + var boroughFilter = Builders.Filter.Eq(r => r.Borough, "Bronx"); var filter = Builders.Filter.And(cuisineFilter, boroughFilter); var options = new DistinctOptions { Comment = "Find all Italian restaurants in the Bronx" }; - var results = collection.Distinct("name", filter).ToList(); + var results = collection.Distinct(r => r.Name, filter).ToList(); foreach (var result in results) { Console.WriteLine(result); @@ -58,7 +58,7 @@ public static void Main(string[] args) private static async void DistinctAsync (IMongoCollection collection) { // start-distinct-async - var results = await collection.DistinctAsync("borough", new BsonDocument()); + var results = await collection.DistinctAsync(r => r.Borough, Builders.Filters.Empty); await results.ForEachAsync(result => Console.WriteLine(result)); // end-distinct-async } @@ -66,8 +66,8 @@ private static async void DistinctAsync (IMongoCollection collection private static async void DistinctWithQueryAsync (IMongoCollection collection) { // start-distinct-with-query-async - var filter = Builders.Filter.Eq("cuisine", "Italian"); - var results = await collection.DistinctAsync("borough", filter); + var filter = Builders.Filter.Eq(r => r.Cuisine, "Italian"); + var results = await collection.DistinctAsync(r => r.Borough, filter); await results.ForEachAsync(result => Console.WriteLine(result)); // end-distinct-with-query-async } @@ -75,21 +75,21 @@ private static async void DistinctWithQueryAsync (IMongoCollection c private static async void DistinctWithCommentAsync (IMongoCollection collection) { // start-distinct-with-comment-async - var cuisineFilter = Builders.Filter.Eq("cuisine", "Pizza"); - var boroughFilter = Builders.Filter.Eq("borough", "Bronx"); + var cuisineFilter = Builders.Filter.Eq(r => r.Cuisine, "Pizza"); + var boroughFilter = Builders.Filter.Eq(r => r.Borough, "Bronx"); var filter = Builders.Filter.And(cuisineFilter, boroughFilter); var options = new DistinctOptions { Comment = "Find all Italian restaurants in the Bronx" }; - var results = await collection.DistinctAsync("name", filter, options); + var results = await collection.DistinctAsync(r => r.Name, filter, options); await results.ForEachAsync(result => Console.WriteLine(result)); // end-distinct-with-comment-async } } -// start-restaurant-class +// start-model public class Restaurant { public ObjectId? Id { get; set; } @@ -102,4 +102,4 @@ public class Restaurant { [BsonElement("borough")] public string? Borough { get; set; } } -// end-restaurant-class \ No newline at end of file +// end-model \ No newline at end of file