Navigation

MongoDB Data Access

The following actions enable access to a linked MongoDB Atlas cluster from a .NET application using the Realm .NET SDK.

Note

Each operation described on this page uses a query filter to match certain documents in the collection upon which the operation executes. When a filter matches multiple documents in a collection, they are returned in an indeterminate order unless you specify a sorting parameter. This means that if you do not specify a sort for the findOne(), updateOne(), or deleteOne() functions, your operation could match any document that matches the query filter. For more information on sorting, see cursor.sort().

Setup

To work directly with the data in your MongoDB Atlas cluster, you first instantiate a MongoClient object, passing in the name of the atlas service in your Realm app. You then instantiate a MongoClient.Database and a MongoClient.Collection for each collection you want to work with. The following code uses the default “mongodb-atlas” Atlas service name and creates a MongoClient.Collection for the “plants” collection in the “inventory” database:

mongoClient = user.GetMongoClient("mongodb-atlas");
dbPlantInventory = mongoClient.GetDatabase("inventory");
plantsCollection = dbPlantInventory.GetCollection<Plant>("plants");

Example Data

The following examples operate on a MongoDB collection that describes inventory in a chain of plant stores. Consider the following collection of documents describing various plants for sale in store:

{ _id: ObjectId("5f87976b7b800b285345a8b4"), name: "venus flytrap", sunlight: "full", color: "white", type: "perennial", _partition: "Store 42" },
{ _id: ObjectId("5f87976b7b800b285345a8b5"), name: "sweet basil", sunlight: "partial", color: "green", type: "annual", _partition: "Store 42" },
{ _id: ObjectId("5f87976b7b800b285345a8b6"), name: "thai basil", sunlight: "partial", color: "green", type: "perennial", _partition: "Store 42" },
{ _id: ObjectId("5f87976b7b800b285345a8b7"), name: "helianthus", sunlight: "full", color: "yellow", type: "annual", _partition: "Store 42" },
{ _id: ObjectId("5f87976b7b800b285345a8b8"), name: "petunia", sunlight: "full", color: "purple", type: "annual", _partition: "Store 47" }

Mapping Classes

When working with objects in MongoDB, you should create .NET classes (POCOs) that correspond to the BSON objects. This allows you to serialize and deserialize the objects directly, rather than working with generic BsonDocument objects. In all of the examples on this page, we are using the following Plant mapping class for this purpose:

public class Plant
{
    [PrimaryKey]
    [BsonElement("_id")]
    public ObjectId Id { get; set; }

    [BsonElement("name")]
    public string Name { get; set; }

    [BsonElement("sunlight")]
    [BsonRepresentation(BsonType.String)]
    public Sunlight Sunlight { get; set; }

    [BsonElement("color")]
    [BsonRepresentation(BsonType.String)]
    public PlantColor Color { get; set; }

    [BsonElement("type")]
    [BsonRepresentation(BsonType.String)]
    public PlantType Type { get; set; }

    [BsonElement("_partition")]
    public string Partition { get; set; }

    public Plant()
    {
        this.Id = ObjectId.GenerateNewId();
    }
}
public enum Sunlight
{
    Full,
    Partial
}
public enum PlantColor
{
    White,
    Green,
    Yellow,
    Purple
}
public enum PlantType
{
    Perennial,
    Annual
}

Note

For more information on using mapping classes, see Mapping Classes in the MongoDB .NET Driver documentation.

Create Documents

To create a document in the MongoDB datastore, you instantiate the mapping class, and pass the new object to InsertOneAsync(). You can also create multiple documents and insert them in a single call by using InsertManyAsync().

Insert a Single Document

You can insert a single document using InsertOneAsync().

The following snippet inserts a single document describing a “Venus Flytrap” plant into our “plants” collection:

var plant = new Plant
{
    Name = "Venus Flytrap",
    Sunlight = Sunlight.Full,
    Color = PlantColor.White,
    Type = PlantType.Perennial,
    Partition = "Store 42"
};

var insertResult = await plantsCollection.InsertOneAsync(plant);
var newId = insertResult.InsertedId;

Insert Multiple Documents

You can insert multiple documents at the same time by using InsertManyAsync().

The following snippet inserts four Plant objects into the “plants” collection by instantiating the objects, adding them to a List<Plant>, and passing that list to InsertManyAsync():

