2

I want to limit how many posts each category can display with a .limit() function and i am not sure how to come by this.

I am using Mongoose and Express.

My code so far is a follows.

router.get('/', function (req, res) {
  MainArticle.find({ category: ['Worldwide', 'U.S. News'] },  function (err, mainArticles) {
    if (err) {
      console.log(err);
    } else {
      res.render('landing', { mainArticles: mainArticles });
    }
  });
});

If i was to output the results with EJS, it will display all the results of both categories. And if i was to limit, it would just limit to the integer i set.

I'm not sure what to pass on so i can display the two articles at different parts of the webpage as well as limit how many posts to show.


router.get('/profile', function (req, res) {
  // Retrieve the desired count for each category (for example, through a query parameter) defaulting to some number as needed.
  var limit = req.query.limit || 10;

  // Create an object to hold the results
  var result = {};

  // Get data for the world wide category
  MainArticle.find({
      category: ['Worldwide'],
    })
    .limit(limit)
    .exec(function (err, worldwideArticles) {
      if (err) {
        console.log(err);
      } else {
        // Add the worldwide data to the result set
        result.worldwideArticles = worldwideArticles;

        // Get data for the US news category
        MainArticle.find({
            category: ['U.S. News'],
          })
          .limit(limit)
          .exec(function (err, usArticles) {
            if (err) {
              console.log(err);
            } else {
              result.usArticles = usArticles;

              // Hand the two different sets separately to the template
              // You will obviously have to change the template code to handle the new data structure of different categories
              res.render('profile', { result: result });
            }
          });
      }
    });
});

EJS

<script type="text/javascript">
   var json_data = <%= JSON.stringify( result ); %>
</script>

This displays articles for "Worldwide", limited to 10 articles.

  <ul>
    <% result.worldwideArticles.forEach(function(mainArticles){ %>
    <li>
      <div class="img-container">
          <a href="/articles/<%= mainArticles._id %>"><img src="<%= mainArticles.image %>" alt=""></a>
           <div class="title-container">
            <a href="/articles/<%= mainArticles._id %>"><%=mainArticles.title %></a>
          </div>
      </div>
     <% }); %>

3 Answers3

2

You could look at how I would do it here. Let me know if this works.

router.get('/', (req, res) => {
  MainArticle
    .where('category')
    .in(['Worldwide', 'U.S. News'])
    .limit(10)
      .then(mainArticles => {
        res.render('landing', { mainArticles })
      })
      .catch(err => {
        console.log(err)
      })
  )
}

You may need to have a select here, but I cannot test it. If it doesn't work just add the properties you want to select after the .in. like .select('name', 'age', 'tags') or even better .select({}) but I don't know what will work without testing. I'm just going off documentation here:

http://mongoosejs.com/docs/2.7.x/docs/finding-documents.html

Reading the Query API another way to do this would be like this:

router.get('/', (req, res) => {
  const fetchArticles = MainArticle.find({})

  fetchArticles
    .where('category')
    .in(['Worldwide', 'U.S. News'])
    .limit(10)
      .then(mainArticles => {
        res.render('landing', { mainArticles })
      })
      .catch(err => {
        console.log(err)
      })
  )
}

.. or if you want to get fancy:

router.get('/', async (req, res) => {

    function getArticles() {
        return MainArticle
            .where('category')
            .in(['Worldwide', 'U.S. News'])
            .limit(10)
            .exec()
    }

    try {
        let mainArticles = await getArticles()
        res.render('landing', { mainArticles })
    } catch (error) {
        console.log(error)
    }
}

UPDATE based on your last commit.

  router.get('/', async (req, res) => {

    function getWorldwideArticles() {
        return worldwideArticles
            .where('category')
            .in(['Worldwide'])
            .limit(10)
            .exec()
    }

    function getUSArticles() {
      return usArticles
          .where('category')
          .in(['U.S. News'])
          .limit(10)
          .exec()
    }

    try {
        let worldwideArticles = await getWorldwideArticles()
        let usArticles = await getUSArticles()
        res.render('landing', { worldwideArticles, usArticles })
    } catch (error) {
        console.log(error)
    }
  }
Joseph Chambers
  • 3,698
  • 4
  • 22
  • 37
  • In the post Chirag Ravindra made, he used result.worldwideArticles = worldwideArticles; and result.usArticles = usArticles; to help me retrieve these two categories separately. How may i implement this with your method? – Michael Sanders Feb 01 '18 at 20:57
  • So looking at this again; we're passing everything to main articles and what I would do is on the ejs side check for which category is there which is why you see I'm doing ['Worldwide', 'U.S. News'] Just grab me those two in one lookup. It's less expensive. Let me modify my response to make it easier for you. Maybe it will open your eyes. – Joseph Chambers Feb 01 '18 at 21:20
  • @MichaelSanders I think that should work. Let me know if it doesn't, and we can screenshare on skype if you want. – Joseph Chambers Feb 01 '18 at 21:30
  • i get error "getWorldwideArticles is not defined" and its referring to "return worldwideArticles ". And if i comment out the first function, i then get "usArticles is not defined", referring to "return usArticles". – Michael Sanders Feb 02 '18 at 00:41
  • You may need to do alike: await Promise.all([ getWorldwideArticles(), getUSArticles() ]) it's something silly. – Joseph Chambers Feb 03 '18 at 00:20
1

I solved this by using what Chirag Ravindra posted and passed on

<script type="text/javascript">
     var json_data = <%- JSON.stringify( result ); %>
 </script>

above my forEach statements on my EJS because EJS can't call variables from clientside

Then i just used

<% result.worldwideArticles.forEach(function(mainArticles){ %>
<% }); %>

<% result.usArticles.forEach(function(mainArticles){ %>
<% }); %>

Where i wanted to post the two categories.

Petter Friberg
  • 21,252
  • 9
  • 60
  • 109
0

One way to do this is to query for each category separately and build your result for the template by setting the result of the two find operations on different keys in the result.

router.get('/', function(req, res) {
  // Retrieve the desired count for each category (for example, through a query parameter) defaulting to some number as needed.
  var limit = req.query.limit || 10;

  // Create an object to hold the results
  var result = {};
  // Get data for the world wide category
  MainArticle.find({
      category: ['Worldwide']
    })
    .limit(limit)
    .exec(function(err, worldwideArticles) {
      if (err) {
        console.log(err);
      } else {
        // Add the worldwide data to the result set
        result.worldwideArticles = worldwideArticles;
        // Get data for the US news category
        MainArticle.find({
            category: ['U.S. News']
          })
          .limit(limit)
          .exec(function(err, usArticles) {
            if (err) {
              console.log(err);
            } else {
              result.usArticles = usArticles;
              // Hand the two different sets separately to the template
              // You will obviously have to change the template code to handle the new data structure of different categories
              res.render('landing', result);

            }
          });
      }
    });
});
Chirag Ravindra
  • 4,760
  • 1
  • 24
  • 35
  • This is what some would call, callback hell. – Joseph Chambers Jan 15 '18 at 08:18
  • @JosephChambers agreed. The exec method returns a Promise which would be the ideal way to handle this. However, the code posted by OP used the callback feature so I opted to keep the answer in the same format to convey the answer without adding additional complexity. – Chirag Ravindra Jan 15 '18 at 08:22
  • oh, yes. That's a good point. Maybe he just doesn't understand my examples which would make sense. – Joseph Chambers Jan 15 '18 at 08:25