Beruflich Dokumente
Kultur Dokumente
1 Introduction
This paper documents some use cases, initial requirements, examples and design
principles for an OData extension for data aggregation. It is non-normative and is
intended to seed discussion in the OASIS OData TC for the development of an OASIS
standard OData extension defining the representation and semantics for aggregation of
data supporting multidimensional modeling.
We want to add the notion of aggregation to OData, without changing any of the base
principles in OData. In the following sections we outline a representation and semantics
for aggregation of data supporting multidimensional modeling, especially:
Annotate entity sets and/or entity types with annotations representing analytic
concepts such as dimensions, hierarchies, measures and key performance
indicators
Define semantics and operations for querying aggregated data
Define results format for queries containing aggregated data
Note: If you as a reader are hoping to find anything remotely related to advanced
analytics capabilities added to OData then you are unfortunately reading the wrong
document. In this context think about augmenting the Entity Data Model (EDM) with
annotations adding analytic type context to the model and some basic
grouping/aggregation functionality (min/max/sum/average/count/distinct-count, provider
specific and rule based aggregations) based on the data exposed in the EDM.
1.1 Status
Version 1.0 2012-05-18
1.2 Authors
Ralf Handl, SAP
Siva Harinath, Microsoft
Hubert Heijkers, IBM
Gerald Krause, SAP
Mike Pizzo, Microsoft
1
OData Extension for Data Aggregation - Direction Document
1.3 Background
OData services expose a data model that describes the schema of the service in terms of
the Entity Data Model (EDM), an Entity-Relationship model that describes the data and
then allows for querying that data. The responses returned by an OData provider are
based on that exposed data model and retain the relationships between the entities in the
model. Adding the notion of aggregation to OData, without changing any of the base
principles in OData as is, has two sides to it:
1. Means for the server to describe an analytical shape of the data represented by
the service
2. Means for the client to query an analytical shape on top of any given data
model (for sufficiently capable servers/services)
Its important to notice that, while each of these two sides might be valuable in its own
right and can be used independently of the other, their combination provides additional
value for clients. The descriptions provided by the server will help a consumer
understand more of the data structure looking at the service's exposed data model from an
analytics perspective, whereas the query extensions allow the clients to express an
analytical shape for a particular query. The query extensions will also allow clients to
refer to the server-described analytical shape as shorthand.
The following diagram shows the relationship between Dimensions and Measures in a
hierarchical star-like schema model.
2
OData Extension for Data Aggregation - Direction Document
Both query extensions and descriptive annotations can be applied to star-like schemas as
well as partly or fully denormalized schemas.
Note that ODatas EDM does not imply a storage model; it may be a completely
conceptual model whose data shape is calculated on-the-fly for each request. The actual
"entity-relationship shape" of the model, and consequently the analytical shape
described by the annotations sketched here, should be chosen to simplify understanding
and querying data by the target audience of a service. Different target audiences may well
require differently shaped services on top of the same storage model: beauty is in the eye
of the beholder.
1.4 Motivation
OData represents data as RESTful resources that make it easy for clients to browse
through this web of data, following relations between data elements ("entities") that are
exposed as hyperlinks to other web resources. In addition to this hypermedia-driven data
access, OData offers query capabilities via a small number of features (filtering, paging,
projecting, and expanding along associations) that are by themselves intentionally simple
and can be freely combined into an astonishingly powerful language.
Adding simple aggregation capabilities to the mix of query features avoids cluttering
OData services with an exponential number of explicitly modeled aggregation level
entities or else restricting the client to a small subset of predefined aggregations.
3
OData Extension for Data Aggregation - Direction Document
o They can link to actions and/or function that can be invoked on them
Describe meaning of OData resources in analytic terms
3 Examples
The following examples describe possible annotations and extensions to OData to
support data aggregation. Although concrete annotations, functions, and behavior are
described, they are intended to be purely illustrative and not prescriptive.
Lets look at a simple data model and project some analytic/OLAP query on top of that.
The following is the schema that well use for these samples that follow. It merely
represents some sales of products for a set of customers:
Time
Date Month Year
1/1/12 2012/1 2012 ProductGroups
1/2/12 2012/1 2012 ProductGroupID Name
1/3/12 2012/1 2012 PG1 Food
PG2 Non-Food
Customers
CustomerID Name Country Products
C1 Joe USA ProductID Name ProductGroup Color
C2 Sue USA P1 Sugar PG1 White
C3 Sue Netherlands P2 Coffee PG1 Brown
C4 Luc France P3 Paper PG2 White
P4 Pencil PG2 Black
Sales
OrderID Customer Time Product Amount
1 C1 1/1/12 P3 1
2 C1 1/2/12 P1 2
3 C1 1/1/12 P2 4
4 C2 1/2/12 P2 8
5 C2 1/3/12 P3 4
6 C3 1/2/12 P1 2
7 C3 1/2/12 P3 1
8 C3 1/3/12 P3 2
Note that the columns marked in orange are navigation properties whereas the columns
marked in green are identifying the properties that make up the key for those entities. The
values shown the in orange columns represent the links to the respective navigation
target.
It would be pretty easy to envision projecting an analytical shape, a cube if you will, on
top of this data model, consisting of a Sales cube with one measure called Amount.
Since this measure represents individual sales values, unlike a balance or count of any
sort, the default aggregation behavior would presumably be summation in this case. The
three dimensions/axes for which data is specified here are:
Product, with a hierarchy based on Product Group and Product
Customer, with a hierarchy based on Country and Customer
Time, with a hierarchy based on Year, Month and Date
4
OData Extension for Data Aggregation - Direction Document
To help the consumer of this service, the $metadata is accordingly annotated using the
vocabulary sketched in section 5.1 Annotations.
Food Non-Food
Sugar Coffee Paper
USA 14 2 12 5 5
Joe 6 2 4 1 1
Sue 8 8 4 4
Netherlands 2 2 3 3
Sue 2 2 3 3
Note that this result contains seven fully qualified aggregate values and fifteen rollup, or
subtotal, rows (shown in bold).
GET ~Sales?$select=Customer/Country,Customer/Name,
Product/ProductGroup/Name,Product/Name,Amount
&$expand=Customer,Product,Product/ProductGroup
5
OData Extension for Data Aggregation - Direction Document
Note that the response in OData is not exactly a flat table, but slightly folded according
to the relationships expressed in the data model exposed by the service. Using a
simplified JSON format, it might look like:
[
{ Customer: { Country: USA, Name: Joe },
Product: { ProductGroup: { Name: Non-Food }, Name: Paper },
Amount: 1
},
{ Customer: { Country: USA, Name: Joe },
Product: { ProductGroup: { Name: Food }, Name: Sugar },
Amount: 2
},
{ Customer: { Country: USA, Name: Joe },
Product: { ProductGroup: { Name: Food }, Name: Coffee },
Amount: 4
},
{ Customer: { Country: USA, Name: Sue },
Product: { ProductGroup: { Name: Food }, Name: Coffee },
Amount: 8
},
{ Customer: { Country: USA, Name: Sue },
Product: { ProductGroup: { Name: Non-Food }, Name: Paper },
Amount: 4
},
{ Customer: { Country: Netherlands, Name: Sue },
Product: { ProductGroup: { Name: Food }, Name: Sugar },
Amount: 2
},
{ Customer: { Country: Netherlands, Name: Sue },
Product: { ProductGroup: { Name: Non-Food }, Name: Paper },
Amount: 1
},
{ Customer: { Country: Netherlands, Name: Sue },
Product: { ProductGroup: { Name: Non-Food }, Name: Paper },
Amount: 2
}
]
Actually wed like to get only one aggregated row instead of the last two as those both
provide data for the same cell in the cross-table we are trying to build. So lets invent a
new query option to tell the server that it should aggregate the amount:
GET ~Sales?$select=Customer/Country,Customer/Name,
Product/ProductGroup/Name,Product/Name,Amount
&$expand=Customer,Product,Product/ProductGroup
&$aggregate=Amount
The results of this request would produce only the seven (aggregated) rows:
6
OData Extension for Data Aggregation - Direction Document
Note that each of these rows has the same structure as an individual row, but the values
are the aggregated values across the different dimension properties. In particular, note
that the row representing the sale of Paper by Sue from the Netherlands is an aggregated
value summing the Amounts from two rows in the source table.
Aggregated rows have the same structure as the individual rows, so the shape of the
results can still mirror the shape described by the service. However, aggregated rows
have different ids and self links than unaggregated rows. An aggregated rows self link
must encode the necessary information to re-retrieve that particular aggregate value, for
instance the set of unique dimension property values that the aggregate represents.
To produce the missing fifteen subtotal rows we introduce a $rollup query option:
GET ~Sales?$select=Customer/Country,Customer/Name,
Product/ProductGroup/Name,Product/Name,Amount
&$expand=Customer,Product,Product/ProductGroup
&$aggregate=Amount
&$rollup=(Customer/Country,Customer/Name),
7
OData Extension for Data Aggregation - Direction Document
(Product/ProductGroup/Name,Product/Name)
The $rollup query option is what specifies the analytical shape, the hierarchies the
client is interested in, for this query. Adding the $rollup results in the addition of those
fifteen aggregated values which make up all intersections for Customer/Country and
Product/ProductGroup/Name in this example.
[
{ Customer: { Country: USA },
Product: { ProductGroup: { Name: Food }, Name: Sugar },
Amount: 2
},
{ Customer: { Country: USA },
Product: { ProductGroup: { Name: Food }, Name: Coffee },
Amount: 12
},
{ Customer: { Country: USA },
Product: { ProductGroup: { Name: Non-Food }, Name: Paper },
Amount: 5
},
{ Customer: { Country: Netherlands },
Product: { ProductGroup: { Name: Food }, Name: Sugar },
Amount: 2
},
{ Customer: { Country: Netherlands },
Product: { ProductGroup: { Name: Non-Food }, Name: Paper },
Amount: 1
},
{ Customer: { Country: USA, Name: Joe },
Product: { ProductGroup: { Name: Food } },
Amount: 6
},
...
{ Customer: { Country: USA },
Product: { ProductGroup: { Name: Food } },
Amount: 14
},
...
]
The properties that are aggregated away are omitted from the response payload.
Note that all properties referenced in the $rollup clause must be part of the $select clause
but that the $select clause might contain more properties by which we still group and that
provide context for the aggregations being returned.
4 Design Principles
OData is an application-level protocol for interacting with data via RESTful web services.
An OData services contract is defined by simple, well-defined conventions and
semantics applied to the data model exposed by the service, providing a high level of
semantic interoperability between loosely coupled clients and services.
8
OData Extension for Data Aggregation - Direction Document
common features for core data retrieval and update scenarios and incremental,
optional features for more advanced scenarios.
Leverage Data Models to guide clients through common interaction patterns
rather than force clients to write complex queries against raw data
Define consistency across the protocol and a single way to express each piece of
functionality
Extending OData to support Data Aggregation should follow the following design
principles:
Extensions for data aggregation should not break existing clients and client
libraries; existing clients should be able to consume models containing predefined
measures and annotations without understanding those annotations or additional
semantics
Clients should trigger aggregation explicitly; unless the client does something
different it should get existing OData behavior.
The shape of the result should follow the shape of the model: dont break ODatas
type system
Aggregate rows should have self-links and unique ids
Supported aggregation behavior should be described via metadata annotations
Client should retain full control over aggregation behavior
Server should exhibit the most useful / minimally astonishing default behavior
Extensions should work on star schemas as well as "flattened" (denormalized)
schemas
Extensions should work with analytical as well as tabular data providers
5 Technical Direction
This section attempts to formalize a proposed extension syntax and describe its
interpretation in a define by example fashion. It is far from complete, and in some
cases alternative interpretations have been stated as a starting point for further
discussions.
5.1 Annotations
The following annotations are added to describe analytic aspects of an entity model.
5.1.1 Measures
Measures are identified using the Measure annotation term. This term may be applied to
any property name that can be used in $aggregate.
9
OData Extension for Data Aggregation - Direction Document
A measure that cannot be specified in $select without being included in $aggregate is not
exposed as an entity property, but rather described through a "Floating Aggregate"
annotation. Since floating aggregates are not exposed as properties on the entity type,
they are generally treated as dynamic properties.
The set of floating measures is represented through a "Measures" annotation term that
may be associated with an entity set or an entity container that supports querying.
5.1.2 Hierarchies
A group of properties can form a hierarchy:
<ComplexType Name="LeveledHierarchy">
<Property Name="Name" Type="String" Nullable="false"/>
<!-- Ordered list of properties in the hierarchy -->
<Property Name="Levels" Type="Collection(Edm.String)" Nullable="false">
<TypeAnnotation Term="Vocabulary.ReferencesProperty"/>
</Property>
</ComplexType>
<ComplexType Name="RecursiveHierarchy">
<Property Name="HierarchyNodeIDProperty" Type="Edm.String"
Nullable="false">
<TypeAnnotation Term="Vocabulary.ReferencesProperty"/>
</Property>
10
OData Extension for Data Aggregation - Direction Document
These terms are applied to the Sales entity type, so that they can be used by clients for
requesting additional aggregation levels, see section $rollup:
<EntityType Name="Sales">
<Key>
<PropertyRef Name="OrderID" />
</Key>
<Property Name="OrderID" Type="Edm.Int32" Nullable="false" />
<Property Name="Amount" Type="Edm.Decimal" Nullable="false"
Precision="5" Scale="2">
<TypeAnnotation Term="DataAggregation.DependentMeasure">
<PropertyValue Property="DefaultAggregationFunction" String="sum">
</TypeAnnotation>
</Property>
<NavigationProperty Name="Product" Relationship="Model1.ProductSales"
ToRole="Product" FromRole="Sales" />
<NavigationProperty Name="Customer" Relationship="Model1.CustomerSales"
ToRole="Customer" FromRole="Sales" />
<NavigationProperty Name="Time" Relationship="Model1.SalesTime"
ToRole="Time" FromRole="Sales" />
</EntityType>
<EntityType Name="Product">
<Key>
<PropertyRef Name="ProductID" />
</Key>
<Property Name="ProductID" Type="Edm.String" Nullable="false" />
<Property Name="Name" Type="Edm.String" Nullable="false" />
<Property Name="Color" Type="Edm.String" Nullable="false" />
<NavigationProperty Name="ProductGroup"
Relationship="Model1.ProductGroupProduct"
ToRole="ProductGroup" FromRole="Product" />
<NavigationProperty Name="Sales" Relationship="Model1.ProductSales"
ToRole="Sales" FromRole="Product" />
<TypeAnnotation Term="DataAggregation.LeveledHierarchy">
<PropertyValue Property="Name" String="ProductHierarchy"/>
<PropertyValue Property="Levels">
<Collection>
<String="ProductGroup/Name"/>
<String="Name"/>
</Collection>
</PropertyValue>
</TypeAnnotation>
</EntityType>
11
OData Extension for Data Aggregation - Direction Document
5.1.4 Dimensions
If an entity set represents a denormalized schema with multiple dimensions, the server
may want to tell the client which properties are grouped together, and which of them
form the dimension key:
<ComplexType Name="Dimension">
<Property Name="Key" Type="Collection(String)">
<TypeAnnotation Term="Vocabulary.ReferencesProperty"/>
</Property>
<Property Name="Properties" Type="Collection(String)">
<TypeAnnotation Term="Vocabulary.ReferencesProperty"/>
</Property>
</ComplexType>
In the presence of $aggregate the system query option $select not only defines the shape
of the result set, it also defines the scope of the aggregation. As in standard OData,
$expand must be used to bring into scope properties from any related entities. Specifying
properties from related entities whose navigation paths are not included in $expand is an
error.
GET ~/Sales?$select=Customer/Name
&$expand=Customer
&$aggregate
12
OData Extension for Data Aggregation - Direction Document
will return
[
{ Customer: { Name: Joe } },
{ Customer: { Name: Sue } }
]
Note that Luc does not appear as he hasnt bought anything and therefore there are no
sales entities that refer/navigate to Luc.
Note also that Sue appears only once although the customer base contains two different
Sues. Including properties that guarantee the right level of uniqueness in the $select
clause, CustomerID for example in this case, will repair that, so be careful what you ask
for.
$aggregate can take a comma-separated list of property names, similar to $select (i.e.
including navigation path segments). The listed properties will be part of the result and
must be included in $select. Their values will be calculated using the server-defined
default aggregation function within the $selected context, and they will not be considered
for grouping.
GET ~/Sales?$select=Customer/Country,Product/Name,Amount
&$expand=Customer,Product
&$aggregate=Amount
will return
[
{ Customer: { Country: Netherlands }, Product: { Name: Paper }, Amount: 3 },
{ Customer: { Country: Netherlands }, Product: { Name: Sugar }, Amount: 2 },
{ Customer: { Country: USA }, Product: { Name: Coffee }, Amount: 12 },
{ Customer: { Country: USA }, Product: { Name: Paper }, Amount: 5 },
{ Customer: { Country: USA }, Product: { Name: Sugar }, Amount: 2 }
]
The shorthand $aggregate=* means: aggregate all properties that have been annotated as
Measure and have a declared default aggregation function. In our example only the
Amount property has been annotated as a Measure, and its default aggregation function is
sum, so the above request could also have been issued as
GET ~/Sales?$select=Customer/Country,Product/Name,Amount
&$expand=Customer,Product
&$aggregate=*
This shorthand is useful if the model represents a cube with strict separation of measures
and dimensions, as it does what consumers of that cube would expect. In this kind of
models measures will have a default aggregation function. If a measures listed in $select
does not have a default aggregation function, it will not be considered part of the
$aggregate list and instead be used for grouping.
13
OData Extension for Data Aggregation - Direction Document
Using $aggregate with no parameters is possible without $select, but the $aggregate will
have no effect on the result as each row is uniquely identified by its key value(s).
Instead of using the server-defined default aggregation function, the client may specify
one of the predefined aggregation functions min, max, sum, average, count,
distinctCount, or a provider-specific function that is qualified with a namespace prefix.
min, max, sum, and average take the name of a numeric property (optionally with
path prefix) as argument. The result property will have the same type as the
argument property if used without an alias name.
count and distinctCount take the name of a simple or complex property
(optionally with path prefix) as argument. The result property will have type
Edm.Int64, and it must be given an alias name.
GET ~/Sales?$select=Customer/Country,Amount,AvgAm
&$expand=Customer
&$aggregate=sum(Amount),average(Amount) as AvgAm
will return
[
{ Customer: { Country: Netherlands }, Amount: 5, AvgAm: 2.5 },
{ Customer: { Country: USA }, Amount: 19, AvgAm: 3.8 }
]
and
GET ~/Products?$select=Name,Sales/Amount,Sales/AvgAmt
&$expand=Sales
&$aggregate=sum(Sales/Amount),
avg(Sales/Amount) as AvgAmt
will return
[
{ Name: Coffee }, Sales: [{ Amount: 12, AvgAmt: 6 }] },
{ Name: Paper }, Sales: [{ Amount: 8, AvgAmt: 2 }] },
{ Name: Pencil }, Sales: [{ Amount: null, AvgAmt: null }] },
{ Name: Sugar }, Sales: [{ Amount: 4, AvgAmt: 2 }] }
]
Note that aggregation does not alter the cardinality of the Sales navigation property, and
that it always returns a one-element array with an object containing all selected properties
14
OData Extension for Data Aggregation - Direction Document
of Sales, even if there were no base entities to be aggregated. This fact is instead
expressed by all properties having NULL values, which makes the result easier to
understand.
The aggregation function count(.) takes the name of a property as its argument. It counts
the non-NULL values of this property. To count the entities in the group to be aggregated
into a single entity count(*) can be used.
GET ~/Sales?$select=Product/Name,SalesCount
&$expand=Product
&$aggregate=count(*) as SalesCount
would return:
[
{ Product: { Name: Coffee }, SalesCount: 2 },
{ Product: { Name: Paper }, SalesCount: 4 },
{ Product: { Name: Sugar }, SalesCount: 2 }
]
You can specify a navigation property (with path if necessary) to count the number of
related entities:
GET ~/Products?$select=Name,SalesCount
&$aggregate=count(Sales) as SalesCount
would return:
[
{ Name: Coffee, SalesCount: 2},
{ Name: Paper, SalesCount: 4},
{ Name: Pencil, SalesCount: 0},
{ Name: Sugar, SalesCount: 2}
]
Note that, when specifying a navigation path, the count appears on the object containing
the final navigation property (i.e., the parent of the entities being counted):
GET ~/ProductGroups?$select=Name,Products/SalesCount
&$expand=Products
&$aggregate=count(Products/Sales) as SalesCount
would return:
[
{ Name: Food, Products:[{SalesCount: 4}] },
{ Name: Non-Food, Products:[{SalesCount: 4}] }
]
15
OData Extension for Data Aggregation - Direction Document
The aggregation function distinctCount(.) takes the name of a property as its argument. It
counts the distinct values of this property, omitting any NULL values. For navigation
properties it counts the distinct entities in the union of all entities related to entities in the
group:
GET ~/Customers?$select=Country,Sales/DistinctProducts
&$expand=Sales
&$aggregate=distinctCount(Sales/Product)
as DistinctProducts
GET ~/Customers?$select=Country,Sales/Product/Name
&$expand=Sales,Sales/Product
&$aggregate
Theres no hard distinction between dimensions and measures: the same property can
be aggregated and used to group the aggregated results:
GET ~/Sales?$select=Amount,TotalSales
&$aggregate=sum(Amount) as TotalSales
will return all distinct amounts appearing in sales orders and how much money was made
with deals of this amount:
[
{ Amount: 1, TotalSales: 2 },
{ Amount: 2, TotalSales: 6 },
{ Amount: 4, TotalSales: 8 },
{ Amount: 8, TotalSales: 8 }
]
16
OData Extension for Data Aggregation - Direction Document
5.2.2 $rollup
The $rollup query option is used to specify the analytical shape(the different levels of
aggregation the client is interested in) for a particular query. Adding the $rollup query
option results in adding additional entries to the result representing the aggregated values
produced as a result of the rollup query option, in which progressively more properties,
based on the specified named hierarchies or ad-hoc hierarchies (expressed as lists of
properties), are omitted from those entities. Note that properties are grouped, using
parentheses, to form a leveled hierarchy along which the aggregation needs to take place
and that hierarchies are themselves comma separated. Aggregations will be provided for
the cartesian product for the intersections along these hierarchies.
In the examples section we demonstrated the use of $rollup to retrieve those aggregated
values that were required for the sample grid using the following request:
GET ~Sales?$select=Customer/Country,Customer/Name,
Product/ProductGroup/Name,
Product/Name,Amount
&$expand=Customer,Product,Product/ProductGroup
&$aggregate=Amount
&$rollup=(Customer/Country,Customer/Name),
(Product/ProductGroup/Name,Product/Name)
which results in additional entries representing the aggregated subtotals being returned. In
this sample, at least the Customer/Name, the Product/Name or both have been ommitted.
An alternative shorthand using the sever-defined leveled hierarchy from the annotation
example in the previous section would produce the same result
&$rollup=(Customer/Country,Customer/Name),
ProductHierarchy
The hierarchy name is not enclosed in parentheses, so it can be distinguished from a one-
level ad-hoc hierarchy using a property name that must be enclosed in parentheses.
Properties that have been rolled up are omitted from the response.
Note that $rollup stops one level earlier than GROUP BY ROLLUP in TSQL, see [TSQL
ROLLUP]: per hierarchy the leftmost property is never rolled up. Thats fine if the model
contains a property for the all level (having only a single value). Otherwise the pseudo-
property $all can be used to force rollup to the point where the leftmost real property is
rolled up:
&$rollup=($all,Customer/Country,Customer/Name)
will return two entities rolled up to country level, and one entity rolled up across all
countries:
17
OData Extension for Data Aggregation - Direction Document
[
...
{ Customer: { Country: Netherlands },
Product: { ProductGroup: { Name: Food } },
Amount: 2
},
{ Customer: { Country: USA },
Product: { ProductGroup: { Name: Food } },
Amount: 14
},
{ Product: { ProductGroup: { Name: Food } },
Amount: 16
},
]
To rollup by the key of each related entity you can simply specify the name of the
navigation property. When rolling up by key, all key fields for the related entity must be
present in the $select list.
Property names not appearing in $aggregate refer to unaggregated values. Alias names
introduced in $aggregate via as can be used in $filter expressions and always refer to
the values after aggregation. Property names appearing in $aggregate without an as
alias also refer to values after aggregation. If all appearances of a property in $aggregate
introduce an alias, the original property name may be used in $filter expressions and
refers to values before aggregation.
This allows us to compute how much money we make with small sales:
GET ~/Sales?$select=TotalAmount
&$aggregate=sum(Amount) as TotalAmount
&$filter=Amount le 1
[
{ TotalAmount: 2 }
]
In some cases, however, requests may span entity sets with no predefined associations.
Such queries could be facilitated by a general extension to OData that would allow
requests to be rooted at the entity container, rather than an individual entity set. The
entity container defines implicit navigation properties to each entity set (and potentially
each function) it contains, and queries across entity sets could be supported by referring
to properties qualified by entity set.
18
OData Extension for Data Aggregation - Direction Document
For example, if Customers and Countries were in separate entity sets with no defined
relationship, to query all Customers for a particular country based on a common country
code one could pose the following query:
GET ~SalesData?$select=Customers/Name,Countries/Name
&$expand=Customers,Countries
&$filter=
(Customers/CountryCode eq Countries/CountryCode)
And (Countries/Name eq 'USA')
would return:
[
{ Customers: [{Name: Joe}], Countries: [{Name: USA}] },
{ Customers: [{Name: Sue}], Countries: [{Name: USA}] }
]
For example, the client could issue a query over the SalesData entity container:
GET ~SalesData?$select=Products/Name,Time/Date,Sales/Amount
&$expand=Products,Time,Sales
&$aggregate=Sales/Amount
The entity container may be annotated with measures that can be applied to aggregations
from the entity container.
19
OData Extension for Data Aggregation - Direction Document
</Record>
</Collection>
</TypeAnnotation>
</EntityContainer>
GET ~SalesData?$select=Products/Name,Time/Month,
ActualOverSales
&$expand=Products,Time
&$aggregate=ActualOverSales
20
OData Extension for Data Aggregation - Direction Document
6.1.3 LeveledHierarchyLevel
We discussed defining a type term, LeveledHierarchyLevel, containing the level (as an
integer) and the property reference to the property of that level. This doesn't rely on
ordering of level properties in the Levels annotation, but requires defining a type for
LeveledHierarchyLevel and then using a record constructor with PropertyValue elements
in specifying the levels, rather than simply specifying the ordered names of property
values.
21
OData Extension for Data Aggregation - Direction Document
<EnumType Name="AggregationFunction">
<Member Name="sum" />
<Member Name="max" />
<Member Name="min" />
<Member Name="average" />
<Member Name="count" />
<Member Name="distinctCount" />
<Member Name="custom" />
</EnumType>
As not all (analytical) engines will offer the same capabilities, we will need annotations
to e.g. express which properties can be used for grouping.
Example: filter for countries with small population, then rollup to continents: do we get
all continents with small countries, or only continents with small population? I.e. just
Antarctica for most definitions of small.
How is the aggregate value for rollup entities calculated: do we get the actual population
per continent, or just the sum of the populations of the small countries?
What if the population is not just by country, but also by age class, and we filter for
teenagers in countries with small population? Would we expect the continent rollup to
reflect only teenagers, or all age groups?
22
OData Extension for Data Aggregation - Direction Document
could be expressed as
GET ~/MySet/$Select(city,sales)/$Aggregate(sales)
/$Select(sales)/$Aggregate()
6.3 References
[CSDL] http://www.odata.org/media/30001/[mc-csdl].pdf and
http://www.odata.org/media/30002/OData CSDL Definition.html
[POLA] http://en.wikipedia.org/wiki/Principle_of_least_astonishment
[TSQL ROLLUP] http://msdn.microsoft.com/en-us/library/bb522495.aspx
23