根据MongoDB中另一个集合中的数据查询一个集合中的数据

我想学习如何使用MongoDB,我真的很困惑如何做到这一点。 我所拥有的是两个藏品,一个拥有多个用户,另一个拥有多个项目。 例如:

用户:

{ "_id" : ObjectId("56dba03438e1a255b97e82b6"), "name" : "john", "age" : 25 } 

项目:

 { "_id" : ObjectId("56dba0db38e1a255b97e82b7"), "name" : "pencil" } 

现在我想在我的应用程序中做的是允许用户select一个项目,但多个用户可以select相同的项目。 所以我需要跟踪哪些用户点击了哪些项目。 我想过使用另一个跟踪用户标识和项目标识的集合(用户只能select一个项目)。 这是正确的方法吗? 我创build了这个集合:

useritems:

 { "_id" : ObjectId("56dba0db38e1a255b97e82b7"), "userid" : "56db9fb038e1a255b97e82b5", "itemid" : "56dba03438e1a255b97e82b6" } 

如果这是正确的方法,那么我希望能够在我的应用程序中点击一个项目,并显示所有select该项目的用户的列表。 我怎样才能做到这一点? 我尽可能只显示在应用程序中select的itemid = itemid的useritems集合文档…但是现在,我将如何显示基于useritems集合中用户集合中的所有用户?

 router.get('/userlist/:id', function(req, res) { var db = req.db; var collection = db.get('useritems'); collection.find({'itemid' : '_id'},{},function(e,docs){ res.json(docs); }); }); 

感谢您的帮助,我真的很难理解这将如何工作。

创build第三个集合的想法是一个解决scheme,它反映了如何在关系数据库中解决这个问题。 使用MongoDB,通常可以根据您访问数据的方式来考虑不同的模式。

在你的情况下,我不会创build另一个集合,而是跟踪哪个用户select了用户文档中的哪个项目,项目文档中或两个文档中的哪个项目。 你这样做的方式取决于你的数据访问模式。

将选定的项目添加到用户文档

 { "_id": ObjectId("56dba03438e1a255b97e82b6"), "name": "john", "age": 25, "selectedItemId": "56dba0db38e1a255b97e82b7" } 

如果您经常希望看到每个用户select的项目,则将该项目存储在用户文档中是有意义的。 当你检索一个用户时,你只需要对这个items集合进行一个额外的调用就可以为这个用户检索这个item。 (如果您决定使用Mongoose作为对象文档映射器(ODM),那么您可以通过使用Mongoose的populate方法来实现这个额外的调用)。

将用户添加到项目文档

 { "_id": ObjectId("56dba03438e1a255b97e82b7"), "name": "pencil", "selectedBy": [ "56dba0db38e1a255b97e82b4", "56dba0db38e1a255b97e82b5", "56dba0db38e1a255b97e82b6" ], } 

如果您经常想要查看哪些用户select了某个特定项目,那么在项目文档中存储一组用户数据是有意义的。 当您检索一个项目时,您将拥有select该项目的用户的ID,然后您可以从数据库中检索该项目。 (同样,如果你决定使用Mongoose,你可以通过使用它的populate方法来做到这一点)。

添加两个解决scheme

之所以select一种解决scheme,是因为在给定访问模式的情况下,您将不必重复整个集合来获取所需的数据。 例如,在将用户数组添加到某个项目的情况下,如果要查找给定用户select的项目,则必须迭代所有项目,然后查看用户标识数组,直到find你想要的用户。 如果您只将项目ID存储在用户文档中,并且突然需要查看给定项目的所有用户,则会发生类似的情况。 如果这两个电话都是经常进行的,那么在两个地方都有回报。 事实上,这个“denormalises”你的数据,你将不得不确保,当你插入,更新和删除这两个地方的数据,但它是一个更具可扩展性的解决scheme,如果你经常进行这两种types的查询。

将整个项目文档embedded到用户文档中

 { "_id": ObjectId("56dba03438e1a255b97e82b6"), "name": "john", "age": 25, "selectedItem": { "name": "pencil" } } 

在OP的评论之后,我也会谈到这个场景。 这也是一个可能的解决scheme,可以在简化访问数据所需的查询方面非常有用。 只需查询用户文档,您就可以访问他/她已select的项目,而无需额外查询项目集合。 这种方法的局限性在于,如果出于某种原因想要将项目的名称从"pencil""Pencil" ,则必须确保您在所有用户文档中更新它,否则您的数据将会不一致。 当embedded式文档更复杂时,这会变得更加复杂。 尽pipe如此,这是一个广泛使用的解决scheme。 如果你很less更新你的数据,但经常阅读它,特别是如果你更感兴趣的是看到给定用户select的项目,那么它肯定会加快你最频繁的数据访问模式。

你是对的,只有你需要填充userid才能获得该集合的所有属性。 我build议你使用(如果你不是) mongoose

mongoose:

 UserItems .find({'itemid' : '_id'}) .populate('userid') .then( useritems => { // here you have all users with their data for a specific item return res.json(useritems); }); 

您可以在项目文档中添加一个数组,用于跟踪单击该项目的用户的ID。 这是假设ID存储在一个活动的会话。

docs.user_who_clicked.push(req.user._id); docs.save()

除非你有充分的理由,否则我不会为此创build一个单独的集合。 只需将selectedBy添加到每个项目文档。 另外,我发现使用自己的唯一名称或ID更简单,而不是使用内部的Mongo ID进行查询。 像这样的东西:

 var items = db.collection('items'); items.updateOne({itemname:'nuts'},{$push:{selectedBy:'johns'}}); //... items.find({itemname:'nuts'}).toArray(function(err,items) { console.log(items[0].selectedBy); db.close(); });