Node.js 使用事务在MongoDB(Mongoose)中创建链接列表

Node.js 使用事务在MongoDB(Mongoose)中创建链接列表,node.js,mongodb,mongoose,linked-list,transactions,Node.js,Mongodb,Mongoose,Linked List,Transactions,我想创建一个Mongo文档的单链接列表;i、 例如,我希望每个文档只有一个父文档和一个子文档,并且我希望没有循环。我也希望只有一个根。我试着用事务来实现这一点,但它实际上似乎没有被正确地阻止 const session=wait mongoose.startSession(); session.startTransaction(); const message=等待消息 .findOne({root:root}) .sort({time:-1}) .届会(届会); const newMessag

我想创建一个Mongo文档的单链接列表;i、 例如,我希望每个文档只有一个父文档和一个子文档,并且我希望没有循环。我也希望只有一个根。我试着用事务来实现这一点,但它实际上似乎没有被正确地阻止

const session=wait mongoose.startSession();
session.startTransaction();
const message=等待消息
.findOne({root:root})
.sort({time:-1})
.届会(届会);
const newMessage=wait Message.create(
{
时间:新日期(),
根:根,
前消息:,
内容:内容,,
},
{会话:会话}
);
等待会话。提交事务();
session.endSession();
在这里,我试图获取最新消息,并创建一个新消息,将最新消息设置为上一条消息,以创建消息的链接列表。但是,当我快速连续地调用它(即,让多个客户端同时尝试发布消息)时,它显然失败了,因为它最终创建的消息没有空的
前一个
字段;它似乎是并行运行事务,而不是串行运行。它应该等到当前事务完成后再执行
findOne
,但看起来它只是在运行所有
findOne
,然后运行所有
create
。这让我相信我在做交易的方式上肯定有问题,但我不知道我做错了什么

为了便于参考,下面是使用10个客户端运行此代码后的集合

rs0:PRIMARY> db.messages.find()
{ "_id" : ObjectId("5f4d81e7e86a0c1a4fd93d38"), "photos" : [ ], "__v" : 0 }
{ "_id" : ObjectId("5f4d81e8e86a0c1a4fd93d43"), "time" : ISODate("2020-08-31T23:04:08.347Z"), "root" : ObjectId("5f4d81e7e86a0c1a4fd93d38"), "previous" : null, "from" : ObjectId("5f4d81e49497e816b74781b2"), "content" : "Hi guys, my name's 6. Nice to meet you.", "photos" : [ ], "__v" : 0 }
{ "_id" : ObjectId("5f4d81e8e86a0c1a4fd93d44"), "time" : ISODate("2020-08-31T23:04:08.350Z"), "root" : ObjectId("5f4d81e7e86a0c1a4fd93d38"), "previous" : null, "from" : ObjectId("5f4d81e49497e816b74781b4"), "content" : "Hi guys, my name's 5. Nice to meet you.", "photos" : [ ], "__v" : 0 }
{ "_id" : ObjectId("5f4d81e8e86a0c1a4fd93d45"), "time" : ISODate("2020-08-31T23:04:08.351Z"), "root" : ObjectId("5f4d81e7e86a0c1a4fd93d38"), "previous" : null, "from" : ObjectId("5f4d81e49497e816b74781b3"), "content" : "Hi guys, my name's 9. Nice to meet you.", "photos" : [ ], "__v" : 0 }
{ "_id" : ObjectId("5f4d81e8e86a0c1a4fd93d46"), "time" : ISODate("2020-08-31T23:04:08.356Z"), "root" : ObjectId("5f4d81e7e86a0c1a4fd93d38"), "previous" : null, "from" : ObjectId("5f4d81e49497e816b74781ae"), "content" : "Hi guys, my name's 1. Nice to meet you.", "photos" : [ ], "__v" : 0 }
{ "_id" : ObjectId("5f4d81e8e86a0c1a4fd93d47"), "time" : ISODate("2020-08-31T23:04:08.369Z"), "root" : ObjectId("5f4d81e7e86a0c1a4fd93d38"), "previous" : null, "from" : ObjectId("5f4d81e49497e816b74781ad"), "content" : "Hi guys, my name's 2. Nice to meet you.", "photos" : [ ], "__v" : 0 }
{ "_id" : ObjectId("5f4d81e8e86a0c1a4fd93d48"), "time" : ISODate("2020-08-31T23:04:08.371Z"), "root" : ObjectId("5f4d81e7e86a0c1a4fd93d38"), "previous" : null, "from" : ObjectId("5f4d81e49497e816b74781b5"), "content" : "Hi guys, my name's 7. Nice to meet you.", "photos" : [ ], "__v" : 0 }
{ "_id" : ObjectId("5f4d81e8e86a0c1a4fd93d49"), "time" : ISODate("2020-08-31T23:04:08.374Z"), "root" : ObjectId("5f4d81e7e86a0c1a4fd93d38"), "previous" : null, "from" : ObjectId("5f4d81e49497e816b74781b1"), "content" : "Hi guys, my name's 8. Nice to meet you.", "photos" : [ ], "__v" : 0 }
{ "_id" : ObjectId("5f4d81e8e86a0c1a4fd93d4a"), "time" : ISODate("2020-08-31T23:04:08.377Z"), "root" : ObjectId("5f4d81e7e86a0c1a4fd93d38"), "previous" : null, "from" : ObjectId("5f4d81e49497e816b74781b0"), "content" : "Hi guys, my name's 3. Nice to meet you.", "photos" : [ ], "__v" : 0 }
{ "_id" : ObjectId("5f4d81e8e86a0c1a4fd93d4b"), "time" : ISODate("2020-08-31T23:04:08.380Z"), "root" : ObjectId("5f4d81e7e86a0c1a4fd93d38"), "previous" : null, "from" : ObjectId("5f4d81e49497e816b74781af"), "content" : "Hi guys, my name's 4. Nice to meet you.", "photos" : [ ], "__v" : 0 }

编辑:
其中一条评论指出我没有使用事务;我更新了代码以使用事务。但是,新代码仍然不起作用。

您可以在
previous
字段上创建唯一的部分索引,将索引中没有该字段的文档排除在外

db.messages.createIndex(
      {previous: 1}, 
      {unique: true, partialFilterExpression: {previous: {$exists:true}}}
)
任何试图为已使用的
previous
设置值的插入或更新都将获得“E11000重复密钥错误”


如果插入过程接收到该错误,它知道其他内容已经链接到它将要执行的操作,因此,它需要再次检查以找到列表的新尾部。

您的代码不使用事务。如果多次尝试同时插入消息,这不是很慢吗?创建单链表必然会序列化。在插入第三个项目之前,您可以插入第四个项目以及指向第三个项目的链接。因此,是的,除非您的应用程序能够插入预链接的成批消息,或者让大多数线程插入未链接的消息,并使用不同的进程对它们进行排序和链接,否则该过程将非常缓慢。