4

我有一个 MongoDB 集合(“用户”),其中包含一个字典字段(“用户注册”)。

字段定义为:

    BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)]
    public Dictionary<string, UserRegistration> UserRegistrations = new Dictionary<string, UserRegistration>();

它是一个字典,由一个键(字符串)和一个作为值的自定义对象组成。

这是它在 MongoDB 中的反映:

"UserRegistrations" : [{
  "k" : "517ba4e1696b03108ccef51a",
  "v" : {
    "RegistrationDate" : ISODate("2013-07-21T18:57:42.589Z"),
    "OtherInfo" : "123456test",
  }
}],

当我使用 $AddToSet 时,如下例所示:

IMongoQuery query = Query.EQ("_id", new ObjectId(uid));

var kvp = new KeyValuePair<string, UserRegistration>("517ba4e1696b03108ccef51a", new UserRegistration()
{
 RegistrationDate = DateTime.Now.ToUniversalTime(),
});

IMongoUpdate update = Update.AddToSet("UserRegistrations", kvp.ToBsonDocument());

collection.Update(query, update, UpdateFlags.Multi);

它不检查是否已经存在相同的键,从而导致 Dictionary 字段中的重复,这随后会导致 MongoDB C# 驱动程序中的反序列化错误。

如何确保不存在相同的密钥?

谢谢!

4

2 回答 2

1

在您的架构的当前状态下,我认为仅使用一个对 mongodb 的更新查询来实现您正在尝试的目标是不可能的。

你有两个选择(也许还有更多我没有想到的)

1.在你的程序上编辑文档

而不是仅仅使用您要附加的键值来更新您的文档,

  • 查询整个文档的数据库,
  • 将其放入本地 C# TDocument 对象中,
  • 将您的键值对附加到文档的本地副本上,确保您没有添加已经存在的键并且
  • 使用Save document方法将其更新到数据库中。

这种方法真的很慢(与直接更新相比),但它是在不更改架构的情况下完成您所要求的唯一方法。

2. 改变你的架构

为每个键值对有一个单独的集合(名称为“UserRegistrations”以便于理解),并且(我猜测包含数组“UserRegistrations”的对象有一些其他字段,如 UserID 或类似的东西)创建一个唯一索引来自键值对的用户 ID 和键的复合索引。

该集合中的记录如下所示:

{ 
  "UserID" : "unique_user_id_of_user_A"
  "k" : "517ba4e1696b03108ccef51a",
  "v" : {
    "RegistrationDate" : ISODate("2013-07-21T18:57:42.589Z"),
    "OtherInfo" : "123456test",
  }
}
{ 
  "UserID" : "unique_user_id_of_user_B"
  "k" : "jkfadifhafo4ho34fo78h34fo7",      #obvious keyboard spazzing here
  "v" : {
    "RegistrationDate" : ISODate("2013-07-21T19:57:42.589Z"),
    "OtherInfo" : "123456test",
  }
}
{ 
  "UserID" : "unique_user_id_of_user_A"     #same user as first record
  "k" : "sfahoihu43o43f7437843f8g348",      #different key-value pair
  "v" : {
    "RegistrationDate" : ISODate("2013-07-22T18:57:42.589Z"),
    "OtherInfo" : "123456test",
  }
}

专业人士:您不再需要担心每个文档的 16MB 限制,每个用户都可以拥有几乎无限的“用户注册”(无论它们是什么)。

缺点:您必须使用索引执行连接,这可能会很快,但是在使用 mongodb 时,您应该尽量避免它们。

这只是我提出的问题的快速解决方案。如果您有兴趣,您应该研究如何构建您的模式。MongoDb 数据建模手册这个问题应该可以帮助您入门。

于 2013-10-15T11:16:01.377 回答
0

感谢您的回答,但这不是我想要的。

将集合分离到另一个“UserRegistrations”集合是反 NoSQL 方法,稍后将需要昂贵的索引。我不需要 16mb,实际上大约 1mb 就足够了,所以这对我来说不是专业的。

我需要像 o(1) 这样的 NoSQL 通过 UID 直接访问数据。

在我的应用程序中编辑文档的第二种方法对我来说也不好,这种方法可以异步调用,如果第一次调用获取当前文档,第二次调用获取它,同时数据会发生冲突并且会出现是一个结果未知的竞争条件。

我需要的是一个 MongoDB 解决方案($AddToSet 或类似的单行函数)。

经过一些研究 - 目前,没有实现这一点的 MongoDB 函数。

无论如何,我最终使用了一个小操作,只需在 Update 语句中添加一个限定条件,它首先检查具有此类 ID 的键是否已经存在。

回答:

IMongoQuery query = Query.And("_id", new ObjectId("RootDocumentKey")), Query.NE(string.Format("{0}.k", "FieldName"), BsonValue.Create("DictionaryKey")));

其中“RootDocumentKey”、“FieldName”和“DictionaryKey”是您的参数。

希望 MongoDB 将很快添加此功能。

于 2013-10-15T18:32:02.747 回答