From 6cc0ce5f1167585c6a7c969850f59cf9578549ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20Thei=C3=9Fen?= Date: Wed, 18 Oct 2023 17:25:02 +0200 Subject: [PATCH] Changes to Aggregation vocabulary for CS03 (#178) * ODATA-1218 - Enhance hierarchy processing (#121) * ODATA-1142 - resolved - Clarify MaxDistance is an optional parameter in hierarchy filter functions (#78) * ODATA-1136 - resolved - Allow hierarchy filter functions for ancestors and descendants ... (#77) * ODATA-1384 - Clarify usage of annotation qualifiers as references to hierarchies (#125) * Digital/Physical -> Food/NonFood (#186) * Aggregation voc - update descriptions: remove rollupall, add rolluprecursive (#193) * Preview on new transformations (#218) * Changes resulting from OData-Aggr walkthrough (#191) * remove commented-out Cycle term (#221) --- .github/workflows/nodejs.yml | 2 +- ...Data.Aggregation.V1.SalesModel-sample.json | 310 +++++++++++++ ...OData.Aggregation.V1.SalesModel-sample.xml | 206 +++++++++ examples/README.md | 11 +- vocabularies/Org.OData.Aggregation.V1.json | 418 +++++++++++++----- vocabularies/Org.OData.Aggregation.V1.md | 251 +++++++---- vocabularies/Org.OData.Aggregation.V1.xml | 386 ++++++++++++---- vocabularies/Org.OData.Core.V1.json | 11 + vocabularies/Org.OData.Core.V1.md | 1 + vocabularies/Org.OData.Core.V1.xml | 8 + 10 files changed, 1317 insertions(+), 287 deletions(-) create mode 100644 examples/Org.OData.Aggregation.V1.SalesModel-sample.json create mode 100644 examples/Org.OData.Aggregation.V1.SalesModel-sample.xml diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 1d62829c..0ca2c3fa 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: - node-version: [18.x,20.x] + node-version: [20.x] steps: - uses: actions/checkout@v3 diff --git a/examples/Org.OData.Aggregation.V1.SalesModel-sample.json b/examples/Org.OData.Aggregation.V1.SalesModel-sample.json new file mode 100644 index 00000000..087d2c7f --- /dev/null +++ b/examples/Org.OData.Aggregation.V1.SalesModel-sample.json @@ -0,0 +1,310 @@ +{ + "$Version": "4.0", + "$Reference": { + "https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Core.V1.json": { + "$Include": [ + { + "$Namespace": "Org.OData.Core.V1", + "$Alias": "Core" + } + ] + }, + "https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Aggregation.V1.json": { + "$Include": [ + { + "$Namespace": "Org.OData.Aggregation.V1", + "$Alias": "Aggregation" + } + ] + } + }, + "org.example.odata.salesservice": { + "$Alias": "SalesModel", + "Currency": { + "$Kind": "EntityType", + "$Key": [ + "Code" + ], + "Code": { + "$Nullable": true + }, + "Name": { + "$Nullable": true, + "@Core.IsLanguageDependent": true + } + }, + "Time": { + "$Kind": "EntityType", + "$Key": [ + "Date" + ], + "Date": { + "$Type": "Edm.Date" + }, + "Month": {}, + "Quarter": {}, + "Year": { + "$Type": "Edm.Int16" + }, + "@Aggregation.LeveledHierarchy#TimeHierarchy": [ + "Year", + "Quarter", + "Month" + ] + }, + "Category": { + "$Kind": "EntityType", + "$Key": [ + "ID" + ], + "ID": {}, + "Name": { + "$Nullable": true + }, + "Products": { + "$Kind": "NavigationProperty", + "$Collection": true, + "$Type": "SalesModel.Product", + "$Partner": "Category" + } + }, + "SalesOrganization": { + "$Kind": "EntityType", + "$Key": [ + "ID" + ], + "ID": {}, + "Name": { + "$Nullable": true + }, + "Superordinate": { + "$Kind": "NavigationProperty", + "$Type": "SalesModel.SalesOrganization", + "$Nullable": true + }, + "Sales": { + "$Kind": "NavigationProperty", + "$Collection": true, + "$Type": "SalesModel.Sale", + "$Partner": "SalesOrganization" + }, + "@Aggregation.RecursiveHierarchy#SalesOrgHierarchy": { + "NodeProperty": "ID", + "ParentNavigationProperty": "Superordinate" + } + }, + "Customer": { + "$Kind": "EntityType", + "$Key": [ + "ID" + ], + "ID": {}, + "Name": { + "$Nullable": true + }, + "Country": { + "$Nullable": true + }, + "Sales": { + "$Kind": "NavigationProperty", + "$Collection": true, + "$Type": "SalesModel.Sale", + "$Partner": "Customer" + } + }, + "Product": { + "$Kind": "EntityType", + "$Abstract": true, + "$Key": [ + "ID" + ], + "ID": {}, + "Name": { + "$Nullable": true + }, + "Color": { + "$Nullable": true + }, + "TaxRate": { + "$Type": "Edm.Decimal", + "$Nullable": true, + "$Scale": 2 + }, + "Category": { + "$Kind": "NavigationProperty", + "$Type": "SalesModel.Category", + "$Partner": "Products" + }, + "Sales": { + "$Kind": "NavigationProperty", + "$Collection": true, + "$Type": "SalesModel.Sale", + "$Partner": "Product" + }, + "@Aggregation.LeveledHierarchy#ProductHierarchy": [ + "Category/Name", + "Name" + ] + }, + "FoodProduct": { + "$Kind": "EntityType", + "$BaseType": "SalesModel.Product", + "Rating": { + "$Type": "Edm.Byte", + "$Nullable": true + } + }, + "NonFoodProduct": { + "$Kind": "EntityType", + "$BaseType": "SalesModel.Product", + "RatingClass": { + "$Nullable": true + } + }, + "Sale": { + "$Kind": "EntityType", + "$Key": [ + "ID" + ], + "ID": {}, + "Amount": { + "$Type": "Edm.Decimal", + "$Nullable": true, + "$Scale": 2, + "@Aggregation.ContextDefiningProperties": [ + "Currency/Code" + ] + }, + "Currency": { + "$Kind": "NavigationProperty", + "$Type": "SalesModel.Currency" + }, + "SalesOrganization": { + "$Kind": "NavigationProperty", + "$Type": "SalesModel.SalesOrganization", + "$Nullable": true, + "$Partner": "Sales" + }, + "Product": { + "$Kind": "NavigationProperty", + "$Type": "SalesModel.Product", + "$Nullable": true, + "$Partner": "Sales" + }, + "Customer": { + "$Kind": "NavigationProperty", + "$Type": "SalesModel.Customer", + "$Nullable": true, + "$Partner": "Sales" + }, + "Time": { + "$Kind": "NavigationProperty", + "$Type": "SalesModel.Time", + "$Nullable": true + } + }, + "SalesData": { + "$Kind": "EntityContainer", + "Time": { + "$Collection": true, + "$Type": "SalesModel.Time" + }, + "Categories": { + "$Collection": true, + "$Type": "SalesModel.Category", + "$NavigationPropertyBinding": { + "Products": "Products" + } + }, + "SalesOrganizations": { + "$Collection": true, + "$Type": "SalesModel.SalesOrganization", + "$NavigationPropertyBinding": { + "Superordinate": "SalesOrganizations", + "Sales": "Sales" + } + }, + "Customers": { + "$Collection": true, + "$Type": "SalesModel.Customer", + "$NavigationPropertyBinding": { + "Sales": "Sales" + } + }, + "Products": { + "$Collection": true, + "$Type": "SalesModel.Product", + "$NavigationPropertyBinding": { + "Category": "Categories", + "Sales": "Sales" + } + }, + "Sales": { + "$Collection": true, + "$Type": "SalesModel.Sale", + "$NavigationPropertyBinding": { + "SalesOrganization": "SalesOrganizations", + "Customer": "Customers", + "Product": "Products", + "Time": "Time" + } + } + }, + "$Annotations": { + "SalesModel.SalesData/Sales": { + "@Aggregation.ApplySupported": { + "Rollup": "MultipleHierarchies", + "GroupableProperties": [ + "Amount", + "Product/ID", + "Product/Name", + "Product/Category/ID", + "Product/Category/Name", + "Customer", + "Customer/ID", + "Customer/Name", + "Customer/Country", + "Time", + "Time/Month", + "Time/Year" + ], + "AggregatableProperties": [ + { + "Property": "Amount", + "SupportedAggregationMethods": [ + "sum", + "min", + "max" + ], + "RecommendedAggregationMethod": "sum" + } + ] + }, + "@Aggregation.CustomAggregate#Forecast@Aggregation.ContextDefiningProperties": [ + "Currency/Code" + ], + "@Aggregation.CustomAggregate#Forecast": "Edm.Decimal" + }, + "SalesModel.SalesData/Customers": { + "@Aggregation.ApplySupported": { + "Rollup": "None", + "GroupableProperties": [ + "Name", + "Country" + ] + } + }, + "SalesModel.SalesData/Products": { + "@Aggregation.ApplySupported": { + "Rollup": "None", + "GroupableProperties": [ + "Name", + "SalesModel.FoodProduct/Rating", + "SalesModel.NonFoodProduct/RatingClass" + ] + } + } + } + }, + "$EntityContainer": "org.example.odata.salesservice.SalesData" +} \ No newline at end of file diff --git a/examples/Org.OData.Aggregation.V1.SalesModel-sample.xml b/examples/Org.OData.Aggregation.V1.SalesModel-sample.xml new file mode 100644 index 00000000..c6ecd330 --- /dev/null +++ b/examples/Org.OData.Aggregation.V1.SalesModel-sample.xml @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Year + Quarter + Month + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Category/Name + Name + + + + + + + + + + + + + + + + + + Currency/Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Amount + Product/ID + Product/Name + Product/Category/ID + Product/Category/Name + Customer + Customer/ID + Customer/Name + Customer/Country + Time + Time/Month + Time/Year + + + + + + + + + sum + min + max + + + + + + + + + + + + Currency/Code + + + + + + + + + + + Name + Country + + + + + + + + + + + + Name + SalesModel.FoodProduct/Rating + SalesModel.NonFoodProduct/RatingClass + + + + + + + + diff --git a/examples/README.md b/examples/README.md index 0cdb9643..76b06398 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,5 +1,10 @@ # Examples +## [Capabilities](Org.OData.Capabilities.V1.capabilities.md) + +The example shows several ways to specify insert and update restrictions on various levels. + + ## [Filter Restrictions](Org.OData.Capabilities.V1.FilterRestrictions-sample.xml) This example shows a filter restriction to "multiple values", i.e. to `eq` or `in` expressions. @@ -14,5 +19,7 @@ This example shows a type definition of a "JSON Stream" property with an attache Permissions property defined under Read, Insert, Update, Delete in the capabilities vocabulary, gives the ability to list Auth flows and scopes within those flows required to perform operations on an entity set. -## [Capabilities](Org.OData.Capabilities.V1.capabilities.md) -The example shows several ways to specify insert and update restrictions on various levels. + +## [Sales Model](Org.OData.Aggregation.V1.SalesModel.md) + +Example model used in the [OData-Aggr] specification. diff --git a/vocabularies/Org.OData.Aggregation.V1.json b/vocabularies/Org.OData.Aggregation.V1.json index 78f98eb0..e41e3e6a 100644 --- a/vocabularies/Org.OData.Aggregation.V1.json +++ b/vocabularies/Org.OData.Aggregation.V1.json @@ -1,5 +1,5 @@ { - "$Version": "4.0", + "$Version": "4.01", "$Reference": { "https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Core.V1.json": { "$Include": [ @@ -9,19 +9,19 @@ } ] }, - "https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Capabilities.V1.json": { + "https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Validation.V1.json": { "$Include": [ { - "$Namespace": "Org.OData.Capabilities.V1", - "$Alias": "Capabilities" + "$Namespace": "Org.OData.Validation.V1", + "$Alias": "Validation" } ] }, - "https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Validation.V1.json": { + "https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Capabilities.V1.json": { "$Include": [ { - "$Namespace": "Org.OData.Validation.V1", - "$Alias": "Validation" + "$Namespace": "Org.OData.Capabilities.V1", + "$Alias": "Capabilities" } ] } @@ -29,6 +29,7 @@ "Org.OData.Aggregation.V1": { "$Alias": "Aggregation", "@Core.Description": "Terms to describe which data in a given entity model can be aggregated, and how.", + "@Core.LongDescription": "Related to the specfication document [OData-Data-Agg-v4.0](http://docs.oasis-open.org/odata/odata-data-aggregation-ext/v4.0/odata-data-aggregation-ext-v4.0.html).", "@Core.Links": [ { "rel": "alternate", @@ -65,53 +66,11 @@ }, "ApplySupportedBase": { "$Kind": "ComplexType", + "@Core.LongDescription": "Services that do not fully implement a certain aggregation-related functionality may document\n this by annotating the [`ApplySupported`](#ApplySupported) or [`ApplySupportedDefaults`](#ApplySupportedDefaults)\n annotation with a description.", "Transformations": { "$Collection": true, - "@Core.Description": "Transformations that can be used in `$apply`", - "@Validation.AllowedValues": [ - { - "Value": "aggregate" - }, - { - "Value": "groupby" - }, - { - "Value": "concat" - }, - { - "Value": "identity" - }, - { - "Value": "filter" - }, - { - "Value": "expand" - }, - { - "Value": "search" - }, - { - "Value": "compute" - }, - { - "Value": "bottomcount" - }, - { - "Value": "bottomsum" - }, - { - "Value": "bottompercent" - }, - { - "Value": "topcount" - }, - { - "Value": "topsum" - }, - { - "Value": "toppercent" - } - ] + "$Type": "Aggregation.Transformation", + "@Core.Description": "Transformations that can be used in `$apply`" }, "CustomAggregationMethods": { "$Collection": true, @@ -121,6 +80,11 @@ "$Type": "Aggregation.RollupType", "$DefaultValue": "MultipleHierarchies", "@Core.Description": "The service supports rollup hierarchies in a `groupby` transformation" + }, + "From": { + "$Type": "Edm.Boolean", + "$DefaultValue": true, + "@Core.Description": "The service supports the `from` keyword in an `aggregate` transformation" } }, "ApplySupportedType": { @@ -139,7 +103,7 @@ }, "GroupableProperties": { "$Collection": true, - "$Type": "Edm.PropertyPath", + "$Type": "Edm.AnyPropertyPath", "@Core.Description": "A non-empty collection indicates that only the listed properties of the annotated target are supported by the `groupby` transformation" }, "AggregatableProperties": { @@ -165,6 +129,105 @@ "@Core.Description": "Recommended method for aggregating values of the property" } }, + "Transformation": { + "$Kind": "TypeDefinition", + "$UnderlyingType": "Edm.String", + "@Core.Description": "A transformation that can be used in `$apply`", + "@Validation.AllowedValues": [ + { + "Value": "aggregate", + "@Core.Description": "OData-Data-Agg-v4.0, section 3.2.1" + }, + { + "Value": "groupby", + "@Core.Description": "OData-Data-Agg-v4.0, section 3.2.3" + }, + { + "Value": "concat", + "@Core.Description": "OData-Data-Agg-v4.0, section 3.2.2" + }, + { + "Value": "identity", + "@Core.Description": "OData-Data-Agg-v4.0, section 3.4.1" + }, + { + "Value": "filter", + "@Core.Description": "OData-Data-Agg-v4.0, section 3.3.2" + }, + { + "Value": "search", + "@Core.Description": "OData-Data-Agg-v4.0, section 3.3.4" + }, + { + "Value": "nest", + "@Core.Description": "OData-Data-Agg-v4.0, section 3.5.2" + }, + { + "Value": "addnested", + "@Core.Description": "OData-Data-Agg-v4.0, section 3.4.3" + }, + { + "Value": "join", + "@Core.Description": "OData-Data-Agg-v4.0, section 3.5.1" + }, + { + "Value": "outerjoin", + "@Core.Description": "OData-Data-Agg-v4.0, section 3.5.1" + }, + { + "Value": "compute", + "@Core.Description": "OData-Data-Agg-v4.0, section 3.4.2" + }, + { + "Value": "bottomcount", + "@Core.Description": "OData-Data-Agg-v4.0, section 3.3.1.1" + }, + { + "Value": "bottomsum", + "@Core.Description": "OData-Data-Agg-v4.0, section 3.3.1.3" + }, + { + "Value": "bottompercent", + "@Core.Description": "OData-Data-Agg-v4.0, section 3.3.1.2" + }, + { + "Value": "topcount", + "@Core.Description": "OData-Data-Agg-v4.0, section 3.3.1.1" + }, + { + "Value": "topsum", + "@Core.Description": "OData-Data-Agg-v4.0, section 3.3.1.3" + }, + { + "Value": "toppercent", + "@Core.Description": "OData-Data-Agg-v4.0, section 3.3.1.2" + }, + { + "Value": "orderby", + "@Core.Description": "OData-Data-Agg-v4.0, section 3.3.3" + }, + { + "Value": "top", + "@Core.Description": "OData-Data-Agg-v4.0, section 3.3.6" + }, + { + "Value": "skip", + "@Core.Description": "OData-Data-Agg-v4.0, section 3.3.5" + }, + { + "Value": "ancestors", + "@Core.Description": "OData-Data-Agg-v4.0, section 6.2.1" + }, + { + "Value": "descendants", + "@Core.Description": "OData-Data-Agg-v4.0, section 6.2.1" + }, + { + "Value": "traverse", + "@Core.Description": "OData-Data-Agg-v4.0, section 6.2.2" + } + ] + }, "AggregationMethod": { "$Kind": "TypeDefinition", "$UnderlyingType": "Edm.String", @@ -196,13 +259,13 @@ }, "RollupType": { "$Kind": "EnumType", - "@Core.Description": "The number of `rollup` or `rollupall` operators allowed in a `groupby` transformation", + "@Core.Description": "The number of `rollup` or `rolluprecursive` operators allowed in a `groupby` transformation", "None": 0, - "None@Core.Description": "No support for `rollup` or `rollupall` ", + "None@Core.Description": "No support for `rollup` or `rolluprecursive`", "SingleHierarchy": 1, - "SingleHierarchy@Core.Description": "Only one `rollup` or `rollupall` operator per `groupby`", + "SingleHierarchy@Core.Description": "Only one `rollup` or `rolluprecursive` operator per `groupby`", "MultipleHierarchies": 2, - "MultipleHierarchies@Core.Description": "Full support for `rollup` and `rollupall`" + "MultipleHierarchies@Core.Description": "Full support for `rollup` and `rolluprecursive`" }, "Groupable": { "$Kind": "Term", @@ -267,171 +330,302 @@ "EntityType", "ComplexType" ], - "@Core.Description": "Defines a leveled hierarchy by defining an ordered list of properties in the hierarchy" + "@Core.Description": "Defines a leveled hierarchy (OData-Data-Agg-v4.0, section 5.5.1)" }, "RecursiveHierarchy": { "$Kind": "Term", "$Type": "Aggregation.RecursiveHierarchyType", "$AppliesTo": [ - "EntityType", - "ComplexType" + "EntityType" ], - "@Core.Description": "Defines a recursive hierarchy." + "@Core.Description": "Defines a recursive hierarchy (OData-Data-Agg-v4.0, section 5.5.2)" }, "RecursiveHierarchyType": { "$Kind": "ComplexType", "NodeProperty": { "$Type": "Edm.PropertyPath", - "@Core.Description": "Property holding the hierarchy node value" + "@Core.Description": "Primitive property holding the node identifier" }, "ParentNavigationProperty": { "$Type": "Edm.NavigationPropertyPath", - "@Core.Description": "Property for navigating to the parent node" - }, - "DistanceFromRootProperty": { - "$Type": "Edm.PropertyPath", - "$Nullable": true, - "@Core.Description": "Property holding the number of edges between the node and the root node" - }, - "IsLeafProperty": { - "$Type": "Edm.PropertyPath", - "$Nullable": true, - "@Core.RequiresType": "Edm.Boolean", - "@Core.Description": "Property indicating whether the node is a leaf of the hierarchy" + "@Core.Description": "Property for navigating to the parent node(s). Its type MUST be the entity type annotated with this term, and it MUST be collection-valued or nullable single-valued." } }, + "HierarchyQualifier": { + "$Kind": "TypeDefinition", + "$UnderlyingType": "Edm.String", + "@Core.Description": "Qualifier of a [`RecursiveHierarchy`](#RecursiveHierarchy) annotation", + "@Core.LongDescription": "Every recursive hierarchy function defined in this vocabulary has\n- a parameter `HierarchyQualifier` of this type and\n- a parameter `HierarchyNodes` that is a collection of entities of a common type without multiple occurrences of the same entity.\n\n`HierarchyQualifier` is the qualifier of a `RecursiveHierarchy` annotation on the entity type of the collection\ngiven by the `HierarchyNodes` parameter. This specifies a recursive hierarchy that is evaluated by the function." + }, + "isnode": [ + { + "$Kind": "Function", + "@Core.Description": "Is the entity a node of the hierarchy specified by the [parameter pair](#HierarchyQualifier) (`HierarchyNodes`, `HierarchyQualifier`)? (See OData-Data-Agg-v4.0, section 5.5.2.1)", + "@Core.LongDescription": "If a node identifier passed to the function is null, the function returns null.", + "$Parameter": [ + { + "$Name": "HierarchyNodes", + "$Collection": true, + "$Type": "Edm.EntityType", + "@Core.Description": "A collection, given through a `$root` expression" + }, + { + "$Name": "HierarchyQualifier", + "$Type": "Aggregation.HierarchyQualifier" + }, + { + "$Name": "Node", + "$Type": "Edm.PrimitiveType", + "$Nullable": true, + "@Core.Description": "Node identifier of the putative node" + } + ], + "$ReturnType": { + "$Type": "Edm.Boolean", + "$Nullable": true + } + } + ], "isroot": [ { "$Kind": "Function", - "$IsBound": true, - "@Core.Description": "Returns true, if and only if the value of the node property of the specified hierarchy is the root of the hierarchy", + "@Core.Description": "Is the entity a root node of the hierarchy specified by the [parameter pair](#HierarchyQualifier) (`HierarchyNodes`, `HierarchyQualifier`)?", + "@Core.LongDescription": "If a node identifier passed to the function is null, the function returns null.", "$Parameter": [ { - "$Name": "Entity", - "$Type": "Edm.EntityType" + "$Name": "HierarchyNodes", + "$Collection": true, + "$Type": "Edm.EntityType", + "@Core.Description": "A collection, given through a `$root` expression" }, { - "$Name": "Hierarchy" + "$Name": "HierarchyQualifier", + "$Type": "Aggregation.HierarchyQualifier" + }, + { + "$Name": "Node", + "$Type": "Edm.PrimitiveType", + "$Nullable": true, + "@Core.Description": "Node identifier of the putative root" } ], "$ReturnType": { - "$Type": "Edm.Boolean" + "$Type": "Edm.Boolean", + "$Nullable": true } } ], "isdescendant": [ { "$Kind": "Function", - "$IsBound": true, - "@Core.Description": "Returns true, if and only if the value of the node property of the specified hierarchy is a descendant of the given parent node with a distance of less than or equal to the optionally specified maximum distance", + "@Core.Description": "Is the entity a descendant node of the ancestor node in the hierarchy specified by the [parameter pair](#HierarchyQualifier) (`HierarchyNodes`, `HierarchyQualifier`) with at most the specified distance? (See OData-Data-Agg-v4.0, section 5.5.2.1)", + "@Core.LongDescription": "If a node identifier passed to the function is null, the function returns null.", "$Parameter": [ { - "$Name": "Entity", - "$Type": "Edm.EntityType" + "$Name": "HierarchyNodes", + "$Collection": true, + "$Type": "Edm.EntityType", + "@Core.Description": "A collection, given through a `$root` expression" }, { - "$Name": "Hierarchy" + "$Name": "HierarchyQualifier", + "$Type": "Aggregation.HierarchyQualifier" }, { "$Name": "Node", - "$Type": "Edm.PrimitiveType" + "$Type": "Edm.PrimitiveType", + "$Nullable": true, + "@Core.Description": "Node identifier of the putative descendant" + }, + { + "$Name": "Ancestor", + "$Type": "Edm.PrimitiveType", + "$Nullable": true, + "@Core.Description": "Node identifier of the ancestor node" }, { "$Name": "MaxDistance", "$Type": "Edm.Int16", - "$Nullable": true + "@Core.OptionalParameter": { + "DefaultValue": 32767 + }, + "@Validation.Minimum": 1 + }, + { + "$Name": "IncludeSelf", + "$Type": "Edm.Boolean", + "@Core.Description": "Whether to include the node itself in the result", + "@Core.OptionalParameter": { + "DefaultValue": "false" + } } ], "$ReturnType": { - "$Type": "Edm.Boolean" + "$Type": "Edm.Boolean", + "$Nullable": true } } ], "isancestor": [ { "$Kind": "Function", - "$IsBound": true, - "@Core.Description": "Returns true, if and only if the value of the node property of the specified hierarchy is an ancestor of the given child node with a distance of less than or equal to the optionally specified maximum distance", + "@Core.Description": "Is the entity an ancestor node of the descendant node in the hierarchy specified by the [parameter pair](#HierarchyQualifier) (`HierarchyNodes`, `HierarchyQualifier`) with at most the specified distance? (See OData-Data-Agg-v4.0, section 5.5.2.1)", + "@Core.LongDescription": "If a node identifier passed to the function is null, the function returns null.", "$Parameter": [ { - "$Name": "Entity", - "$Type": "Edm.EntityType" + "$Name": "HierarchyNodes", + "$Collection": true, + "$Type": "Edm.EntityType", + "@Core.Description": "A collection, given through a `$root` expression" }, { - "$Name": "Hierarchy" + "$Name": "HierarchyQualifier", + "$Type": "Aggregation.HierarchyQualifier" }, { "$Name": "Node", - "$Type": "Edm.PrimitiveType" + "$Type": "Edm.PrimitiveType", + "$Nullable": true, + "@Core.Description": "Node identifier of the putative ancestor" + }, + { + "$Name": "Descendant", + "$Type": "Edm.PrimitiveType", + "$Nullable": true, + "@Core.Description": "Node identifier of the descendant node" }, { "$Name": "MaxDistance", "$Type": "Edm.Int16", - "$Nullable": true + "@Core.OptionalParameter": { + "DefaultValue": 32767 + }, + "@Validation.Minimum": 1 + }, + { + "$Name": "IncludeSelf", + "$Type": "Edm.Boolean", + "@Core.Description": "Whether to include the node itself in the result", + "@Core.OptionalParameter": { + "DefaultValue": "false" + } } ], "$ReturnType": { - "$Type": "Edm.Boolean" + "$Type": "Edm.Boolean", + "$Nullable": true } } ], "issibling": [ { "$Kind": "Function", - "$IsBound": true, - "@Core.Description": "Returns true, if and only if the value of the node property of the specified hierarchy has the same parent node as the specified node", + "@Core.Description": "Is the entity a sibling node of the other node in the hierarchy specified by the [parameter pair](#HierarchyQualifier) (`HierarchyNodes`, `HierarchyQualifier`)? (See OData-Data-Agg-v4.0, section 5.5.2.1)", + "@Core.LongDescription": "A node is not a sibling of itself. If a node identifier passed to the function is null, the function returns null.", "$Parameter": [ { - "$Name": "Entity", - "$Type": "Edm.EntityType" + "$Name": "HierarchyNodes", + "$Collection": true, + "$Type": "Edm.EntityType", + "@Core.Description": "A collection, given through a `$root` expression" }, { - "$Name": "Hierarchy" + "$Name": "HierarchyQualifier", + "$Type": "Aggregation.HierarchyQualifier" }, { "$Name": "Node", - "$Type": "Edm.PrimitiveType" + "$Type": "Edm.PrimitiveType", + "$Nullable": true, + "@Core.Description": "Node identifier of the putative sibling" + }, + { + "$Name": "Other", + "$Type": "Edm.PrimitiveType", + "$Nullable": true, + "@Core.Description": "Node identifier of the other node" } ], "$ReturnType": { - "$Type": "Edm.Boolean" + "$Type": "Edm.Boolean", + "$Nullable": true } } ], "isleaf": [ { "$Kind": "Function", - "$IsBound": true, - "@Core.Description": "Returns true, if and only if the value of the node property of the specified hierarchy has no descendants", + "@Core.Description": "Is the entity a leaf node in the hierarchy specified by the [parameter pair](#HierarchyQualifier) (`HierarchyNodes`, `HierarchyQualifier`)? (See OData-Data-Agg-v4.0, section 5.5.2.1)", + "@Core.LongDescription": "If a node identifier passed to the function is null, the function returns null.", "$Parameter": [ { - "$Name": "Entity", - "$Type": "Edm.EntityType" + "$Name": "HierarchyNodes", + "$Collection": true, + "$Type": "Edm.EntityType", + "@Core.Description": "A collection, given through a `$root` expression" + }, + { + "$Name": "HierarchyQualifier", + "$Type": "Aggregation.HierarchyQualifier" }, { - "$Name": "Hierarchy" + "$Name": "Node", + "$Type": "Edm.PrimitiveType", + "$Nullable": true, + "@Core.Description": "Node identifier of the putative leaf" } ], "$ReturnType": { - "$Type": "Edm.Boolean" + "$Type": "Edm.Boolean", + "$Nullable": true } } ], + "rollupnode": [ + { + "$Kind": "Function", + "@Core.Description": "During `rolluprecursive` for a hierarchy node, this function returns the node", + "@Core.LongDescription": "This function may only occur in the second parameter of a `groupby` transformation whose first parameter\ncontains `rolluprecursive(...)`. It is evaluated as part of the transformation `R(x)` in the \"`rolluprecursive` algorithm\"\n(OData-Data-Agg-v4.0, section 6.3). Its behavior is undefined outside of this algorithm.\n```\nSales?$apply=groupby((rolluprecursive(...)), filter(SalesOrganization eq Aggregation.rollupnode())/aggregate(...))\n```\nconstructs a rollup that contains aggregates per hierarchy node while excluding descendants from the aggregation.", + "$Parameter": [ + { + "$Name": "Position", + "$Type": "Edm.Int16", + "@Core.Description": "Position N among the `rolluprecursive` operators in the first argument of `groupby`", + "@Core.LongDescription": "Every instance in the output set of a `groupby` transformation with M `rolluprecursive` operators\n has M relationships to M nodes in M recursive hierarchies. This function returns the node x with path r to the root\n in relationship number N.\n If several such `groupby` transformations are nested, this function refers to the innermost one.", + "@Core.OptionalParameter": { + "DefaultValue": 1 + }, + "@Validation.Minimum": 1 + } + ], + "$ReturnType": { + "$Type": "Edm.EntityType" + } + } + ], + "UpPath": { + "$Kind": "Term", + "$Collection": true, + "$AppliesTo": [ + "EntityType" + ], + "@Core.Description": "The string values of the node identifiers in a path from the annotated node to a start node in a traversal of a recursive hierarchy", + "@Core.LongDescription": "This instance annotation occurs in the result set after a `traverse` transformation (OData-Data-Agg-v4.0, section 6.2.2.2).\n A use case for this is traversal with multiple parents, when this annotation takes\n as value one parent node identifier followed by one grandparent node identifier and so on." + }, "AvailableOnAggregates": { "$Kind": "Term", "$Type": "Aggregation.AvailableOnAggregatesType", "$AppliesTo": [ - "Action", "Function" ], - "@Core.Description": "This action or function is available on aggregated entities if the `RequiredProperties` are still defined" + "@Core.Description": "This function is available on aggregated entities if the `RequiredProperties` are still defined" }, "AvailableOnAggregatesType": { "$Kind": "ComplexType", "RequiredProperties": { "$Collection": true, "$Type": "Edm.PropertyPath", - "@Core.Description": "Properties required to apply this action or function" + "@Core.Description": "Properties required to apply this function" } }, "NavigationPropertyAggregationCapabilities": { diff --git a/vocabularies/Org.OData.Aggregation.V1.md b/vocabularies/Org.OData.Aggregation.V1.md index 0af77e98..2acbabb1 100644 --- a/vocabularies/Org.OData.Aggregation.V1.md +++ b/vocabularies/Org.OData.Aggregation.V1.md @@ -3,127 +3,219 @@ Terms to describe which data in a given entity model can be aggregated, and how. +Related to the specfication document [OData-Data-Agg-v4.0](http://docs.oasis-open.org/odata/odata-data-aggregation-ext/v4.0/odata-data-aggregation-ext-v4.0.html). + ## Terms Term|Type|Description :---|:---|:---------- -[ApplySupported](./Org.OData.Aggregation.V1.xml#L76:~:text=This entity set or collection supports the `$apply` system query option -[ApplySupportedDefaults](./Org.OData.Aggregation.V1.xml#L82:~:text=Default support of the `$apply` system query option for all collection-valued resources in the container
Annotating term [`ApplySupported`](#ApplySupported) for a specific collection-valued resource overrides the default support with the specified properties using PATCH semantics:
- Primitive or collection-valued properties specified in `ApplySupported` replace the corresponding properties specified in `ApplySupportedDefaults`
- Complex-valued properties specified in `ApplySupported` override the corresponding properties specified in `ApplySupportedDefaults` using PATCH semantics recursively
- Properties specified neither in `ApplySupported` nor in `ApplySupportedDefaults` have their default value -[Groupable](./Org.OData.Aggregation.V1.xml#L241:~:text=Deprecated in favor of [`ApplySupported/GroupableProperties`](#ApplySupported) -[Aggregatable](./Org.OData.Aggregation.V1.xml#L253:~:text=Deprecated in favor of [`ApplySupported/AggregatableProperties`](#ApplySupported) -[CustomAggregate](./Org.OData.Aggregation.V1.xml#L265:~:text=Dynamic property that can be used in the `aggregate` transformation
This term MUST be applied with a Qualifier, the Qualifier value is the name of the dynamic property. The value of the annotation MUST be the qualified name of a primitive type. The aggregated value will be of that type. -[ContextDefiningProperties](./Org.OData.Aggregation.V1.xml#L271:~:text=The annotated property or custom aggregate is only well-defined in the context of these properties
The context-defining properties need either be part of the result entities, or be restricted to a single value by a pre-filter operation. Examples are postal codes within a country, or monetary amounts whose context is the unit of currency. -[LeveledHierarchy](./Org.OData.Aggregation.V1.xml#L278:~:text=Defines a leveled hierarchy by defining an ordered list of properties in the hierarchy -[RecursiveHierarchy](./Org.OData.Aggregation.V1.xml#L282:~:text=Defines a recursive hierarchy. -[AvailableOnAggregates](./Org.OData.Aggregation.V1.xml#L342:~:text=This action or function is available on aggregated entities if the `RequiredProperties` are still defined +[ApplySupported](./Org.OData.Aggregation.V1.xml#L82:~:text=This entity set or collection supports the `$apply` system query option +[ApplySupportedDefaults](./Org.OData.Aggregation.V1.xml#L88:~:text=Default support of the `$apply` system query option for all collection-valued resources in the container
Annotating term [`ApplySupported`](#ApplySupported) for a specific collection-valued resource overrides the default support with the specified properties using PATCH semantics:
- Primitive or collection-valued properties specified in `ApplySupported` replace the corresponding properties specified in `ApplySupportedDefaults`
- Complex-valued properties specified in `ApplySupported` override the corresponding properties specified in `ApplySupportedDefaults` using PATCH semantics recursively
- Properties specified neither in `ApplySupported` nor in `ApplySupportedDefaults` have their default value +[Groupable](./Org.OData.Aggregation.V1.xml#L309:~:text=Deprecated in favor of [`ApplySupported/GroupableProperties`](#ApplySupported) +[Aggregatable](./Org.OData.Aggregation.V1.xml#L321:~:text=Deprecated in favor of [`ApplySupported/AggregatableProperties`](#ApplySupported) +[CustomAggregate](./Org.OData.Aggregation.V1.xml#L333:~:text=Dynamic property that can be used in the `aggregate` transformation
This term MUST be applied with a Qualifier, the Qualifier value is the name of the dynamic property. The value of the annotation MUST be the qualified name of a primitive type. The aggregated value will be of that type. +[ContextDefiningProperties](./Org.OData.Aggregation.V1.xml#L339:~:text=The annotated property or custom aggregate is only well-defined in the context of these properties
The context-defining properties need either be part of the result entities, or be restricted to a single value by a pre-filter operation. Examples are postal codes within a country, or monetary amounts whose context is the unit of currency. +[LeveledHierarchy](./Org.OData.Aggregation.V1.xml#L346:~:text=Defines a leveled hierarchy (OData-Data-Agg-v4.0, section 5.5.1) +[RecursiveHierarchy](./Org.OData.Aggregation.V1.xml#L350:~:text=Defines a recursive hierarchy (OData-Data-Agg-v4.0, section 5.5.2) +[UpPath](./Org.OData.Aggregation.V1.xml#L523:~:text=The string values of the node identifiers in a path from the annotated node to a start node in a traversal of a recursive hierarchy
This instance annotation occurs in the result set after a `traverse` transformation (OData-Data-Agg-v4.0, section 6.2.2.2). A use case for this is traversal with multiple parents, when this annotation takes as value one parent node identifier followed by one grandparent node identifier and so on. +[AvailableOnAggregates](./Org.OData.Aggregation.V1.xml#L532:~:text=This function is available on aggregated entities if the `RequiredProperties` are still defined ## Functions + +### [isnode](./Org.OData.Aggregation.V1.xml#L375:~:text= -### [isroot](./Org.OData.Aggregation.V1.xml#L302:~:text= -### [isdescendant](./Org.OData.Aggregation.V1.xml#L309:~:text= -### [isancestor](./Org.OData.Aggregation.V1.xml#L318:~:text= -### [issibling](./Org.OData.Aggregation.V1.xml#L327:~:text= -### [isleaf](./Org.OData.Aggregation.V1.xml#L335:~:text= +### [rollupnode](./Org.OData.Aggregation.V1.xml#L494:~:text=Every instance in the output set of a `groupby` transformation with M `rolluprecursive` operators has M relationships to M nodes in M recursive hierarchies. This function returns the node x with path r to the root in relationship number N. If several such `groupby` transformations are nested, this function refers to the innermost one. +[→](./Org.OData.Aggregation.V1.xml#L520:~:text= -## [ApplySupportedBase](./Org.OData.Aggregation.V1.xml#L97:~:text= -## [ApplySupportedType](./Org.OData.Aggregation.V1.xml#L154:~:text= -## [AggregatablePropertyType](./Org.OData.Aggregation.V1.xml#L173:~:text= +## [Transformation](./Org.OData.Aggregation.V1.xml#L159:~:text= -## [AggregationMethod](./Org.OData.Aggregation.V1.xml#L191:~:text=For navigation properties, it counts the distinct entities in the union of all entities related to entities in the input set. For collection-valued primitive properties, it counts the distinct items in the union of all collection values in the input set. +[sum](./Org.OData.Aggregation.V1.xml#L268:~:text=For navigation properties, it counts the distinct entities in the union of all entities related to entities in the input set. For collection-valued primitive properties, it counts the distinct items in the union of all collection values in the input set. -## [RollupType](./Org.OData.Aggregation.V1.xml#L228:~:text= -## [RecursiveHierarchyType](./Org.OData.Aggregation.V1.xml#L286:~:text= +## [HierarchyQualifier](./Org.OData.Aggregation.V1.xml#L363:~:text= -## [AvailableOnAggregatesType](./Org.OData.Aggregation.V1.xml#L345:~:text= -## [NavigationPropertyAggregationCapabilities](./Org.OData.Aggregation.V1.xml#L351:~:text= -## [CustomAggregateType](./Org.OData.Aggregation.V1.xml#L369:~:text= - + + + + @@ -56,6 +59,9 @@ Terms to describe which data in a given entity model can be aggregated, and how. + + Related to the specfication document [OData-Data-Agg-v4.0](http://docs.oasis-open.org/odata/odata-data-aggregation-ext/v4.0/odata-data-aggregation-ext-v4.0.html). + @@ -95,54 +101,13 @@ - + + Services that do not fully implement a certain aggregation-related functionality may document + this by annotating the [`ApplySupported`](#ApplySupported) or [`ApplySupportedDefaults`](#ApplySupportedDefaults) + annotation with a description. + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -150,6 +115,9 @@ + + + @@ -163,7 +131,7 @@ - + @@ -188,6 +156,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Standard or custom aggregation method @@ -226,15 +294,15 @@ - + - + - + - + @@ -276,75 +344,197 @@ - + - - + + - + - - - - - - - - + - - - - - + + + + Every recursive hierarchy function defined in this vocabulary has +- a parameter `HierarchyQualifier` of this type and +- a parameter `HierarchyNodes` that is a collection of entities of a common type without multiple occurrences of the same entity. + +`HierarchyQualifier` is the qualifier of a `RecursiveHierarchy` annotation on the entity type of the collection +given by the `HierarchyNodes` parameter. This specifies a recursive hierarchy that is evaluated by the function. + + + + + + + + + + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + - - - - - + + + + This function may only occur in the second parameter of a `groupby` transformation whose first parameter +contains `rolluprecursive(...)`. It is evaluated as part of the transformation `R(x)` in the "`rolluprecursive` algorithm" +(OData-Data-Agg-v4.0, section 6.3). Its behavior is undefined outside of this algorithm. +``` +Sales?$apply=groupby((rolluprecursive(...)), filter(SalesOrganization eq Aggregation.rollupnode())/aggregate(...)) +``` +constructs a rollup that contains aggregates per hierarchy node while excluding descendants from the aggregation. + + + + + Every instance in the output set of a `groupby` transformation with M `rolluprecursive` operators + has M relationships to M nodes in M recursive hierarchies. This function returns the node x with path r to the root + in relationship number N. + If several such `groupby` transformations are nested, this function refers to the innermost one. + + + + + + + + + - - + + + + This instance annotation occurs in the result set after a `traverse` transformation (OData-Data-Agg-v4.0, section 6.2.2.2). + A use case for this is traversal with multiple parents, when this annotation takes + as value one parent node identifier followed by one grandparent node identifier and so on. + + + + + - + diff --git a/vocabularies/Org.OData.Core.V1.json b/vocabularies/Org.OData.Core.V1.json index 7bccbbc9..2d000672 100644 --- a/vocabularies/Org.OData.Core.V1.json +++ b/vocabularies/Org.OData.Core.V1.json @@ -726,6 +726,17 @@ "$Nullable": true, "@Core.Description": "Commonly used identifer for a Feature" } + }, + "AnyStructure": { + "$Kind": "Term", + "$Type": "Core.Tag", + "$DefaultValue": true, + "$AppliesTo": [ + "EntityType", + "ComplexType" + ], + "@Core.Description": "Instances of a type are annotated with this tag if they have no common structure in a given response payload", + "@Core.LongDescription": "The select-list of a context URL MUST be `(@Core.AnyStructure)` if it would otherwise be empty,\n but this instance annotation SHOULD be omitted from the response value." } } } \ No newline at end of file diff --git a/vocabularies/Org.OData.Core.V1.md b/vocabularies/Org.OData.Core.V1.md index ddb36291..f9fc51b5 100644 --- a/vocabularies/Org.OData.Core.V1.md +++ b/vocabularies/Org.OData.Core.V1.md @@ -50,6 +50,7 @@ Term|Type|Description [ExplicitOperationBindings](./Org.OData.Core.V1.xml#L527:~:text=The qualified names of explicitly bound operations that are supported on the target model element. These operations are in addition to any operations not annotated with RequiresExplicitBinding that are bound to the type of the target model element. [SymbolicName](./Org.OData.Core.V1.xml#L536:~:text=A symbolic name for a model element [GeometryFeature](./Org.OData.Core.V1.xml#L545:~:text=A [Feature Object](https://datatracker.ietf.org/doc/html/rfc7946#section-3.2) represents a spatially bounded thing +[AnyStructure](./Org.OData.Core.V1.xml#L561:~:text=Instances of a type are annotated with this tag if they have no common structure in a given response payload
The select-list of a context URL MUST be `(@Core.AnyStructure)` if it would otherwise be empty, but this instance annotation SHOULD be omitted from the response value. ## [RevisionType](./Org.OData.Core.V1.xml#L80:~:text= + + + + The select-list of a context URL MUST be `(@Core.AnyStructure)` if it would otherwise be empty, + but this instance annotation SHOULD be omitted from the response value. + + +