Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API Pagination & Submissions Page Performance #97

Closed
wants to merge 6 commits into from

Conversation

hallahan
Copy link
Contributor

@hallahan hallahan commented Dec 6, 2016

I've made a couple of improvements that allows the UI to get submissions for forms with a large number of responses.

API Pagination

I've added pagination to the REST API. I'm using the most common nomenclature of using query params of limit and offset. For example:

http://localhost:3210/omk/odk/submissions/sl_buildings.json?offset=25&limit=10

This will get me 10 submissions starting at the index of 25. Responses are sorted alphabetically by the UUID.

Submissions Page Fetch Limit

Right now, I've limited the submissions page to fetch at maximum 5000 submissions to be shown in the table. Any more takes a bit too long. I experimented with iteratively paginating more data into the JQuery Datatable, but this nonetheless needs to modify the DOM, so it can't be done in a thread.

We easily hit the limitations of working in the browser. The right solution would be to implement pagination of a table with the REST API. I'm not seeing a straightforward way of doing this with JQuery Datatables, so we'd want to go with something else.

This gives room for a good smaller project of revamping the submissions table UI. Perhaps a React project?


So, right now, if the number of responses is over 5000, we just get 5000 and notify the user in a toast at the bottom. You can get the complete dataset by downloading the JSON, CSV, or OSM. Also note that the CSV download actually hits the server's CSV endpoint instead of having the browser do the work.

@danbjoseph
Copy link
Member

You mention both 500 and 5000?

@hallahan
Copy link
Contributor Author

hallahan commented Dec 6, 2016

5000, not 500

@hallahan
Copy link
Contributor Author

hallahan commented Dec 6, 2016

#89

var offset = req.query.offset;

// default to 100 for limit
if (isNaN(limit) || !Number.isInteger(limit) || limit < 1) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When will Number.isInteger be false? I think parseInt either returns a number or NaN.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Will remove.

}

// check if valid offset. we paginate with valid offset.
if (typeof offset !== 'undefined' && offset !== null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

offset != null is a good way to check for both null and undefined values.

@@ -7,7 +7,7 @@ var aggregate = require('../helpers/aggregate-submissions');
*/
module.exports = function (req, res, next) {

aggregate(req.params.formName, errorCallback, aggregateCallback);
aggregate(req, errorCallback, aggregateCallback);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the background on the paired callbacks? Convention would be a single callback with err, data, data2, ... args.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Will do.

@@ -5,9 +5,34 @@ var async = require('async');

var ASYNC_LIMIT = 10;

module.exports = function (formName, errorCallback, aggregateCallback) {
module.exports = function (req, errorCallback, aggregateCallback) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a "helper", I would keep req out of the signature and move limit and offset args just before the callback(s), allowing it to be more cleanly used elsewhere.

@@ -36,6 +62,13 @@ module.exports = function (formName, errorCallback, aggregateCallback) {
});
return;
}

// if offset, we do pagination
if (typeof offset !== 'undefined' && offset !== null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!= null

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing? This is a convention I adopted years ago from what the Coffeescript compiler would do.

submissionDirs = submissionDirs.slice(offset, offset + limit);
}

// no results
if (submissionDirs.length === 0) {
aggregateCallback([]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to short-circuit, since submissionDirs == [] at this point, and eachLimit will skip straight to the callback.

@@ -1,6 +1,6 @@
{
"name": "open-map-kit-server",
"version": "0.7.0",
"version": "0.8.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably shouldn't be bumped in a pull request, preferring to tag a release using npm version ... off of the primary branch once it's ready.

@mojodna
Copy link
Member

mojodna commented Dec 6, 2016

👍

@hallahan
Copy link
Contributor Author

hallahan commented Dec 6, 2016

There is something wrong with Github. This PR was indeed merged. For a few minutes, Github was giving me 500 notices, and now this seems to be an inconsistent state. You will see in Git that this has been merged into master. So, closing...

@hallahan hallahan closed this Dec 6, 2016
@hallahan hallahan deleted the large-dataset-ui branch December 6, 2016 20:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants