简体   繁体   中英

Mongoose - Query from multiple collections

I am a newbie when it comes to NoSQL, so in the process of learning it with MongoDB, I decided to make a Help Desk application. Now, designing the data model, I have users and tickets which I model as follows.

User
----
 |- mid  (member id, String)
 |- name (user name, String)
 |- mail (e-mail, String)
 \_ pass (md5 hashed password, String)

I'm trying to follow this with references, the tickets will refer to the user who created them and also to the users who are replying to the tickets. I structure it as follows:

Ticket
------
  |- tid      (ticket id, String)
  |- title    (title of ticket, String)
  |- status   (status of ticket, String)
  |- replies  (embedded replies doc, [{ mid: ..., msg: ... }])
  |- assignee (whom the topic is assigned to, String, refers to users)
  \_ priority (priority level of the ticket, 1-5, Number)

Now I'd like to display to the users a list of all the open tickets; in the following way:

| Title                  | Num Replies | Assignee | Author | Priority |
|------------------------|-------------|----------|--------|----------|
| Unblock this URL       | 5           | SHC      | ABC    |        5 |
| Install MS Office here | 2           | STC      | XYZ    |        4 |
                                 ...

I just did to fetch all the tickets that where open, with this code:

tickets.find({ "status": "open" }, (err, data) =>
{
    if (err)
    {
        console.error(err);
        res.json({
            "success": false,
            "message": "Failure to get from database"
        });
    }
    else
    {
        // TODO: Convert the "mid" references in the data
    }
});

This looks simple enough till now, but data here is an array of open tickets, with the fields of the author and the assignee as member id's where I need the names of the members.

I could of course do another query to find the member name, but assuming that there are a 100 open tickets, that would get me 201 queries, which is not cool.

I did try to optimize this thing, and got around by creating an array of distinct users, fetching the user objects from the DB using the $in query and then looping over all the tickets and using the got results to map them in the server, making this to just 2 queries. It works, but I want to know how to approach it properly and not with any hacks.

Coming from the MySQL background, I would have done this in a single query in MySQL:

select t.tid, t.title, t.priority, a1.name, a2.name
    from tickets t, users a1, users a2
    where a1.mid = t.assignee and
          a2.mid = t.author;

How should I approach this kind of query in MongoDB with Mongoose?

Your MySQL query uses a join - which as you've discovered isn't supported in mongodb.

You have a couple of options, one of which you've already provided.

creating an array of distinct users, fetching the user objects from the DB using the $in query and then looping over all the tickets and using the got results to map them in the server, making this to just 2 queries

... doesn't sound bad to me.

Second, is using populate - http://mongoosejs.com/docs/populate.html

You would need to change your ticket schema to something like:

var ticketSchema = Schema({
  assignee : { type: String, ref: 'User' },
  title    : String,
  //etc...
});

Then, call populate:

tickets.find({ "status": "open" })
    .populate('assignee')
    .exec(err, data) =>
    {
        if (err)
        {
            console.error(err);
            res.json({
                "success": false,
                "message": "Failure to get from database"
            });
        }
        else
        {
            // TODO: Convert the "mid" references in the data
        }
    });

Note, that under the hood, this is actually wrapping multiple queries - But don't be alarmed - this is an accepted trade-off with NoSQL.

If your "hacky" way is working, I would suggest sticking with it - It's not really a hack, just a workaround for the lack of JOIN.

You are looking for the populate(...) method, which allows you to insert the relevant object you are after (the user) rather than the mid .

http://mongoosejs.com/docs/populate.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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