Tutorial 7 - Sorting documents

Your event app currently allows you to add new user accounts, as well as create, edit and delete event documents as a specific user. With the implementation of access control rules, your app also prevents unauthorized access to event documents, by only allowing the user who created their event documents to access these documents.

When a collection of event documents is retrieved, the documents returned are sorted by their creation date and time only.

In this tutorial, you will extend the event app functionality (utilising the JS SDK) to implement document sorting features in collection retrieval.

This tutorial takes around 15-25 minutes to complete.

Sorting implementation approach

Sorting is a common feature of web apps and you may want to sort the documents retrieved by your app according to a specific property/criterion of the document.

The are typically two approaches to implementing sort functionality in web apps - client- or server-side.

Web apps that perform client-side sorting work well with small collections of documents, but rapidly encounter performance problems as the number of documents per collection increases.

Therefore, Datastore natively provides server-side document sorting capabilities, which allows collections of documents to be sorted rapidly by properties defined in the document’s JSON schema files of your API specification.

Copy the 'tutorial 7' files over to your event-app directory

Copy the next level of your app’s functionality (which handles sorting) from the source tutorial repository over to your event-app directory.

  1. From the event-app directory, copy the content of the step8 directory to the event-app directory:

    $ cp -r ../tutorial/step8/*.* .
  2. Refresh or re-open the initial index.html file in the event-app directory.

Test the updated app

  1. Ensure you have logged in with an account you created in a previous tutorial, and notice the sort dropdown list at the top of the event app’s page.

  2. Ensure you have created at least two events with different Category and Cost values.

  3. Choose a sort criterion from the dropdown list and notice how the events list is re-sorted on the page.

  4. Try this for other sort criteria and notice how the events list is re-sorted on the page.

The JS SDK sortBy() method

The JS SDK includes a sortBy() method (which operates on a collection() method that returns a collection request object) to sort the list of documents in the Datastore service’s response, based on the values specified in the sortBy() method’s arguments.

The sortBy( …​ , …​ ) method accepts the following two arguments (listed in their order of usage):

  • property ( required ) - the name of the property (as a string value) as defined in the document’s JSON schema file, and

  • order ( optional ) - a string value of either:

    • asc - for ascending order, or

    • desc - for descending order.

    Omitting the second argument assumes the default order value of asc, and results in the documents being sorted in ascending order.

Examine the sort criteria values in the index.html file

In the event-app directory, open the index.html file and scroll about 1/4 of the way through the file to the start of the <!-- Sorting --> section.

The following code snippet shows how an HTML <select/> element is used for your event app’s sorting feature, whose <option/> elements' value attribute values (used by the sortBy() method in the JavaScript code of your event app) have been specified:

    <!-- Sorting -->
    <select class="form-control" id="sortEvents" onchange="refreshEvents(true)">
        <option value="">Created Date</option>
        <option value="cost-asc">Price - Low to high</option>
        <option value="cost-desc">Price - High to low</option>
        <option value="category-asc">Category - Ascending</option>
        <option value="category-desc">Category - Descending</option>
    </select>
    <!-- end Sorting -->

Each of these value attribute values are used to construct the Datastore JS SDK calls to sort event documents in collections returned from your Datastore service.

Be aware that any appropriate HTML elements or structured data within your HTML files can be used in your own JavaScript apps to sort Datastore documents in collections.

Examine the revised getEvents function in the main.js file

In the event-app directory, open the main.js file and examine the revised const getEvents …​ function definition towards the top of the file.

The following code snippet shows the JS SDK call to retrieve a list of sorted event documents from a specific user’s events collection.

/**
 * Gets all events for My Event Manager
 */
const getEvents = () => {
    const eventsCollection = datastore.collection('users').doc(currentUser.userid).collection('events'); (1)

    // Sorting.
    const sortSettings = document.querySelector('#sortEvents').value; (2)
    if (sortSettings) {
        const [sortBy, direction] = sortSettings.split('-'); (3)
        if (direction !== 'asc') {
            eventsCollection.sortBy(sortBy, direction); (4)
        } else {
            eventsCollection.sortBy(sortBy); (5)
        }
    }

    eventsCollection.get().then( (6)
        (events) => {
            if (events.length === 0) {
                ...
                // Code to handle what happens when there are no events.
                ...
            } else {
                events.forEach((event) => {
                    printEvent(event);
                });
            }
        }
    );
};
1 The JS SDK call to create a collection request object, which in turn is used to prepare a GET request for a list of events (belonging to the current user) from your Datastore service.
The final collection() method in this line prepares this collection request on the events collection, of the current user’s user document within the users collection defined in the modified API specification in tutorial 5.
The collection request object is assigned to the eventsCollection const.
2 JavaScript code to select the select element in your event app’s index.html file above and assigns the currently selected option element’s value attribute’s value to the sortSettings const.
3 Splits the sortSettings value into an array whose first value (assigned to sortBy before the hyphen) represents the property name (defined in the JSON schema file) and second value (assigned to direction after the hyphen) is either asc or desc.
4 If the direction's value is not ascending (i.e. not asc), then call the JS SDK sortBy() method on the collection request object with the two arguments: sortBy, whose value is the JSON schema file’s property name, and direction, which should be desc.
5 Otherwise, call the JS SDK sortBy() method with the single argument of sortBy instead, where the direction value is assumed to be asc instead.
6 The remainder of the JS SDK call to get the list of events.
The get() method is finally called on the collection request object (eventsCollection) to send a GET request to your Datastore service that retrieves the events collection’s array of individual event document objects, which have been sorted.
This method uses the API’s get definition within /users/{userid}/events, defined in the modified API specification in tutorial 5, to retrieve the requested collection of event documents (associated with your app’s currentUser.userid value) from your Datastore service.

How JS SDK calls resolve to API requests

The JS SDK sortBy() method actually resolves to a URL query string parameter with the initial field name sort in the API GET request made to your Datastore service.

Furthermore, multiple sortBy() method calls can be strung together to sort documents in collections by the values of subsequent properties defined in a document’s JSON schema file.

To examine how these JS SDK calls are resolved into API requests:

  1. Log in through your event app as one of the users you created above (e.g. jsmith).

  2. Access your browser’s Network feature:

    • In Google Chrome, you can access this through More tools  Developer tools  Network.

    • In Firefox, this would be through Web Developer  Network.

  3. Choose the Price - Low to high sort criterion from the dropdown list (representing a JS SDK method call of sortBy('cost','asc')) or just sortBy('cost')), and notice the GET request sent, which should be similar to this:

    http://0.0.0.0:8001/abcd1234/users/jsmith/events?sort=document.cost

    where the parameter value document.cost refers to sorting documents from the events collection according to their cost property (defined in the API spec’s JSON schema), in ascending order.

  4. Choose the Price - High to low sort criterion from the dropdown list (representing a JS SDK method call of sortBy('cost','desc')), and then notice the latest GET request sent, which should be similar to:

    http://0.0.0.0:8001/abcd1234/users/jsmith/events?sort=-document.cost

    where the value -document.cost (with a preceding 'minus' sign) refers to sorting documents from the events collection according to their cost property (defined in the API spec’s JSON schema), in descending order.

    Since your event app doesn’t support secondary/subsequent sort criteria, examining how this functionality works can be simulated through your browser’s Console feature.

  5. Access your browser’s Console feature:

    • In Google Chrome, you can access this through More tools  Developer tools  Console.

    • In Firefox, this would be through Web Developer  Web Console.

  6. Specify the following command:

    datastore.collection('users').doc('jsmith').collection('events').sortBy('cost').sortBy('category','desc').get()

    where jsmith is the example username for the user you logged in as.

  7. Click the Network tab to access your browser’s Network feature.

  8. Notice the latest GET request sent, which should look similar to this:

    http://0.0.0.0:8001/abcd1234/users/jsmith/events?sort=document.cost,-document.category

    or with an escaped comma:

    http://0.0.0.0:8001/abcd1234/users/jsmith/events?sort=document.cost%2C-document.category

    where each subsequent sortBy() method (after the initial one) resolves to a comma (,) followed by the method’s value, which becomes a part of the same URL parameter.

    Therefore, in this example:

  • document.cost refers to initially sorting documents from the events collection according to their cost property (defined in the API spec’s JSON schema), in ascending order, and

  • -document.category refers to sorting documents from the events collection (which have the same cost value), according to their category property, in descending order.


Now that you understand how to implement functionality to utilize your Datastore service’s server-side document sorting features for collection retrieval, you can now extend the functionality of your event app to filter event documents according to an event document’s property values.