Session: Time Series Querying
Learn more about time series queries in the section dedicated to this subject.
- LINQ Queries
To query time series using LINQ expressions, usesession.Query
.
RavenDB will translate a LINQ query to RQL before transmitting it to the server for execution. -
RQL Queries
Clients can express time series queries in RQL and send them to the server for execution using thesession.Advanced.RawQuery
method. -
In this page:
Time Series LINQ Queries
To build a time series LINQ query, start with session.Query
or session.Advanced.DocumentQuery
and extend it using LINQ expressions.
Here is a simple LINQ query that chooses users by their age and retrieves their HeartRates time
series, and the RQL equivalent for this query.
// Query - LINQ format
using (var session = store.OpenSession())
{
var baseline = new DateTime(2020, 5, 17, 00, 00, 00);
IRavenQueryable<TimeSeriesRawResult> query =
(IRavenQueryable<TimeSeriesRawResult>)session.Query<User>()
.Where(u => u.Age < 30)
.Select(q => RavenQuery.TimeSeries(q, "HeartRates")
.Where(ts => ts.Tag == "watches/fitbit")
.ToList());
var result = query.ToList();
}
// May 17 2020, 00:00:00
var baseline = new DateTime(2020, 5, 17, 00, 00, 00);
// Raw query with no aggregation - Select syntax
IRawDocumentQuery<TimeSeriesRawResult> nonAggregatedRawQuery =
session.Advanced.RawQuery<TimeSeriesRawResult>(@"
from Users as u where Age < 30
select timeseries (
from HeartRates
)");
var nonAggregatedRawQueryResult = nonAggregatedRawQuery.ToList();
Syntax
Session.Query
session.Query
Definition:
IRavenQueryable<T> Query<T>(string indexName = null,
string collectionName = null, bool isMapReduce = false);
Learn more about session.Query
here.
Document Query
The session Document Query accessible
at session.Advanced
can be extended with several useful time series methods. To access these
methods, begin with the LINQ method SelectTimeSeries()
:
IDocumentQuery SelectTimeSeries(Func<ITimeSeriesQueryBuilder, TTimeSeries> timeSeriesQuery);
SelectTimeSeries()
takes an ITimeSeriesQueryBuilder
. The builder has the following methods:
From(string name);
Between(DateTime start, DateTime end);
FromLast(Action<ITimePeriodBuilder> timePeriod);
FromFirst(Action<ITimePeriodBuilder> timePeriod);
LoadByTag<TTag>();
//LoadByTag is extended by a special version of Where():
Where(Expression<Func<TimeSeriesEntry, TTag, bool>> predicate);
Parameter | Type | Description |
---|---|---|
name | string |
The name of the time series (in one or more documents) to query |
start | DateTime |
First parameter for Between() ; the beginning of the time series range to filter. |
end | DateTime |
Second parameter for Between() ; the end of the time series range to filter. |
timePeriod | Action<ITimePeriodBuilder> |
Expression returning a number of time units representing a time series range either at the beginning or end of the queried time series. |
LoadByTag type parameter |
TTag |
Time series entry tags can be just strings, but they can also be document IDs, representing a reference to an entity. LoadByTag takes the type of the entity. |
predicate | Expression<Func<TimeSeriesEntry, TTag, bool>> |
FromLast()
and FromFirst()
take an ITimePeriodBuilder
, which is used to represent
a range of time from milliseconds to years:
public interface ITimePeriodBuilder
{
Milliseconds(int duration);
Seconds(int duration);
Minutes(int duration);
Hours(int duration);
Days(int duration);
Months(int duration);
Quarters(int duration);
Years(int duration);
}
Return Value:
IRavenQueryable<TimeSeriesAggregationResult>
for aggregated data.
When the query aggregates time series entries, the results are returned in an aggregated array.IRavenQueryable<TimeSeriesRawResult>
for non-aggregated data.
When the query doesn't aggregate time series entries, the results are returned in a list of time series results.
Usage Flow
- Open a session
-
Call
session.Query
orsession.Advanced.DocumentQuery
.- Run a document query to locate documents whose time series you want to query.
- Extend the query using LINQ expressions to find and project time series data.
Start withSelect
to choose a time series.
- Retrieve the results using -
TimeSeriesAggregationResult
for aggregated data
-or-
TimeSeriesRawResult
for non-aggregated data
Examples
Session.Query
-
In this sample, we select a three-day range from the HeartRates time series.
IRavenQueryable<TimeSeriesRawResult> query = (IRavenQueryable<TimeSeriesRawResult>)session.Query<User>() .Where(u => u.Age < 30) .Select(q => RavenQuery.TimeSeries(q, "HeartRates", baseline, baseline.AddDays(3)) .ToList()); var result = query.ToList();
-
The first occurance of
Where
in the following example, filters documents.
The second occurance ofWhere
filters entries.IRavenQueryable<TimeSeriesRawResult> query = (IRavenQueryable<TimeSeriesRawResult>)session.Query<User>() // Choose user profiles of users under the age of 30 .Where(u => u.Age < 30) .Select(q => RavenQuery.TimeSeries(q, "HeartRates", baseline, baseline.AddDays(3)) // Filter entries by tag. .Where(ts => ts.Tag == "watches/fitbit") .ToList()); var result = query.ToList();
-
Here, we retrieve a company's stock trade data.
Note the usage of named values, so we may address trade Volume by name.
// Same query, only unnamed using (var session = store.OpenSession()) { IRavenQueryable<TimeSeriesRawResult> query = session.Query<Company>() .Where(c => c.Address.City == "New York") .Select(q => RavenQuery.TimeSeries(q, "StockPrices", baseline, baseline.AddDays(3)) .Where(ts => ts.Tag == "companies/kitchenAppliances") .ToList()); var result = query.ToList()[0]; day1Volume = result.Results[0].Values[4]; day2Volume = result.Results[1].Values[4]; day3Volume = result.Results[2].Values[4]; }
// Named Values Query using (var session = store.OpenSession()) { IRavenQueryable<TimeSeriesRawResult<StockPrice>> query = session.Query<Company>() .Where(c => c.Address.City == "New York") .Select(q => RavenQuery.TimeSeries<StockPrice>(q, "StockPrices", baseline, baseline.AddDays(3)) .Where(ts => ts.Tag == "companies/kitchenAppliances") .ToList()); var result = query.ToList()[0]; day1Volume = result.Results[0].Value.Volume; day2Volume = result.Results[1].Value.Volume; day3Volume = result.Results[2].Value.Volume; }
-
Here, we group heart-rate data of people above the age of 72 into 1-day groups, and retrieve each group's average heart rate and number of measurements.
The aggregated results are retrieved into anIRavenQueryable<TimeSeriesAggregationResult>
array.
IRavenQueryable<TimeSeriesAggregationResult> query = session.Query<User>() .Where(u => u.Age > 72) .Select(q => RavenQuery.TimeSeries(q, "HeartRates", baseline, baseline.AddDays(10)) .Where(ts => ts.Tag == "watches/fitbit") .GroupBy(g => g.Days(1)) .Select(g => new { Avg = g.Average(), Cnt = g.Count() }) .ToList()); var result = query.ToList();
Document Query
-
A Document Query using only the
From()
method.
var query = session.Advanced.DocumentQuery<User>() .SelectTimeSeries(builder => builder .From("Heartrate") .ToList());
-
A Document Query using
Between()
.
var query = session.Advanced.DocumentQuery<User>() .SelectTimeSeries(builder => builder .From("Heartrate") .Between(DateTime.Now, DateTime.Now.AddDays(1)) .ToList());
-
Two Document Queries using
FromFirst()
andFromLast()
. These return the first three days of the 'HeartRates' time series, and the last three days, respectively.
var query1 = session.Advanced.DocumentQuery<User>() .SelectTimeSeries(builder => builder .From("Heartrate") .FromFirst(x => x.Days(3)) .ToList()); var query2 = session.Advanced.DocumentQuery<User>() .SelectTimeSeries(builder => builder .From("Heartrate") .FromLast(x => x.Days(3)) .ToList());
-
A Document Query that filters time series entries by tags of the type
Monitor
. That is, the tags are document IDs of entities of typeMonitor
.
var query = session.Advanced.DocumentQuery<User>() .SelectTimeSeries(builder => builder .From("Heartrate") .LoadByTag<Monitor>() .Where((entry, monitor) => entry.Value <= monitor.Accuracy) .ToList());
public class Monitor { public double Accuracy { get; set; } }
Client Raw RQL Queries
To send a raw RQL query to the server, use session.Advanced.RawQuery
.
RawQuery
transmits queries to the server without checking or altering
their contents, time series contents or otherwise
RQL Queries Syntax
-
session.Advanced.RawQuery
-
Definition
IRawDocumentQuery<T> RawQuery<T>(string query);
-
Parameters
Parameters Type Description query
string Raw RQL Query -
Return Value:
IRawDocumentQuery<TimeSeriesAggregationResult>
for aggregated data.
When the query aggregates time series entries, the results are returned in an aggregated array.IRawDocumentQuery<TimeSeriesRawResult>
for non-aggregated data.
When the query doesn't aggregate time series entries, the results are returned in a list of time series results.
-
RQL Queries Usage Flow
- Open a session
- Call
session.Advanced.RawQuery
Pass it your query -
Retrieve the results into
TimeSeriesAggregationResult
for aggregated data
-or-
TimeSeriesRawResult
for non-aggregated data
RQL Queries Usage Samples
-
In this sample, a raw RQL query retrieves 24 hours of HeartRate data from users under the age of 30.
The query does not aggregate data, so we retrieve its results using aTimeSeriesRawResult
list.
We define an offset, to adjust retrieved results to the client's local time-zone.// May 17 2020, 00:00:00 var baseline = new DateTime(2020, 5, 17, 00, 00, 00); // Raw query with no aggregation - Declare syntax IRawDocumentQuery<TimeSeriesRawResult> nonAggregatedRawQuery = session.Advanced.RawQuery<TimeSeriesRawResult>(@" declare timeseries getHeartRates(user) { from user.HeartRates between $start and $end offset '02:00' } from Users as u where Age < 30 select getHeartRates(u) ") .AddParameter("start", baseline) .AddParameter("end", baseline.AddHours(24)); var nonAggregatedRawQueryResult = nonAggregatedRawQuery.ToList();
// May 17 2020, 00:00:00 var baseline = new DateTime(2020, 5, 17, 00, 00, 00); // Raw query with no aggregation - Select syntax IRawDocumentQuery<TimeSeriesRawResult> nonAggregatedRawQuery = session.Advanced.RawQuery<TimeSeriesRawResult>(@" from Users as u where Age < 30 select timeseries ( from HeartRates between $start and $end offset '02:00' )") .AddParameter("start", baseline) .AddParameter("end", baseline.AddHours(24)); var nonAggregatedRawQueryResult = nonAggregatedRawQuery.ToList();
-
In this sample, the query aggregates 7 days of HeartRate entries into 1-day groups.
From each group, two values are selected and projected to the client: the min and max hourly HeartRate values.
The aggregated results are retrieved using aTimeSeriesAggregationResult
array.// May 17 2020, 00:00:00 var baseline = new DateTime(2020, 5, 17, 00, 00, 00); // Raw Query with aggregation IRawDocumentQuery<TimeSeriesAggregationResult> aggregatedRawQuery = session.Advanced.RawQuery<TimeSeriesAggregationResult>(@" from Users as u select timeseries( from HeartRates between $start and $end group by '1 days' select min(), max()) ") .AddParameter("start", baseline) .AddParameter("end", baseline.AddDays(7)); var aggregatedRawQueryResult = aggregatedRawQuery.ToList();