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

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

LVL 1
Black SulfurAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Julian HansenCommented:
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.
Black SulfurAuthor 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.
Julian HansenCommented:
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.
Price Your IT Services for Profit

Managed service contracts are great - when they're making you money. Yes, you’re getting paid monthly, but is it actually profitable? Learn to calculate your hourly overhead burden so you can master your IT services pricing strategy.

Black SulfurAuthor 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.
Black SulfurAuthor 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.
Julian HansenCommented:
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

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Julian HansenCommented:
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.
Black SulfurAuthor 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!
Black SulfurAuthor Commented:
Legend! I just tried it and it works perfectly :)
Black SulfurAuthor Commented:
Thanks Julian, this is brilliant.
Julian HansenCommented:
You are welcome.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Node.js

From novice to tech pro — start learning today.