简体   繁体   English

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

[英]Query data in one collection based on data in another collection in MongoDB

I am trying to learn how to use MongoDB and am really confused how to do this.我正在尝试学习如何使用 MongoDB,我真的很困惑如何做到这一点。 What I have are two collections, one which has a number of users and another collection which has a number of items.我有两个集合,一个有多个用户,另一个有多个项目。 For example:例如:

users:用户:

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

items:项目:

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

Now what I want to do in my app is to allow users to select an item but multiple users can select the same item.现在我想在我的应用程序中做的是允许用户选择一个项目,但多个用户可以选择同一个项目。 So I need to keep track of which users clicked which items.所以我需要跟踪哪些用户点击了哪些项目。 I thought about doing this using another collection which keeps track of the user id and item id (a user can only select an item once).我想过使用另一个集合来执行此操作,该集合跟踪用户 ID 和项目 ID(用户只能选择一次项目)。 Is this the correct approach?这是正确的方法吗? I created this collection:我创建了这个集合:

useritems:用户项:

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

If this is the right approach, then I want to be able to click on an item in my app and for it to display a list of all the users who selected that item.如果这是正确的方法,那么我希望能够点击我的应用程序中的一个项目,并让它显示选择该项目的所有用户的列表。 How can I do this?我怎样才能做到这一点? I got as far as to display only the useritems collection documents where the itemid = itemid selected on the app...but now how would I display all of the users from the users collection based on the ones in the useritems collection?我只显示在应用程序上选择了 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);     
    });  
});  

Thanks for the help, I'm really having a hard time understanding how this would work.感谢您的帮助,我真的很难理解这是如何工作的。

The idea of creating a third collection is a solution that mirrors how you would solve this problem in a relational database.创建第三个集合的想法是一种解决方案,它反映了您将如何在关系数据库中解决此问题。 With MongoDB, it often pays off to think about different patterns based on how you access your data.使用 MongoDB,根据您访问数据的方式考虑不同的模式通常是值得的。

In your case, I would not create another collection, but track which user has selected which item within the user document, within the item document, or within both documents.在您的情况下,我不会创建另一个集合,而是跟踪哪个用户在用户文档、项目文档或两个文档中选择了哪个项目。 Which way you do this depends on your data access patterns.执行此操作的方式取决于您的数据访问模式。

Adding Selected Item to User Document将所选项目添加到用户文档

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

If you will often want to see the item each user has selected, it makes sense to store the item inside the user document.如果您经常想查看每个用户选择的项目,则将项目存储在用户文档中是有意义的。 When you retrieve a user, you would only have to do one extra call to the items collection to retrieve the item for that user.当您检索用户时,您只需对 items 集合进行一次额外调用即可检索该用户的项目。 (If you decide to use Mongoose as an object-document mapper (ODM), then you can achieve this extra call by using Mongoose's populate method ). (如果您决定使用Mongoose作为对象文档映射器 (ODM),那么您可以使用Mongoose 的populate方法来实现此额外调用)。

Adding User to the Item Document将用户添加到项目文档

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

If you will often want to see which users have selected a given item, it makes sense to store an array of users inside the item document.如果您经常想查看哪些用户选择了给定的项目,那么在项目文档中存储一组用户是有意义的。 When you retrieve an item, you would then have the IDs of the users that selected that item, which you could then retrieve from the database.当您检索一个项目时,您将拥有选择该项目的用户的 ID,然后您可以从数据库中检索这些 ID。 (Again, if you decide to use Mongoose you can do this by using its populate method ). (同样,如果您决定使用Mongoose,您可以使用它的populate方法来做到这一点)。

Adding Both Solutions添加两种解决方案

The reason why you would prefer one solution over another is that given your access pattern, you will be spared from iterating through the whole collection to get the data you need.您更喜欢一种解决方案而不是另一种解决方案的原因是,鉴于您的访问模式,您无需遍历整个集合来获取所需的数据。 For example, in the case were you add the array of users to an item, if you wanted to find the item a given user has selected, you would have to iterate though all the items and look in the array of user IDs until you found the user you wanted.例如,如果您将用户数组添加到项目中,如果您想查找给定用户选择的项目,则必须遍历所有项目并查看用户 ID 数组,直到找到你想要的用户。 Something similar would occur if you only stored the item ID inside a user document and suddenly needed to look at all the users for a given item.如果您仅将项目 ID 存储在用户文档中并且突然需要查看给定项目的所有用户,则会发生类似的情况。 If both of these calls are made often, then it pays off having the data in both places.如果这两个调用都经常进行,那么在两个地方都有数据是值得的。 Indeed this "denormalises" your data and you will have to make sure that when you insert, update, and delete the data you do so in both places, but it's a far more scalable solution if you're making both types of queries often.事实上,这会“非规范化”您的数据,您必须确保在插入、更新和删除数据时在两个地方都这样做,但如果您经常进行两种类型的查询,这是一个更具可扩展性的解决方案。

Embedding the Whole Item Document inside the User Document将整个项目文档嵌入到用户文档中

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

Following the OP's comment, I'll address this scenario too.按照 OP 的评论,我也将解决这种情况。 This is also a possible solution and can be very useful in simplifying the query needed to access the data.这也是一种可能的解决方案,对于简化访问数据所需的查询非常有用。 Just by querying the user document, you will be able to access what item he/she has selected without the extra query to a collection of items.只需查询用户文档,您就可以访问他/她选择的项目,而无需额外查询项目集合。 The limitation of this approach is that if for whatever reason you want to update the name of the item from say "pencil" to "Pencil" , you will have to ensure that you update it across all of the user documents, otherwise your data will be inconsistent.这种方法的局限性在于,无论出于何种原因,如果您想将项目名称从"pencil""Pencil" ,您必须确保在所有用户文档中更新它,否则您的数据将不一致。 This gets more complicated when your embedded documents are more complex.当您的嵌入文档更复杂时,这会变得更复杂。 Nonetheless, it is a widely used solution.尽管如此,它仍然是一种广泛使用的解决方案。 If you're rarely updating your data, but reading it very often, especially if you are more interested in seeing the item picked by a given user, then it definitely speeds up your most frequent data access patterns.如果您很少更新数据,但经常阅读它,尤其是如果您对查看给定用户选择的项目更感兴趣,那么它肯定会加快您最频繁的数据访问模式。

You are right, only you need populate the userid to get all atributes of that collection.您是对的,只需填充用户 ID即可获取该集合的所有属性。 I suggest you use (if your are not) Mongoose我建议你使用(如果你不是)猫鼬

With 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);
  });

You can add an array in the item document that keeps track of the IDs of the users who clicked that item.您可以在项目文档中添加一个数组来跟踪单击该项目的用户的 ID。 This is assuming the ID is stored in a active session.这是假设 ID 存储在活动会话中。

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

I wouldn't create a separate collection just for that unless you have a good reason.除非你有充分的理由,否则我不会为此创建一个单独的集合。 Just add a selectedBy to each Item document.只需将 selectedBy 添加到每个 Item 文档即可。 Also, I find it simpler to just use my own unique names or IDs rather than looking things up with the internal Mongo IDs.此外,我发现使用我自己的唯一名称或 ID 比使用内部 Mongo ID 查找更简单。 Something like this:像这样的东西:

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();
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM