是否有一种方法来创建或更新MongoDB索引
本文关键字:创建 更新 索引 MongoDB 方法 一种 是否 | 更新日期: 2023-09-27 18:13:58
根据createIndexes
命令的文档:
如果你用一组选项创建一个索引,然后用相同的索引字段但不同的选项发出createIndexes, MongoDB不会改变选项也不会重建索引。
解决方案是删除索引并从头创建索引,但这样做成本很高。
是否有一种方法可以在没有索引的情况下创建索引,如果有具有相同选项的索引,则不做任何操作,如果选项发生变化,则替换索引?
这个问题最初是由Phil Barresi在这里提出的,但后来被删除了。
查看驱动程序,我实现了一个CreateOrUpdateIndex
扩展方法,用于比较原始索引文档,如果索引选项发生了变化,则替换索引(只要索引名称保持不变):
public static WriteConcernResult CreateOrUpdateIndex(
this MongoCollection mongoCollection,
IMongoIndexKeys keys,
IMongoIndexOptions options = null)
{
if (mongoCollection.IndexExists(keys))
{
var indexDocument = mongoCollection.GenerateIndexDocument(keys, options);
if (!mongoCollection.GetIndexes().RawDocuments.Any(indexDocument.Equals))
{
mongoCollection.DropIndex(keys);
}
}
return mongoCollection.CreateIndex(keys, options);
}
生成原始索引文档:
public static BsonDocument GenerateIndexDocument(this MongoCollection mongoCollection, IMongoIndexKeys keys, IMongoIndexOptions options)
{
var optionsDocument = options.ToBsonDocument();
var keysDocument = keys.ToBsonDocument();
var indexDocument = new BsonDocument
{
{ "ns", mongoCollection.FullName },
{ "name", GenerateIndexName(keysDocument, optionsDocument) },
{ "key", keysDocument }
};
if (optionsDocument != null)
{
indexDocument.Merge(optionsDocument);
}
return indexDocument;
}
public static string GenerateIndexName(IEnumerable<BsonElement> keys, BsonDocument options)
{
const string name = "name";
if (options != null && options.Contains(name)) return options[name].AsString;
return string.Join("_", keys.Select(element =>
{
var value = "x";
switch (element.Value.BsonType)
{
case BsonType.Int32: value = ((BsonInt32)element.Value).Value.ToString(); break;
case BsonType.Int64: value = ((BsonInt64)element.Value).Value.ToString(); break;
case BsonType.Double: value = ((BsonDouble)element.Value).Value.ToString(); break;
case BsonType.String: value = ((BsonString)element.Value).Value; break;
}
return string.Format("{0}_{1}", element.Name, value.Replace(' ', '_'));
}));
}
我遇到了同样的问题,我发现克服这个问题的最可靠的方法是捕获异常并显式指定Index name。如果您没有指定索引名称,驱动程序将使用键生成一个名称,即使您可以确定该名称,它也不是真正健壮的,因为它可能随着不同版本的驱动程序而更改。
try
{
_collection.CreateIndex(keys, options);
}
catch (MongoWriteConcernException ex)
{
//probably index exists with different options, lets check if name is specified
var optionsDoc = options.ToBsonDocument();
if (!optionsDoc.Names.Contains("name"))
throw;
var indexName = optionsDoc["name"].AsString;
_collection.DropIndexByName(indexName);
_collection.CreateIndex(keys, options);
}
我知道在正常的操作流程中使用异常的代码是丑陋的,我也知道这段代码应该检查WriteConcernException引发的确切原因,但它实际上是有效的。
如果options不包含name属性,则简单地重新抛出异常,但如果指定了名称,则尝试删除索引,然后重新创建索引。
如果错误是由于不同的原因(不是不同的选项/字段),第二个CreateIndex可能会再次抛出,然后调用者代码有责任了解到底发生了什么。
我在使用mongo c#驱动程序(v2.2.3)时遇到了同样的问题,并看到了Alkampfer
的答案。
在2.2.3版本中,索引是通过集合的Indexes
属性来管理的。
我已经写了一个扩展方法的索引属性基于Alkampfer's
的答案如下:
public static async Task AddOrUpdateAsync<TDocument>(this IMongoIndexManager<TDocument> indexes, CreateIndexOptions options, IndexKeysDefinition<TDocument> keys = null, CancellationToken cancellationToken = default(CancellationToken))
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
if (keys == null)
{
keys = Builders<TDocument>.IndexKeys.Ascending(options.Name);
}
try
{
await indexes.CreateOneAsync(keys, options, cancellationToken).ConfigureAwait(false);
}
catch (MongoCommandException e)
{
await indexes.DropOneAsync(options.Name, cancellationToken).ConfigureAwait(false);
await AddOrUpdateAsync(indexes, options, keys, cancellationToken).ConfigureAwait(false);
}
}
虽然它有点脏(捕获异常并重试不是最佳实践),但它是解决问题的最简单的解决方案
如果您担心代码的性能,您通常在每次更改索引时使用dirty部分(据我回忆,这种情况并不经常发生)。
如果你想,有一个替代迭代集合的索引,但这是一个相当混乱的过程(移动和访问当前位置)
这些天与MongoDB。驱动程序,您应该能够通过以下方式重新创建索引:
var client = new MongoClient("connection-string-here");
var database = client.GetDatabase("MyDatabaseName");
var index = Builders<MyDocument>.IndexKeys.Ascending(x => x.MyDocRefNumber);
var idexModel = new CreateIndexModel<MyDocument>(index);
database.<MyDocument>()Indexes.CreateOne(indexModel);