How to create index based on nested array element in C# Mongodb strongly typed driver

Refresh

March 2019

Views

0 time

1

This question is quite on the same principles as this one but I'd like to create an index using strongly typed approach on an object property when this object is nested in an array of the collection.

I can use:

new CreateIndexModel<T>( Builders<T>.IndexKeys.Ascending( a ) )

where a is an Expression which accesses to a direct property.

But I've found nothing similar to:

Builders<Library>.Filter.ElemMatch(x => x.Author.Books, b => b.IsVerified == false));

so that I can define as index some field of an object nested in an array which is a member of the collection.

Is that possible to do it and how?

1 answers

1

Consider the following data model:

public class Course
{
   public string Name { get; set; }
   public string Teacher { get; set; }
}

public class Student
{
  public string Name { get; set; }
  public int Age { get; set; }
  public ReadOnlyCollection<Course> Courses { get; set; }
}

You can create an ascending multikey index on the field Courses in the following manner:

using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ConsoleApp1
{
  public static class Program
  {
    private static MongoClient Client;
    private static IMongoDatabase Database;
    private static IMongoCollection<Student> Collection;

    public static async Task Main(string[] args)
    {
      Client = new MongoClient();
      Database = Client.GetDatabase("test-index");
      Collection = Database.GetCollection<Student>("students");

      var courses1 = new List<Course>()
      {
        new Course { Name = "Math", Teacher = "Bob" }
      }.AsReadOnly();

      var courses2 = new List<Course>()
      {
        new Course { Name = "Computer Science", Teacher = "Alice" }
      }.AsReadOnly();

      var mark = new Student
      {
        Name = "Mark",
        Courses = courses1,
        Age = 20
      };

      var lucas = new Student
      {
        Name = "Lucas",
        Courses = courses2,
        Age = 22
      };

      await Collection.InsertManyAsync(new[] { mark, lucas }).ConfigureAwait(false);


      var model = new CreateIndexModel<Student>(
        Builders<Student>.IndexKeys.Ascending(s => s.Courses));

      await Collection.Indexes.CreateOneAsync(model).ConfigureAwait(false);

      Console.WriteLine("All done !");
    }
  }
}

This query is served by the index you created: db.students.find({Courses: {"Name": "Math", "Teacher": "Bob"}})

If you don't want to create an index on the entire array Courses, but instead you want an index on the field Name of the nested object (the Course object), this is the way to go:

using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ConsoleApp1
{
  public static class Program
  {
    private static MongoClient Client;
    private static IMongoDatabase Database;
    private static IMongoCollection<Student> Collection;

    public static async Task Main(string[] args)
    {
      Client = new MongoClient();
      Database = Client.GetDatabase("test-index");
      Collection = Database.GetCollection<Student>("students");

      var courses1 = new List<Course>()
      {
        new Course { Name = "Math", Teacher = "Bob" }
      }.AsReadOnly();

      var courses2 = new List<Course>()
      {
        new Course { Name = "Computer Science", Teacher = "Alice" }
      }.AsReadOnly();

      var mark = new Student
      {
        Name = "Mark",
        Courses = courses1,
        Age = 20
      };

      var lucas = new Student
      {
        Name = "Lucas",
        Courses = courses2,
        Age = 22
      };

      await Collection.InsertManyAsync(new[] { mark, lucas }).ConfigureAwait(false);


      var model = new CreateIndexModel<Student>(
        Builders<Student>.IndexKeys.Ascending("Courses.Name"));

      await Collection.Indexes.CreateOneAsync(model).ConfigureAwait(false);

      Console.WriteLine("All done !");
    }
  }
}

This query is served by the index you created: db.students.explain("executionStats").find({"Courses.Name": "Math"})

A possible way to avoid the usage of magic strings in my second example is exploiting the power of the nameof C# operator: $"{nameof(Student.Courses)}.{nameof(Course.Name)}"