OData Extension for Data Aggregation Version 1.0 Working Draft 01 02 November 2012 Technical Committee: OASIS Open Data Protocol (OData) TC Chairs: Barbara Hartel ([email protected]), SAP AG Ram Jeyaraman ([email protected]), Microsoft Editor: Ralf Handl ([email protected]), SAP AG Hubert Heijkers ([email protected]), IBM Gerald Krause ([email protected]), SAP AG Michael Pizzo ([email protected]), Microsoft Additional artifacts: This prose specification is one component of a Work Product which also includes: XML schemas: (list file names or directory name) Other parts (list titles and/or file names) Related work: This specification replaces or supersedes: Specifications replaced by this specification (hyperlink, if available) This specification is related to: Related specifications (hyperlink, if available) Declared XML namespaces: list namespaces declared within this specification Abstract: This specification adds the notion of aggregation to OData without changing any of the base principles of OData. It defines a representation and semantics for aggregation of data, especially: Semantics and operations for querying aggregated data Results format for queries containing aggregated data Annotations to indicate what can be aggregated, and how Status: This Working Draft (WD) has been produced by one or more TC Members; it has not yet been voted on by the TC or approved as a Committee Draft (Committee Specification Draft or a Committee Note Draft). The OASIS document Approval Process begins officially with a TC vote to approve a WD as a Committee Draft. A TC may approve a Working Draft, revise it, and reapprove it any number of times as a Committee Draft. Copyright © OASIS Open 2012. All Rights Reserved. All capitalized terms in the following text have the meanings assigned to them in the OASIS Intellectual Property Rights Policy (the "OASIS IPR Policy"). The full Policy may be found at the OASIS website. This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published, and distributed, in whole or in part, without restriction of any kind, provided that the above copyright notice and this section are included on all such copies and derivative works. However, this document itself may not be modified in any way, including by removing the copyright notice or references to OASIS, except as needed for the purpose of developing any document or deliverable produced by an OASIS Technical odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 1 of 31 Committee (in which case the rules applicable to copyrights, as set forth in the OASIS IPR Policy, must be followed) or as required to translate it into languages other than English. The limited permissions granted above are perpetual and will not be revoked by OASIS or its successors or assigns. This document and the information contained herein is provided on an "AS IS" basis and OASIS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY OWNERSHIP RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 2 of 31 Table of Contents 1 Introduction ........................................................................................................................................... 4 1.1 Terminology ........................................................................................................................................ 4 1.2 Normative References ........................................................................................................................ 4 1.3 Non-Normative References ................................................................................................................ 4 2 Background .......................................................................................................................................... 5 2.1 Motivation............................................................................................................................................ 6 2.2 Design Principles ................................................................................................................................ 6 2.3 Scope of Data Aggregation by Example ............................................................................................. 8 3 Aggregation-Related Extensions to OData Core ............................................................................... 10 3.1 $aggregate ........................................................................................................................................ 10 3.1.1 Distinct Values ........................................................................................................................... 10 3.1.2 Aggregated Values .................................................................................................................... 11 3.1.2.1 Producer-Controlled Aggregation....................................................................................................... 11 3.1.2.2 Consumer-Controlled Aggregation..................................................................................................... 12 3.1.3 Boundary Conditions ................................................................................................................. 16 3.2 $rollup ............................................................................................................................................... 16 3.3 Identifying Aggregated Entities ......................................................................................................... 18 3.4 Sequencing (Sprint 2) ....................................................................................................................... 19 3.5 Cross-Joins (Sprint 3) ....................................................................................................................... 21 3.6 ABNF for Extended URL Conventions ............................................................................................. 22 4 Aggregation-Related Annotations ...................................................................................................... 24 4.1 Aggregatable Properties ................................................................................................................... 24 4.2 Groupable Properties ........................................................................................................................ 25 4.3 Hierarchies (Sprint 2) ........................................................................................................................ 25 4.4 Functions and Actions on Aggregated Entities (Sprint 2) ................................................................. 27 5 Conformance ...................................................................................................................................... 28 Appendix A. Acknowledgments ............................................................................................................. 29 Appendix B. Non-Normative Text .......................................................................................................... 30 B.1 Subsidiary section ............................................................................................................................ 30 B.1.1 Sub-subsidiary section .............................................................................................................. 30 Appendix C. Revision History ................................................................................................................ 31 odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 3 of 31 1 Introduction This specification adds the notion of aggregation to OData without changing any of the base principles of OData. It defines a representation and semantics for aggregation of data, especially: Semantics and operations for querying aggregated data Results format for queries containing aggregated data Annotations to indicate what can be aggregated, and how 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 Terminology The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be in [OData-CSDL] OData Common Schema Definition Language (CSDL) Version 1.0. DD Month 2012. OASIS Committee Specification Draft 01. http://docs.oasis-open.org/odata/odata-csdl/v1.0/csd01/odata-csdl-v1.0csd01.doc. [RFC2119]. 1.2 Normative References [OData-CSDL] [RFC2119] OData Common Schema Definition Language (CSDL) Version 1.0. DD Month 2012. OASIS Committee Specification Draft 01. http://docs.oasisopen.org/odata/odata-csdl/v1.0/csd01/odata-csdl-v1.0-csd01.doc. Bradner, S., “Key words for use in RFCs to Indicate Requirement Levels”, BCP 14, RFC 2119, March 1997. http://www.ietf.org/rfc/rfc2119.txt. 1.3 Non-Normative References [POLA] [TSQL ROLLUP] http://en.wikipedia.org/wiki/Principle_of_least_astonishment http://msdn.microsoft.com/en-us/library/bb522495.aspx odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 4 of 31 2 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 consumer to query aggregated data on top of any given data model (for sufficiently capable data producers) 2. Means for the producer to annotate what data can be aggregated, and what it can be aggregated by, allowing consumers to avoid asking questions that the producer cannot answer. It’s 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 consumers. The descriptions provided by the producer will help a consumer understand more of the data structure looking at the service's exposed data model. The query extensions allow the consumers to explicitly express the desired aggregation behavior for a particular query. It will also allow consumers to formulate queries that refer to the annotations as shorthand. As part of the extension we are introducing the following terms: Aggregatable property – a property for which the values can be aggregated, Groupable property – a property that can be used to define the aggregation scope. Hierarchy – an arrangement of groupable properties whose values are represented as being “above”, “below”, or “at the same level as” one another. The following diagram shows these terms applied to a simple model that we will use throughout this document. In our terminology “Amount” is an aggregatable property and the properties of the Customer, Time, Product, and ProductGroup entity types are groupable. These can be arranged in three hierarchies: Product hierarchy based on groupable properties of the Product Group and Product entity types odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 5 of 31 Customer hierarchy based on Country and Customer Time hierarchy based on Year, Month and Date In this example we’ll presume that Amount has summation as its default aggregation behavior, To help the consumer of this service, the $metadata is accordingly annotated using the vocabulary defined in section 4. If you have an OLAP background, you can view this as a Sales “cube” with an Amount “measure” and three “dimensions”; as these terms are heavily overloaded we avoid them in this document. Both query extensions and descriptive annotations can be applied to normalized as well as partly or fully denormalized schemas. Note that OData’s 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 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. 2.1 Motivation OData is an application-level protocol for interacting with data via RESTful web services. It represents data as RESTful resources that make it easy for consumers to “browse” through this “web of data”, following relations between data elements ("entities") that are exposed as hyperlinks to other web resources. An OData service’s 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 consumers and producers. 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 consumer to a small subset of predefined aggregations. 2.2 Design Principles The design principles of OData are to: Make it easy to implement and consume a basic OData service over a variety of data sources. Rather than try and expose the full functionality of all stores, define common features for core data retrieval and update scenarios and incremental, optional features for more advanced scenarios. Leverage Data Models to guide consumers through common interaction patterns rather than force consumers to write complex queries against raw data Define consistency across the protocol and a single way to express each piece of functionality odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 6 of 31 The design principles of OData extensions are to: Ensure extensions do not violate the core semantics of OData Avoid defining different representations for common concepts across extensions Ensure independent extensions compose well Ensure consumers can ignore extended functionality and still query and consume data correctly Extending OData to support Data Aggregation should follow the following design principles: Extensions for data aggregation should not break existing consumers and client libraries; existing consumers should be able to consume models containing predefined measures and annotations without understanding those annotations or additional semantics Consumers should trigger aggregation explicitly; unless the consumer does something different it should get existing OData behavior. The shape of the result should follow the shape of the model: don’t break OData’s type system Aggregation results consist of entities that are normal OData entities. In particular; o They can be individually identified and addressed, i.e. have a self-link and a unique id o They can link to other entities “Inherited” links from the group they represent, and that still are meaningful for the aggregate, or New links to “drill” to sets of less aggregated entities o They can link to actions and/or function that can be invoked on them Supported aggregation behavior can be described via metadata annotations Consumers should retain full control over what gets aggregated: o Specify a set of entities (entries) that are to be grouped into subsets and retrieved as a single entity per subset representing the aggregate of that subset o Specify how these groups are formed o Specify how these groups are aggregated Producers should exhibit the most useful / minimally astonishing default behavior (see [POLA]) Extensions should work on “star” schemas as well as "flattened" (denormalized) schemas Extensions should work with “analytical” as well as “tabular” data providers odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 7 of 31 2.3 Scope of Data Aggregation by Example Using the model from section 2 we add data to illustrate the capabilities introduced in this extension. The first question we’d like to ask is: which customers bought which products? Which leads to the second question: who bought how much of what? This may be visualized as a crosstable: Food Non-Food Sugar USA Joe Sue Netherlands Sue USD USD USD EUR EUR 14 6 8 2 2 Coffee 2 2 Paper 12 4 8 2 2 5 1 4 3 3 5 1 4 3 3 The data in this cross-table can be written down in a shape that more closely resembles the structure of the data returned by OData in response to an aggregate query: Customer/Country USA USA USA USA USA Netherlands Netherlands USA USA USA Netherlands Netherlands USA USA Customer/Name Joe Joe Joe Sue Sue Sue Sue NULL NULL NULL NULL NULL Joe Joe Product/ProductGroup/Name Non-Food Food Food Food Non-Food Food Non-Food Food Food Non-Food Food Non-Food Food Non-Food Product/Name Paper Sugar Coffee Coffee Paper Sugar Paper Sugar Coffee Paper Sugar Paper NULL NULL odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. Amount 1 2 4 8 4 2 3 2 12 5 2 1 6 1 Currency /Code USD USD USD USD USD EUR EUR USD USD USD EUR EUR USD USD 16 October 2012 Page 8 of 31 USA USA Netherlands Netherlands USA USA Netherlands Netherlands Sue Sue Sue Sue NULL NULL NULL NULL Food Non-Food Food Non-Food Food Non-Food Food Non-Food NULL NULL NULL NULL NULL NULL NULL NULL 8 4 2 3 14 5 2 3 USD USD EUR EUR USD USD EUR EUR Note that this result contains seven fully qualified aggregate values and fifteen rollup, or “subtotal”, rows (shown in bold). odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 9 of 31 3 Aggregation-Related Extensions to OData Core 3.1 $aggregate Aggregation behavior is triggered using the new query option $aggregate. 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. 3.1.1 Distinct Values Without parameters $aggregate returns the distinct value combinations of properties listed in the $select system query option. GET ~/Customers?$select=Name &$aggregate will return [ { Name: “Luc” }, { Name: “Joe” }, { Name: “Sue” } ] Note that “Sue” appears only once although the customer base contains two different Sues. The above result is “display-formatted JSON”, omitting the quotes around property names. It is not intended to be valid OData JSON, and it is definitely non-normative. We will use this notation throughout the document. The result also doesn’t contain the unique IDs for the result entities; these will be introduced later. Aggregation is also possible across related entities. Let’s look at customers that bought something: GET ~/Sales?$select=Customer/Name &$expand=Customer &$aggregate will return [ { Customer: { Name: “Joe” } }, { Customer: { Name: “Sue” } } ] Note that “Luc” does not appear as he hasn’t bought anything and therefore there are no sales entities that refer/navigate to Luc. However, even though both Sues bought products, only one “Sue” appears in the aggregate result. Including properties that guarantee the right level of uniqueness in the $select clause can repair that: GET ~/Sales?$select=Customer/Name,Customer/CustomerID &$expand=Customer &$aggregate will return [ { Customer: { Name: “Joe”, CustomerID: “C1” } }, { Customer: { Name: “Sue”, CustomerID: “C2” } }, odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 10 of 31 { Customer: { Name: “Sue”, CustomerID: “C3” } } ] 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). The first question in the motivating example in section 2.3, which customers bought which products, can now be expressed as GET ~Sales?$select=Customer/Name,Customer/CustomerID,Product/Name &$expand=Customer,Product &$aggregate and will return [ { { { { { { { Customer: Customer: Customer: Customer: Customer: Customer: Customer: { { { { { { { Name: Name: Name: Name: Name: Name: Name: “Joe”, “Joe”, “Joe”, “Sue”, “Sue”, “Sue”, “Sue”, CustomerID: CustomerID: CustomerID: CustomerID: CustomerID: CustomerID: CustomerID: “C1” “C1” “C1” “C2” “C2” “C3” “C3” }, }, }, }, }, }, }, Product: Product: Product: Product: Product: Product: Product: { { { { { { { Name: Name: Name: Name: Name: Name: Name: “Coffee”} “Paper” } “Sugar” } “Coffee”} “Paper” } “Paper” } “Sugar” } }, }, }, }, }, }, } ] 3.1.2 Aggregated Values 3.1.2.1 Producer-Controlled Aggregation $aggregate can take a comma-separated list of property names that may be prefixed with a navigation path. These properties MUST also be included in the projection defined by $expand and $select. Their values will be calculated using the producer-defined default aggregation behavior (summation in this example) within the selected context, and they will not be considered for grouping. The context is given by all the properties in $select that are not listed in $aggregate. For every distinct value combination of these context-defining properties an aggregated value will be returned for the properties listed in $aggregate. GET ~/Sales?$select=Customer/Country,Product/Name,Amount &$expand=Customer,Product &$aggregate=Amount will return [ { { { { { Customer: Customer: Customer: Customer: Customer: { { { { { Country: Country: Country: Country: Country: “Netherlands” }, Product: “Netherlands” }, Product: “USA” }, Product: { Name: “USA” }, Product: { Name: “USA” }, Product: { Name: { Name: “Paper” }, Amount: 3 }, { Name: “Sugar” }, Amount: 2 }, “Coffee” }, Amount: 12 }, “Paper” }, Amount: 5 }, “Sugar” }, Amount: 2 } ] The aggregated values will be returned in the entity property whose values are aggregated, of course without changing its type. GET ~/Product?$select=Name,Sales/Amount &$expand=Sales &$aggregate=Sales/Amount will return odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 11 of 31 [ { { { { Name: Name: Name: Name: “Coffee”, “Paper”, “Pencil”, “Sugar”, Sales: Sales: Sales: Sales: [ { Amount: 12 } ] }, [ { Amount: 8 } ] }, [] }, [ { Amount: 4 } ] } ] Note that aggregation does not alter the cardinality of the Sales navigation property, and that it always returns an array with at most one object. If there are no “base” entities to be aggregated, the array is empty. Careful observers will notice that the above amounts have been aggregated across currencies, which is semantically wrong. Yet it is the correct response to the question asked, so be careful what you ask for. The semantically meaningful question GET ~/Product?$select=Name,Sales/Amount,Sales/Currency/Code &$expand=Sales &$aggregate=Sales/Amount will return [ { Name: “Coffee”, Sales: [ { Amount: 12, Currency: { Code: “USD” { Name: “Paper”, Sales: [ { Amount: 3, Currency: { Code: “EUR” { Amount: 5, Currency: { Code: “USD” { Name: “Pencil”, Sales: [] }, { Name: “Sugar”, Sales: [ { Amount: 2, Currency: { Code: “EUR” { Amount: 2, Currency: { Code: “USD” } } ] }, } }, } } ] }, } }, } } ] } ] Also note that associations are “expanded” in a left-outer-join fashion, starting from the target of the aggregation request, before grouping the entities for aggregation. Afterwards the results are “folded back” to match the cardinality: GET ~/Customers?$select=Country,Sales/Product/Name &$expand=Sales,Sales/Product &$aggregate returns the different products sold per country: [ { Country: “Netherlands”, Sales: [{ { { Country: “USA”, Sales: [{ { { Product: Product: Product: Product: Product: { { { { { Name: Name: Name: Name: Name: “Paper” }, “Sugar” }] }, “Coffee” }, “Paper” }, “Sugar” }] } ] 3.1.2.2 Consumer-Controlled Aggregation 3.1.2.2.1 Standard Aggregation Functions Instead of using the producer-defined default aggregation behavior, the consumer may specify one of the predefined aggregation functions min, max, sum, average, count, distinctCount: GET ~/Sales?$select=Customer/Country,Amount &$expand=Customer &$aggregate=sum(Amount) will return odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 12 of 31 [ { Customer: { Country: “Netherlands” }, Amount: 5 }, { Customer: { Country: “USA” }, Amount: 19 } ] And GET ~/Sales?$select=Customer/Country,Amount &$expand=Customer &$aggregate=average(Amount) will return [ { Customer: { Country: “Netherlands” }, Amount: 1.6666667 }, { Customer: { Country: “USA” }, Amount: 3.8 } ] The aggregated values will be returned in the entity property whose values are aggregated, of course without changing its type. In this example Amount is an Edm.Decimal without a fixed Scale. If it were an integer or had a fixed Scale, the aggregated values would have been rounded accordingly, see section 3.1.2.2.3. 3.1.2.2.2 Aliasing When applying an aggregation function to a property, an alias name may be provided to allow multiple aggregates for a single property. The alias will introduce a dynamic property in the entity type of the aggregated property used as the function argument to preserve the result shape. The alias must be a SimpleIdentifier (see [OData-CSDL, section 20.2]), and it must not collide with names of declared properties of that entity type, nor with other aliases for properties of the same entity type. The dynamic property introduced by $aggregate does not exist in the base set, so it cannot appear in the $select clause. GET ~/Sales?$select=Customer/Country,Amount &$expand=Customer &$aggregate=sum(Amount),average(Amount) as AvgAmt will return [ { Customer: { Country: “Netherlands” }, Amount: 5, AvgAmt: 1.6666667 }, { Customer: { Country: “USA” }, Amount: 19, AvgAmt: 3.8 } ] If the aggregated property is not part of the base set, but of a related set, we need a slightly different syntax. The introduced dynamic property MUST always be in the same set as the original property: GET ~/Products?$select=Name,Sales/Amount &$expand=Sales &$aggregate=sum(Sales/Amount), average(Sales/Amount) as Sales/AvgAmt will return [ { { { { Name: Name: Name: Name: “Coffee”, “Paper”, “Pencil”, “Sugar”, Sales: Sales: Sales: Sales: [{ [{ [] [{ Amount: Amount: }, Amount: 12, AvgAmt: 8, AvgAmt: 4, AvgAmt: 6 }] }, 2 }] }, 2 }] } ] To simplify the syntax of these queries and OData queries in general we propose a “scoping” syntax, see ODATA-144: GET ~/Products?$select=Name,Sales/Amount odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 13 of 31 &$expand=Sales &$aggregate=Sales/(sum(Amount),average(Amount) as AvgAmt) that also makes sense for other query options, e.g. $select=Items/(Price,Currency,Quantity,Unit). There’s no hard distinction between groupable and aggregatable properties: the same property can be aggregated and used to group the aggregated results: GET ~/Sales?$select=Amount &$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: Amount: Amount: Amount: 1, 2, 4, 8, TotalSales: TotalSales: TotalSales: TotalSales: 2 6 8 8 }, }, }, } ] 3.1.2.2.3 The Standard Aggregation Functions sum, min, max, average The standard aggregation functions 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. For min and max this poses no problem. The results of the aggregation function sum may in some cases exceed the limits of the type of the aggregated entity property. In this case the producer MUST respond with 400 Bad Request and a meaningful human-readable error message. If sum is used with an alias name, the producer MUST choose a type for the result property that is capable of representing the aggregated values. This may require switching to a larger integer type, to Edm.Decimal with sufficient Precision and Scale, or to Edm.Double. If average is used without an alias name, the result will be rounded to the next number that can be represented with the type of the aggregated property. If average is used with an alias, the result property will be of type Edm.Double. 3.1.2.2.4 The Standard Aggregation Function count The aggregation function count has two overloads: the one without parameters counts the entities in the group to be aggregated into a single entity. The other takes the name of a property as its argument and counts the non-NULL values of this property. Both forms must always specify an alias, and the result property will have type Edm.Decimal with Scale="0" and sufficient Precision. GET ~/Sales?$select=Product/Name &$expand=Product &$aggregate=count() as SalesCount would return: [ { Product: { Name: “Coffee” }, SalesCount: 2 }, { Product: { Name: “Paper” }, SalesCount: 4 }, { Product: { Name: “Sugar” }, SalesCount: 2 } ] When used with a navigation property (with path if necessary) it counts the number of related entities: GET ~/Products?$select=Name odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 14 of 31 &$aggregate=count(Sales) as SalesCount would return: [ { { { { Name: Name: Name: Name: “Coffee”, “Paper”, “Pencil”, “Sugar”, SalesCount: SalesCount: SalesCount: SalesCount: 2}, 4}, 0}, 2} ] Note that, when specifying a navigation path, the count MUST appear on the object containing the final navigation property (i.e., the parent of the entities being counted): GET ~/ProductGroups?$select=Name &$expand=Products &$aggregate=count(Products/Sales) as Products/SalesCount would return: [ { Name: “Food”, Products:[{SalesCount: 4}] }, { Name: “Non-Food”, Products:[{SalesCount: 4}] } ] This is consistent with the behavior of other aggregation functions: the aliased aggregated value is a sibling of the original value. If we add the product name to the select list, we get a similarly structured result. GET ~/ProductGroups?$select=Name,Products/Name &$expand=Products &$aggregate=count(Products/Sales) as Products/SalesCount would return: [ { Name: “Food”, Products:[{Name: {Name: { Name: “Non-Food”, Products:[{Name: {Name: “Coffee”, “Sugar”, “Paper”, “Pencil”, SalesCount: SalesCount: SalesCount: SalesCount: 2}, 2}] }, 4}, 0}] } ] 3.1.2.2.5 The Standard Aggregation Function distinctCount The aggregation function distinctCount always takes the name of a property as its argument. It counts the distinct values of this property, omitting any NULL values. For navigation properties and collection properties it counts the distinct entities in the union of all entities related to entities in the group. It must always specify an alias, and the result property will have type Edm.Decimal with Scale="0" and sufficient Precision. The request GET ~/Customers?$select=Country &$expand=Sales &$aggregate=distinctCount(Sales/Product) as Sales/DistinctProducts returns the number of different products sold per country: [ { Country: “Netherlands”, Sales: [{ DistinctProducts: 2 }] }, { Country: “USA”, Sales: [{ DistinctProducts: 3 }] } ] odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 15 of 31 Note that DistinctProducts is located in the Sales entity as it is an aggregate of Sales/Product: as stated above aliasing just “renames” a property, it does not relocate it. 3.1.3 Boundary Conditions The aggregation behavior relies on the context specified by the properties in $select that are not listed in $aggregate. If for any of these properties the service provider is not able to group same values in a single aggregate entity, it MUST reject the request with an error response. It MUST NOT apply any implicit rule to form aggregate entities indirectly by another property related to it in some way. In the above example, although product IDs can be basically any string and amount values can be any decimal value, if the provider is able to group their values, then the request for their distinct value combinations GET ~/Sales?$select=Product/Name,Amount &$expand=Product &$aggregate would result in [ { { { { { { Product: Product: Product: Product: Product: Product: { { { { { { Name: Name: Name: Name: Name: Name: “Coffee” “Coffee” “Paper” “Paper” “Paper” “Sugar” }, }, }, }, }, }, Amount: Amount: Amount: Amount: Amount: Amount: 4 8 1 2 4 2 }, }, }, }, }, } ] 3.2 $rollup The $rollup query option is used to request additional levels of aggregation in addition to the most granular level defined by $select and $aggregate. Adding the $rollup query option results in adding additional entries to the result representing the aggregated values produced as a result of the rollup, in which, starting with the deepest level, progressively more properties, based on the specified named hierarchies (see section 4.3) 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. Revisiting the second question of the motivating example in section 2.3 we can retrieve the first seven rows of the desired result with the request GET ~Sales?$select=Customer/Country,Customer/Name, Product/ProductGroup/Name,Product/Name,Amount,Currency/Code &$expand=Customer,Product,Product/ProductGroup &$aggregate=Amount resulting in [ { Customer: { Country: “USA”, Name: “Joe” }, Product: { ProductGroup: { Name: “Non-Food” }, Name: “Paper” }, Amount: 1, Currency: { Code: "USD" } }, { Customer: { Country: “USA”, Name: “Joe” }, Product: { ProductGroup: { Name: “Food” }, Name: “Sugar” }, Amount: 2, Currency: { Code: "USD" } }, odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 16 of 31 { Customer: { Country: “USA”, Name: “Joe” }, Product: { ProductGroup: { Name: “Food” }, Name: “Coffee” }, Amount: 4, Currency: { Code: "USD" } }, { Customer: { Country: “USA”, Name: “Sue” }, Product: { ProductGroup: { Name: “Food” }, Name: “Coffee” }, Amount: 8, Currency: { Code: "USD" } }, { Customer: { Country: “USA”, Name: “Sue” }, Product: { ProductGroup: { Name: “Non-Food” }, Name: “Paper” }, Amount: 4, Currency: { Code: "USD" } }, { Customer: { Country: “Netherlands”, Name: “Sue” }, Product: { ProductGroup: { Name: “Food” }, Name: “Sugar” }, Amount: 2, Currency: { Code: "EUR" } }, { Customer: { Country: “Netherlands”, Name: “Sue” }, Product: { ProductGroup: { Name: “Non-Food” }, Name: “Paper” }, Amount: 3, Currency: { Code: "EUR" } } ] To produce the missing fifteen subtotals we add a $rollup query option: GET ~Sales?$select=Customer/Country,Customer/Name, Product/ProductGroup/Name,Product/Name,Amount,Currency/Code &$expand=Customer,Product,Product/ProductGroup &$aggregate=Amount &$rollup=(Customer/Country,Customer/Name), (Product/ProductGroup/Name,Product/Name) which returns the same seven entires shown above plus additional fifteen entries representing the aggregated subtotals: [ ... { Customer: { Country: “USA” }, Product: { ProductGroup: { Name: “Food” }, Name: “Sugar” }, Amount: 2, Currency: { Code: "USD" } }, { Customer: { Country: “USA” }, Product: { ProductGroup: { Name: “Food” }, Name: “Coffee” }, Amount: 12, Currency: { Code: "USD" } }, { Customer: { Country: “USA” }, Product: { ProductGroup: { Name: “Non-Food” }, Name: “Paper” }, Amount: 5, Currency: { Code: "USD" } }, { Customer: { Country: “Netherlands” }, Product: { ProductGroup: { Name: “Food” }, Name: “Sugar” }, Amount: 2, Currency: { Code: "EUR" } }, { Customer: { Country: “Netherlands” }, Product: { ProductGroup: { Name: “Non-Food” }, Name: “Paper” }, Amount: 1, Currency: { Code: "EUR" } }, { Customer: { Country: “USA”, Name: “Joe” }, Product: { ProductGroup: { Name: “Food” } }, Amount: 6, Currency: { Code: "USD" } }, ... { Customer: { Country: “USA” }, Product: { ProductGroup: { Name: “Food” } }, Amount: 14, Currency: { Code: "USD" } }, odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 17 of 31 ... ] The properties that are aggregated away during rollup 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. 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. That’s 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=(Customer/Country,Customer/Name), ($all,Product/ProductGroup/Name,Product/Name) will return five additional entities rolled up across all product groups: [ ... { Customer: { Amount: 5, }, { Customer: { Amount: 5, }, { Customer: { Amount: 7, }, { Customer: { Amount: 12, }, { Customer: { Amount: 19, }, Country: “Netherlands”, Name: “Sue” }, Currency: { Code: "EUR" } Country: “Netherlands” }, Currency: { Code: "EUR" } Country: “USA”, Name: “Joe” }, Currency: { Code: "USD" } Country: “USA”, Name: “Sue” }, Currency: { Code: "USD" } Country: “USA” }, Currency: { Code: "USD" } ] 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. How does $rollup work together with $top, $skip, and $inlinecount? Will it only take the finest level of granularity into account and add subtotals for a group when a group boundary is part of that chunk? 3.3 Identifying Aggregated Entities Aggregated entities have the same structure as the individual entities from which they have been calculated, so the shape of the results can still mirror the shape described by the service. However, aggregated entities have different ids and self links than unaggregated entities. An aggregated entity’s self link must encode the necessary information to re-retrieve that particular aggregate value, for instance the set of unique groupable property values that the aggregate represents. Therefore, every entity must provide the canonical URI by which it can be identified. This URI can be constructed from the original URI requesting the aggregate result whose $filter expression is extended by conditions for the entity’s restrictions for all groupable properties in $select. The same restrictions can also be used for constructing an entity id which must be provided for every entity. Looking again to the sample request for getting sales amounts per product and country presented in section 3.1.2.1Error! Reference source not found. GET ~/Sales?$select=Customer/Country,Product/Name,Amount &$expand=Customer,Product &$aggregate=Amount will return corresponding metadata as shown here for a single entity: odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 18 of 31 [ { odata.id: “~/Sales(Customer-Country=‘Netherlands’,Product-Name=‘Paper’)”, odata.readLink: “~/Sales?$select=Customer/Country,Product/Name,Amount&$expand=Customer,Product &$aggregate=Amount&$filter=Customer(Country eq ‘Netherlands’),Product(Name eq ‘Paper’)”, Customer: { Country: “Netherlands” }, Product: { Name: “Paper” }, Amount: 3 }, ... ] 3.4 Sequencing (Sprint 2) Elaborate for next working draft version The core system query options are processed in the following sequence: $filter $inlinecount $orderby $skiptoken $skip $top $expand $select $format The two new aggregation-related system query options are integrated into this processing sequence: $aggregate (taking $select into account) $filter (restricted to properties in $select and added by $aggregate) $inlinecount $orderby (restricted to properties in $select and added by $aggregate) $skiptoken $skip $top $expand (restricted to navigation properties used in $select) $select $rollup $format Applying aggregation first covers the most prominent use cases like “find the five best-selling products per country”. Yet it is insufficient to answer more sophisticated questions like “how much money do we make with small sales”, which requires filtering the base set before applying the aggregation. To enable this type of question, and even more sophisticated ones, we introduce the concept of sequencing: defining in which order the system query options are to be applied. This is done by separating the system query options with a colon instead of an ampersand: GET ~/Sales?$filter=Amount le 1 :$select=Amount :$aggregate=Amount means “filter first, then aggregate”, and results in [ { Amount: 2 } ] odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 19 of 31 Sequences can contain the system query options $aggregate $filter $orderby $rollup $select Each of these system query options can appear multiple times: GET ~Sales?$filter=Amount le 5.1 :$select=Product/Name,Amount :$aggregate=Amount :$filter=Amount ge 100000 More examples: Population per country GET ~/Cities?$select=Country/Name,Continent/Name,Population &$aggregate=Population All countries with megacities GET ~/Cities?$select=Country/Name,Continent/Name,Population :$filter=Population ge 1000000 :$aggregate=Population :$rollup=(Continent/Name,Country/Name) All countries with millions of city dwellers and their continents GET ~/Cities?$select=Country/Name,Continent/Name,Population :$aggregate=Population :$filter=Population ge 1000000 :$rollup=(Continent/Name,Country/Name) All continents and all countries with millions of city dwellers GET ~/Cities?$select=Country/Name,Continent/Name,Population :$aggregate=Population :$rollup=(Continent/Name,Country/Name) :$filter=Population ge 1000000 Allow using new filtering on expanded items together with sequencing GET ~/SalesOrders?$filter=State eq 'incomplete' :$expand=Items($filter=not Shipped),Customer :$select=Items/Amount,Customer/Country :$aggregate=Items/Amount Express different functions in different directions with sequencing GET ~/Sales?$expand=Customer,Time :$select=Customer/Country,Time/Month,Time/Date,Amount :$aggregate=Amount :$select=Customer/Country,Time/Month,Amount :$aggregate=Amount(average) Now enter $rollup GET ~/Sales?$expand=Customer,Time :$select=Customer/Country,Customer/Name, Time/Year,Time/Month,Time/Date,Amount odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 20 of 31 :$aggregate=Amount :$select=Customer/Country,Customer/Name,Time/Year,Time/Month,Amount :$aggregate=Amount.average :$rollup=(Customer/Country-sum-Customer/Name), (Time/Year-average-Time/Month) Alternative notations: sum(Amount) - Amount(sum) - Amount.sum - sum(Amount over Time) 3.5 Cross-Joins (Sprint 3) Old section on Queries Spanning Entity Sets OData supports querying related entities through defining relationship and navigation properties in the data model. These navigation paths help guide simple consumers in understanding and navigating relationships. 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. 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”}] } ] Where useful navigations exist it is beneficial to expose those as explicit navigation properties in the model, but the ability to pose queries that span entity sets not related by an association provides a mechanism for advanced consumers to pose queries across entity sets based on other join conditions, such as relationships implied by a measure. For example, the consumer 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 Where the result would look like: [ { { { { { { Sales:[{Amount:4}], Sales:[{Amount:4}], Sales:[{Amount:8}], Sales:[{Amount:1}], Sales:[{Amount:1}], Sales:[{Amount:5}], Products:[{Name:“Sugar”}],Time:[{Date:1/2/12}] }, Products:[{Name:“Coffee”}],Time:[{Date:1/1/12}] }, Products:[{Name:“Coffee”}],Time:[{Date:1/2/12}] }, Products:[{Name:“Paper”}],Time:[{Date:1/1/12}] }, Products:[{Name:“Paper”}],Time:[{Date:1/2/12}] }, Products:[{Name:“Paper”}],Time:[{Date:1/3/12}] }, ] The entity container may be annotated with measures that can be applied to aggregations from the entity container. Applying such a term to the SalesData entity container for an "ActualOverSales" aggregate would look like: odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 21 of 31 <EntityContainer Name="SalesData" m:IsDefaultEntityContainer="true"> <...> <TypeAnnotation Term="Measures"> <Collection> <Record> <PropertyValue Property="Name" String="ActualOverSales" /> <PropertyValue Property="Type" String="Edm.Integer" /> </Record> </Collection> </TypeAnnotation> </EntityContainer> The SalesData entity container would support the query: GET ~SalesData?$select=Products/Name,Time/Month, ActualOverSales &$expand=Products,Time &$aggregate=ActualOverSales with the result: [ { ActualOverSales:10, Products:[{Name:“Sugar”}],Time:[{Month:“2012/1”}] }, { ActualOverSales:-20, Products:[{Name:“Coffee”}],Time:[{Month:“2012/1”}] }, { ActualOverSales:25, Products:[{Name:“Paper”}],Time:[{Month:“2012/1”}] }, ] 3.6 ABNF for Extended URL Conventions Formally define syntax for $aggregate, $rollup, and sequencing as delta to the core URL ABNF aggregate = "$aggregate" [ "=" ( "*" / aggregateItem *( COMMA aggregateItem ) ) ] aggregateItem = propertyWithPath / aggregationFunction "(" propertyWithPath ")" [ WSP "as" WSP newDynamicPropertyWithSamePath ] rollup = "$rollup=" rollupAxis *( COMMA rollupAxis ) rollupAxis = annotatedHierarchyName / "(" ( "$all" / propertyWithPath ) *( COMMA propertyWithPath ) ")" queryOptions = sequenceOption *( "&" harmlessOption ) / queryOption *( "&" queryOption ) sequenceOption = sequencableOption 1*(":" sequencableOption ) queryOption = sequencableOption / harmlessOption harmlessOption = / / / nonSequencableOption aliasAndValue parameterNameAndValue customQueryOption sequencableOption = / / / / filter orderby aggregate rollup select odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 22 of 31 nonSequencableOption = / / / / / define skip top format inlinecount skiptoken Unrelated idea: $orderby=Size:$filter=(Top(5) or Bottom(4)) and Color eq 'green' $select provides context for $aggregate, which provides context for $rollup Are there more dependencies? Is $orderby harmless? odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 23 of 31 4 Aggregation-Related Annotations The following annotations allow to describe which data in a given entity model can be aggregated, and how. Entity sets that support aggregation are annotated with the term <ValueTerm Name="SupportsAggregation" Type="Self.SupportsAggregationType"> <Documentation> <Summary> This entity set supports the $aggregate query option </Summary> </Documentation> <ValueAnnotation Term="Core.AppliesToEntitySet" /> </ValueTerm> This term lists the properties that can be used in $aggregate and $select for aggregate queries: <ComplexType Name="SupportsAggregationType"> <Documentation> <Summary> AggregatableProperties contains the list of property names that can be used in the $aggregate query option. GroupableProperties contains the list of property names that can be used in the $select query option in conjunction with $aggregate. </Summary> </Documentation> <ValueAnnotation Term="Core.AppliesToEntitySet" /> <Property Name="AggregatableProperties" Type="Collection(Self.AggregatableProperty)" /> <Property Name="GroupableProperties" Type="Collection(Self.GroupableProperty)" /> </ComplexType> 4.1 Aggregatable Properties A property that can be used in the $aggregate is described with <ComplexType Name="AggregatableProperty"> <Property Name="Name" Type="Edm.String"> <ValueAnnotation Term="Core.IsPropertyName" /> </Property> <Property Name="DefaultAggregrationFunction" Type="Edm.String" Nullable="true"/> <Property Name="AcceptedAggregrationFunctions" Type="Collection(Edm.String)" Nullable="true"/> </ComplexType> Allowed values for the default aggregation function and the accepted aggregation functions are the standard aggregation functions sum, min, max, and average, or a namespace-qualified name identifying a producer-specific function. Missing; dependency of number on unit (here: Amount on Currency/Code); use/define separate CurrQuan vocabulary or have a more abstract aggregation-specific “dependency” term? A service SHOULD accept the shorthand $aggregate=* and aggregate all properties that have been listed in AggregatableProperties and have a declared default aggregation function. In our example only the Amount property has been listed, and its default aggregation function is sum, so the request GET ~/Sales?$select=Customer/Country,Product/Name,Amount &$expand=Customer,Product odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 24 of 31 &$aggregate=Amount 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” (aggregatable properties) and “dimensions” (groupable properties), as it does what consumers of that cube would expect. In this kind of model “measures” will always have a default aggregation function, which may be determined depending on the properties listed in $select. If a property 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. TODO: “floating measures” are only defined for a subset of the “dimensions” of a “cube”. If represented as declared properties, they would not possess values for queries without $aggregate, which would be somewhat confusing. Alternatively they can be represented as dynamic properties and would be declared in an additional collection property of the SupportsAgggregation annotation on entity set level: <ComplexType Name="FloatingMeasure" Base="Self.AggregatableProperty"> <Property Name="Type" Type="Edm.String" /> <Property Name="InputProperties" Type="Collection(Edm.String)"> <ValueAnnotation Term="Core.IsPropertyName" /> </Property> </ComplexType> 4.2 Groupable Properties A property that can be used to define the aggregation scope is described with <ComplexType Name="GroupableProperty"> <Property Name="Name" Type="Edm.String"> <ValueAnnotation Term="Core.IsPropertyName" /> </Property> <Property Name="DependentProperties" Type="Collection(Edm.String) Nullable="true""> <ValueAnnotation Term="Core.IsPropertyName"/> </Property> </ComplexType> It MAY specify a list of properties that are functionally dependent on the grouping property and should not be used on $select without the grouping property. In our example the customer name could be listed as dependent on the customer id. Producers MUST respond with 400 Bad Request if a dependent property is part of the $select list without its groupable property. 4.3 Hierarchies (Sprint 2) A hierarchy is an arrangement of groupable properties whose values are represented as being “above”, “below”, or “at the same level as” one another. <ComplexType Name="Hierarchy" Abstract="true"> <ValueAnnotation Term="Core.AppliesToProperty"/> <Property Name="Name" Type="String" Nullable="false"/> </ComplexType> We distinguish between two types of hierarchies: leveled hierarchies … <ComplexType Name="LeveledHierarchy" BaseType="Self.Hierarchy"> <!--Ordered list of properties in the hierarchy--> <Property Name="Levels" Type="Collection(Edm.String)" Nullable="false"> <ValueAnnotation Term="Core.IsPropertyName"/> </Property> odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 25 of 31 </ComplexType> Using them in $rollup Drill via links Functions operating on hierarchies for hierarchy selection $filter=IsSiblingOf(’CostCenterHierarchy’,’123’) → require to state path leading to cost center? $filter=IsDescendantOf(‘EmployeeHierarchy’.’SeBigBoss’) Or use +/ path notation instead Amount(sum,average over Time) with Time=Hierarchy(Year,Quarter,Month,Date) From old examples section: 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. From old Hierarchies section: 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=" Core.IsPropertyName "/> </Property> </ComplexType> <ComplexType Name="RecursiveHierarchy"> <Property Name="HierarchyNodeIDProperty" Type="Edm.String" Nullable="false"> <TypeAnnotation Term=" Core.IsPropertyName "/> </Property> <Property Name="HierarchyParentNodeIDProperty" Type="Edm.String" Nullable="false"> <TypeAnnotation Term=" Core.IsPropertyName "/> </Property> <Property Name="HierarchyLevelProperty" Type="Edm.String"> <TypeAnnotation Term=" Core.IsPropertyName "/> </Property> </ComplexType> These terms are applied to the Sales entity type, so that they can be used by consumers for requesting additional aggregation levels, see section Error! Reference source not found.: <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" odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 26 of 31 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> 4.4 Functions and Actions on Aggregated Entities (Sprint 2) Functions and actions with a binding parameter may or may not be applicable to an aggregated entity. By default we assume such bindings are not applicable to aggregated entities, and define a term to annotate those functions/actions that are also applicable to (a subset of the) aggregated entities. The applicability most likely will depend on the aggregation level, so these functions/actions must not be “always bindable”. Assume the product is an implicit input for a function bindable to Sales, then aggregating away the product makes this function inapplicable. <ComplexType Name="AvailableOnAggregates" BaseType="Core.Tag"> <Property Name="DependsOnProperties" Type="Collection(String)" Nullable="true"> <TypeAnnotation Term=" Core.IsPropertyName "/> </Property> </ComplexType> odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 27 of 31 5 Conformance The last numbered section in the specification must be the Conformance section. Conformance Statements/Clauses go here. [Remove # marker] odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 28 of 31 Appendix A. Acknowledgments The following individuals have participated in the creation of this specification and are gratefully acknowledged: Participants: [Participant Name, Affiliation | Individual Member] [Participant Name, Affiliation | Individual Member] odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 29 of 31 Appendix B. Non-Normative Text text B.1 Subsidiary section text B.1.1 Sub-subsidiary section text odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 30 of 31 Appendix C. Revision History Revision 01 Date 2012-mm-dd Editor Ralf Handl Changes Made Translated contribution into OASIS format odata-data-aggregation-ext-v1.0-wd01 Working Draft 01 Standards Track Draft Copyright © OASIS Open 2012. All Rights Reserved. 16 October 2012 Page 31 of 31
© Copyright 2026 Paperzz