大家好,我是Edison。
最近,在使用MongoDB時,碰到這樣的一個需求:針對某個Collection手動在開發環境創建了索引,但在測試環境和生產環境不想再手動操作了,於是就想着通過代碼的方式在ASP.NET 6應用啓動時自動創建。
背景知識
索引本質上是樹,最小的值在最左邊的葉子上,最大的值在最右邊的葉子上,使用索引可以提高查詢速度(而不用全表掃描),也可以預防髒數據的插入(如唯一索引)。索引既支持普通字段,也支持內嵌文檔中某個鍵和數組元素進行索引。
在MongoDB中可以創建的索引類型:
-
唯一索引 unique:保證數據的唯一不重複
-
稀疏索引 sparse
-
複合索引:用於提高查詢速度
-
TTL 索引 : 設置文檔的緩存時間,時間到了會自動刪除掉
-
全文索引:便於大文本查詢(如概要、文章等長文本)
-
二維平面索引:便於2d平面查詢
-
地理空間索引:便於地理查詢
通過Mongo Shell管理索引:
// 創建索引 db.collection.createIndex(keys, options); // 查詢索引 db.collection.getIndexes(filter); // 刪除索引 db.collection.dropIndex("IndexName"); // 刪除所有索引 db.collection.dropIndexes() // explain 查看查詢是否走索引 // "stage" : "COLLSCAN", 表示全集合掃描 // "stage" : "IXSCAN" ,基於索引的掃描 db.collection.find(query,options).explain(options)
準備工作
假設我們有一個Entity定義如下:
[Table("MyTasks")] public class MyTaskEntity : IEntity { [BsonId] [BsonRepresentation(BsonType.ObjectId)] public ObjectId Id { get; set; } public string OrderNumber { get; set; } public List<TransmissionEntity> Transmissions { get; set; } public DispatchTaskEntity() { this.Transmissions = new List<TransmissionEntity>(); } public DispatchTaskEntity(string orderNumber) { this.OrderNumber = orderNumber; this.Transmissions = new List<TransmissionEntity>(); } ...... }
這裏,我們以之前分享的一篇文章《在ASP.NET 6中使用工作單元操作MongoDB》爲基礎,不熟悉的朋友可以先看看這篇文章。
下面,我們將使用基於上面提到的那篇文章中的 EDT.MongoProxy組件中 的內容 MongoDbConection,這是一個包裹MongoClient的單例對象:
public class MongoDbConnection : IMongoDbConnection { public IMongoClient DatabaseClient { get; } public string DatabaseName { get; } public MongoDbConnection(MongoDatabaseConfigs configs, IConfiguration configuration) { DatabaseClient = new MongoClient(configs.GetMongoClientSettings(configuration)); DatabaseName = configs.DatabaseName; } }
方式一:使用Builders.IndexKeys
這裏創建一個靜態類AppDbContext用於進行MongoDB索引創建,假設我們需要創建一個針對OrderNumber字段升序排列的唯一索引,那麼創建的代碼如下所示:
public static class AppDbContext { /// <summary> /// Create indexes in MongoDB when startup /// NOTE: It'll skip creation when the indexes already exists. /// </summary> public static void Initialize(IApplicationBuilder app) { var dbInstance = app.ApplicationServices.GetService<IMongoDbConnection>(); var logger = app.ApplicationServices.GetService<ILogger<MongoRepository>>(); var db = dbInstance.DatabaseClient.GetDatabase(dbInstance.DatabaseName); var collection = db.GetCollection("MyTasks"); // Index definitions var indexKeysDefine = Builders<MyTaskEntity>.IndexKeys.Ascending(indexKey => indexKey.OrderNumber); // Create indexes by RunCommand try { await collection.Indexes.CreateOneAsync(new CreateIndexModel(indexKeysDefine)); } catch (Exception ex) { logger.LogError(ex, "{service}.{method} - throws an exception when creating indexes in database", nameof(AppDbContext), nameof(Initialize)); } } }
使用Builders.IndexKeys可以方便快速的聲明索引,並且它只會在對應索引不存在的時候創建,已存在時則會跳過。
但是如果你想要給集合字段的某個字段聲明索引,則不太容易實現。這個時候,你可以考慮方式二。
方式二:使用RunCommand
這裏我們修改一下上面AppDbContext中Initialize方法,通過構造兩個Mongo Shell命令的方式來創建索引。
與上面不同的是,這裏我們還針對集合類型的幾個常用查詢字段創建了一個組合索引,代碼如下所示:
public static class AppDbContext { /// <summary> /// Create indexes in MongoDB when startup /// NOTE: It'll skip creation when the indexes already exists. /// </summary> public static void Initialize(IApplicationBuilder app) { var dbInstance = app.ApplicationServices.GetService<IMongoDbConnection>(); var logger = app.ApplicationServices.GetService<ILogger<MongoRepository>>(); var db = dbInstance.DatabaseClient.GetDatabase(dbInstance.DatabaseName); // Index definitions var indexCommand1 = @"{ createIndexes: 'MyTasks', indexes: [ { key: { 'OrderNumber': 1 }, name:'Idx_OrderNumber', unique: true } ] }"; var indexCommand2 = @"{ createIndexes: 'MyTasks', indexes: [ { key: { 'Transmissions.Type': 1, 'Transmissions.Status':1, 'Transmissions.Retries':1 }, name:'Idx_Transmission_TypeStatusRetries', unique: false } ] }"; // Create indexes by RunCommand try { db.RunCommand<BsonDocument>(BsonDocument.Parse(indexCommand1)); db.RunCommand<BsonDocument>(BsonDocument.Parse(indexCommand2)); } catch (Exception ex) { logger.LogError(ex, "{service}.{method} - throws an exception when creating indexes in database", nameof(AppDbContext), nameof(Initialize)); } } }
在Program.cs中使用
這裏我們僅僅需要在Program.cs中添加以下語句即可實現在ASP.NET 6應用啓動時創建MongoDB索引啦:
......
AppDbContext.Initialize(app);
......
小結
本文我們瞭解瞭如何在ASP.NET 6應用啓動時實現自動創建MongoDB的索引,相信會對你在ASP.NET 6中使用MongoDB有一定幫助!
參考資料
Kevin Smith,《Creating MongoDB indexes in ASP.NET Core 3.1》
TheCodeBuzz,《Create MongoDB indexes in C#.NET Part 1》
TheCodeBuzz,《Create MongoDB indexes in C#.NET Part 2》