{"id":76240,"date":"2019-03-28T10:30:06","date_gmt":"2019-03-28T17:30:06","guid":{"rendered":"https:\/\/cloudblogs.microsoft.com\/opensource\/?p=76240"},"modified":"2025-06-27T10:00:54","modified_gmt":"2025-06-27T17:00:54","slug":"trill-101-how-to-add-temporal-queries-to-your-applications","status":"publish","type":"post","link":"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/","title":{"rendered":"Trill 101: how to add temporal queries to your applications"},"content":{"rendered":"\n<p>Last December, we&nbsp;<a href=\"https:\/\/azure.microsoft.com\/en-us\/blog\/microsoft-open-sources-trill-to-deliver-insights-on-a-trillion-events-a-day\/\">released Trill<\/a>, an open source .NET library designed to process one trillion events a day. Trill provides a temporal query language enabling you to embed real-time analytics in your own application. In this blog post, we spend some time introducing how to get started using Trill.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"trill-s-query-and-data-model\">Trill\u2019s query and data model<\/h2>\n\n\n\n<p>A key innovation of Trill\u2019s query and data model \u2013 which has its roots in&nbsp;<a href=\"https:\/\/channel9.msdn.com\/posts\/StreamInsight\">StreamInsight<\/a>&nbsp;and the&nbsp;<a href=\"https:\/\/www.microsoft.com\/en-us\/research\/publication\/temporal-analytics-on-big-data-for-web-advertising-2\/\">CEDR<\/a>&nbsp;tempo-relational model \u2013 is the&nbsp;<a href=\"https:\/\/www.microsoft.com\/en-us\/research\/publication\/consistent-streaming-through-time-a-vision-for-event-stream-processing\/\">separation of query logic from time management<\/a>. You\u2019ll find in many temporal systems that time and transformations are tightly coupled. For instance, grouping and aggregation are often combined with windowing into a single operation. In this post, you will see why that is not the case in Trill.<\/p>\n\n\n\n<p>Trill uses&nbsp;<em>snapshot semantics<\/em>&nbsp;in its data processing. In short, what Trill does is the equivalent of evaluating a standard, atemporal query but at every point in time. For a simple example of what that means, consider the following set of sensor temperature data:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nnew SensorReading { time = 7:00am, temperature = 70, id = 0 },\nnew SensorReading { time = 7:20am, temperature = 68, id = 1 },\nnew SensorReading { time = 7:40am, temperature = 72, id = 2 },\nnew SensorReading { time = 8:00am, temperature = 76, id = 3 },\nnew SensorReading { time = 8:20am, temperature = 80, id = 4 },\nnew SensorReading { time = 8:40am, temperature = 84, id = 5 }\n<\/pre><\/div>\n\n\n<p>In a standard relational database, if one were to issue this query:<br>SELECT Avg(temperature) FROM SensorReading<\/p>\n\n\n\n<p>The query runs once, over all data. In this case, the result would be 75, an average of all temperature values. One way to think about this scenario is as if each data value is valid at all points of time \u2013 it is always in existence. Graphically:<\/p>\n\n\n<figure class=\"wp-block-image aligncenter\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-1-1024x328.webp\" alt=\"Trill image 1\" class=\"wp-image-76249 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-1.webp 1453w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-1-300x96.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-1-1024x328.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-1-768x246.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-1-330x106.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-1-800x257.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-1-400x128.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-1.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-1.png 1453w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-1-300x96.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-1-1024x328.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-1-768x246.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-1-330x106.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-1-800x257.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-1-400x128.png 400w\"><\/figure>\n\n\n\n<p>This graphic may be the most boring graphic in the history of blogging, but it gets the point across: a standard relational database in standard query logic is static and does not change. If it does change, a whole new query answer is produced.<\/p>\n\n\n\n<p>Now, our sample data above is inherently temporal, meaning that what we want does not look like the above diagram, but rather something like this:<\/p>\n\n\n<figure class=\"wp-block-image aligncenter\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-2-1024x363.webp\" alt=\"Trill image 2\" class=\"wp-image-76252 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-2.webp 1533w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-2-300x106.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-2-1024x363.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-2-768x273.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-2-330x117.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-2-800x284.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-2-400x142.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-2.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-2.png 1533w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-2-300x106.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-2-1024x363.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-2-768x273.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-2-330x117.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-2-800x284.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-2-400x142.png 400w\"><\/figure>\n\n\n\n<p>Each data point occurs at a point in time. One can think of this data set as a single data point that comes into existence at 7am, then disappears. Then another at 7:20am, then disappears. And so on.<\/p>\n\n\n\n<p>Trill could still run the same query above on this data. If you do, you will get the following result:<\/p>\n\n\n<figure class=\"wp-block-image aligncenter\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-3-1024x335.webp\" alt=\"Trill image 3\" class=\"wp-image-76255 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-3.webp 1459w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-3-300x98.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-3-1024x335.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-3-768x252.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-3-330x108.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-3-800x262.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-3-400x131.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-3.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-3.png 1459w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-3-300x98.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-3-1024x335.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-3-768x252.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-3-330x108.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-3-800x262.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-3-400x131.png 400w\"><\/figure>\n\n\n\n<p>Yes, that\u2019s the same diagram cut and pasted again. The snapshot semantics here say that at time 7am there is only one currently valid data point, and that point has a value of 70. So, the average of all data is 70. Then at 7:20am, the average is 68. At 7:40am, the average is 72. And so on.<\/p>\n\n\n\n<p>That\u2019s not a particularly useful query answer. A more likely scenario would be a tumbling window, where we take the average temperature of all readings in a given hour period. How do we do this in Trill?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"tumbling-windows-the-hello-world-of-windowing\">Tumbling Windows, the \u201cHello World\u201d of windowing<\/h2>\n\n\n\n<p>The way to think about it is in two steps. First, think about the time periods in the result. For a tumbling window, we\u2019d want one result for the hour from 7am to 8am, and one from 8am to 9am. Second, we want to identify which data points should contribute to the query answer. For the 7am to 8am result, we want to query over data points 0 through 2. For the 8am to 9am result, we want points 3 through 5.<\/p>\n\n\n\n<p>To get the proper result, one must&nbsp;<em>alter the event lifetimes<\/em>&nbsp;to match the results they contribute to. From the above logic, we want to take point 0 (originally occurring at 7am) and change its lifetime to be 7am-8am. Same with point 1, originally occurring at 7:20am, etc. The desired result is to have a set of lifetimes that looks like the following:<\/p>\n\n\n<figure class=\"wp-block-image aligncenter\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-4-1024x354.webp\" alt=\"Trill image 4\" class=\"wp-image-76258 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-4.webp 1504w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-4-300x104.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-4-1024x354.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-4-768x266.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-4-330x114.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-4-800x277.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-4-400x138.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-4.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-4.png 1504w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-4-300x104.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-4-1024x354.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-4-768x266.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-4-330x114.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-4-800x277.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-4-400x138.png 400w\"><\/figure>\n\n\n\n<p>There is a built-in operation that does this in Trill: TumblingWindowLifetime. However, this method is not magic. It calls another, more general method (AlterEventLifetime) that redefines each event\u2019s lifetime by setting its start time and duration. For a tumbling window, the new lifespan has a start time of the original time modulo the window size, and a duration equal to the window size:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>newStartTime = oldStartTime \u2013 (oldStartTime % 60 minutes)<\/li>\n\n\n\n<li>newDuration = 60 minutes<\/li>\n<\/ul>\n\n\n\n<p>Then, when Trill executes our Average query over this new dataset, the result is:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>No result until 7am<\/li>\n\n\n\n<li>An average of points 0, 1, and 2 from 7am to 8am (yielding a result of 70)<\/li>\n\n\n\n<li>An average of points 3, 4, and 5 from 8am to 9am (yielding a result of 80)<\/li>\n\n\n\n<li>No result after 9am<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"and-now-hopping-windows\">\u2026 and now, Hopping Windows<\/h2>\n\n\n\n<p>What if we instead have a different pair of functions determining event start time and duration:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>newStartTime = oldStartTime \u2013 (oldStartTime % 20 minutes)<\/li>\n\n\n\n<li>newDuration = 60 minutes<\/li>\n<\/ul>\n\n\n\n<p>With these two functions, we instead get a hopping window of hop size 20 minutes and window size 60 minutes:<\/p>\n\n\n<figure class=\"wp-block-image aligncenter\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-5-1024x321.webp\" alt=\"Trill image 5\" class=\"wp-image-76261 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-5.webp 1521w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-5-300x94.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-5-1024x321.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-5-768x241.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-5-330x103.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-5-800x251.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-5-400x125.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-5.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-5.png 1521w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-5-300x94.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-5-1024x321.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-5-768x241.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-5-330x103.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-5-800x251.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-5-400x125.png 400w\"><\/figure>\n\n\n\n<p>Trill has a built-in method for this operation, HoppingWindowLifetime(windowSize, hopSize), but again it is just a helper method that creates the two functions above. By allowing the user to assign lifetimes independently from other operations, Trill opens a window (pun intended) to many possibilities. Consider a situation where the user assigns lifetimes as follows:<\/p>\n\n\n<figure class=\"wp-block-image aligncenter\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-6-1024x333.webp\" alt=\"Trill image 6\" class=\"wp-image-76264 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-6.webp 1478w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-6-300x97.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-6-1024x333.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-6-768x249.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-6-330x107.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-6-800x260.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-6-400x130.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-6.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-6.png 1478w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-6-300x97.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-6-1024x333.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-6-768x249.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-6-330x107.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-6-800x260.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/03\/Trill-6-400x130.png 400w\"><\/figure>\n\n\n\n<p>This allocation of event lifetimes provides a progressive tumbling window per hour. One can now, for instance, get the average temperature per hour, but with increasing accuracy over the course of the hour as new data arrives.<\/p>\n\n\n\n<p>These built-in operations are only the tip of the iceberg with window definition in Trill. One can define data-dependent windows, or windows that produce progressive results as more data arrives, and so on.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"trill-s-api\">Trill\u2019s API<\/h2>\n\n\n\n<p>Anyone familiar with C# will already know Trill\u2019s query logic language: it\u2019s&nbsp;<a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/programming-guide\/concepts\/linq\/\">LINQ<\/a>!<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nstream.Select(event => \u2026) \/\/ Projection\nstream.Where(event => \u2026) \/\/ Filtration\nstream.Join(stream2, o1 => \u2026, o2 => \u2026, (o1, o2) => \u2026) \/\/ Join\n<\/pre><\/div>\n\n\n<p>These methods inherited from LINQ are supplemented by the timespan management methods mentioned in the previous section, along with many others, for instance:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nstream.TumblingWindowLifetime(TimeSpan.FromMinutes(5).Ticks)\nstream.HoppingWindowLifetime(TimeSpan.FromMinutes(5).Ticks, TimeSpan.FromSeconds(5).Ticks)\nstream.SlidingWindowLifetime(TimeSpan.FromMinutes(5).Ticks)\n<\/pre><\/div>\n\n\n<p>The query operations and the lifetime operations are separate; the query author uses the lifetime methods to state when each event is to contribute to the query answer, and the LINQ methods to evaluate the query itself.<\/p>\n\n\n\n<p>To illustrate, consider a simple example of a stream of data coming from temperature sensors as in the previous section, but now with an added field representing the ID of the sensor taking the reading. The data arrives as an observable stream with this schema:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\npublic struct SensorReading\n       { public int sensorId; public double temperature; public long time; }\nIObservable incomingData = ...;\n<\/pre><\/div>\n\n\n<p>We do have one step to take before processing this data, and that is to identify the time dimension in the incoming data. In this simple case, the time for each data point is stored in the time field. We identify that field when we convert this data into an IStreamable, in this case using the built-in extension method ToTemporalStreamable:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nvar stream = incomingData.ToTemporalStreamable(s => s.time );\n<\/pre><\/div>\n\n\n<p>There\u2019s a lot going on in that simple method ToTemporalStreamable. For instance, this method is responsible for handling any temporal disorder that may occur in the input data. We will cover the extensive power and expressiveness available in the ToStreamable methods in a later post, but for now just know that once data has been handled by ToTemporalStreamable that data in all subsequent operations will be in time order.<\/p>\n\n\n\n<p>The variable stream above is of type IStreamable, and with it we have all operations available in Trill. Now let\u2019s ask some questions!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"example-of-simple-queries-in-trill\">Example of simple queries in Trill<\/h2>\n\n\n\n<p>Query 1: What is the maximum temperature over all sensors in the building each hour?<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nvar hourly = stream\n               .TumblingWindowLifetime(TimeSpan.FromHours(1).Ticks)\n               .Max(event => event.temperature);\n<\/pre><\/div>\n\n\n<p>Notice here that the call to TumblingWindowLifetime in this example and all others is separate from the aggregate and grouping operation. As noted, this separation is not just clever syntax.<\/p>\n\n\n\n<p>Query 2: What is the maximum temperature for each sensor in the building each hour?<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nvar hourlyPerSensor = stream\n          .TumblingWindowLifetime(TimeSpan.FromHours(1).Ticks)\n          .GroupApply(\n              event => event.sensorID,\n              data => data.Max(event => event.temperature),\n              (group, value) => new { sensor = group.Key, value.temperature }\n          );\n<\/pre><\/div>\n\n\n<p>The GroupApply operation over IStreamable is Trill\u2018s analog of the Group operation in LINQ combined with an aggregate; it groups data, applies a query to each group, then selects a value from each group. It is, essentially, a Map-Reduce operation.<\/p>\n\n\n\n<p>Query 3: What sensor is reporting the maximum temperature each hour for temperatures exceeding 30 degrees?<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nvar warmRooms = stream\n           .TumblingWindowLifetime(TimeSpan.FromHours(1).Ticks)\n           .GroupApply(\n               event => event.sensorID,\n               data => data.Max(event => event.temperature),\n               (group, value) => new { sensor = group.Key, value.temperature }\n           ).Where(event => event.temperature > 30)\n           .Max(event => event.temperature)\n           .Select(event => event.sensor);\n<\/pre><\/div>\n\n\n<p>To see the result of these queries, we need to get data back into a familiar structure like IObservable:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nvar result = hourlyPerSensor.ToTemporalObservable(\n                     (start, end, data) =>\n                           new { time = start, data.sensor, data.temperature });\n<\/pre><\/div>\n\n\n<p>Each result in the observable will be associated with two time points \u2013 its start time (when the row becomes valid) and its end time (when it ceases being valid). As this result comes from a tumbling window query, the resulting data will have \u201cstart\u201d and \u201cend\u201d times that look like beginnings and ends of hour intervals.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"what-s-next\">What\u2019s next?<\/h2>\n\n\n\n<p>This post covered the very basics of what a Trill query looks like. In future posts (<a href=\"https:\/\/cloudblogs.microsoft.com\/opensource\/2019\/05\/01\/trill-102-temporal-joins\/\">Trill 102<\/a>&nbsp;and&nbsp;<a href=\"https:\/\/cloudblogs.microsoft.com\/opensource\/2019\/08\/13\/trill-103-ingress-egress-trills-notion-of-time-tutorial\/\">Trill 103<\/a>), we will cover some additional features of the API, such as joins, regular expressions, ingress policies, and partitioned timelines. To learn more about Trill, check out our&nbsp;<a href=\"https:\/\/github.com\/microsoft\/TrillSamples\">samples repository<\/a>&nbsp;on&nbsp;<a href=\"https:\/\/github.com\/microsoft\/Trill\">GitHub<\/a>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"questions-or-feedback-let-us-know-in-the-comments\">Questions or feedback? Let us know in the comments.<\/h4>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Last December, we&nbsp;released Trill, an open source .NET library designed to process one trillion events a day. Trill provides a temporal query language enabling you to embed real-time analytics in your own application. In this blog post, we spend some time introducing how to get started using Trill.<\/p>\n","protected":false},"author":5562,"featured_media":95473,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"msxcm_post_with_no_image":false,"ep_exclude_from_search":false,"_classifai_error":"","_classifai_text_to_speech_error":"","footnotes":""},"post_tag":[2272],"content-type":[346,361],"topic":[2239],"programming-languages":[2253],"coauthors":[424],"class_list":["post-76240","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","tag-microsoft","content-type-news","content-type-project-updates","topic-analytics","programming-languages-net","review-flag-1593580428-734","review-flag-1593580415-931","review-flag-1-1593580432-963","review-flag-2-1593580437-411","review-flag-3-1593580442-169","review-flag-4-1593580448-609","review-flag-5-1593580453-725","review-flag-7-1593580463-151","review-flag-8-1593580468-572","review-flag-alway-1593580310-39","review-flag-new-1593580248-669"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.2 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Trill 101: how to add temporal queries to your applications | Microsoft Open Source Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Trill 101: how to add temporal queries to your applications | Microsoft Open Source Blog\" \/>\n<meta property=\"og:description\" content=\"Last December, we&nbsp;released Trill, an open source .NET library designed to process one trillion events a day. Trill provides a temporal query language enabling you to embed real-time analytics in your own application. In this blog post, we spend some time introducing how to get started using Trill.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/\" \/>\n<meta property=\"og:site_name\" content=\"Microsoft Open Source Blog\" \/>\n<meta property=\"article:published_time\" content=\"2019-03-28T17:30:06+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-27T17:00:54+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/CLO24-Azure-Fintech-006.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1170\" \/>\n\t<meta property=\"og:image:height\" content=\"640\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"James Terwilliger\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@OpenAtMicrosoft\" \/>\n<meta name=\"twitter:site\" content=\"@OpenAtMicrosoft\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"James Terwilliger\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 min read\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/\"},\"author\":[{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/author\/james-terwilliger\/\",\"@type\":\"Person\",\"@name\":\"James Terwilliger\"}],\"headline\":\"Trill 101: how to add temporal queries to your applications\",\"datePublished\":\"2019-03-28T17:30:06+00:00\",\"dateModified\":\"2025-06-27T17:00:54+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/\"},\"wordCount\":1447,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/CLO24-Azure-Fintech-006.webp\",\"keywords\":[\"Microsoft\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/\",\"url\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/\",\"name\":\"Trill 101: how to add temporal queries to your applications | Microsoft Open Source Blog\",\"isPartOf\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/CLO24-Azure-Fintech-006.webp\",\"datePublished\":\"2019-03-28T17:30:06+00:00\",\"dateModified\":\"2025-06-27T17:00:54+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/#primaryimage\",\"url\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/CLO24-Azure-Fintech-006.webp\",\"contentUrl\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/CLO24-Azure-Fintech-006.webp\",\"width\":1170,\"height\":640},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/opensource.microsoft.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Trill 101: how to add temporal queries to your applications\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#website\",\"url\":\"https:\/\/opensource.microsoft.com\/blog\/\",\"name\":\"Microsoft Open Source Blog\",\"description\":\"Open dialogue about openness at Microsoft \u2013 open source, standards, interoperability\",\"publisher\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/opensource.microsoft.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#organization\",\"name\":\"Microsoft Open Source Blog\",\"url\":\"https:\/\/opensource.microsoft.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Microsoft-Logo.png\",\"contentUrl\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Microsoft-Logo.png\",\"width\":259,\"height\":194,\"caption\":\"Microsoft Open Source Blog\"},\"image\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/OpenAtMicrosoft\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Trill 101: how to add temporal queries to your applications | Microsoft Open Source Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/","og_locale":"en_US","og_type":"article","og_title":"Trill 101: how to add temporal queries to your applications | Microsoft Open Source Blog","og_description":"Last December, we&nbsp;released Trill, an open source .NET library designed to process one trillion events a day. Trill provides a temporal query language enabling you to embed real-time analytics in your own application. In this blog post, we spend some time introducing how to get started using Trill.","og_url":"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/","og_site_name":"Microsoft Open Source Blog","article_published_time":"2019-03-28T17:30:06+00:00","article_modified_time":"2025-06-27T17:00:54+00:00","og_image":[{"width":1170,"height":640,"url":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/CLO24-Azure-Fintech-006.png","type":"image\/png"}],"author":"James Terwilliger","twitter_card":"summary_large_image","twitter_creator":"@OpenAtMicrosoft","twitter_site":"@OpenAtMicrosoft","twitter_misc":{"Written by":"James Terwilliger","Est. reading time":"7 min read"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/#article","isPartOf":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/"},"author":[{"@id":"https:\/\/opensource.microsoft.com\/blog\/author\/james-terwilliger\/","@type":"Person","@name":"James Terwilliger"}],"headline":"Trill 101: how to add temporal queries to your applications","datePublished":"2019-03-28T17:30:06+00:00","dateModified":"2025-06-27T17:00:54+00:00","mainEntityOfPage":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/"},"wordCount":1447,"commentCount":0,"publisher":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#organization"},"image":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/#primaryimage"},"thumbnailUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/CLO24-Azure-Fintech-006.webp","keywords":["Microsoft"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/","url":"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/","name":"Trill 101: how to add temporal queries to your applications | Microsoft Open Source Blog","isPartOf":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/#primaryimage"},"image":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/#primaryimage"},"thumbnailUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/CLO24-Azure-Fintech-006.webp","datePublished":"2019-03-28T17:30:06+00:00","dateModified":"2025-06-27T17:00:54+00:00","breadcrumb":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/#primaryimage","url":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/CLO24-Azure-Fintech-006.webp","contentUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2024\/06\/CLO24-Azure-Fintech-006.webp","width":1170,"height":640},{"@type":"BreadcrumbList","@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/03\/28\/trill-101-how-to-add-temporal-queries-to-your-applications\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/opensource.microsoft.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Trill 101: how to add temporal queries to your applications"}]},{"@type":"WebSite","@id":"https:\/\/opensource.microsoft.com\/blog\/#website","url":"https:\/\/opensource.microsoft.com\/blog\/","name":"Microsoft Open Source Blog","description":"Open dialogue about openness at Microsoft \u2013 open source, standards, interoperability","publisher":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/opensource.microsoft.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/opensource.microsoft.com\/blog\/#organization","name":"Microsoft Open Source Blog","url":"https:\/\/opensource.microsoft.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/opensource.microsoft.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Microsoft-Logo.png","contentUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Microsoft-Logo.png","width":259,"height":194,"caption":"Microsoft Open Source Blog"},"image":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/OpenAtMicrosoft"]}]}},"msxcm_display_generated_audio":false,"msxcm_animated_featured_image":null,"distributor_meta":false,"distributor_terms":false,"distributor_media":false,"distributor_original_site_name":"Microsoft Open Source Blog","distributor_original_site_url":"https:\/\/opensource.microsoft.com\/blog","push-errors":false,"_links":{"self":[{"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts\/76240","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/users\/5562"}],"replies":[{"embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/comments?post=76240"}],"version-history":[{"count":3,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts\/76240\/revisions"}],"predecessor-version":[{"id":97789,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts\/76240\/revisions\/97789"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/media\/95473"}],"wp:attachment":[{"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/media?parent=76240"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/post_tag?post=76240"},{"taxonomy":"content-type","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/content-type?post=76240"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/topic?post=76240"},{"taxonomy":"programming-languages","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/programming-languages?post=76240"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/coauthors?post=76240"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}