var sweetBasil = new Plant
{
    Name = "Sweet Basil",
    Sunlight = Sunlight.Partial,
    Color = PlantColor.Green,
    Type = PlantType.Annual,
    Partition = "Store 42"
};
var thaiBasil = new Plant
{
    Name = "Thai Basil",
    Sunlight = Sunlight.Partial,
    Color = PlantColor.Green,
    Type = PlantType.Perennial,
    Partition = "Store 42"
};
var helianthus = new Plant
{
    Name = "Helianthus",
    Sunlight = Sunlight.Full,
    Color = PlantColor.Yellow,
    Type = PlantType.Annual,
    Partition = "Store 42"
};
var petunia = new Plant
{
    Name = "Petunia",
    Sunlight = Sunlight.Full,
    Color = PlantColor.Purple,
    Type = PlantType.Annual,
    Partition = "Store 47"
};

var listofPlants = new List<Plant>
{
    sweetBasil,
    thaiBasil,
    helianthus,
    petunia
};

var insertResult = await plantsCollection.InsertManyAsync(listofPlants);
var newIds = insertResult.InsertedIds;

Read Documents

To retrieve documents from the datastore, you create a BsonDocument filter that defines the properties you want to search on, and then pass that filter to either FindOneAsync(). or FindAsync(). You can also get the count of all documents that match the filter by calling CountAsync().

Find a Single Document

The following example shows how to find a plant where the “name” property is “petunia”:

var petunia = await plantsCollection.FindOneAsync(
    new BsonDocument("name", "Petunia"), null);

Find Multiple Documents

The following example shows how to find all plants where the “type” property is “perennial”:

var allPerennials = await plantsCollection.FindAsync(
    new BsonDocument("type", PlantType.Perennial.ToString()), null);

Important

We are using the third parameter of FindAsync(), which specifies the sort order. If you are querying for more than one document, you should include the sort order to ensure consistent results.

Count Documents in the Collection

The following example returns a count of all plants in the collection:

var allPlants = await plantsCollection.CountAsync();

Update Documents

To update an existing document in the MongoDB datastore, you create a BsonDocument filter that defines the properties you want to search on, and then create a second BsonDocument that defines the properties you want to change. If you are updating only one document, you pass both objects to UpdateOneAsync(). If you want to bulk update multiple documents, you call UpdateManyAsync().

Update a Single Document

The following code finds the plant whose “name” property is “petunia” and changes its “sunlight” property to “partial”:

var updateResult = await plantsCollection.UpdateOneAsync(
    new BsonDocument("sunlight", Sunlight.Partial.ToString()),
    new BsonDocument("name", "Petunia"));

Update Multiple Documents

The following code finds all plants with a “_partition” value of “store 47” and changes them all to “area 51”:

var updateResult = await plantsCollection.UpdateManyAsync(
    new BsonDocument("_partition", "Store 47"),
    new BsonDocument("$set", new BsonDocument("_partition", "Area 51")));

Upsert Documents

Both UpdateOneAsync() and UpdateManyAsync() have an optional Boolean property that specifies whether the update should be an upsert (that is, if the document doesn’t exist, it should be created). By default, no upsert is performed.

The following example looks for a plant whose name property is “Pothos”, type property is “perennial”, and sunlight property is “full”. If a plant matches these criteria, the method updates the plant’s _partition value to “Store 42”. If no plant exists in the collection with that name, the method will create a new plant with all of the defined properties, including the update.

var filter = new BsonDocument("name", "Pothos")
    .Add("type", PlantType.Perennial)
    .Add("sunlight", Sunlight.Full);

var updateResult = await plantsCollection.UpdateOneAsync(
    filter,
    new BsonDocument("$set", new BsonDocument("_partition", "Store 42")),
    upsert: true);

/* The upsert will create the following object:

{
   "name": "pothos",
   "sunlight": "full",
   "type": "perennial",
   "_partition": "Store 42"
}
*/

Delete Documents

The process for deleting documents is much the same as creating (or updating) documents: you create a BsonDocument that defines the properties you want to match on, and then call either DeleteOneAsync(). or DeleteManyAsync().

Delete a Single Document

The following example deletes the first document it finds with a “name” property value of “Thai Basil”:

var filter = new BsonDocument("name", "Thai Basil");
var deleteResult = await plantsCollection.DeleteOneAsync(filter);

Delete Multiple Documents

The following example deletes all documents that have a “type” property value of “annual”:

var filter = new BsonDocument("type", PlantType.Annual);
var deleteResult = await plantsCollection.DeleteManyAsync(filter);

Aggregate Documents

Aggregation enables complex querying of your data in MongoDB. Aggregation operations run all documents in a collection through a series of data aggregation stages called an aggregation pipeline that allow you to filter and shape documents as well as collect summary data about groups of related documents.

Aggregation operations accept an array of aggregation stages as input, and return a Task that resolves to a collection of documents processed by the pipeline.

