4

I have this simple app that I created using IOS, it is a questionnaire app, whenever user clicks play, it will invoke a request to node.js/express server

enter image description here

enter image description here

Technically after a user clicks an answer it will go to the next question

enter image description here

I'm confused to use which method, to fetch the questions/question

  1. fetch all the data at once and present it to the user - which is an array
  2. Fetch the data one by one as user progress with the next question - which is one data per call

API examples

// Fetch all the data at once
app.get(‘/api/questions’, (req, res, next) => {
  Question.find({}, (err, questions) => {
    res.json(questions);
  });
});

// Fetch the data one by one
app.get('/api/questions/:id', (req, res, next) => {
  Question.findOne({ _id: req.params.id }, (err, question) => {
   res.json(question);
  });
});

The problem with number 1 approach is that, let say there are 200 questions, wouldn’t it be slow for mongodb to fetch at once and possibly slow to do network request

The problem with number 2 approach, I just can’t imagine how to do this, because every question is independent and to trigger to next api call is just weird, unless there is a counter or a level in the question mongodb.

Just for the sake of clarity, this is the question database design in Mongoose

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const QuestionSchema = new Schema({
    question: String,
    choice_1: String,
    choice_2: String,
    choice_3: String,
    choice_4: String,
    answer: String
});
airsoftFreak
  • 1,450
  • 5
  • 34
  • 64

4 Answers4

1

Very good question. I guess the answer to this question depends on your future plans about this app.

If you are planning to have 500 questions, then getting them one by one will require 500 api calls. Not the best option always. On the other hand, if you fetch all of them at once, it will delay the response depending on the size of each object.

So my suggestion will be to use pagination. Bring 10 results, when the user reaches 8th question update the list with next 10 questions.

This is a common practice among mobile developers, this will also give you the flexibility to update next questions on the basis of previous responses from user. Like Adaptive test and all.

EDIT

You can add pageNumber & pageSize query parameter in your request for fetching questions from server, something like this.

myquestionbank.com/api/questions?pageNumber=10&pageSize=2

receive these parameters in on server

var pageOptions = {
    pageNumber: req.query.pageNumber || 0,
    pageSize: req.query.pageSize || 10
}

and while querying from your database provide these additional parameters.

Question.find()
    .skip(pageOptions.pageNumber * pageOptions.pageSize)
    .limit(pageOptions.pageOptions)
    .exec(function (err, questions) {
        if(err) {
        res.status(500).json(err); return; 
        };

        res.status(200).json(questions);
    })

Note: start your pageNumber with zero (0) it's not mandatory, but that's the convention.

skip() method allows you to skip first n results. Consider the first case, pageNumber will be zero, so the product (pageOptions.pageNumber * pageOptions.pageSize) will become zero, and it will not skip any record. But for next time (pageNumber=1) the product will result to 10. so it will skip first 10 results which were already processed.

limit() this method limits the number of records which will be provided in result.

Remember that you'll need to update pageNumber variable with each request. (though you can vary limit also, but it is advised to keep it same in all the requests)

So, all you have to do is, as soon as user reaches second last question, you can request for 10 (pageSize) more questions from the server as put it in your array.

code reference : here.

Community
  • 1
  • 1
Dave Ranjan
  • 2,966
  • 24
  • 55
1

I'd use Dave's approach, but I'll go a bit more into detail here. In your app, create an array that will contain the questions. Then also store a value which question the user currently is on, call it index for example. You then have the following pseudocode:

index = 0
questions = []

Now that you have this, as soon as the user starts up the app, load 10 questions (see Dave's answer, use MongoDB's skip and limit for this), then add them to the array. Serve questions [index] to your user. As soon as the index reaches 8 (= 9th question), load 10 more questions via your API, and add them to the array. This way, you will always have questions available for the user.

NikxDa
  • 4,137
  • 1
  • 26
  • 48
0

you can use the concept of limit and skip in mongodb.

when you are hitting the api for the first time you can have your limit=20 and skip=0 increase your skip count every time you that api again.

1st time=> limit =20, skip=0

when you click next => limit=20 , skip=20 and so on

app.get(‘/api/questions’, (req, res, next) => {
  Question.find({},{},{limit:20,skip:0}, (err, questions) => {
    res.json(questions);
  });
});
Shumi Gupta
  • 1,505
  • 2
  • 19
  • 28
  • Will it repeat the same data? let say if I click next and then it hit the same documents? – airsoftFreak Mar 19 '17 at 11:18
  • for the 1st time limit =20 skip =0. when you will click next limit=20 but this time skip =20 as you want to view next 20 results and so on. – Shumi Gupta Mar 19 '17 at 12:33
0

You're right, the first option is a never-to-use option in my opinion too. Fetching that much data is useless if it is not being used or has a chance to not to be used in the context. What you can do is that you can expose a new api call:

app.get(‘/api/questions/getOneRandom’, (req, res, next) => {
    Question.count({}, function( err, count){
        console.log( "Number of questions:", count );
        var random = Math.ceil(Math.random() * count);
        // random now contains a simple var
        now you can do
        Question.find({},{},{limit:1,skip:random}, (err, questions) => {
            res.json(questions);
        });
    })

});

The skip:random will make sure that each time a random question is fetched. This is just a basic idea of how to fetch a random question from all of your questions. You can put further logics to make sure that the user doesn't get any question which he has already solved in the previous steps.

Hope this helps :)

Muhammad Ahsan Ayaz
  • 1,867
  • 10
  • 12
  • 2
    So technically this solution is just to get a random document from the Question's collection? – airsoftFreak Mar 19 '17 at 11:21
  • 1
    This may lead to the same question twice. – NikxDa Mar 19 '17 at 12:51
  • @NikxDa I've already mentioned in my comment "You can put further logics to make sure that the user doesn't get any question which he has already solved in the previous steps." :) airsoftFreak. Yes. Since you don't have different categorized questions and want the app to be flexible (i.e. to not show the exact same questions in same sequence to each user or to the same user when he starts from scratch (a possibility). If you want to know possibilities to know how you can maintain to not show questions that a user has already solved, let me know :) Thanks. – Muhammad Ahsan Ayaz Mar 20 '17 at 07:24