Select distinct values with odata

Note: I know this is old, but still shows up in search responses. There is now a good solution to this problem.

In OData v4, there is support for $apply, amongst other things, $apply can be used to return a distinct set of fields from a result set.

See related discussion: Applying Distinct to ODataQuery

from the OData v4 spec,
The query option $apply takes a sequence of set transformations, separated by forward slashes to express that they are consecutively applied, e.g. the result of each transformation is the input to the next transformation $apply is a function that requires

tl;dr;
Basically, if I have a table that has many fields, but I want to return just the distinct records of a number of specific fields the first step is to identify the distinct columns, lets call them Name and Id.
Simply use the $apply function to get a distinct result set containing just the Names and Id columns with syntax like this:

http://url.to/tableresource?$apply=groupby((Name,Id))

Lets do this by example, TuberInspections has 1000s of rows, but only a few contractors, I want to display the names of the contractors in a drop down list to use in a filtering query.

My distinct columns are ContractorName and ContractorId

GET /odata/TuberInspections?$apply=groupby((ContractorName,ContractorId)) HTTP/1.1
Host: localhost:1230

Response:
    { 
        "@odata.context": "http://localhost:1230/odata/$metadata#TuberInspections(ContractorName,ContractorId)",
        "value": [
            {
                "@odata.id": null,
                "ContractorId": 11534,
                "ContractorName": "Kermit d'Frog"
            },
            {
                "@odata.id": null,
                "ContractorId": 11539,
                "ContractorName": "Carlos Spicy Wiener"
            },
            {
                "@odata.id": null,
                "ContractorId": 16827,
                "ContractorName": "Jen Gelfling"
            }
        ]
    }

If your data structure is no flat enough, there is limited OOTB support for retrieving simple nested results structures.

If his data structure is hard to consume and you have control over the API, then you should consider creating a custom Function to return the specific recordset that you desire, that is trivial exercise that has many different options available to you, but outside of the scope of this response.

This response is directed at how to query an existing OData service for distinct values, not how to modify a service to provide such information.

Currently only a single navigation path is supported with the following syntax. The above query can also be written as:

GET /odata/TuberInspections?$apply=groupby((Contractor/EntityID,Contractor/Initials))
Host: localhost:1230

Response:
    { 
         "@odata.context": "http://localhost:1230/odata/$metadata#TuberInspections(Contractor(EntityID,Initials))",
         "@odata.count": 3,
         "value": [
             {
                 "@odata.id": null,
                 "Contractor": {
                     "@odata.id": null,
                     "Initials": "KdF",
                     "EntityID": 11534
                 }
             },
             {
                 "@odata.id": null,
                 "Contractor": {
                     "@odata.id": null,
                     "Initials": "SW",
                     "EntityID": 11539
                 }
             },
             {
                 "@odata.id": null,
                 "Contractor": {
                     "@odata.id": null,
                     "Initials": "Jen",
                     "EntityID": 16827
                 }
             }
         ]
    } 

There are more advanced (complicated) variations to nested or stacked $apply transformations that include returning a count of the number of unique rows that correspond to each of the distinct results.

Including a $count of distinct records within the group is sort of documented here System Query Option $apply - groupby and in response to OData v4 groupby with $count

GroupBy supports an aggregate extension, so we can use this in the previous URL, remember to alias the projected column name.

groupby((Contractor/EntityID,Contractor/Initials),aggregate($count as Items))

the response now includes the count:

       {
            "@odata.id": null,
            "Items": 260,
            "Contractor": {
                "@odata.id": null,
                "Initials": "SW",
                "EntityID": 11539
            }
        },

For more information please post specific questions and tag with OData-v4 and you'll find all the help you need :)


Currently the OData protocol doesn't support the distinct operator, or any other operator which would help with such query (assuming you're looking for disctinct values of a primitive property on some entity). You should be able to workaround this by implementing a service operation on the server which performs such query on the underlying provider which usually has that ability. And then the client can call that service operation instead.

Tags:

Odata