MongoDB—获取文档最新版本的最有效方法

MongoDB—获取文档最新版本的最有效方法,mongodb,nosql,Mongodb,Nosql,我正在使用MongoDB保存一组文档 每个文档都有一个作为ObjectId的_id(版本)。每个文档都有一个documentId,可在不同版本之间共享。这也是创建第一个文档时分配的对象ID 给定documentId,查找文档最新版本的最有效方法是什么 也就是说,我想得到_id=max(_id)和documentId=x的记录 我需要使用MapReduce吗 提前感谢, Sam添加包含这两个字段(documentId,\u id)的索引,但不使用max(为什么)?使用documentId=x的查询

我正在使用MongoDB保存一组文档

每个文档都有一个作为ObjectId的_id(版本)。每个文档都有一个documentId,可在不同版本之间共享。这也是创建第一个文档时分配的对象ID

给定documentId,查找文档最新版本的最有效方法是什么

也就是说,我想得到_id=max(_id)和documentId=x的记录

我需要使用MapReduce吗

提前感谢,


Sam

添加包含这两个字段(documentId,\u id)的索引,但不使用max(为什么)?使用documentId=x的查询,按_id排序DESC,并限制(1)结果以获取最新结果。记住索引的正确排序顺序(也包括DESC)

诸如此类

db.collection.find({documentId : "x"}).sort({_id : -1}).limit(1)
其他方法(更非规范化)是使用其他集合处理以下文档:

{
    documentId : "x",
    latestVersionId : ...
}
使用原子操作可以安全地更新此集合。添加适当的索引将使查询变得像闪电一样快


有一件事需要考虑——我不确定ObjectID是否总是可以安全地用于订购最新版本。使用时间戳可能是更确定的方法。

我键入的内容与Daimon的第一个答案相同,使用了
排序
限制
。这可能是不推荐的,特别是对于一些驱动程序(对于最低有效部分,使用随机数而不是增量),因为生成_id的方式。它的最重要部分是秒(与毫秒等更小的分辨率相反),但最后一个数字可以是随机数。因此,如果您让用户在一秒钟内保存两次(可能不太可能,但值得注意),您可能会得到一个稍微有点混乱的最新文档

有关ObjectID结构的更多详细信息,请参阅

我建议您在文档中添加一个显式的versionNumber字段,这样您就可以使用该字段以类似的方式进行查询,如下所示:

db.coll.find({documentId: <id>}).sort({versionNum: -1}).limit(1);
BsonDocument doc = new BsonDocument("dt", DateTime.UtcNow.Ticks);
coll.Insert (doc);
doc = coll.FindOne();
DateTime dt = new DateTime(doc.GetValue("dt").AsInt64);
// see it does have precision  
Console.WriteLine(dt.Ticks);
如果您想要.NET DateTime(ticks)/时间戳精度,可以执行一系列强制转换以使其正常工作,如:

BsonDocument doc = new BsonDocument("dt", new BsonTimestamp(DateTime.UtcNow.Ticks));
coll.Insert (doc);
doc = coll.FindOne();
// see it does have precision
Console.WriteLine(new DateTime(doc.GetValue("dt").AsBsonTimestamp.Value).Ticks);
再次更新

看起来BsonTimestamp的真正用途是在第二个分辨率内生成唯一的时间戳。所以,你不应该像我在最后几行代码中那样滥用它们,它实际上可能会扰乱结果的顺序。如果需要以滴答(100纳秒)的分辨率存储DateTime,您可能只需要存储64位int“ticks”,它将在mongodb中进行排序,然后在再次将其从数据库中取出后将其包装在DateTime中,如下所示:

db.coll.find({documentId: <id>}).sort({versionNum: -1}).limit(1);
BsonDocument doc = new BsonDocument("dt", DateTime.UtcNow.Ticks);
coll.Insert (doc);
doc = coll.FindOne();
DateTime dt = new DateTime(doc.GetValue("dt").AsInt64);
// see it does have precision  
Console.WriteLine(dt.Ticks);

太好了,谢谢戴蒙。关于第二个选项,只是一个问题,如果一条新记录被插入到主文档集合中,您如何原子化地插入和更新非规范化的“索引”集合?是否可以在更新第一个文档的索引记录之前插入另一个文档实例?这有意义吗?您是否可以执行查找和修改权限以确保文档未更改?我猜如果它有最新的id并重试?这就是要点吗?再次感谢。MongoDB不支持RDBMS之类的触发器,所以您必须在应用程序端进行操作。您可以使用findAndModify查找id/时间戳较低的文档,然后更新并更新它。因为findAndModify是原子操作,所以只有当新值比当前值年轻时,它才会更新文档-这样您就不必担心并发更新。再次感谢。假设我使用版本号或时间戳,如果另一个进程添加了另一个文档实例并更新了“索引”文档,findAndModfy将失败。findAndModify将尝试更新“索引”文档,其中版本现在比当前更新更新(更高)。如果发生这种情况,我是否应该获取最新版本并再次尝试更新索引文档?对不起,如果我重复了什么,只是想让我的头脑清楚这是一个单一的结果。对于一系列文档是否有可比性
.find({documentId:{$in:[…]}})
谢谢wes,你的意思是像版本的整数一样吗?使用整数计数器是可能的,但它根本不可伸缩。。。使用高分辨率时间戳是更好的方法-两个文档总是有可能共享相同的时间戳-但如果在您的情况下是任务关键型的,那么使用RDBMS可能是更好的方法?好的。有道理。这不是关键任务,但显然需要工作。很难将RDBMS概念抛在脑后。感谢您的输入(两者)@wes+daimon-使用.NET驱动程序,我声明了一个DateTime,但在将is表示为bsontimestap时遇到问题。你们中有谁有使用.NET驱动程序的经验吗?我最好如何使用Timestamp属性。谢谢。@WesFreeman-谢谢!好例子。我确实研究过使用BsonDocument和DateTime/ticks,如下所述,这与您所描述的类似,尽管只使用ticks更有效。回到您最初提出的使用版本号的建议,是否最好使用此处所述的序列-它可以是长的(有符号的64位),甚至可以按实体排序,以提供更高的分辨率。有什么想法吗?