Note

MongoDB Compass provides a utility for building aggregation pipelines and exporting them to C# and other languages. For more information, see Aggregation Pipeline Builder.

Group Documents in a Collection

The .NET SDK supports aggregation on a collection with the AggregateAsync() method and its generic overload.

The following example groups all documents in the plants collection by their type value, aggregates a count of the number of each type, and then sorts them in ascending order:

var groupStage =
    new BsonDocument("$group",
        new BsonDocument
        {
            { "_id", "$type" },
            { "count", new BsonDocument("$sum", 1) }
        });

var sortStage = new BsonDocument("$sort",
    new BsonDocument("_id", 1));

var aggResult = await plantsCollection.AggregateAsync(groupStage, sortStage);
foreach (var item in aggResult)
{
    var id = item["_id"];
    var count = item["count"];
    Console.WriteLine($"Plant type: {id}; count: {count}");
}

The example above builds the pipeline with a series of nested BsonDocuments, which can get complicated to write and debug. If you are already familiar with the Mongo Query Language, you can pass queries as a string the BsonDocument_Parse() method. The following example performs the same aggregation as the preceding example:

var groupStep = BsonDocument.Parse(@"
  {
    $group: {
      _id: '$type', 
      count: {
        $sum: 1
      }
    }
  }
");

var sortStep = BsonDocument.Parse("{$sort: { _id: 1}}");

aggResult = await plantsCollection.AggregateAsync(groupStep, sortStep);
foreach (var item in aggResult)
{
    var id = item["_id"];
    var count = item["count"];
    Console.WriteLine($"Id: {id}, Count: {count}");
}

Filter Documents

You can use the $match stage to filter documents according to a standard query filter.

The following example shows how to filter documents when using Aggregation. Since we know that this aggregation pipeline returns a collection of Plant objects, we use the generic override of the AggregateAsync() method:

var matchStage = new BsonDocument("$match",
        new BsonDocument("type",
            new BsonDocument("$eq",
                PlantType.Perennial)));

// Alternate approach using BsonDocument.Parse(...)
matchStage = BsonDocument.Parse(@"{
  $match: {
    type: { $eq: '" + PlantType.Perennial + @"' }
  }}");

var sortStage = BsonDocument.Parse("{$sort: { _id: 1}}");

var aggResult = await plantsCollection.AggregateAsync<Plant>(matchStage, sortStage);
foreach (var plant in aggResult)
{
    Console.WriteLine($"Plant Name: {plant.Name}, Color: {plant.Color}");
}

Project Data

You can use the $project stage to include or omit specific fields from documents or to calculate new fields using aggregation operators. Projections work in two ways:

  • Explicitly include fields with a value of 1. This has the side-effect of implicitly excluding all unspecified fields.
  • Implicitly exclude fields with a value of 0. This has the side-effect of implicitly including all unspecified fields.

These two methods of projection are mutually exclusive: if you explicitly include fields, you cannot explicitly exclude fields, and vice versa.

Note

The _id field is a special case: it is always included in every query unless explicitly specified otherwise. For this reason, you can exclude the _id field with a 0 value while simultaneously including other fields, like _partition, with a 1. Only the special case of exclusion of the _id field allows both exclusion and inclusion in one $project stage.

The following example shows how to use project when using Aggregation. In this example, we are:

  1. Excluding the “Id” property,
  2. Including the “Partition”, “Type”, and “Name” properties,
  3. Creating a new property called “storeNumber”, which is built by splitting the _partition value on the whitespace and returning only the second part.
var projectStage = new BsonDocument("$project",
    new BsonDocument
    {
        { "_id", 0 },
        { "_partition", 1 },
        { "type", 1 },
        { "name", 1 },
        { "storeNumber",
            new BsonDocument("$arrayElemAt",
                new BsonArray {
                    new BsonDocument("$split",
                    new BsonArray
                    {
                        "$_partition",
                        " "
                    }), 1 }) }
    });

var sortStage = BsonDocument.Parse("{$sort: { storeNumber: 1}}");

var aggResult = await plantsCollection.AggregateAsync(projectStage, sortStage);
foreach (var item in aggResult)
{
    Console.WriteLine($"{item["name"]} is in store #{item["storeNumber"]}.");
}

The following shows how you can also build the projectStage by using the BsonDocument.Parse() method:

projectStage = BsonDocument.Parse(@"
    {
      _id:0,
      _partition: 1,
      type: 1,
      name: 1,
      storeNumber: {
        $arrayElemAt: [
          { $split:[
            '$_partition', ' '
            ]
          }, 1 ]
      }
    }");
←   Sync Data Call a Function  →