OData Extension for Temporal Data Version 1.0



OData Extension for Temporal Data Version 4.0Working Draft 012159 April November 20175Technical Committee:OASIS Open Data Protocol (OData) TCChairs:Ralf Handl (ralf.handl@), SAP SERam Jeyaraman (Ram.Jeyaraman@), MicrosoftEditors:Ralf Handl (ralf.handl@), SAP SEHubert Heijkers (hubert.heijkers@nl.), IBMGerald Krause (gerald.krause@), SAP SEMichael Pizzo (mikep@), MicrosoftMartin Zurmuehl (martin.zurmuehl@), SAP SEAdditional artifacts:This prose specification is one component of a Work Product that consists of:OData Extension for Temporal Data Version 4.0 (this document) HYPERLINK "" OData Temporal ABNF Construction Rules Version 4.0 HYPERLINK "" OData Temporal ABNF Test Cases HYPERLINK "" OData Temporal VocabularyRelated work:This specification is related to:OData Version 4.0 Part 1: ProtocolOData Version 4.0 Part 2: URL ConventionsOData Version 4.0 Part 3: CSDLOData ABNF Construction Rules Version 4.0OData Core VocabularyOData JSON Format Version 4.0This specification replaces or supersedes:NoneDeclared XML namespaces:NoneAbstract:This specification defines how to represent and interact with time-dependent data using the Open Data Protocol (OData).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 re-approve it any number of times as a Committee Draft.Copyright ? OASIS Open 2013. 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 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.Table of Contents TOC \o "1-4" \h \z \u 1Introduction PAGEREF _Toc498519439 \h 51.1 Terminology PAGEREF _Toc498519440 \h 51.2 Normative References PAGEREF _Toc498519441 \h 51.3 Non-Normative References PAGEREF _Toc498519442 \h 51.4 Typographical Conventions PAGEREF _Toc498519443 \h 62Overview PAGEREF _Toc498519444 \h 72.1 Interacting with Temporal Data PAGEREF _Toc498519445 \h 72.1.1 Single Entities PAGEREF _Toc498519446 \h 72.1.2 Collections of Entities PAGEREF _Toc498519447 \h 102.1.3 Related Entities PAGEREF _Toc498519448 \h 112.1.4 Application Time as a Key Part PAGEREF _Toc498519449 \h 142.1.5 System Time PAGEREF _Toc498519450 \h 162.2 Definitions PAGEREF _Toc498519451 \h 202.2.1 Application Time PAGEREF _Toc498519452 \h 202.2.2 System Time PAGEREF _Toc498519453 \h 202.2.3 Bi-Temporal PAGEREF _Toc498519454 \h 202.2.4 Time-Travel Query PAGEREF _Toc498519455 \h 202.2.5 Time-Series Query PAGEREF _Toc498519456 \h 202.2.6 Temporal Entity PAGEREF _Toc498519457 \h 202.2.7 Time Slice PAGEREF _Toc498519458 \h 212.2.8 Time Series PAGEREF _Toc498519459 \h 212.3 Example Model PAGEREF _Toc498519460 \h 212.4 Example Data PAGEREF _Toc498519461 \h 222.5 Example Use Cases PAGEREF _Toc498519462 \h 223Temporal Query Options PAGEREF _Toc498519463 \h 233.1 Temporal Expression PAGEREF _Toc498519464 \h 233.2 Time-Travel Query Options PAGEREF _Toc498519465 \h 233.2.1 System Query Option $at PAGEREF _Toc498519466 \h 243.2.2 System Query Option $systemat PAGEREF _Toc498519467 \h 243.3 Time-Series Query Options PAGEREF _Toc498519468 \h 243.3.1 System Query Option $from PAGEREF _Toc498519469 \h 243.3.2 System Query Option $to PAGEREF _Toc498519470 \h 243.3.3 System Query Option $systemfrom PAGEREF _Toc498519471 \h 243.3.4 System Query Option $systemto PAGEREF _Toc498519472 \h 244Vocabulary for Temporal Entities PAGEREF _Toc498519473 \h 255Representing Temporal Entities PAGEREF _Toc498519474 \h 275.1 Time Slices PAGEREF _Toc498519475 \h 275.2 Time Series PAGEREF _Toc498519476 \h 286Temporal Requests PAGEREF _Toc498519477 \h 296.1 Querying Temporal Entities PAGEREF _Toc498519478 \h 296.1.1 Time-Travel Queries PAGEREF _Toc498519479 \h 296.1.2 Time-Series Queries PAGEREF _Toc498519480 \h 306.1.3 Interaction with Standard System Query Options PAGEREF _Toc498519481 \h 316.2 Modifying Temporal Entities PAGEREF _Toc498519482 \h 326.2.1 Create Entity PAGEREF _Toc498519483 \h 346.2.2 Update Entity PAGEREF _Toc498519484 \h 346.2.3 Delete Entity PAGEREF _Toc498519485 \h 356.2.4 Create Relation PAGEREF _Toc498519486 \h 356.2.5 Update Relation PAGEREF _Toc498519487 \h 356.2.6 Delete Relation PAGEREF _Toc498519488 \h 356.3 ETags PAGEREF _Toc498519489 \h 366.4 Actions and Functions PAGEREF _Toc498519490 \h 367Conformance PAGEREF _Toc498519491 \h 37Appendix A.Acknowledgments PAGEREF _Toc498519492 \h 38Appendix B.Revision History PAGEREF _Toc498519493 \h 39IntroductionThis specification adds the notion of time-dependency to the Open Data Protocol (OData) without changing any of the base principles of OData. It defines semantics and a representation for temporal data, especially:Semantics and operations for querying temporal data,Results format for queries containing temporal data,Vocabulary terms to annotate which data depends on time, and how.TerminologyThe key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in REF rfc2119 \h [RFC2119].Normative References[OData-ABNF]OData ABNF Construction Rules Version 4.0. See the link in "Related work" section on cover page.[OData-Tmp-ABNF]OData Temporal ABNF Construction Rules Version 4.0. See link in "Additional artifacts" section on cover page. [OData-CSDL]OData Version 4.0 Part 3: CSDL. See link in "Related work" section on cover page.[OData-Protocol]OData Version 4.0 Part 1: Protocol. See link in "Related work" section on cover page.[OData-URL]OData Version 4.0 Part 2: URL Conventions. See link in "Related work" section on cover page.[OData-VocCore]OData Core Vocabulary. See link in "Additional artifacts" section on cover page.[OData-VocTemp]OData Temporal Vocabulary. See link in "Additional artifacts" section on cover page.[RFC2119]Bradner, S., “Key words for use in RFCs to Indicate Requirement Levels”, BCP 14, RFC 2119, March 1997. References[Fowler]Temporal Patterns, Martin Fowler, .[Kulkarni]Temporal Features in SQL standard, Krishna Kulkarni, May 13, 2011, .[Saracco]A matter of time: Temporal data management in DB2 10, IBM developerWorks, Cynthia M. Saracco, Matthias Nicola, Lenisha Gandhi, April 3, 2012, .[SQL:2011]ISO/IEC 9075-2:2011 Information technology - Database languages - SQL - Part 2: Foundation (SQL/Foundation).Typographical ConventionsKeywords defined by this specification use this monospaced font.Normative source code uses this paragraph style.Some sections of this specification are illustrated with non-normative examples.Example SEQ Example \* ARABIC 1: text describing an example uses this paragraph styleNon-normative examples use this paragraph style.All examples in this document are non-normative and informative only.All other text is normative unless otherwise labeled.OverviewWhen keeping track of time, the most important questions are:When did or will something happen?When did we learn that it happened?This leads to two dimensions of time: application time, also called actual time, business time, or valid time, and system time, also called recording time or transaction time.Keeping track of time is typically done by storing each fact together with the period of time for which this fact is valid, using separate periods for each time dimension, and the time periods are part of the logical key for “fact records”. See [SQL:2011] or [Kulkarni] on how this is done in the SQL standard.A consumer's perspective on these facts can be different: even if a time dimension is tracked internally, it may or may not be visible in a consumer's perspective, and even if visible it is often not considered part of the logical entity key. For example an employee is still the same person even after switching to another department.The goals of this extension are:Keep the models as simple as possible by allowing to hide time-dependency,Provide easy means for time-travel and time-series queries even if time is hidden, andProvide easy means for modifying time-dependent data.More sophisticated queries are still possible in models that make time visible, even if time is not made part of the entity keys.Interacting with Temporal DataSingle EntitiesAssume a simple model: employees will work in different roles and in different departments during their career, and sometimes they even change their name. So employees are assigned an immutable ID when first entering the company and all employee-related information will be recorded with a validity period attached:Example SEQ Example \* ARABIC 2: possible temporal database table for employee data using the validity start date as part of the database keyIf an employee gets a different job title, this information can already be entered into the system and stored as a new record:Example SEQ Example \* ARABIC 3: employee data stored with from-inclusive, to-exclusive validity periodNote that this also allows entering planned changes ahead of time, using a future validity start date.For consumers only interested in the information as of now, the natural OData model uses the employee ID as the entity key and either omits the validity start and end dates, or represents them only as dependent information:Example SEQ Example \* ARABIC 4: OData model with time-independent entity keyThe OData service will expose only the information from the one record valid at the time the request is processed. Example SEQ Example \* ARABIC 5: request processed before 2013-10-01, $at not specified implies $at=nowGET ~/Employees('E314')results in{ "@odata.context": "$metadata#Employees/$entity", "ID": "E314", "Name": "McDevitt", "Jobtitle": "Junior"}Example SEQ Example \* ARABIC 6: request processed at or after 2013-10-01, $at not specified implies $at=nowGET ~/Employees('E314')results in{ "@odata.context": "$metadata#Employees/$entity", "ID": "E314", "Name": "McDevitt", "Jobtitle": "Senior"}Clients that are aware of the time-tracking capabilities underlying this seemingly timeless model may be interested in the information at a certain point in time. These time-traveling clients use the system query option $at to request data at a specific point in time:Example SEQ Example \* ARABIC 7: retrieve employee in the pastGET ~/Employees('E314')?$at=2012-01-01results in{ "@odata.context": "$metadata#Employees/$entity", "@Temporal.From": "2011-01-01", "@Temporal.To": "2013-10-01", "ID": "E314", "Name": "McDevitt", "Jobtitle": "Junior"}Example SEQ Example \* ARABIC 8: retrieve employee in the futureGET ~/Employees('E314')?$at=2025-01-01results in{ "@odata.context": "$metadata#Employees/$entity", "@Temporal.From": "2013-10-01", "@Temporal.To": null, "ID": "E314", "Name": "McDevitt", "Jobtitle": "Senior"}The returned results are called time slices, and the validity period of each time slice is automatically included in the response. A null value for the period start means the beginning of time, and a null value for the period end means the end of time. Time-period boundaries that are not declared properties of the requested entities are represented as instance annotations with terms from a vocabulary for temporal entities.This is necessary for the next type of question time-tracking aware clients will want to ask: how did the information change over time:Example SEQ Example \* ARABIC 9: retrieve employee in a time periodGET ~/Employees('E314')?$from=2012-01-01&$to=2025-01-01results in{ "@odata.context": "$metadata#Employees/$entity/$timeseries", "value": [ { "@Temporal.From": "2011-01-01", "@Temporal.To": "2013-10-01", "ID": "E314", "Name": "McDevitt", "Jobtitle": "Junior" }, { "@Temporal.From": "2013-10-01", "@Temporal.To": null, "ID": "E314", "Name": "McDevitt", "Jobtitle": "Senior" } ]}This returns all time slices whose validity period overlaps with the requested time period.Collections of EntitiesThese temporal query options also work with resource paths identifying collections of entities. Requesting data at a specific point in time results in a collection of time slices:Example SEQ Example \* ARABIC 10: retrieve multiple employee in the pastGET ~/Employees?$filter=contains(Name,'i')&$at=2012-01-01results in one time slice for each employee matching the filter at the specified point in time{ "@odata.context": "$metadata#Employees", "value": [ { "@Temporal.From": "2011-01-01", "@Temporal.To": "2013-10-01", "ID": "E314", "Name": "McDevitt", "Jobtitle": "Junior" } ]}Requesting data for a period of time results in a collection of time series, one for each entity. The time series for each entity is itself a collection of time slices for that entity: Example SEQ Example \* ARABIC 11: retrieve multiple employee in the pastGET ~/Employees?$filter=contains(Name,'i')&$from=2012-01-01&$to=2025-01-01results in one time series for each employee matching the filter at some point within the specified period, and returns the time slices within that series that match the filter. So the value is an array of arrays:{ "@odata.context": "$metadata#Employees/$timeseries", "value": [ [ { "@Temporal.From": "2011-01-01", "@Temporal.To": "2013-10-01", "ID": "E314", "Name": "McDevitt", "Jobtitle": "Junior" }, { "@Temporal.From": "2013-10-01", "@Temporal.To": null, "ID": "E314", "Name": "McDevitt", "Jobtitle": "Senior" } ], [ { "@Temporal.From": "2012-03-01", "@Temporal.To": null, "ID": "E401", "Name": "Gibson", "Jobtitle": "Expert" } ] ]}The interpretation of non-temporal system query options in combination with $at is straight-forward: the request is simply interpreted at this point in time. The combination with $from and $to is less straight-forward and needs to be defined per standard system query option: a $filter is considered matching if the validity period of the time slice partially or fully overlaps with the request period and filter condition is true for the property values of the time slice.For $orderby the last value in the resulting time series (after filtering) is used, assuming that typically the last value is of most interest. For details see section REF _Ref367092962 \r \h 6.1.3.Related EntitiesEach employee is assigned to a department, and this assignment can change over time, as can properties of the department. Both types of changes are tracked, and again this need not be visible in the model. Example SEQ Example \* ARABIC 12: OData model with time-independent entity key and navigationIf the assigned department is internally stored per employee record, this can influence the visible representation; see next example. Reassigning an employee to a different department without changing any of the visible employee properties may result in time slices that do not differ other than in their time-period boundaries, see REF _Ref366752223 \h \* MERGEFORMAT Example 14. Services may or may not “compress” the returned time series by joining adjacent time slices that do not differ in the selected properties, so the service could also have returned the response of REF _Ref366752735 \h \* MERGEFORMAT Example 9. This can also happen if only a subset of the properties is requested using the system query option $select.Example SEQ Example \* ARABIC 13: employee data stored with foreign key to departmentExample SEQ Example \* ARABIC 14: retrieve employee in a time period during which a relationship changedGET ~/Employees('McDevitt')?$from=2012-01-01&$to=2025-01-01results in{ "@odata.context": "$metadata#Employees/$entity/$timeseries", "value": [ { "@Temporal.From": "2011-01-01", "@Temporal.To": "2013-10-01", "ID": "E314", "Name": "McDevitt", "Jobtitle": "Junior" }, { "@Temporal.From": "2013-10-01", "@Temporal.To": "2014-01-01", "ID": "E314", "Name": "McDevitt", "Jobtitle": "Senior" }, { "@Temporal.From": "2014-01-01", "@Temporal.To": null, "ID": "E314", "Name": "McDevitt", "Jobtitle": "Senior" } ]}Expanding related entities in combination with $at is straight-forward: nested within the time slice of a base entity are those time slices of related entities that are valid at the requested point in time:Example SEQ Example \* ARABIC 15: retrieve employee in the past with expanded departmentGET ~/Employees('E314')?$at=2012-01-01&$expand=Departmentresults in{ "@odata.context": "$metadata#Employees/$entity", "@Temporal.From": "2011-01-01", "@Temporal.To": "2013-10-01", "ID": "E314", "Name": "McDevitt", "Jobtitle": "Junior", "Department": { "@Temporal.From": "2010-01-01", "@Temporal.To": "2012-06-01", "ID": "D08", "Name": "Support" }}Example SEQ Example \* ARABIC 16: retrieve department in the future with expanded employeesGET ~/Departments('D15')?$at=2025-01-01&$expand=Employeesresults in{ "@odata.context": "$metadata#Departments/$entity", "@Temporal.From": "2010-01-01", "@Temporal.To": null, "ID": "D15", "Name": "Services", "Employees": [ { "@Temporal.From": "2014-01-01", "@Temporal.To": null, "ID": "E314", "Name": "McDevitt", "Jobtitle": "Senior" }, { "@Temporal.From": "2012-03-01", "@Temporal.To": null, "ID": "E401", "Name": "Gibson", "Jobtitle": "Expert" }, ... ]}The time-slice boundaries of the nested entities reflect the actual validity of the related entities and are independent of the time-slice boundaries of the base entity.Expanding related entities in combination with $from and $to adds the challenge that for single-valued navigation properties the related entity needs to be represented as a time series, i.e. in JSON as an array instead of as an object.Example SEQ Example \* ARABIC 17: retrieve employee in a time period and expand the departmentGET ~/Employees('McDevitt')?$from=2012-01-01&$to=2025-01-01&$expand=Departmentresults inAn employee time slice with an array of expanded departments because the department (name) changed during the validity period of the employee time slice. Note that the validity periods of the department time slices completely cover the validity period of the employee time slice, but are not truncated to match the employee time slice.An employee time slice with a different job title. The expanded department time slice is a copy of the last time slice in the expanded department of the preceding employee time slice. If the request had specified a preference of odata.allow-entityreferences (see [ REF odata \h \* MERGEFORMAT OData-Protocol, section 8.2.8.1], this could have been expressed as an entity reference containing annotations for the validity period.An employee time slice with a different expanded department.{ "@odata.context": "$metadata#Employees/$entity/$timeseries", "value": [ { "@Temporal.From": "2011-01-01", "@Temporal.To": "2013-10-01", "ID": "E314", "Name": "McDevitt", "Jobtitle": "Junior", "Department": [ { "@Temporal.From": "2010-01-01", "@Temporal.To": "2012-06-01", "ID": "D08", "Name": "Support" }, { "@Temporal.From": "2012-06-01", "@Temporal.To": null, "ID": "D08", "Name": "1st Level Support" } ] }, { "@Temporal.From": "2013-10-01", "@Temporal.To": "2014-01-01", "ID": "E314", "Name": "McDevitt", "Jobtitle": "Senior", "Department": [ { "@Temporal.From": "2012-06-01", "@Temporal.To": null, "ID": "D08", "Name": "1st Level Support" } ] }, { "@Temporal.From": "2014-01-01", "@Temporal.To": null, "ID": "E314", "Name": "McDevitt", "Jobtitle": "Senior", "Department": [ { "@Temporal.From": "2010-01-01", "@Temporal.To": null, "ID": "D15", "Name": "Services" } ] } ]}Application Time as a Key PartFor some entities it may be desirable to make their time-dependency visible in the model, e.g. the budget allocated to a department in a certain period of time.Example SEQ Example \* ARABIC 18: entity type with visible time-dependencyExample SEQ Example \* ARABIC 19: department budget over timeThe temporal query options also work in this case: although time-traveling could be achieved with $filter conditions, $at, $from, and $to are more concise and even work when only one of the time-slice boundaries is visible in the model. If both time-slice boundaries are visible in the model, tThe result of a $at request is almost identical to the result of a request with an equivalent $filter on the temporal properties, the only difference is the context URL.If a time-slice boundary is visible in the model, the corresponding property is annotated in $metadata with Temporal.From or Temporal.To and the result does not contain a corresponding instance annotation.Example SEQ Example \* ARABIC 20: retrieve department budget at a specific point in time: bothGET ~/DepartmentBudgets?$at=2012-07-01andGET ~/DepartmentBudgets?$filter=ValidFrom le 2012-07-01 and (ValidTo gt 2012-07-01 or ValidTo eq null)result in{ "@odata.context": "$metadata#DepartmentBudgets", "value": [ { "DepartmentID": "D08", "ValidFrom": "2012-01-01", "ValidTo": "2014-01-01", "Budget": 1250 }, { "DepartmentID": "D15", "ValidFrom": "2011-01-01", "ValidTo": null, "Budget": 1170 } ]}The result of a $from-$to request groups the time slices per entity into nested arrays, whereas the equivalent $filter request contains the same time slices in a single array.Example SEQ Example \* ARABIC 21: retrieve department budgets during a time period: bothGET ~/DepartmentBudgets?$from=2010-07-01&$to=2012-07-01andGET ~/DepartmentBudgets?$filter=(ValidFrom lt 2012-07-01 or ValidFrom eq null and (ValidTo gt 2010-07-01 or ValidTo eq null)result in{ "@odata.context": "$metadata#DepartmentBudgets", "value": [ { "DepartmentID": "D08", "ValidFrom": "2010-01-01", "ValidTo": "2012-01-01", "Budget": 1000 }, { "DepartmentID": "D08", "ValidFrom": "2012-01-01", "ValidTo": "2014-01-01", "Budget": 1250 }, { "DepartmentID": "D15", "ValidFrom": "2010-01-01", "ValidTo": "2011-01-01", "Budget": 1100 }, { "DepartmentID": "D15", "ValidFrom": "2011-01-01", "ValidTo": null, "Budget": 1170 } ]}System TimeErrors happen and need to be corrected, and in many situations the system needs to keep track of these corrections and allow clients to access the system state as of before the error correction. This is done by attaching a validity period to data that is completely managed by the system behind the scenes, disallowing changes in the past or future. When data is corrected, the validity end date of the currently valid record is set to the current system timestamp, and a new record is inserted with the same timestamp as its validity start date.Example SEQ Example \* ARABIC 22: entity type with partially visible system timeExample SEQ Example \* ARABIC 23: department budget over time with correctionsExample SEQ Example \* ARABIC 24: budget for department D0:Record 1 was inserted first, and then corrected by 2. The new budget for 2012 was entered as 4, the additional record 3 was created to cover the remaining time area.Record 6 was entered with the budget for 2014, creating 5.Example SEQ Example \* ARABIC 25: budget for department D15:The OData model may show the system validity period partly or fully as read-only properties, and will show the current data in standard OData requests.The query option $systemat allows accessing the system state at a given point in the past.Example SEQ Example \* ARABIC 26: retrieve department budgets as of a past point in timeGET ~/DepartmentBudgets?$systemat=2009-11-11T00:00results in{ "@odata.context": "$metadata#DepartmentBudgets", "value": [ { "DepartmentID": "D08", "ValidFrom": "2010-01-01", "ValidTo": "2012-01-01", "Budget": 950, "ChangedAt": "2009-10-15T23:41", "@Temporal.SystemTo": "2009-12-21T14:00" }, { "DepartmentID": "D15", "ValidFrom": "2010-01-01", "ValidTo": "2011-01-01", "Budget": 1100 "ChangedAt": "2009-10-15T23:42", "@Temporal.SystemTo": null } ]}andGET ~/DepartmentBudgets?$systemat=2010-10-24T12:00results in{ "@odata.context": "$metadata#DepartmentBudgets", "value": [ { "DepartmentID": "D08", "ValidFrom": "2010-01-01", "ValidTo": "2012-01-01", "Budget": 1000, "ChangedAt": "2009-12-21T14:00", "@Temporal.SystemTo": null }, { "DepartmentID": "D15", "ValidFrom": "2010-01-01", "ValidTo": "2011-01-01", "Budget": 1100, "ChangedAt": "2009-10-15T23:42", "@Temporal.SystemTo": null }, { "DepartmentID": "D15", "ValidFrom": "2011-01-01", "ValidTo": null, "Budget": 1170, "ChangedAt": "2010-10-21T17:22", "@Temporal.SystemTo": null } ]}The query options $systemfrom and $systemto allow accessing the corrections made over a period of time.Example SEQ Example \* ARABIC 27: retrieve department budgets at past point in application time for a system time periodGET ~/DepartmentBudgets?$at=2010-07-01 &$systemfrom=2009-01-01T00:00&$systemto=maxresults in{ "@odata.context": "$metadata#DepartmentBudgets/$timeseries", "value": [ [ { "DepartmentID": "D08", "ValidFrom": "2010-01-01", "ValidTo": "2012-01-01", "Budget": 950, "ChangedAt": "2009-10-15T23:41", "@Temporal.SystemTo": "2009-12-21T14:00" }, { "DepartmentID": "D08", "ValidFrom": "2010-01-01", "ValidTo": "2012-01-01", "Budget": 1000, "ChangedAt": "2009-12-21T14:00", "@Temporal.SystemTo": null } ], [ { "DepartmentID": "D15", "ValidFrom": "2010-01-01", "ValidTo": "2011-01-01", "Budget": 1100 "ChangedAt": "2009-10-15T23:42", "@Temporal.SystemTo": null } ] ]}When combining $systemfrom and $systemto with $from and $to the result is tagged as a time series and grouped by temporal entity although it actually is a "time surface": adding another nesting level and context URL part would add more complexity than value.Example SEQ Example \* ARABIC 28: retrieve department budgets over periods in both application time and system timeGET ~/DepartmentBudgets?$from=min&$to=max &$systemfrom=min&$systemto=nowresults in{ "@odata.context": "$metadata#DepartmentBudgets/$timeseries", "value": [ [ { "DepartmentID": "D08", "ValidFrom": "2010-01-01", "ValidTo": "2012-01-01", "Budget": 950, "ChangedAt": "2009-10-15T23:41", "@Temporal.SystemTo": "2009-12-21T14:00" }, { "DepartmentID": "D08", "ValidFrom": "2010-01-01", "ValidTo": "2012-01-01", "Budget": 1000, "ChangedAt": "2009-12-21T14:00", "@Temporal.SystemTo": null }, { "DepartmentID": "D08", "ValidFrom": "2012-01-01", "ValidTo": "2014-01-01", "Budget": 1250, "ChangedAt": "2011-11-11T11:11", "@Temporal.SystemTo": null }, { "DepartmentID": "D08", "ValidFrom": "2014-01-01", "ValidTo": null, "Budget": 1400, "ChangedAt": "2013-10-04T09:21", "@Temporal.SystemTo": null } ], [ { "DepartmentID": "D15", "ValidFrom": "2010-01-01", "ValidTo": "2011-01-01", "Budget": 1100 "ChangedAt": "2009-10-15T23:42", "@Temporal.SystemTo": null }, { "DepartmentID": "D15", "ValidFrom": "2011-01-01", "ValidTo": null, "Budget": 1170 "ChangedAt": "2010-10-21T17:22", "@Temporal.SystemTo": null } ] ]}DefinitionsApplication TimeApplication time is used to describe facts that are known to change over time, e.g. the budget of a department, or which department an employee works for. This time dimension may capture planned changes in the future, e.g. transferring an employee to a new department next month, or capturing next year's budget for a department. Both future and past facts can be changed.System TimeSystem time is used to record when a fact has become known in the “system of record”. This time dimension does not extend into the future, and record entries are only added and are never changed.System time is never manipulated directly. If it is visible, the corresponding properties are read-only.Bi-TemporalBi-temporal facts are tracked both in application time and system time to capture their change over time as well as the changes in the knowledge about these facts.Time-Travel QueryTime-travel queries request the knowledge of facts as of a certain point in time. If the queried model is bi-temporal, this requires specifying two time coordinates.Time-Series QueryTime-series queries request how knowledge of facts has varied over a certain period in time. In bi-temporal models this can be combined with time-travel in the second time dimension, or it can request all variations over a time period in each dimension.Temporal Entity A temporal entity is an entity whose change over application and/or system time is tracked by the service. The tracked time dimensions may or may not be part of the entity key.Time SliceA temporal entity does not have a single representation; its full representation is a collection of instances of its entity type, each instance annotated with the start and end point of the time period in each tracked time dimension. These instances are called entity time slices.Time slices are non-overlapping, so at any given point in (one- or two-dimensional) time at most one time slice is valid. Time slices need not cover the complete time line or time plane. There can be points in time for which no time slice is valid, meaning that the entity does not exist at this point in time.Time SeriesA collection of time slices for a temporal entity.Example ModelExample SEQ Example \* ARABIC 29: The following example OData model will be used to further illustrate the capabilities introduced by this extension. Example DataExample SEQ Example \* ARABIC 30: The following example data will be used to further illustrate the capabilities introduced by this extension. It assumes that the example service only supports four-digit years.Example Use CasesA client might wish to query the example model in a number of ways:Retrieve a current employee, together with the employee’s current department.Retrieve employees that worked for the “Performance Analysis” department in 2010.Retrieve an employee as of a particular moment, including the employee’s department as of that same moment.Retrieve all time slices of an employee in the past, present, and future.Retrieve all time slices of the department that a specific time slice of an employee works for, within the application-time period of that employee time slice.Retrieve all time slices of an employee for a given application-time period, including all time slices of the related departments within the application-time period of each employee time slice that occurred within that time period.Change a department's budget in the past, present, or future.Change an employee’s association to a department in the past, present, or future.Temporal Query OptionsSix system query options allow time-travel as well as time-series queries in both time dimensions, and can be combined for bi-temporal models. They take a temporal expression as their argument whose type MUST match the granularity of the corresponding time dimension.TODO: what to do for changes within the most fine-grained time slice, or the underlying store uses “sequence numbers” or “version numbers” instead of dates or timestamps?Allow also integer and decimal numbers, or even strings?In query options or also in From/To annotations?Only for system time?If allowed for application time we also need to allow it in data modification requests for precise updatesIdea:; add @Temporal.Version of any (ordered?) type, service can issue it in addition to From and To, can be used in $at to get that exact version could be used as ETagIf $at is used with a date/timestamp there now could be more than one version per date/timestamp, service has to pick one so navigation property cardinality isn’t brokenSuccessFactors case with numbers within dates is most likely a “weak two-dim temporal” with just a sequence number as a “system version” not correlated to a system timeTemporal ExpressionA temporal expression may beA date in the form dateValue, see [OData-ABNF]A timestamp in the form dateTimeOffsetValue, see [OData-ABNF] One of the literals now, min, and maxAn arithmetic expression resulting in a date or timestamp valueThe literals now, min, and max are interpreted depending on the granularity of the time dimension. For granularity DateTimeOffset they are defined asnow ? now()min ? mindatetime()max ? maxdatetime()For granularity Date they are defined asnow ? date(now())min ? date(mindatetime())max ? date(maxdatetime())Note that services are free to choose the time zone for the current point in time returned by now(), so the result of date(now()) may differ from the current date in the client's local time zone.Time-Travel Query OptionsIf one of the following query options is combined with a resource path that addresses a single temporal entity, the resulting URL addresses a single time slice for that entity. If no matching time slice exists, the service MUST return 404 Not Found.If combined with a resource path that addresses a collection of temporal entities, the resulting URL addresses a collection of time slices for these entities. The collection can be empty. System Query Option $atThe $at system query option takes a temporal expression as its argument.It restricts the result to time slices with application-time start less than or equal to the result of the temporal expression, and application-time end greater than the result of the temporal expression.System Query Option $systematThe $systemat system query option takes a temporal expression as its argument.It restricts the result to time slices with system-time start less than or equal to the result of the temporal expression, and system-time end greater than the result of the temporal expression.Time-Series Query OptionsIf one of the following query options is combined with a resource path that addresses a single temporal entity, the resulting URL addresses a collection of time slices for that entity. The collection can be empty.If combined with a resource path that addresses a collection of temporal entities, the resulting URL addresses a collection of time slices for these entities. The collection can be empty.System Query Option $fromThe $from system query option takes a temporal expression as its argument.If $to is not explicitly specified in the same request, it defaults to max.It restricts the result to time slices with application-time start less than the result of the $to temporal expression, and application-time end greater than the result of the $from temporal expression.System Query Option $toThe $to system query option takes a temporal expression as its argument.If $from is not explicitly specified in the same request, it defaults to min.It restricts the result to time slices with application-time start less than the result of the $to temporal expression, and application-time end greater than the result of the $from temporal expression.System Query Option $systemfromThe $systemfrom system query option takes a temporal expression as its argument.If $systemto is not explicitly specified in the same request, it defaults to max.It restricts the result to time slices with system-time start less than the result of the $systemto temporal expression, and system-time end greater than the result of the $systemfrom temporal expression.System Query Option $systemtoThe $systemto system query option takes a temporal expression as its argument.If $systemfrom is not explicitly specified in the same request, it defaults to min.It restricts the result to time slices with system-time start less than the result of the $systemto temporal expression, and system-time end greater than the result of the $systemfrom temporal expression.Vocabulary for Temporal EntitiesThe following terms are defined in the vocabulary for temporal data [OData-VocTemp].The term TemporalSupported describes the temporal capabilities of the annotated entity set or container. It has a structured type with the following properties:ApplicationTimeGranularity describes the granularity of the application-time dimension. It is either Date or DateTimeOffset.SystemTimeGranularity describes the granularity of the system-time dimension. It is either Date or DateTimeOffset.NonTemporalProperties lists the paths to properties whose value changes over time are not tracked although they are logically part of the temporal entity.At least ApplicationTimeGranularity or SystemTimeGranularity needs to be specified. If both are specified, the entity set or container is bi-temporal.Four terms describe the boundaries of the application- and system-time periods. They use the abstract type Edm.PrimitiveType, but in fact their type MUST match the granularity of the corresponding time dimension: From is the start of the application-time period and included in the period. To is the end of the application-time period and excluded from the period. SystemFrom is the start of the system-time period and included in the period. SystemTo is the end of the system-time period and excluded from the period. Example SEQ Example \* ARABIC 31: example model annotated with temporal terms. Employee has no metadata annotations for the application-time boundaries, so they have to be represented as instance annotations. Department has three of the four boundaries annotated, so only SystemTo will be represented as an instance annotation.<edmx:Edmx ... Version="4.0"> <edmx:Reference Uri=""> <edmx:Include Namespace="Org.OData.Core.V1" Alias="Core" /> </edmx:Reference> <edmx:Reference Uri=""> <edmx:Include Alias="Temporal" Namespace="Org.OData.Temporal.V1" /> </edmx:Reference> <edmx:DataServices> <Schema ... Alias="OrgModel" Namespace="org.example.service"> <EntityType Name="Employee"> <Key> <PropertyRef Name="ID" /> </Key> <Property Name="ID" Type="Edm.String" Nullable="false" /> <Property Name="Name" Type="Edm.String" Nullable="false" /> <NavigationProperty Name="Department" Type="OrgModel.Department" /> </EntityType> <EntityType Name="DepartmentBudget"> <Annotation Term="Temporal.From" Path="ValidFrom" /> <Annotation Term="Temporal.To" Path="ValidTo" /> <Annotation Term="Temporal.SystemFrom" Path="ChangedAt" /> <Key> <PropertyRef Name="ID" /> <PropertyRef Name="ValidFrom" /> </Key> <Property Name="ID" Type="Edm.String" Nullable="false" /> <Property Name="Name" Type="Edm.String" Nullable="false" /> <Property Name="Budget" Type="Edm.Decimal" /> <Property Name="ValidFrom" Type="Edm.Date" Nullable="false" /> <Property Name="ValidTo" Type="Edm.Date" Nullable="false" /> <Property Name="ChangedAt" Type="Edm.DateTimeOffset" Nullable="false"> <Annotation Term="Core.Permissions" EnumMember="Core.Permission/Read" /> </Property> <NavigationProperty Name="Employees" Type="Collection(OrgModel.Employee)" /> </NavigationProperty> </EntityType> <EntityContainer Name="Default"> <EntitySet Name="Employees" EntityType="OrgModel.Employee"> <Annotation Term="Temporal.TemporalSupported"> <Record> <PropertyValue Property="ApplicationTimeGranularity" EnumMember="@Temporal.Granularity/Date" /> </Record> </Annotation> </EntitySet> <EntitySet Name="Departments" EntityType="OrgModel.Department"> <Annotation Term="Temporal.TemporalSupported"> <Record> <PropertyValue Property="ApplicationTimeGranularity" EnumMember="@Temporal.Granularity/Date" /> <PropertyValue Property="SystemTimeGranularity" EnumMember="@Temporal.Granularity/DateTimeOffset" /> </Record> </Annotation> </EntitySet> </EntityContainer> </Schema> </edmx:DataServices></edmx:Edmx>Rework above example according to. <!-- TODO: move example to word document --> <Annotation Term="Temporal.TemporalSupported"> <Record> <PropertyValue Property="ApplicationTime"> <Record Type="Temporal.TimeDimensionGranularityDate" /> </PropertyValue> <PropertyValue Property="SystemTime"> <Record> <PropertyValue Property="Precision" Int="12" /> </Record> </PropertyValue> </Record> </Annotation>Representing Temporal Entities Payload ordering: @Temporal MUST appear at the beginning after the odata.* annotations that must be at the beginning$from has to be less than $to, no "circular time" where $from ge $to means "everything outside of the [$to,$from[ interval"@Temporal.From etc. MUST appear even in odata.metadata=none responses because they cannot be computed and are part of the temporal dataAdd examples for all variants of context URLs, where they appear and what they implyTime SlicesA time slice describes of the state of an entity for a specific period of each tracked time dimension. It is represented as an entity that is annotated with the terms From, To, SystemFrom, and SystemTo defined in [OData-VocTemp].For application-time temporal data the values of the terms From and To MUST be defined for each instance, either as metadata annotations or as instance annotations. The value of From MUST be less than the value of To for each time slice.For system-time temporal data the values of the terms SystemFrom and SystemTo MUST be defined for each instance, either as metadata annotations, or as instance annotations. The value of SystemFrom MUST be less than the value of SystemTo for each time slice.If more than one time slice of an entity is part of a response, the time periods of any two time slices of the same entity MUST NOT overlap. For bi-temporal time slices this means that the periods for at least one of the time dimensions do not overlap.Entity-id identifies an entity, not a time slice of an entity, Time slices could be identified as entity-id&$at=..&$systemat=…Edit URLs, read URLs, navigation URLs and association URLs MUST NOT encode the time periods.Example SEQ Example \* ARABIC 32: time slice of an employee with hidden application time properties{"@odata.context": "$metadata#Employees/$entity", "@Temporal.From": "2013-01-01", "@Temporal.To": null, "ID": "E314","Name": "McDevitt", "Jobtitle": "Senior",}Example SEQ Example \* ARABIC 33: time slice of a department with both application and system time and three of the time properties explicitly modeled, only fourth non-modeled property is represented as an instance annotation{"@odata.context": "$metadata#Departments/$entity", "ValidFrom": "2013-01-01", "ValidTo": "2013-12-31", "ChangedAt": "2012-12-28T14:31:25Z", "@Temporal.SystemTo": null, "ID": "D1", "Name": "Help Desk", "Budget": 735.41}Representation of expanded to-many navigation property with multiple related time slicesRepresentation of a collection of time slices in a time-series query to a resource path for a single entityRepresentation of a collection of time series in a time-series query to a resource path for a collection of entitiesContainment combined with Temporal: can annotate containment navigation property via container/set/navprop1/navprop2/…Representation of constituents of temporal entities, e.g. ~/Employees('E314')/Address Property representation annotated with time period boundaries; collection in case of time-series queries Time SeriesAtom?JSON: don’t use arrays of arrays, have wrapper object inbetween that can contain etag, context, non-temporal properties plus array-valued property “value” for objects containing time-dependent properties. This would also solve the cardinality problem for single-valued navigation properties when $from/$to and $expand are combinedsRalf: insert whiteboard photoTemporal RequestsSystem-time tracking does not change the requests → system time properties are read-onlyApplication time that is not part of the key works similar to system time in temporal databasesi.e. modifications are valid from now to infinity if not explicitly specified otherwiseThis allows clients that don't know the temporal extension to naturally interact with the serviceIf time is explicitly specified by client, convenience-default for "from" is minApplication time as part of the key works as a normal OData serviceFrom/to default to the values implied by the key of the current and next adjacent entityIf the non-key boundary is specified explicitly (query option, declared property, or annotation), the behavior is identical to application time not as key part, i.e. slices are split or added, and multiple slices can be affected by one requestworks identical to application time that is not part of the key, it just requires the temporal key values in a different part of the request. And the "from=now" default is unnecessaryTemporal entities with non-temporal propertiesAnnotate non-temporal propertiesUpdate them "forever" even if a validity period is specified in the requestNon-temporal entities with temporal query options: are silently ignored but “cascaded” along $expand and evaluated on all temporal entities in the result. Context URL on first temporalQuerying Temporal EntitiesRead, edit, navigation and association URLs in responses, and especially in the service document, MUST NOT encode time periods. Requests to these URLs without appending one of the temporal query options return the current representation of each temporal entity matching the request.Example SEQ Example \* ARABIC 34: Retrieve a current employeeGET ~/Employees('McDevitt')results in...Time-Travel QueriesSpecifying $at=now and $systemat=now also returns the current time slice for each matching temporal entity, and in addition includes instance annotations for all time-period boundaries that are not declared and annotated properties of the entity type.Example SEQ Example \* ARABIC 35: Retrieve a current employee, with instance annotations for time-period boundaries.GET ~/Employees('McDevitt')?$at=nowresults in...Specifying $at or $systemat for entities that are not tracked in the corresponding time dimension MUST NOT change the service behavior, the result MUST be identical to the result of a request without the superfluous time-travel option. This is semantically correct: without tracking time the current facts are the only known truth at any point in time.Example SEQ Example \* ARABIC 36: Retrieve an employee as of a particular moment, including the employee’s department as of that same moment.GET ~/Employees('McDevitt')?$expand=Departments&$at=2011-07-01GET ~/Employees('McDevitt')?$expand=Departments&$at=2011-07-01&$systemat=minboth result in...Time-Series QueriesThe query options $from and $to allow requesting all time slices whose application-time periods overlap with the specified time range. The lower boundary is included, the upper boundary excluded from the requested period. Example SEQ Example \* ARABIC 37: Retrieve employees that worked for the “Performance Analysis” department in 2010. Note that the value of $to is not included in the results.GET ~/Departments('Performance Analysis')/Employees?$from=2010-01-01 &$to=2011-01-01results in...Example SEQ Example \* ARABIC 38: Retrieve all time slices of an employee in the past, present, and future.GET ~/Employees('McDevitt')?$from=min&$to=maxresults in...Specifying $from $to, $systemfrom, or $systemto for entities that are not tracked in the corresponding time dimension MUST NOT change the service behavior, the result MUST be identical to the result of a request without the superfluous time-series option. This is semantically correct: without tracking time the current facts are the only known truth in any period of time.Allow services to "condense/defragment" time slices, especially when combined with $selectAllow services to "refine/align" time slices, especially when combined with $expand SAP calls this a “temporal join”: overlay slices of all joined tables to get the “coarsest common slicing” that refines the slicing of all joined tablesThe temporal system query options apply to all returned time slices: to those of entities identified by the resource path as well as to those of expanded entities. Example SEQ Example \* ARABIC 39: Retrieve all time slices of an employee for a given time period, including all time slices of the related departments within the application-time period of each employee time slice that occurred within that time period.GET ~/Employees('McDevitt')?$expand=Department &$from=2010—01-01&$to=2010-12-31results in...The special resource $entity that allows resolving entity-ids SHOULD also allow resolving time slice ids.Example SEQ Example \* ARABIC 40: Retrieve all time slices of the department that a specific time slice of an employee works for, within the application-time period of that employee time slice.GET ~/$entity?$id=<id of employee time slice>?$expand=Departmentresults in...Note: could also be achieved by selecting the employee and specifying the from- and to-property values of the time slice in $from and $to.Interaction with Standard System Query Options$at: all options are evaluated at this point in time, same semantics.$from/$to needs special handling:$filter: entity matches if condition is true at any point within [$from,$to[ (right-open), returned are only those time slices that match $filter and intersect with [$from,$to[Note that the department name is time-dependent and can change independently of the employee-department relationship:GET Employees?$from…&$to…&$filter=Department/Name eq ‘A’All employee time slices intersecting [$from,$to[ whose department was named ‘A’ at any point in [$from,$to[GET Employees?$from…&$to…&$filter=Department/Name eq ‘A’$expand=DepartmentAll employee time slices intersecting [$from,$to[ whose department was named ‘A’ at any point in [$from,$to[Inline department time slices that cover [$from,$to[ independent of their nameGET Employees?$from…&$to…&$filter=Department/Name eq ‘A’$expand=Department($filter=Name eq ‘A’)All employee time slices intersecting [$from,$to[ whose department was named ‘A’ at any point in [$from,$to[Inline department time slices that intersect with [$from,$to[ and are named ‘A’any(): …all(): …Ralf: insert whiteboard photo$orderby: order is determined with value of latest time slice returned per entity$top: entities, i.e. number of returned time series, no limit to number of time slices within series, [$from,$to[ is completely covered$skip: entities, i.e. number of skipped time series$count: entities, i.e. number of time series$select: allow compression of adjacent time slices that do not differ in selected properties, later requests with more selected properties may return finer-grained time slices, which could also be caused by data changes between the two requests$search: entity matches if condition is true at any point within [$from,$to[ (right-open), returned are only matching time slices that also intersect with [$from,$to[$expand without nested $at or $from/$to: these “cascade” into the expanded entities, are silently ignored if related entities are non-temporal, same behavior as for requests into that path that specify themfor each time slice: related time series that covers [T.From,T.To[ (right-open) of this time slice; means that "border" time slices of related entities may show up twice, once as last, then as first time slice$expand with nested $at or $from/$to: these are valid from the specified place until the next nested $at/$from/$to is encountered$apply: $at and $systemat evaluated first because time-dependency is a feature of the service model and these query options set a global context, then $apply, which might remove time dimensiondo not combine with $from/$to, reserved for future version of this specificationtime-dependent aggregation better done with a time dimension, i.e. time is explicit part of keyModifying Temporal EntitiesSystem time is by definition managed by the system, so properties containing system-time period boundaries cannot be changed by the client and should be annotated with the term puted, see REF VocCore \h [OData-VocCore].Goals:System-time tracking does not change the requestsi.e. system time properties are read-onlyApplication time that is not part of the key works similar to system time in temporal databasesi.e. modifications are valid from now to infinity if not explicitly specified otherwiseThis allows clients that don't know the temporal extension to naturally interact with the serviceIf time is explicitly specified by client, convenience-default for "from" is minApplication time as part of the key works as a normal OData serviceFrom/to default to the values implied by the key of the current and next adjacent entityIf the non-key boundary is specified explicitly (query option, declared property, or annotation), the behavior is identical to application time not as key part, i.e. slices are split or added, and multiple slices can be affected by one requestworks identical to application time that is not part of the key, it just requires the temporal key values in a different part of the request. And the "from=now" default is unnecessaryTemporal entities with non-temporal propertiesAnnotate non-temporal propertiesUpdate them "forever" even if a validity period is specified in the requestApplication timeInvisibleVisible as from/to propertiesFrom/to property is part of keyApplication time part of keyValidFrom → allowValidTo → ???Both → do not allowPrecedence: URL wins over body$from/$to wins over key values because client demonstrated knowledge of Temporalannotation wins over property because client demonstrated knowledge of Temporal and we don’t want Bad RequestsPOSTPATCHPUTDELETEfrom and to specified (as key part or otherwise)Treated identical to PUT, only exception are key propertiesExisting slices before/after truncatedExisting fully covered slices changed, optionally compactedNon-existing slices createdSame as PATCH, setting unspecified properties to default values (normal PUT behavior)Existing slices before/after truncatedExisting fully covered slices deleted/shadowedOnly from specified, only as key part →assume client is unaware of Temporal to defaults to start of next time slice, or max if no next time slice existsto defaults to start of next time slice, or max if no next slice existsto defaults to start of next time slice, or max if no next time slice existsto defaults to start of next time slice, or max if no next time slice existsDelete/shadow the identified sliceOnly from specified, not as key part → client is aware of Temporalto defaults to maxto defaults to maxto defaults to maxto defaults to maxOnly to specified, only as key part →assume client is unaware of Temporalfrom defaults to end start of previous identified time slice, or min if no next previous time slice existsfrom defaults to start of identified time slicefrom defaults to end of previous time slice, or min if no next previous time slice existsfrom defaults to start of identified time slicefrom defaults to end of previous time slice, or min if no next previous time slice existsfrom defaults to start of identified time sliceDelete/shadow the identified sliceOnly to specified (as key part or otherwise)from defaults to minfrom defaults to minfrom defaults to minfrom defaults to minNeither specified → not part of key →assume client is unaware of TemporalTreated identical to PUT, only exception are key propertiesfrom defaults to now, to defaults tomaxRationale: change is visible immediately afterwards and remains as requested until changed again Same as PATCH, setting unspecified properties to default values (normal PUT behavior)from defaults to now, to defaults to maxRationale: same as for PATCHat specified, application time is not part of keyNot allowedModify time slice identified via $atModify time slice identified via $atdelete time slice identified via $atCreate EntityAllow $from/$to in request URL: cascades down to nested entities in deep inserts and relationships created with @odata.bindInstance annotations or modeled properties can be used within entity representations$from/$to can be used in URLs specified with @odata.bind is problematic as clients/servers won’t expect query options in these URLsIf we treat temporal POST as UPSERT we should consider how it combines with If-(None-)Match? Check with Part 1.Update EntityUpsert now allowed, so update within undefined period no conceptual problem, even if no period is defined at all, see table above@Temporal.From allows string values min and now, @Temporal.To allows string values now and max. as years have no digit limit in our ABNF, limit is service/server-specific, and instance annotation wins over (dummy) value in declared propertyNull is not appropriate as the declared properties would have to be nullableOmitting the declared properties is not appropriate as it means "don't change" in standard PATCHExample SEQ Example \* ARABIC 41: Change a department's name for a period of time.PATCH ~/Departments('D01') HTTP/1.1Content-Type: application/json{"@Temporal.From": "2013-04-01","@Temporal.To": "2013-06-30","Name": "Quality Control"}Example SEQ Example \* ARABIC 42: Change a department's budget for a period of time.PATCH ~/DepartmentBudgets(DepartmentID='D01',ValidFrom=2013-04-01) HTTP/1.1Content-Type: application/json{"ValidTo": "2013-06-30","Budget": 2e6}Delete EntityDELETE requests don’t allow a request body, so we have to use system query options for partial deletion if time is not part of the keyExample SEQ Example \* ARABIC 43: Delete an entity for a period of time.DELETE ~/Employees('E314')?$from=2013-04-01&$to=2013-06-30 HTTP/1.1Create Relation@Temporal.From/To can be placed inside entity reference representation to limit time validity of relationUpdate Relation@Temporal.From/To can be placed inside entity reference representation to limit time validity of relationExample SEQ Example \* ARABIC 44: Change an employee’s department in the past, present, or future.PATCH ~/Employees('McDevitt')/Department/$ref HTTP/1.1Content-Type: application/json{"@odata.id": "~/Departments('Support')","@Temporal.From": "2010-01-01","@Temporal.To": "2010-12-31"}Example SEQ Example \* ARABIC 45: Modify a department's employees in the past, present, or future.…Delete RelationDELETE requests don’t allow a request body, so we have to use system query options for partial deletion if time is not part of the keyExample SEQ Example \* ARABIC 46: Delete a relation for a period of time in a single-valued relationshipDELETE ~/Employees('E314')/Department/$ref?$from=2013-04-01&$to=2013-06-30Example SEQ Example \* ARABIC 47: Delete a relation for a period of time in a multi-valued relationshipDELETE ~/Departments('D14')/Employees/$ref?$id=Employees('E314')?$from=2013-04-01&$to=2013-06-30ETagsETags need special handling: in system-temporal stores you can't really change data, just add new versions, so actually "write conflict" doesn't mean losing data, it just means "shadowing" data unintentionally.How do we want to use ETags in this context?Ideas:Time slice can have an ETagTime series gets “maximum” ETag over all time slices (service implementation defines what “maximum” means)Can be for full time series or just requested interval or returned time slices, service implementation choiceETag for requests affecting multiple time slices “matches” if it is not “lower” than any ETag of the affected time slices (service implementation defines meaning of “matches” and “lower”) Actions and FunctionsBound actions and functions: can use $at to identify the time slice that is used as input to the action/function, result type remains unchanged$from/$to: apply to each time slice intersecting with [$from,$to[? What is now the result type? Need to find use casesUnbound actions and functions: temporal query options may influence result$at should just work: return time slice$from/$to will reshape the result type in the canonical wayConformanceConforming services MUST follow all rules of this specification for the time dimensions and temporal system query options they support. They MUST implement all time dimensions they advertise via the TemporalSupported annotation. Conforming clients MUST be prepared to consume a model that uses any or all constructs defined in this specification, and MUST ignore any temporal constructs not defined in this version of the specification.AcknowledgmentsThe contributions of the OASIS OData Technical Committee members, enumerated in [OData-Protocol], are gratefully acknowledged.Especial thanks to Andrew Eisenberg, whose contributions in the early stages of the OData TC were invaluable to getting this extension specification on track.Revision HistoryRevisionDateEditorChanges MadeWD012013-09-05Ralf HandlInitial working draft ................
................

In order to avoid copyright disputes, this page is only a partial summary.

Google Online Preview   Download