Fetch two different sets of database records to pass to one controller

Black Sulfur
Black Sulfur used Ask the Experts™
on
I have a products and category collection. I want to query both of them and send the data through one controller so I can display products and categories in the same view.

This seems to work but I am not sure if it is the correct way of doing it? If not, please could you you advise me on a more appropriate way.

exports.getIndex = (req, res, next) => {
  Product.find({ status: "approved" })
    .sort({ createdAt: -1 })
    .then(products => {
      Category.find({})
        .sort({ catName: 1 })
        .then(category => {
          res.render("shop/index", {
            pageTitle: "My home page",
            path: "/",
            prods: products,
            category: category
          });
        });
    })
    .catch(err => {
      console.log(err);
    });
};

Open in new window

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Most Valuable Expert 2017
Distinguished Expert 2018

Commented:
What is it about your solution you are not sure about?

What ORM are you using? node-orm2?

If so there are options to relate tables such that you can easily access records from related tables.

Author

Commented:
Hey Julian,

I am using Mongoose. The part I am unsure about is doing the first query:

Product.find({ status: "approved" })

Open in new window


And then doing my second query in the .then() block

Category.find({})

Open in new window



So, I have 2 then blocks. I am unsure if I can put both DB queries together and only have 1 then block. But if I did the below I wouldn't know how to get the data back from both collections where I have split it now.

Product.find({ status: "approved" })
Category.find({})
.then()

I also don't know 100% if I can use 1 .catch() block or if I need to have one for each then block().

I am just trying to get 2 different sets of data and send to 1 view through 1 controller. I need to list all the categories in a dropdown and I want to display all approved products to the frontend user.

The other alternative I guess would be to only send product data and on the frontend make an Ajax call to a separate database query to get the categories but that seems really unnecessary for something as trivial as this.
Most Valuable Expert 2017
Distinguished Expert 2018

Commented:
Ok, that changes things.

Have you had a look at the $lookup function
https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/#pipe._S_lookup

There is also some debate around whether you do it the way you have or using a populate on a virtual property.

There is quite a bit of discussion around this if you google it - too much to try and summarise here.
Ensure you’re charging the right price for your IT

Do you wonder if your IT business is truly profitable or if you should raise your prices? Learn how to calculate your overhead burden using our free interactive tool and use it to determine the right price for your IT services. Start calculating Now!

Author

Commented:
Thanks Julian. I have used populate() but that was more for a join where I had a user ID in the products collection and the users collection and I wanted to get products created by that user. Something like:

  Product.findById(productId)
    .populate("userId", "firstName lastName")

Open in new window


That isn't the same as what I want to do now as the 2 queries are totally independent and don't require a join or anything like that.

But, let me have a look at the link you posted about $lookup and maybe I will find something there.

Author

Commented:
After reading the first sentence it seems like $lookup is also a join.

Performs a left outer join to an unsharded collection in the same database to filter in documents from the “joined” collection for processing.

Achieving what I want is a lot easier to do in PHP :)

I would simply run 2 separate database query functions and send both through the controller separately. Easy peasy.
Most Valuable Expert 2017
Distinguished Expert 2018
Commented:
Ok, I see what you are doing.

A better option will be to run both queries at the same time. As you have it the Category query only runs AFTER the products query finishes - this is not very time efficient as the Category query is not dependent on the results of the Product query it is not necessary to wait for the Products query to complete before the Category one starts..

You are using Promises so you should be to do a something like a Promise.all
exports.getIndex = (req, res, next) => {
  var products = Product.find({ status: "approved" }).sort({ createdAt: -1 });
  var categories = Category.find({}).sort({ catName: 1 });
  Promise.all([products, categories])
    .then(values => {
      res.render("shop/index", {
        pageTitle: "My home page",
        path: "/",
        prods: values[0],
        category: values[1]
      });
    })
  .catch(err => {
    console.log(err);
  });
};

Open in new window

Most Valuable Expert 2017
Distinguished Expert 2018

Commented:
NB: The above is a suggestion - not working code. I have not tested it but it gives the basic idea.
Promise.all() is used to create a new Promise that resolves when all the submitted promises have resolved. The results from each resolved promise are passed in the values array in the same order that the promises are specified in the input array.

This allows you to run the queries simultaneously instead of synchronously.

Author

Commented:
Thanks Julian, I haven't tried to implement the code yet but it looks like it is exactly what I am trying to achieve. I will give a go as soon as I can and let you know how it goes. Thanks!

Author

Commented:
Legend! I just tried it and it works perfectly :)

Author

Commented:
Thanks Julian, this is brilliant.
Most Valuable Expert 2017
Distinguished Expert 2018

Commented:
You are welcome.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial