In the present tutorial, we will continue implementing OData system query options, this time focusing on $orderby

Note:
The final source code can be found in the project git repository.
A detailed description how to checkout the tutorials can be found here.
This tutorial can be found in subdirectory \samples\tutorials\p7_queryoptions-o

Disclaimer:
Again, in the present tutorial, we will focus only on the relevant implementation, in order to keep the code small and simple. The sample code as it is, shouldn’t be reused for advanced scenarios.

The system query options we’re focusing on are applied to the entity collection only, therefore our
implementation for all query options is done in the class
myservice.mynamespace.service.DemoEntityCollectionProcessor

When requesting a list of entities from a service, it is up to the service implementation to decide in which order they are presented. This can depend on the backend data source, anyways, it is undefined.
But the consumer of an OData service might want to be able to specify the order, according to his needs.

For example, a usual case would be that the list of entities is sorted as per default by its ID number, but for a user, the ID is not relevant and he would prefer a sorting e.g. by the name
OData supports this requirement with the system query option $orderby
It is specified as follows:

In this example, all the products are sorted by their name. Moreover, all products with the same name are sorted by their description in descending order.
Another example could be that I want to display all my customers, they should be sorted by their country. Additionally, within each country, they should be sorted by their name

In order to support such sorting, the OData service implementation has to make use of the ExpressionVisitor concept.
We haven’t used it in the present tutorial, because the ExpressionVisitor will be explained in the $filter section

The instance of an OrderByOption can be asked for the list of its OrderByItems.
Why a list?
Because the $orderby expression can be composed with multiple properties
For example, for the following URL, we get 2 OrderByItems:

In our example, we support only one property, therefore we directly access the first OrderByItem in the list.

3. Analyze the value

What do we want to do?
From the backend we got a list of entities that are products. We want to apply a sorter to that list and we want to sort by the property name that is given in the URI.
In our example, the property name that is provided in the URI can be “Name”, “Description” or “ID”
So we have to retrieve the property name from the URI.

The remaining work is to do the sorting.
We have a list of entities that has to be sorted, therefore we create a java.util.Comparator for Entity:

Collections.sort(entityList,newComparator<Entity>(){

In the compare method, we extract the required property from the entity.
The required property is the one that we retrieved from the URI.
In our sample, the properties can be of type String or Integer, therefore we have to distinguish these 2 cases.
The actual work of comparing can then be delegated to the String and Integer classes.

After the sorting is done, we still have to consider, if the required order is ascending or descending.
So we have to retrieve that information from the OrderByItem and then we can simply reverse the current order accordingly:

if(orderByItem.isDescending()){return-compareResult;// just convert the result to negative value to change the order}

The full implementation of the readEntityCollection() method:

publicvoidreadEntityCollection(ODataRequestrequest,ODataResponseresponse,UriInfouriInfo,ContentTyperesponseFormat)throwsODataApplicationException,SerializerException{// 1st retrieve the requested EntitySet from the uriInfoList<UriResource>resourcePaths=uriInfo.getUriResourceParts();UriResourceEntitySeturiResourceEntitySet=(UriResourceEntitySet)resourcePaths.get(0);EdmEntitySetedmEntitySet=uriResourceEntitySet.getEntitySet();// 2nd: fetch the data from backendEntityCollectionentityCollection=storage.readEntitySetData(edmEntitySet);List<Entity>entityList=entityCollection.getEntities();// 3rd apply $orderbyOrderByOptionorderByOption=uriInfo.getOrderByOption();if(orderByOption!=null){List<OrderByItem>orderItemList=orderByOption.getOrders();finalOrderByItemorderByItem=orderItemList.get(0);// we support only oneExpressionexpression=orderByItem.getExpression();if(expressioninstanceofMember){UriInfoResourceresourcePath=((Member)expression).getResourcePath();UriResourceuriResource=resourcePath.getUriResourceParts().get(0);if(uriResourceinstanceofUriResourcePrimitiveProperty){EdmPropertyedmProperty=((UriResourcePrimitiveProperty)uriResource).getProperty();finalStringsortPropertyName=edmProperty.getName();// do the sorting for the list of entities Collections.sort(entityList,newComparator<Entity>(){// delegate the sorting to native sorter of Integer and Stringpublicintcompare(Entityentity1,Entityentity2){intcompareResult=0;if(sortPropertyName.equals("ID")){Integerinteger1=(Integer)entity1.getProperty(sortPropertyName).getValue();Integerinteger2=(Integer)entity2.getProperty(sortPropertyName).getValue();compareResult=integer1.compareTo(integer2);}else{StringpropertyValue1=(String)entity1.getProperty(sortPropertyName).getValue();StringpropertyValue2=(String)entity2.getProperty(sortPropertyName).getValue();compareResult=propertyValue1.compareTo(propertyValue2);}// if 'desc' is specified in the URI, change the orderif(orderByItem.isDescending()){return-compareResult;// just reverse order}returncompareResult;}});}}}// 4th: create a serializer based on the requested format (json)ODataSerializerserializer=odata.createSerializer(responseFormat);// and serialize the content: transform from the EntitySet object to InputStreamEdmEntityTypeedmEntityType=edmEntitySet.getEntityType();ContextURLcontextUrl=ContextURL.with().entitySet(edmEntitySet).build();finalStringid=request.getRawBaseUri()+"/"+edmEntitySet.getName();EntityCollectionSerializerOptionsopts=EntityCollectionSerializerOptions.with().contextURL(contextUrl).id(id).build();SerializerResultserializerResult=serializer.entityCollection(serviceMetadata,edmEntityType,entityCollection,opts);InputStreamserializedContent=serializerResult.getContent();// 5th: configure the response object: set the body, headers and status coderesponse.setContent(serializedContent);response.setStatusCode(HttpStatusCode.OK.getStatusCode());response.setHeader(HttpHeader.CONTENT_TYPE,responseFormat.toContentTypeString());}

4. Run the implemented service

After building and deploying your service to your server, you can try the following URLs:

In this tutorial we have learned how to implement a simple $orderby.
We have decided to not go for the advanced way of implementing $orderby, which would have been using an ExpressionVisitor, because that is treated in the $filter implementation.