Skip to content

DOCSP-45007: Distinct #292

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 3 commits into from
Nov 19, 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
2 changes: 2 additions & 0 deletions source/fundamentals/crud/read-operations.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ Read Operations

Retrieve Data </fundamentals/crud/read-operations/retrieve>
Count Documents </fundamentals/crud/read-operations/count>
/fundamentals/crud/read-operations/distinct
Monitor Data Changes </fundamentals/crud/read-operations/change-streams>

- :ref:`csharp-retrieve`
- :ref:`csharp-count-documents`
- :ref:`csharp-distinct`
- :ref:`csharp-change-streams`
260 changes: 260 additions & 0 deletions source/fundamentals/crud/read-operations/distinct.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
.. _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 </sample-data>`. To learn how to create a
free MongoDB Atlas cluster and load the sample datasets, see the :ref:`<csharp-quickstart>`.

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

To retrieve the distinct values for a specified field, call the ``Distinct()`` or
``DistinctAsync()`` method of an ``IMongoCollection<TDocument>`` instance 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.
| **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.
| **Data type**: ``TimeSpan``

* - ``Comment``
- | Attaches a comment to the operation.
| **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
``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>`__
105 changes: 105 additions & 0 deletions source/includes/fundamentals/code-examples/Distinct.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson.Serialization.Conventions;
using MongoDB.Driver;

public class Distinct
{
// Replace with your connection string
private const string MongoConnectionString = "<connection string URI>>";

public static void Main(string[] args)
{
var mongoClient = new MongoClient(MongoConnectionString);
var database = mongoClient.GetDatabase("sample_restaurants");
var collection = database.GetCollection<Restaurant>("restaurants");

{
// start-distinct
var results = collection.Distinct<string>(r => r.Borough, Builders<Restaurant>.Filters.Empty).ToList();
foreach (var result in results)
{
Console.WriteLine(result);
}
// end-distinct
}

{
// start-distinct-with-query
var filter = Builders<Restaurant>.Filter.Eq(r => r.Cuisine, "Italian");
var results = collection.Distinct<string>(r => r.Borough, filter).ToList();
foreach (var result in results)
{
Console.WriteLine(result);
}
// end-distinct-with-query
}

{
// start-distinct-with-comment
var cuisineFilter = Builders<Restaurant>.Filter.Eq(r => r.Cuisine, "Pizza");
var boroughFilter = Builders<Restaurant>.Filter.Eq(r => r.Borough, "Bronx");
var filter = Builders<Restaurant>.Filter.And(cuisineFilter, boroughFilter);

var options = new DistinctOptions {
Comment = "Find all Italian restaurants in the Bronx"
};

var results = collection.Distinct<string>(r => r.Name, filter).ToList();
foreach (var result in results)
{
Console.WriteLine(result);
}
// end-distinct-with-comment
}

}

private static async void DistinctAsync (IMongoCollection<Restaurant> collection)
{
// start-distinct-async
var results = await collection.DistinctAsync<string>(r => r.Borough, Builders<Restaurant>.Filters.Empty);
await results.ForEachAsync(result => Console.WriteLine(result));
// end-distinct-async
}

private static async void DistinctWithQueryAsync (IMongoCollection<Restaurant> collection)
{
// start-distinct-with-query-async
var filter = Builders<Restaurant>.Filter.Eq(r => r.Cuisine, "Italian");
var results = await collection.DistinctAsync<string>(r => r.Borough, filter);
await results.ForEachAsync(result => Console.WriteLine(result));
// end-distinct-with-query-async
}

private static async void DistinctWithCommentAsync (IMongoCollection<Restaurant> collection)
{
// start-distinct-with-comment-async
var cuisineFilter = Builders<Restaurant>.Filter.Eq(r => r.Cuisine, "Pizza");
var boroughFilter = Builders<Restaurant>.Filter.Eq(r => r.Borough, "Bronx");
var filter = Builders<Restaurant>.Filter.And(cuisineFilter, boroughFilter);

var options = new DistinctOptions {
Comment = "Find all Italian restaurants in the Bronx"
};

var results = await collection.DistinctAsync<string>(r => r.Name, filter, options);
await results.ForEachAsync(result => Console.WriteLine(result));
// end-distinct-with-comment-async
}
}

// start-model
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-model
Loading