如何在C# MongoDB强类型驱动程序中基于嵌套的数组元素创建索引

4
这个问题与这个问题的原理非常相似,但是我想在集合的数组中嵌套对象属性时使用强类型方法来创建索引。
我可以使用:
new CreateIndexModel<T>( Builders<T>.IndexKeys.Ascending( a ) )

其中a是访问直接属性的表达式。

但我没有找到类似于:

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

我希望能够定义一个对象数组中的某个字段作为集合索引。

请问是否可以实现,如何实现?

2个回答

4
请考虑以下数据模型:
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; }
}

你可以按照以下方式在字段“Courses”上创建升序多键索引:
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 !");
    }
  }
}

这个查询是通过你创建的索引来提供服务的: db.students.find({Courses: {"Name": "Math", "Teacher": "Bob"}}) 如果你不想在整个Courses数组上创建索引,而是想在嵌套对象(Course对象)的Name字段上创建索引,这就是正确的方法:
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 !");
    }
  }
}

这个查询是由您创建的索引提供的: db.students.explain("executionStats").find({"Courses.Name": "Math"})

避免在我的第二个示例中使用魔术字符串的一种可能方法是利用C#运算符nameof的强大功能: $"{nameof(Student.Courses)}.{nameof(Course.Name)}"


1
感谢您提供这么详细的答案。但是它似乎证实了我们必须使用字符串("Courses.Name")而不是基于强类型表达式的定义来定义索引,是吗? - lemon
嗨@lemon,我认为Mongodb的jira上的this issue证实了目前没有对类型安全提供支持。 - Enrico Massone
@lemon 请看我上面的评论,了解可能缓解该问题的方法。 - Enrico Massone
好的,非常感谢,我会在你提到的问题没有解决之前使用你的字符串方法。 - lemon

2

以下是使用MongoDB.Entities便捷库创建嵌套字段索引的强类型方法。[免责声明:我是作者]

using MongoDB.Entities;
using System.Collections.Generic;

namespace StackOverflow
{
    public class Program
    {
        public class Parent : Entity
        {
            public Child[] Children { get; set; }
        }

        public class Child
        {
            public List<Friend> Friends { get; set; }
        }

        public class Friend
        {
            public string Name { get; set; }
        }

        static void Main(string[] args)
        {
            new DB("test");

            DB.Index<Parent>()
              .Key(p => p.Children[-1].Friends[-1].Name, KeyType.Ascending)
              .Create();

        }
    }
}

以上命令在嵌套两层的name字段上创建了一个升序索引:

db.Parent.createIndex({
    "Children.Friends.Name": NumberInt("1")
}, {
    name: "Children.Friends.Name(Asc)",
    background: true
})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接