All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.pushtechnology.diffusion.client.features.TimeSeries Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2023 DiffusionData Ltd., All Rights Reserved.
 *
 * Use is subject to license terms.
 *
 * NOTICE: All information contained herein is, and remains the
 * property of Push Technology. The intellectual and technical
 * concepts contained herein are proprietary to Push Technology and
 * may be covered by U.S. and Foreign Patents, patents in process, and
 * are protected by trade secret or copyright law.
 *******************************************************************************/
package com.pushtechnology.diffusion.client.features;

import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.stream.Stream;

import com.pushtechnology.diffusion.client.features.control.topics.TopicControl;
import com.pushtechnology.diffusion.client.session.Feature;
import com.pushtechnology.diffusion.client.session.PermissionsException;
import com.pushtechnology.diffusion.client.session.Session;
import com.pushtechnology.diffusion.client.session.SessionClosedException;
import com.pushtechnology.diffusion.client.session.SessionException;
import com.pushtechnology.diffusion.client.topics.details.TopicSpecification;
import com.pushtechnology.diffusion.client.types.PathPermission;
import com.pushtechnology.diffusion.datatype.Bytes;
import com.pushtechnology.diffusion.datatype.DataType;


/**
 * This feature allows a session to update and query time series topics.
 *
 * 

Time series topics

* *

* A time series is a sequence of events. Each event contains a value * and has server-assigned metadata comprised of a sequence number, timestamp, * and author. Events in a time series are ordered by increasing sequence * number. Sequence numbers have values between {@code 0} and * {@link Long#MAX_VALUE} and are contiguous: an event with sequence number * {@code n} will be followed by one with sequence number {@code n + 1}. Two * events with the same sequence number will be equal – having the same * timestamp, author, and value. * *

* A time series topic allows sessions to access a time series that is * maintained by the server. A time series topic has an associated * {@link DataType event data type}, such as {@code Binary}, {@code String}, or * {@code JSON}, that determines the type of value associated with each event. * *

* This feature provides a historic query API for time series topics, allowing a * session to query arbitrary sub-sequences of a time series. The * {@link TopicControl} and {@link Topics} features complete the API, providing * ways to create and subscribe to a time series topic. * *

* The API presents a time series as an append-only data structure of immutable * events that is only changed by adding new events. * *

Edit events

* *

* Although a time series is append-only, an event can be overridden by * appending an edit event. An edit event is a special type of event * that overrides an earlier event in the time series (referred to as the * original event) with a new value. When an edit event is added to a * time series, the server retains both the original event and the edit event, * allowing subscription and query results to reflect the edit. * *

* For example, suppose a time series has two events with the values {@code A} * and {@code B}, and the first event has been overridden by a later edit event * that provides a new value of {@code X}. The server has the following * information about the time series. * *

* * * * * * * * * * * * * * * * * * * * * *
SequenceValueType
0Aoriginal event
1Boriginal event
2Xedit of sequence 0
* *

* The current value of the event with sequence number 0 is {@code X}. * *

* If an original event has several edit events, the latest edit event (the one * with the highest sequence number) determines its current value. Each edit * event refers to an original event, never to another edit event. * *

* Extending the example by appending a further edit event to the time series: * *

* * * * * * * * * * * *
SequenceValueType
3Ysecond edit of sequence 0
* *

* The current value of the event with sequence number 0 is now {@code Y}. * *

Retained range

* *

* A time series topic retains a range of the most recent events. When a new * event is added to the time series, older events that fall outside of the * range are discarded. By default, this range includes the ten most recent * events. A different range can be configured by setting the * {@link TopicSpecification#TIME_SERIES_RETAINED_RANGE * TIME_SERIES_RETAINED_RANGE} property. * * *

Subscribing to a time series topic

* *

* A session can {@link Topics#subscribe(String) subscribe} to a time series * topic and * {@link Topics#addTimeSeriesStream(String, Class, com.pushtechnology.diffusion.client.features.Topics.ValueStream) * add a value stream} to receive updates about events appended to the time * series. Events are represented by {@link Event} instances. Each event has a * value and {@link EventMetadata metadata}. An edit event has two sets of * metadata – its own metadata and that of the original event that it * replaces. * *

Subscription range

* *

* New subscribers are sent a range of events from the end of the time series. * This is known as the subscription range. Configuring a subscription * range is a convenient way to provide new subscribers with an appropriate * subset of the latest events. * *

* The default subscription range depends on whether the topic is configured to * publish delta streams. If delta streams are enabled, new subscribers are sent * the latest event if one exists. If delta streams are disabled, new * subscribers are sent no events. Delta streams are enabled by default and can * be disabled by setting the {@link TopicSpecification#PUBLISH_VALUES_ONLY * PUBLISH_VALUES_ONLY} property to "true". * *

* A larger subscription range can be configured by setting the * {@link TopicSpecification#TIME_SERIES_SUBSCRIPTION_RANGE * TIME_SERIES_SUBSCRIPTION_RANGE} property. Regardless of the * {@code TIME_SERIES_SUBSCRIPTION_RANGE} property, if delta streams are * enabled, new subscribers will be sent at least the latest event if one * exists. * *

* If the range of events is insufficient, the subscribing session can use a * {@link TimeSeries#rangeQuery() range query} to retrieve older events. * *

* When configuring a non-default subscription range for a time series topic, * register value streams before subscribing to the topic. The session only * maintains a local cache of the latest value received for a topic, not the * full subscription range. If a value stream is added after a session has * subscribed to a matching time series topic, the new stream will only be * notified of the latest value. * *

Updating a time series topic

* *

* A session can use {@link #append append} to submit a value to be added to a * time series. The server will add an event to the end of the time series based * on the supplied value, with a new sequence number, timestamp, and the author * set to the authenticated principal of the session. * *

* Using {@link #append(String, Class, Object, Instant) append} allows a session * to submit a value and supplied {@link Instant}. This provides control over * the timestamp of the event. The supplied instant must not be before the * latest event stored by the time series topic. There are no other * restrictions. * *

* A session can use {@link #edit edit} to submit an edit to an original time * series event, identified by its sequence number. The server will add an edit * event to the end of the time series based on the supplied value, with a new * sequence number, timestamp, and the author set to the authenticated principal * of the session. * *

* Time series topics can also be updated using the functionality provided by * the {@link TopicUpdate} feature. This includes * {@link TopicUpdate#set(String, Class, Object) set}, * {@link TopicUpdate#addAndSet}, and {@link UpdateStream}s. This usage performs * an append operation with the added benefits of {@link UpdateConstraint}s, * topic creation when updating (upsert), and delta streams. When using methods * from {@link TopicUpdate}, the sequence number, timestamp, and author metadata * will be generated using the same rules as * {@link #append(String, Class, Object)} but the associated * {@link EventMetadata} will not be returned to the caller. * *

Querying a time series topic

*

* A {@link Query} is a configured query that can be evaluated for a time series * topic using {@link Query#selectFrom selectFrom(topicPath)}. Results are * provided as streams of {@link Event Event} instances. *

* {@link RangeQuery} is a builder for configuring a Query that selects a range * of a time series. There are two types of range query that differ in how edits * are processed – value range queries and edit range queries. * *

Value range queries

* *

* A value range query returns a merged view of part of a time series. This is * the most common time series query and appropriate for most applications. * *

* The result of a value range query reflects the latest available edits and the * {@link QueryResult#stream() query result stream} is ordered by the original * event sequence number, presenting edit events instead of the original events * they replace. Original events that have no edit events are included verbatim. * Original events that have edit events are replaced by the latest edit event. * *

* A value range query of the example time series, with no range constraints so * the entire time series is selected, returns two events: * *

 * sequence=3, value=Y; original event sequence=0
 * sequence=1, value=B
 * 
* *

* The original value of the first event is not provided. It's apparent that the * first event is an edit event because it provides the metadata of the original * event it replaces. * *

Edit range queries

* *

* Applications with auditing and other administrative requirements can access * original event values using an edit range query. An edit range query returns * an unmerged view of a time series that can include both original events and * the edit events that replace them. Edit range queries are rarely needed * – value range queries satisfy most use cases. * *

* Edit range queries provide a detailed view of a time series. Because this is * potentially sensitive information, an edit range query can only be performed * by a session that has the {@code QUERY_OBSOLETE_TIME_SERIES_EVENTS} * permission for the target topic. * *

* There are two sub-types of edit range query. * *

* A full audit trail of edit events can be obtained using an all edits * edit range query. The result contains all original events selected by the * query, together with all subsequent edit events that affect the original * events. The query result stream provides events in time series order. An all * edits query of the example time series, with no range constraints so the * entire time series is selected, returns four events: * *

 * sequence=0; value=A
 * sequence=1; value=B
 * sequence=2; value=X; original event sequence=0
 * sequence=3; value=Y; original event sequence=0
 * 
* *

* A latest edits edit range query returns a query result stream in * time series order that contains all original events selected by the query, * together with the latest edit events that affect the original events. A * latest edits query of the example time series, with no range constraints so * the entire time series is selected, returns three events: * *

 * sequence=0; value=A
 * sequence=1; value=B
 * sequence=3; value=Y; original event sequence=0
 * 
* *

* The initial range of events delivered for a subscription to a time series * topic is derived from a latest edits edit range query. See * Subscription Range. * *

* When evaluated for a time series that has no edit events, an edit range query * will return the same results as a similarly configured value range query. * *

Changes to a time series made outside the API

* *

* The API presents a time series as an append-only data structure of immutable * events that is only changed by adding new events. The API does not allow * events to be deleted or edited. * *

* There are circumstances in which events can be removed from a time series by * server operations outside the API. For example, a time series topic can be * configured to discard or archive older events to save storage space; or the * time series may be held in memory and lost if the server restarts. Subscribed * sessions are not notified when events are removed in this way, but a session * can infer the removal of events that are no longer included in query results. * Similarly, an event's value can be changed on the server. For example, if an * administrator changes its value to redact sensitive data. Again, subscribed * sessions are not notified when events are modified, but a session can infer * this has happened from query results. * *

* Whether such changes can happen for a particular time series topic depends on * the topic specification, and the administrative actions that are allowed. To * write a robust application, do not rely on two Event instances with the same * sequence number but obtained though different API calls, being equal; nor * that there are no sequence number gaps between events in query results. * *

Access control

*

* The session must have the {@link PathPermission#READ_TOPIC READ_TOPIC} * permission for a topic to query a time series topic. The * {@link PathPermission#QUERY_OBSOLETE_TIME_SERIES_EVENTS * QUERY_OBSOLETE_TIME_SERIES_EVENTS} permission is additionally required * to evaluate an {@link RangeQuery#forEdits edit range} query, or a * {@link RangeQuery#forValues value range query} with an * {@link RangeQuery#editRange edit range}. *

* The session must have the {@link PathPermission#UPDATE_TOPIC UPDATE_TOPIC} * permission for a topic to {@link #append(String, Class, Object) append} * a new event to a time series topic. The * {@link PathPermission#EDIT_TIME_SERIES_EVENTS EDIT_TIME_SERIES_EVENTS} * permission is additionally required to * {@link #edit(String, long, Class, Object) submit an edit} to any time series * topic event. The more restrictive * {@link PathPermission#EDIT_OWN_TIME_SERIES_EVENTS EDIT_OWN_TIME_SERIES_EVENTS} * permission allows a session to submit edits to time series topic * events that are authored by the principal of the calling session. * * @author DiffusionData Limited * @since 6.0 */ public interface TimeSeries extends Feature { /** * Update a time series topic by appending a new value. * *

* The server will add an event to the end of the time series based on the * supplied value, with a new sequence number, timestamp, and the author set * to the authenticated principal of the session. * * @param topicPath the path of the time series topic to update * @param valueClass the type of the supplied value. This must match the * value type of the {@link DataType} configured as the time series * topic's {@link TopicSpecification#TIME_SERIES_EVENT_VALUE_TYPE * event value type}. * @param value the event value * @return a CompletableFuture that completes when a response is received * from the server. * *

* If the update was successful, the CompletableFuture will complete * successfully and provide the {@link EventMetadata} of the new * event. * *

* Otherwise, the CompletableFuture will complete exceptionally with * a {@link CompletionException}. Common reasons for failure, listed * by the exception reported as the * {@link CompletionException#getCause() cause}, include: * *

    *
  • {@link NoSuchTopicException} – if there is no topic * bound to {@code topicPath}; * *
  • {@link IncompatibleTopicException} – if the topic bound * to {@code topicPath} is not a time series topic; * *
  • {@link IncompatibleTopicException} – if the * {@code valueClass} does not match the event data type of the time * series topic bound to {@code topicPath}; * *
  • {@link UpdateFailedException} – if the update failed, * for example if the topic is set to * {@link TopicSpecification#VALIDATE_VALUES validate values} and * {@code value} is not valid or the server generated timestamp is * before the most recent event; * *
  • {@link PermissionsException} – if the calling * session does not have {@code UPDATE_TOPIC} permission for * {@code topicPath}; * *
  • {@link SessionClosedException} – if the session is * closed. *
* * @throws IllegalArgumentException if there is no data type that supports * values of class {@code valueClass} */ CompletableFuture append(String topicPath, Class valueClass, V value) throws IllegalArgumentException; /** * Update a time series topic by appending a new value with a supplied * timestamp. * *

* The server will add an event to the end of the time series based on the * supplied value and timestamp, with a new sequence number, and the author * set to the authenticated principal of the session. * * @param topicPath the path of the time series topic to update * @param valueClass the type of the supplied value. This must match the * value type of the {@link DataType} configured as the time series * topic's {@link TopicSpecification#TIME_SERIES_EVENT_VALUE_TYPE * event value type}. * @param value the event value * @param timestamp the supplied timestamp, must be greater or equal to that * of the most recent event appended to the topic * @return a CompletableFuture that completes when a response is received * from the server. * *

* If the update was successful, the CompletableFuture will complete * successfully and provide the {@link EventMetadata} of the new * event. * *

* Otherwise, the CompletableFuture will complete exceptionally with * a {@link CompletionException}. Common reasons for failure, listed * by the exception reported as the * {@link CompletionException#getCause() cause}, include: * *

    *
  • {@link NoSuchTopicException} – if there is no topic * bound to {@code topicPath}; * *
  • {@link IncompatibleTopicException} – if the topic bound * to {@code topicPath} is not a time series topic; * *
  • {@link IncompatibleTopicException} – if the * {@code valueClass} does not match the event data type of the time * series topic bound to {@code topicPath}; * *
  • {@link UpdateFailedException} – if the update failed, * for example if the topic is set to * {@link TopicSpecification#VALIDATE_VALUES validate values} and * {@code value} is not valid or the supplied instant is before the * most recent event; * *
  • {@link PermissionsException} – if the calling * session does not have {@code UPDATE_TOPIC} permission for * {@code topicPath}; * *
  • {@link SessionClosedException} – if the session is * closed. *
* * @throws IllegalArgumentException if there is no data type that supports * values of class {@code valueClass} * * @since 6.6 */ CompletableFuture append(String topicPath, Class valueClass, V value, Instant timestamp) throws IllegalArgumentException; /** * Update a time series topic by appending a new value that overrides the * value of an existing event. * *

* The existing event is identified by its sequence number and must be an * original event. * *

* The server will add an edit event to the end of the time series based on * the supplied value, with a new sequence number, timestamp, and the author * set to the authenticated principal of the session. * * @param topicPath the path of the time series topic to update * @param originalSequence the sequence number of the original event to edit * @param valueClass the type of the supplied value. This must match the * value type of the {@link DataType} configured as the time series * topic's {@link TopicSpecification#TIME_SERIES_EVENT_VALUE_TYPE * event value type}. * * @param value the event value * @return a CompletableFuture that completes when a response is received * from the server. * *

* If the update was successful, the CompletableFuture will complete * successfully and provide the {@link EventMetadata} of the new * event. * *

* Otherwise, the CompletableFuture will complete exceptionally with * a {@link CompletionException}. Common reasons for failure, listed * by the exception reported as the * {@link CompletionException#getCause() cause}, include: * *

    *
  • {@link NoSuchTopicException} – if there is no topic * bound to {@code topicPath}; * *
  • {@link IncompatibleTopicException} – if the topic bound * to {@code topicPath} is not a time series topic; * *
  • {@link IncompatibleTopicException} – if the * {@code valueClass} does not match the event data type of the time * series topic bound to {@code topicPath}; * *
  • {@link NoSuchEventException} – if the topic does not * have an original event with the sequence number {@code sequence}, * perhaps because the original event has been discarded; * *
  • {@link PermissionsException} – if the calling * session does not have the {@code UPDATE_TOPIC} permission for * {@code topicPath} or neither of the following is true: *
      *
    • the calling session has the {@code EDIT_TIME_SERIES_EVENTS} * permission for {@code topicPath}; *
    • the calling session has the * {@code EDIT_OWN_TIME_SERIES_EVENTS} permissions for * {@code topicPath} and {@code originalSequence} refers to an * event authored by the principal of the calling session. *
    * *
  • {@link SessionClosedException} – if the session is * closed. *
* * @throws IllegalArgumentException if there is no data type that supports * values of class {@code valueClass} */ CompletableFuture edit(String topicPath, long originalSequence, Class valueClass, V value) throws IllegalArgumentException; /** * A configured query. * *

* A default query that performs a value range query of an entire time * series can be obtained using {@link TimeSeries#rangeQuery} and further * configured using methods of the {@link RangeQuery} interface. * * @param query value type */ interface Query { /** * Evaluate this query for a time series topic. * *

* The session must have the {@code READ_TOPIC} topic permission for * {@code topicPath} to evaluate a query. The * {@code QUERY_OBSOLETE_TIME_SERIES_EVENTS} topic permission is also * required if this is an {@link RangeQuery#forEdits edit range} query, * or a {@link RangeQuery#forValues value range query} with an * {@link RangeQuery#editRange edit range}. * * @param topicPath the path of the time series topic to query * * @return a CompletableFuture that completes when a response is * received from the server. * *

* If the query returned results, the CompletableFuture will * complete successfully and provide an {@link QueryResult}. * *

* Otherwise, the CompletableFuture will complete exceptionally * with a {@link CompletionException}. Common reasons for * failure, listed by the exception reported as the * {@link CompletionException#getCause() cause}, include: * *

    *
  • {@link NoSuchTopicException} – if there is no topic * bound to {@code topicPath}; * *
  • {@link IncompatibleTopicException} – if the topic * bound to {@code topicPath} is not a time series topic; * *
  • {@link IncompatibleTopicException} – if the * {@link RangeQuery#as query value type} is incompatible with * the event data type of the time series topic bound to * {@code topicPath}; * *
  • {@link InvalidQueryException} – if the range query * is not valid for the time series; * *
  • {@link PermissionsException} – if the calling * session does not have {@code READ_TOPIC} permission for * {@code topicPath}; * *
  • {@link PermissionsException} – if the calling * session does not have * {@code QUERY_OBSOLETE_TIME_SERIES_EVENTS} permission for * {@code topicPath} and this is an {@link RangeQuery#forEdits * edit range} query, or a {@link RangeQuery#forValues value * range query} with an {@link RangeQuery#editRange edit range}; * *
  • {@link SessionClosedException} – if the session is * closed. *
*/ CompletableFuture> selectFrom(String topicPath); } /** * Return a default range query that performs a value range query of an * entire time series. * *

* Further queries with different parameters can be configured using the * {@link RangeQuery} methods. * *

* The result provides {@link Bytes} values, making it compatible with any * event data type supported by time series topics. A query with a more * specific value type can be configured using {@link RangeQuery#as(Class)}. * *

* A RangeQuery equal to the one returned by this method can be created from * an arbitrary RangeQuery as follows. * *

     * RangeQuery defaults = anyRangeQuery
     *     .forValues()
     *     .fromStart()
     *     .untilLast(0)
     *     .limit(Long.MAX_VALUE)
     *     .as(Bytes.class);
     * 
* * @return a RangeQuery with the default settings */ RangeQuery rangeQuery(); /** * Builder for queries that select a range of events from a time series. * *

* See {@link TimeSeries} for an overview of the various types of range * query: *

    *
  • value range queries, *
  • latest edits edit range queries, and *
  • all edits edit range queries. *
* *

* {@link TimeSeries#rangeQuery()} returns a default RangeQuery. Further * queries with different parameters can be configured using the methods of * this interface. {@link RangeQuery} instances are immutable. Each method * returns a copy of this query with a modified setting. Method calls can be * chained together in a fluent manner to create a query. For example: * *

     * RangeQuery<Bytes> defaultQuery =
     *     session.feature(TimeSeries.class).rangeQuery();
     *
     * // A value range query that selects up to 100 original events from the
     * // start of a time series.
     * RangeQuery<Bytes> first100 = defaultQuery.forValues().fromStart().next(100);
     * 
* *

Creating value range queries

* *

* A value range query returns a merged view of part of a time series. This * is the most common time series query and appropriate for most * applications. * *

* The syntax of a value range query is shown in the following diagram. * *

* Value range query
     * syntax. * *

* A value range query begins with the {@link #forValues()} operator, * followed by the view range. The view range determines the range * of original events the time series that are of interest. See Range * expressions below for the various ways to specify {@code RANGE}. *

* The events returned by the query are constrained by an optional edit * range, introduced by the {@link #editRange()} operator. An event * will only be included in the result if it is in the edit range. Let's * consider some examples to see how the view range and the edit range * interact. * *

* * * * * * * * * * * * * * * * *
QueryMeaning
{@code rangeQuery().forValues();}For each original event in the time series, either return the latest * edit event or if it has no edit events, return the original event.
{@code rangeQuery().forValues().from(100).to(150);}For each original event with a sequence number between 100 and 150 * (inclusive), either return the latest edit event or if it has no edit * events, return the original event.
* {@code rangeQuery().forValues().from(100).to(150).editRange().from(400);} * For each original event with a sequence number between 100 and 150 * (inclusive), return the latest edit event with a sequence number greater * than or equal to 400. *

* The result of this query will not include any original events because * there is no overlap between the view range and the edit range.

* *

* Value range queries can be further refined using the {@link #limit * limit()} and {@link #as as()} operators. * *

Creating edit range queries

* *

* An edit range query returns an unmerged view of a time series than can * include both original events and the edit events that replace them. Edit * range queries are rarely needed – value range queries satisfy most * use cases. * *

* The syntax of an edit range query is shown in the following diagram. * *

* Edit range query syntax. * *

* An edit range query begins with the {@link #forEdits()} operator, * followed by the view range. The view range determines the range * of original events the time series that are of interest. The result will * only contain original events that are in the view range, and edit events * for original events in the view range. See Range expressions * below for the various ways to specify {@code RANGE}. *

* The events returned by the query are constrained by an optional edit * range, introduced by the {@link #latestEdits()} or * {@link #allEdits()} operators. An event will only be included in the * result if it is in the edit range. Let's consider some example edit range * queries. * *

* * * * * * * * * * * * * * * * * * * * *
QueryMeaning
{@code rangeQuery().forEdits();}Return all events in a time series.
{@code rangeQuery().forEdits().from(100).to(150);}Return the original events with a sequence number between 100 and 150 * (inclusive) and all edit events in the time series that refer to the * original events.
{@code rangeQuery().forEdits().from(100).to(150).latestEdits();}Return the original events with a sequence number between 100 and 150 * (inclusive) and the latest edit events in the time series that refer to * the original events.
* {@code rangeQuery().forEdits().from(100).to(150).allEdits().from(400);} * For each original event with a sequence number between 100 and 150, * (inclusive) return all edit events with a sequence number greater than or * equal to 400. *

* The result of this query will not include any original events because * there is no overlap between the view range and the edit range.

* *

* Edit range queries can be further refined using the {@link #limit * limit()} and {@link #as as()} operators. * *

Range expressions

*

* Range expressions are used to specify the view and edit ranges in value * range and edit range queries. Each range expression has an * anchor that determines where to start, and a span that * determines where the range ends. Both anchor and span are * inclusive – if an anchor or span falls on an event, the * event is included in the result. * *

* Range syntax. *

* Both anchor and the span are optional. If the anchor is unspecified, the * range begins at the start of the time series. If the span is unspecified, * the range continues until the end of the time series. * *

Anchors

* *

* There are five ways to specify an anchor. *

* Anchor syntax. * *

* * * * * * * * * * * * * * * * * * * * * * * * *
AnchorMeaning
{@link #from(long)}Sets the anchor at an absolute sequence number.
{@link #fromStart()}Sets the anchor at the start of the time series.
{@link #from(Instant)}
* {@link #from(Date)}
Sets the anchor at an absolute time.
{@link #fromLast(long)}Sets the anchor at a relative offset before the end of the time * series. For value range queries, {@code count} is the number of original * events. For edit range queries, {@code count} is the number of events of * any type.
{@link #fromLast(Duration)}
* {@link #fromLastMillis(long)}
Sets the anchor at a relative time before the timestamp of the last * event of the time series.
*

* An anchor point can be before the start or after the end of the time * series. * *

Spans

* *

* There are nine ways to specify a span. *

* Span syntax. * *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
SpanMeaning
{@link #to(long)}The range ends at an absolute sequence number. The {@code sequence} * argument may be before or after the anchor.
{@link #toStart()}The range ends at the start of the time series.
{@link #to(Instant)}
* {@link #to(Date)}
The range ends at an absolute time. The {@code instant} argument may * be before or after the anchor.
{@link #next(long)}The range ends at an event that is a relative number of events after * the anchor. For value range queries, {@code count} is the number of * original events. For edit range queries, {@code count} is the number of * events of any type.
{@link #next(Duration)}
* {@link #nextMillis(long)}
The range ends at an event that is a relative time after the * anchor.
{@link #previous(long)}The range ends at an event that is a relative number of events before * the anchor. For value range queries, {@code count} is the number of * original events. For edit range queries, {@code count} is the number of * events of any type.
{@link #previous(Duration)}
* {@link #previousMillis(long)}
The range ends at an event that is a relative time before the * anchor.
{@link #untilLast(long)}The range ends at an event that is a relative number of events before * the end of the time series. For value range queries, {@code count} is the * number of original events. For edit range queries, {@code count} is the * number of events of any type.
{@link #untilLast(Duration)}
* {@link #untilLastMillis(long)}
The range ends at an event that is a relative time before the * timestamp of the last event of the time series.
* *

* A span can specify an end point that is before the start or after the end * of the time series. *

* If the span specifies an end point after the anchor, the range includes * the first event at or following the anchor and ends at the last event at * or preceding the end point. If the span specifies an end point before the * anchor, the range includes the first event at or preceding the anchor and * ends at the last event at or after the end point. * *

Using the builder methods

* *

* Although the natural order of operators in a query is as shown in the * syntax diagrams above, RangeQuery builder methods – those that * return another RangeQuery – can be applied in any order with the * following exceptions: *

    *
  • {@link #editRange()} only applies to value range queries, so cannot * follow {@code forEdits()} without an intervening {@code forValues()}; *
  • {@link #latestEdits()} and {@link #allEdits()} only apply to edit * range queries, so cannot follow {@code forValues()} without an * intervening {@code forEdits()}. *
* *

* Each method overrides some configuration of the RangeQuery to which it is * applied, as summarized in the following table. * *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Builder methodOperator typeOverridden configuration
{@code forValues()}Value rangeOverrides the existing query type to create a new value range query. * Overrides the existing view range with a new view range that selects the * entire time series. The existing edit range is copied unchanged.
{@code forEdits()}Value rangeOverrides the existing query type to create a new edit range query * that includes all edits. Overrides the existing view range with a new * view range that selects the entire time series. The existing edit range * is copied unchanged.
{@code editRange()}Edit rangeOverrides the existing edit range with a new edit range that selects * the entire time series. The existing view range is copied unchanged.
* Throws {@code IllegalStateException} if this is not a value range * query.
{@code latestEdits()}
* {@code allEdits()}
Edit rangeOverrides the existing edit range with a new edit range that selects * the entire time series. The existing view range is copied unchanged. *
* Throws {@code IllegalStateException} if this is not an edit range * query.
{@code from()}
* {@code fromStart()}
* {@code fromLast()}
AnchorOverrides the anchor of the current range.
{@code to()}
* {@code toStart()}
* {@code next()}
* {@code previous()}
* {@code untilLast()}
SpanOverrides the span of the current range.
{@code limit()}LimitOverrides the limit.
{@code as()}Query value typeOverrides the query value type.
* * @param query value type * @see TimeSeries#rangeQuery() */ interface RangeQuery extends Query { /** * Return a copy of this RangeQuery configured to perform a value range * query with the view range set to the entire time series. * *

* Operator type: value range * * @return a copy of this range query configured to perform a view range * query with a new view range that selects the entire time * series */ RangeQuery forValues(); /** * Return a copy of this RangeQuery configured to perform an edit range * query with the view range set to the entire time series. * *

* Operator type: value range * * @return a copy of this range query configured to perform an edit * range query with a new view range that selects the entire * time series */ RangeQuery forEdits(); /** * Return a copy of this RangeQuery configured to perform a value range * query with the edit range set to the entire time series. * *

* This operator can only be applied to value range queries. The default * query returned by {@link TimeSeries#rangeQuery() rangeQuery()} is a * value range query. The {@link #forValues()} operator can be used to * create a value range query from an edit range query. * *

* Operator type: edit range * * @return a copy of this range query configured to perform a view range * query with a new edit range that selects the entire time * series * @throws IllegalStateException if this is not a value range query */ RangeQuery editRange() throws IllegalStateException; /** * Return a copy of this RangeQuery configured to perform an edit range * query with the edit range that selects all edits in the entire time * series. * *

* This operator can only be applied to edit range queries. The default * query returned by {@link TimeSeries#rangeQuery() rangeQuery()} is a * value range query. The {@link #forEdits()} operator can be used to * create an edit range query from a value range query. * *

* Operator type: edit range * * @return a copy of this range query configured to perform an edit * range query with a new edit range that selects all edits in * the entire time series * @throws IllegalStateException if this is not an edit range query */ RangeQuery allEdits() throws IllegalStateException; /** * Return a copy of this RangeQuery configured to perform an edit range * query with the edit range that selects latest edits in the entire * time series. * *

* This operator can only be applied to edit range queries. The default * query returned by {@link TimeSeries#rangeQuery() rangeQuery()} is a * value range query. The {@link #forEdits()} operator can be used to * create an edit range query from a value range query. * *

* Operator type: edit range * * @return a copy of this range query configured to perform an edit * range query with a new edit range that selects the latest * edits in the entire time series * @throws IllegalStateException if this is not an edit range query */ RangeQuery latestEdits() throws IllegalStateException; /** * Return a copy of this RangeQuery with the anchor of the current range * configured to be an absolute sequence number. * *

* Operator type: anchor * * @param sequence absolute sequence number specifying the anchor of * the returned range * @throws IllegalArgumentException if sequence is negative * @return a copy of this range query with a new anchor */ RangeQuery from(long sequence) throws IllegalArgumentException; /** * Return a copy of this RangeQuery with the anchor of the current range * configured to be the start of the time series. * *

* There is a difference between {@code fromStart()} and {@code from(0)} * if the range also ends before the first event of the time series. For * example, {@code fromStart()).toStart()} is always empty, but * {@code from(0).toStart()} includes the event with sequence number * {@code 0}. * *

* Operator type: anchor * * @return a copy of this range query with a new anchor */ RangeQuery fromStart(); /** * Return a copy of this RangeQuery with the anchor of the current range * configured to be an absolute time. * *

* Operator type: anchor * * @param instant absolute time specifying the anchor of range * @throws ArithmeticException if instant cannot be represented as a * long number of milliseconds from the epoch of * 1970-01-01T00:00:00Z, see {@link Instant#toEpochMilli()} * @return a copy of this range query with a new anchor */ RangeQuery from(Instant instant) throws ArithmeticException; /** * Return a copy of this RangeQuery with the anchor of the current range * configured to be an absolute time. * *

* Operator type: anchor * * @param date absolute time specifying the anchor of range * @return a copy of this range query with a new anchor * @see #from(Instant) */ RangeQuery from(Date date); /** * Return a copy of this RangeQuery with the anchor of the current range * configured to be a relative offset before the end of the time series. * *

* Operator type: anchor * * @param count specifies the anchor as a number of events before the * end of the time series. For value range queries, count is the * number of original events. For edit range queries, count is * the number of events of any type. * @throws IllegalArgumentException if count is negative * @return a copy of this range query with a new anchor */ RangeQuery fromLast(long count) throws IllegalArgumentException; /** * Return a copy of this RangeQuery with the anchor of the current range * configured to be a relative time from the timestamp of the last event * in the time series. * *

* Operator type: anchor * * @param timeSpan specifies anchor relative to the timestamp of the * latest event in the time series * @throws IllegalArgumentException if timeSpan is negative * @throws ArithmeticException if timeSpan cannot be represented as a * long number of milliseconds, see {@link Duration#toMillis()} * @return a copy of this range query with a new anchor */ RangeQuery fromLast(Duration timeSpan) throws ArithmeticException, IllegalArgumentException; /** * Return a copy of this RangeQuery with the anchor of the current range * configured to be a relative time from the timestamp of the last event * in the time series. * *

* Operator type: anchor * * @param timeSpan specifies anchor as a number of milliseconds relative * to the timestamp of the latest event in the time series * @throws IllegalArgumentException if timeSpan is negative * @return a copy of this range query with a new anchor * @see #fromLast(Duration) */ RangeQuery fromLastMillis(long timeSpan) throws IllegalArgumentException; /** * Return a copy of this RangeQuery with the span of the current range * configured to end at an absolute sequence number. * *

* Operator type: span * * @param sequence absolute sequence number specifying the end of the * returned range * @throws IllegalArgumentException if sequence is negative * @return a copy of this range query with a new span */ RangeQuery to(long sequence) throws IllegalArgumentException; /** * Return a copy of this RangeQuery with the span of the current range * configured to end at the start of the time series. * *

* There is a difference between {@code toStart()} and {@code to(0)} if * the range also starts before the first event of the time series. For * example, {@code fromStart().toStart()} is always empty, but * {@code fromStart().to(0)} includes the event with sequence number * {@code 0}. * *

* Operator type: span * * @return a copy of this range query with a new span */ RangeQuery toStart(); /** * Return a copy of this RangeQuery with the span of the current range * configured to end at an absolute time. * *

* Operator type: span * * @param instant absolute time specifying the end of the range * @throws ArithmeticException if instant cannot be represented as a * long number of milliseconds from the epoch of * 1970-01-01T00:00:00Z, see {@link Instant#toEpochMilli()} * @return a copy of this range query with a new span */ RangeQuery to(Instant instant) throws ArithmeticException; /** * Return a copy of this RangeQuery with the span of the current range * configured to end at an absolute time. * *

* Operator type: span * * @param date absolute time specifying the end of the range * @return a copy of this range query with a new span * @see #to(Instant) */ RangeQuery to(Date date); /** * Return a copy of this RangeQuery with the span of the current range * configured to select a range of events following the anchor. * *

* Operator type: span * * @param count specifies the end of the range of events to select * following the anchor. For value range queries, count is the * number of original events. For edit range queries, count is * the number of events of any type. * @throws IllegalArgumentException if count is negative * @return a copy of this range query with a new span */ RangeQuery next(long count) throws IllegalArgumentException; /** * Return a copy of this RangeQuery with the span of the current range * configured to select a temporal range of events following the anchor. * *

* Operator type: span * * @param timeSpan the time span of events following the anchor to * select * @throws IllegalArgumentException if timeSpan is negative * @throws ArithmeticException if timeSpan cannot be represented as a * long number of milliseconds, see {@link Duration#toMillis()} * @return a copy of this range query with a new span */ RangeQuery next(Duration timeSpan) throws ArithmeticException, IllegalArgumentException; /** * Return a copy of this RangeQuery with the span of the current range * configured to select a temporal range of events following the anchor. * *

* Operator type: span * * @param timeSpan the time span in milliseconds of events following the * anchor to select * @throws IllegalArgumentException if timeSpan is negative * @return a copy of this range query with a new span * @see #next(Duration) */ RangeQuery nextMillis(long timeSpan) throws IllegalArgumentException; /** * Return a copy of this RangeQuery with the span of the current range * configured to select a range of events preceding the anchor. * *

* Operator type: span * * @param count specifies the end of the range of events to select * preceding the anchor. For value range queries, count is the * number of original events. For edit range queries, count is * the number of events of any type. * @throws IllegalArgumentException if count is negative * @return a copy of this range query with a new span */ RangeQuery previous(long count) throws IllegalArgumentException; /** * Return a copy of this RangeQuery with the span of the current range * configured to select a temporal range of events preceding the anchor. * *

* Operator type: span * * @param timeSpan the time span of events preceding the anchor to * select * @throws IllegalArgumentException if timeSpan is negative * @throws ArithmeticException if timeSpan cannot be represented as a * long number of milliseconds, see {@link Duration#toMillis()} * @return a copy of this range query with a new span */ RangeQuery previous(Duration timeSpan) throws ArithmeticException, IllegalArgumentException; /** * Return a copy of this RangeQuery with the span of the current range * configured to select a temporal range of events preceding the anchor. * *

* Operator type: span * * @param timeSpan the time span in milliseconds of events preceding the * anchor to select * @throws IllegalArgumentException if timeSpan is negative * @return a copy of this range query with a new span * @see #previous(Duration) */ RangeQuery previousMillis(long timeSpan) throws IllegalArgumentException; /** * Return a copy of this RangeQuery with the span of the current range * configured to end a number of events before the end of the time * series. * *

* Operator type: span * * @param count specifies the end of the range of events to select as a * number of events before the end of the time series. For value * range queries, count is the number of original events. For * edit range queries, count is the number of events of any type. * @throws IllegalArgumentException if count is negative * @return a copy of this range query with a new span */ RangeQuery untilLast(long count) throws IllegalArgumentException; /** * Return a copy of this RangeQuery with the span of the current range * configured to end at a relative time from the timestamp of the last * event in the time series. * *

* Operator type: span * * @param timeSpan specifies the end of the range of events to select * relative to the timestamp of the latest event in the time * series * @throws IllegalArgumentException if timeSpan is negative * @throws ArithmeticException if timeSpan cannot be represented as a * long number of milliseconds, see {@link Duration#toMillis()} * @return a copy of this range query with a new span */ RangeQuery untilLast(Duration timeSpan) throws ArithmeticException, IllegalArgumentException; /** * Return a copy of this RangeQuery with the span of the current range * configured to end at a relative time from the timestamp of the last * event in the time series. * *

* Operator type: span * * @param timeSpan specifies the end of the range of events to select as * a number of milliseconds relative to the timestamp of the * latest event in the time series * @throws IllegalArgumentException if timeSpan is negative * @return a copy of this range query with a new span * @see #untilLast(Duration) */ RangeQuery untilLastMillis(long timeSpan) throws IllegalArgumentException; /** * Return a copy of this RangeQuery that returns at most count events. * *

* If the query would otherwise select more than count events, only the * latest count values (those with the highest sequence numbers) are * returned. * *

* This is most useful when a temporal span has been configured with * {@link #next(Duration)} or {@link #previous(Duration)}, * where the potential number of returned events is unknown. * *

* {@link QueryResult#isComplete()} can be used to determine whether a * query has returned an incomplete result. * *

* Operator type: limit * * @param count the maximum number of events to return * @throws IllegalArgumentException if count is negative * @return a copy of this range query with a new limit */ RangeQuery limit(long count) throws IllegalArgumentException; /** * Return a copy of this RangeQuery with a different query value type. * *

* A query can only be evaluated successfully against time series topics * with a compatible event data type. If a query method is called for a * time series topic with an incompatible event data type, the query * will complete exceptionally. * *

* If the event data type of the time series topic is known, * compatibility of a particular {@code valueClass} can be checked using * {@link com.pushtechnology.diffusion.datatype.DataType#canReadAs(Class) * dataType.canReadAsClass(valueClass)}. The * {@link TimeSeries#rangeQuery() default range query} has a query value * type of {@link Bytes}, which is compatible with all time series value * data types. * *

* Operator type: query value type * * @return a copy of this range query with a new query value type */ RangeQuery as(Class valueType); } /** * Time series event metadata. */ interface EventMetadata { /** * Sequence number identifying this event within its time series. * Assigned by the server when the event is created. * *

* Sequence numbers are unique within a time series. Each event appended * to a time series is assigned a sequence number that is is equal to * the sequence number of the preceding event plus one. * * @return sequence number; non-negative */ long sequence(); /** * Event timestamp. Assigned by the server when the event is created. * *

* Events do not have unique timestamps. Events with different sequence * numbers may have the same timestamp. * *

* Subsequent events in a time series usually have timestamps that are * greater or equal to the timestamps of earlier events, but this is not * guaranteed due to changes to the time source used by the server. * * @return the difference, measured in milliseconds, between the time * the server added the event to the time series and midnight, * January 1, 1970 UTC */ long timestamp(); /** * Server-authenticated identity of the session that created the event. * * @return the principal that created the event, or * {@link Session#ANONYMOUS} if the session that created the * event was not authenticated */ String author(); } /** * An event in a time series. * *

* Two instances are {@link Object#equals(Object) equal} if and only if they * have identical attributes. Typically two Event instances that have the * same sequence number will be equal, but this may not be true if the event * has changed on the server – see Changes to a time series made * outside the API in the {@link TimeSeries TimeSeries} documentation. * * @param query value type */ interface Event extends EventMetadata { /** * The value associated with the event. * * @return event value */ V value(); /** * If this is an edit event, returns the metadata of the original event * that this event replaces; otherwise returns this event. * *

* The result is always the metadata of an original event, never that of * an edit event. * * @return if equal to {@code this}, this is not an edit event; * otherwise, the sequence number of the event replaced by this * edit event */ EventMetadata originalEvent(); /** * Return whether this is an edit event. * *

* {@code x.isEditEvent()} is equivalent to * {@code x.originalEvent() != x}. * * @return true if this is an edit event, otherwise this is an original * event */ boolean isEditEvent(); /** * Clone this event with a different value. * *

* This method is useful when further transformation of the received * value is needed, but the application wishes to preserve other event * attributes. For example, if a Bytes value is received which the * session wishes to interpret as JSON, it can do the following. * *

         * Event<JSON> transformToJSON(Event<Bytes> bytesEvent) {
         *   JSON json = Diffusion.dataTypes().json().readValue(bytesEvent.value();
         *   return bytesEvent.withValue(json);
         * }
         * 
* *

* All attributes other than the value will be copied from this event. * The result will only equal this event if newValue equals this event's * value. * * @return a copy of this event with a different value * @param the new value type */ Event withValue(T newValue); } /** * Query result providing a {@link #stream() stream of events}. * * @param query value type */ interface QueryResult { /** * Returns the number of events selected by the query. * *

* This number may be greater than {@code stream().count()} due to a * policy of the time series topic to limit the number of returned * results, or the use of {@link RangeQuery#limit}. * * @return the number of events selected by the query */ long selectedCount(); /** * @return the events, as a {@link java.util.stream.Stream * java.util.stream.Stream} interface. Instances benefit from * the various combinator and reduction methods provided by * Stream. */ Stream> stream(); /** * Returns whether this result includes all events selected by the * query. * *

* Equivalent to * *

         * return this.selectedCount() == this.stream().count();
         * 
*/ boolean isComplete(); /** * Returns a description of the structure of the {@link #stream() result * stream}. * * @return a StreamStructure that describes the structure of the result * stream */ StreamStructure streamStructure(); /** * Merge this result with {@code other}, combining original events and * edit events, to produce an {@link QueryResult} of type * {@link StreamStructure#VALUE_EVENT_STREAM VALUE_EVENT_STREAM} * *

* The following rules are applied to calculate the result: *

    *
  • If this result and {@code other} have an event with equal * sequence numbers, the event from {@code other} is selected. *
  • An edit event is selected in place of its original event. *
  • If there are multiple edit events of an original edit, the one * with the highest sequence is selected. *
* *

* The returned result implements {@link #isComplete()} to return true * and {@link #selectedCount()} to return the count of events in the * stream, regardless of whether this result is complete. */ QueryResult merge(QueryResult other); /** * Describes the structural properties of a stream. */ enum StreamStructure { /** * The stream is ordered by the original event sequence number, * presenting edit events instead of the original events they * replace. * *

* The original event sequence number of an event {@code e} * is {@code e.originalEvent().sequence()}. It is equal to * {@code e.sequence()}}, if and only if {@code e} is an original * event. * *

* The stream has the following properties: * *

    *
  • The sequence of each event in the stream is unique. *
  • The original event sequence of each event in the stream is * unique. *
  • The stream is ordered by original event sequence. The * original event sequence of each subsequent event in the stream is * greater than its predecessor. *
  • If no events have been removed from the time series, the * original event sequence of each subsequent event is one greater * than its predecessor. *
  • If an event is an original event, the query found no * corresponding edit events. *
  • If an event is an edit event, its timestamp attribute may lie * outside the query range. Consequentially, the sequence and * timestamp attributes of the events may be non-sequential. *
*/ VALUE_EVENT_STREAM, /** * The stream is presented in time series order. * *

* The stream has the following properties: * *

    *
  • The sequence of each event in the stream is unique. *
  • The stream is ordered by sequence. The sequence of each * subsequent event in the stream is greater than its predecessor. *
  • Edit event timestamps may lie outside the query range. *
  • The stream can have multiple edit events for the same * original event. *
*/ EDIT_EVENT_STREAM; } } /** * Exception used to report a query that is invalid for the time series. * *

* An example invalid query is one where the anchor is a sequence number * beyond the end of the time series (for example, it is specified using * {@link RangeQuery#from(long)} with a sequence number greater than the * latest sequence number, or {@link RangeQuery#fromLast(long)} with a * {@code count} greater than the number of events in the time series), * and the span is a relative time. Since no timestamp is associated * with the anchor, the range is meaningless. */ final class InvalidQueryException extends SessionException { private static final long serialVersionUID = 3932960724502537357L; /** * Constructor. * * @param message the exception message */ public InvalidQueryException(String message) { super(message); } } /** * Exception used to report a time series topic does not have an original * event with the sequence number provided by an {@link TimeSeries#edit * edit} operation. */ final class NoSuchEventException extends SessionException { private static final long serialVersionUID = -6796182903526667279L; /** * Constructor. * * @param message the exception message */ public NoSuchEventException(String message) { super(message); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy