Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TSDB: Add time series information to field caps #78790

Merged
merged 10 commits into from
Oct 13, 2021
14 changes: 13 additions & 1 deletion docs/reference/search/field-caps.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ field types are all described as the `keyword` type family.
`aggregatable`::
Whether this field can be aggregated on all indices.

`time_series_dimension`::
Whether this field is used as a time series dimension.

`time_series_metrics`::
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it time_series_metric - no trailing s?

Contains metric type if this fields is used as a time series metrics, null if the field is not used as metric.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is absent if the field is not a metric.


`indices`::
The list of indices where this field has the same type family, or null if all indices
have the same type family for the field.
Expand All @@ -123,6 +129,13 @@ field types are all described as the `keyword` type family.
The list of indices where this field is not aggregatable, or null if all
indices have the same definition for the field.

`non_dimension_indices`::
The list of indices where this field is not marked as a dimension field, or null if all
indices have the same definition for the field.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a more obvious way of saying this could be "If this is present in response then some indices have the field marked as a dimension and other indices, the ones in this list, do not."


`metric_conflicts_indices`::
The list of indices where this field is present but don't have the same metrics type.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe s/metrics type/time_series_metric/


`meta`::
Merged metadata across all indices as a map of string keys to arrays of values.
A value length of 1 indicates that all indices had the same value for this key,
Expand Down Expand Up @@ -179,7 +192,6 @@ The API returns the following response:
"metadata_field": false,
"searchable": true,
"aggregatable": false

}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
---
setup:
- skip:
version: " - 7.99.99"
reason: introduced in 8.0.0

- do:
indices.create:
index: tsdb_index1
body:
settings:
index:
number_of_replicas: 0
number_of_shards: 2
mappings:
properties:
"@timestamp":
type: date
metricset:
type: keyword
time_series_dimension: true
non_tsdb_field:
type: keyword
k8s:
properties:
pod:
properties:
availability_zone:
type: short
time_series_dimension: true
uid:
type: keyword
time_series_dimension: true
name:
type: keyword
ip:
type: ip
time_series_dimension: true
network:
properties:
tx:
type: long
time_series_metric: counter
rx:
type: integer
time_series_metric: gauge
packets_dropped:
type: long
time_series_metric: gauge
latency:
type: double
time_series_metric: gauge

- do:
indices.create:
index: tsdb_index2
body:
settings:
index:
number_of_replicas: 0
number_of_shards: 2
mappings:
properties:
"@timestamp":
type: date
metricset:
type: keyword
non_tsdb_field:
type: keyword
k8s:
properties:
pod:
properties:
availability_zone:
type: short
time_series_dimension: true
uid:
type: keyword
time_series_dimension: true
name:
type: keyword
ip:
type: ip
time_series_dimension: true
network:
properties:
tx:
type: long
time_series_metric: gauge
rx:
type: integer
packets_dropped:
type: long
time_series_metric: gauge
latency:
type: double
time_series_metric: gauge

---
"Get simple time series field caps":

- skip:
version: " - 7.99.99"
reason: introduced in 8.0.0

- do:
field_caps:
index: 'tsdb_index1'
fields: [ "metricset", "non_tsdb_field", "k8s.pod.*" ]

- match: {fields.metricset.keyword.searchable: true}
- match: {fields.metricset.keyword.aggregatable: true}
- match: {fields.metricset.keyword.time_series_dimension: true}
- is_false: fields.metricset.keyword.time_series_metric
- is_false: fields.metricset.keyword.indices
- is_false: fields.metricset.keyword.non_searchable_indices
- is_false: fields.metricset.keyword.non_aggregatable_indices
- is_false: fields.metricset.keyword.non_dimension_indices

- match: {fields.non_tsdb_field.keyword.searchable: true}
- match: {fields.non_tsdb_field.keyword.aggregatable: true}
- is_false: fields.non_tsdb_field.keyword.time_series_dimension
- is_false: fields.non_tsdb_field.keyword.time_series_metric
- is_false: fields.non_tsdb_field.keyword.indices
- is_false: fields.non_tsdb_field.keyword.non_searchable_indices
- is_false: fields.non_tsdb_field.keyword.non_aggregatable_indices
- is_false: fields.non_tsdb_field.keyword.non_dimension_indices

- match: {fields.k8s\.pod\.availability_zone.short.time_series_dimension: true}
- is_false: fields.k8s\.pod\.availability_zone.short.time_series_metric
- is_false: fields.k8s\.pod\.availability_zone.short.non_dimension_indices

- match: {fields.k8s\.pod\.uid.keyword.time_series_dimension: true}
- is_false: fields.k8s\.pod\.uid.keyword.time_series_metric
- is_false: fields.k8s\.pod\.uid.keyword.non_dimension_indices

- is_false: fields.k8s\.pod\.name.keyword.time_series_dimension
- is_false: fields.k8s\.pod\.name.keyword.time_series_metric
- is_false: fields.k8s\.pod\.name.keyword.non_dimension_indices

- match: {fields.k8s\.pod\.ip.ip.time_series_dimension: true}
- is_false: fields.k8s\.pod\.ip.ip.time_series_metric
- is_false: fields.k8s\.pod\.ip.ip.non_dimension_indices

- is_false: fields.k8s\.pod\.network\.tx.long.time_series_dimension
- match: {fields.k8s\.pod\.network\.tx.long.time_series_metric: counter}
- is_false: fields.k8s\.pod\.network\.tx.long.non_dimension_indices

- is_false: fields.k8s\.pod\.network\.rx.integer.time_series_dimension
- match: {fields.k8s\.pod\.network\.rx.integer.time_series_metric: gauge}
- is_false: fields.k8s\.pod\.network\.rx.integer.non_dimension_indices

- is_false: fields.k8s\.pod\.network\.packets_dropped.long.time_series_dimension
- match: {fields.k8s\.pod\.network\.packets_dropped.long.time_series_metric: gauge}
- is_false: fields.k8s\.pod\.network\.packets_dropped.long.non_dimension_indices

- is_false: fields.k8s\.pod\.network\.latency.double.time_series_dimension
- match: {fields.k8s\.pod\.network\.latency.double.time_series_metric: gauge}
- is_false: fields.k8s\.pod\.network\.latency.double.non_dimension_indices

---
"Get time series field caps with conflicts":

- skip:
version: " - 7.99.99"
reason: introduced in 8.0.0

- do:
field_caps:
index: tsdb_index1,tsdb_index2
fields: [ "metricset", "non_tsdb_field", "k8s.pod.*" ]

- match: {fields.metricset.keyword.searchable: true}
- match: {fields.metricset.keyword.aggregatable: true}
- is_false: fields.metricset.keyword.time_series_dimension
- is_false: fields.metricset.keyword.time_series_metric
- is_false: fields.metricset.keyword.indices
- is_false: fields.metricset.keyword.non_searchable_indices
- is_false: fields.metricset.keyword.non_aggregatable_indices
- match: {fields.metricset.keyword.non_dimension_indices: ["tsdb_index2"]}
- is_false: fields.metricset.keyword.mertric_conflicts_indices

- match: {fields.non_tsdb_field.keyword.searchable: true}
- match: {fields.non_tsdb_field.keyword.aggregatable: true}
- is_false: fields.non_tsdb_field.keyword.time_series_dimension
- is_false: fields.non_tsdb_field.keyword.time_series_metric
- is_false: fields.non_tsdb_field.keyword.indices
- is_false: fields.non_tsdb_field.keyword.non_searchable_indices
- is_false: fields.non_tsdb_field.keyword.non_aggregatable_indices
- is_false: fields.non_tsdb_field.keyword.non_dimension_indices
- is_false: fields.non_tsdb_field.keyword.mertric_conflicts_indices

- match: {fields.k8s\.pod\.availability_zone.short.time_series_dimension: true}
- is_false: fields.k8s\.pod\.availability_zone.short.time_series_metric
- is_false: fields.k8s\.pod\.availability_zone.short.non_dimension_indices
- is_false: fields.k8s\.pod\.availability_zone.short.mertric_conflicts_indices

- match: {fields.k8s\.pod\.uid.keyword.time_series_dimension: true}
- is_false: fields.k8s\.pod\.uid.keyword.time_series_metric
- is_false: fields.k8s\.pod\.uid.keyword.non_dimension_indices
- is_false: fields.k8s\.pod\.uid.keyword.mertric_conflicts_indices

- is_false: fields.k8s\.pod\.name.keyword.time_series_dimension
- is_false: fields.k8s\.pod\.name.keyword.time_series_metric
- is_false: fields.k8s\.pod\.name.keyword.non_dimension_indices
- is_false: fields.k8s\.pod\.name.keyword.mertric_conflicts_indices

- match: {fields.k8s\.pod\.ip.ip.time_series_dimension: true}
- is_false: fields.k8s\.pod\.ip.ip.time_series_metric
- is_false: fields.k8s\.pod\.ip.ip.non_dimension_indices
- is_false: fields.k8s\.pod\.ip.ip.mertric_conflicts_indices

- is_false: fields.k8s\.pod\.network\.tx.long.time_series_dimension
- is_false: fields.k8s\.pod\.network\.tx.long.time_series_metric
- is_false: fields.k8s\.pod\.network\.tx.long.non_dimension_indices
- match: {fields.k8s\.pod\.network\.tx.long.mertric_conflicts_indices: ["tsdb_index1", "tsdb_index2"]}

- is_false: fields.k8s\.pod\.network\.rx.integer.time_series_dimension
- is_false: fields.k8s\.pod\.network\.rx.integer.time_series_metric
- is_false: fields.k8s\.pod\.network\.rx.integer.non_dimension_indices
- match: {fields.k8s\.pod\.network\.rx.integer.mertric_conflicts_indices: ["tsdb_index1", "tsdb_index2"]}

- is_false: fields.k8s\.pod\.network\.packets_dropped.long.time_series_dimension
- match: {fields.k8s\.pod\.network\.packets_dropped.long.time_series_metric: gauge}
- is_false: fields.k8s\.pod\.network\.packets_dropped.long.non_dimension_indices
- is_false: fields.k8s\.pod\.network\.packets_dropped.long.mertric_conflicts_indices

- is_false: fields.k8s\.pod\.network\.latency.double.time_series_dimension
- match: {fields.k8s\.pod\.network\.latency.double.time_series_metric: gauge}
- is_false: fields.k8s\.pod\.network\.latency.double.non_dimension_indices
- is_false: fields.k8s\.pod\.network\.latency.double.mertric_conflicts_indices
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.XContentFactory;
import org.elasticsearch.index.mapper.DocumentParserContext;
import org.elasticsearch.index.mapper.KeywordFieldMapper;
import org.elasticsearch.index.mapper.MetadataFieldMapper;
import org.elasticsearch.index.mapper.TimeSeriesParams;
import org.elasticsearch.index.query.AbstractQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
Expand All @@ -30,6 +29,8 @@
import org.elasticsearch.plugins.SearchPlugin;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.transport.RemoteTransportException;
import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.XContentFactory;
import org.junit.Before;

import java.io.IOException;
Expand All @@ -45,8 +46,10 @@

import static java.util.Collections.singletonList;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.hamcrest.Matchers.array;
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo;

public class FieldCapabilitiesIT extends ESIntegTestCase {

Expand All @@ -69,6 +72,14 @@ public void setUp() throws Exception {
.startObject("playlist")
.field("type", "text")
.endObject()
.startObject("some_dimension")
.field("type", "keyword")
.field("time_series_dimension", true)
.endObject()
.startObject("some_metric")
.field("type", "long")
.field("time_series_metric", TimeSeriesParams.MetricType.counter)
.endObject()
.startObject("secret_soundtrack")
.field("type", "alias")
.field("path", "playlist")
Expand Down Expand Up @@ -98,6 +109,13 @@ public void setUp() throws Exception {
.startObject("new_field")
.field("type", "long")
.endObject()
.startObject("some_dimension")
.field("type", "keyword")
.endObject()
.startObject("some_metric")
.field("type", "long")
.field("time_series_metric", TimeSeriesParams.MetricType.gauge)
.endObject()
.endObject()
.endObject()
.endObject();
Expand Down Expand Up @@ -285,6 +303,25 @@ public void testWithRunntimeMappings() throws InterruptedException {
assertTrue(runtimeField.get("keyword").isAggregatable());
}

public void testFieldMetricsAndDimensions() {
FieldCapabilitiesResponse response = client().prepareFieldCaps("old_index").setFields("some_dimension", "some_metric").get();
assertIndices(response, "old_index");
assertEquals(2, response.get().size());
assertTrue(response.get().containsKey("some_dimension"));
assertTrue(response.get().get("some_dimension").get("keyword").isDimension());
assertNull(response.get().get("some_dimension").get("keyword").nonDimensionIndices());
assertTrue(response.get().containsKey("some_metric"));
assertEquals(TimeSeriesParams.MetricType.counter, response.get().get("some_metric").get("long").getMetricType());
assertNull(response.get().get("some_metric").get("long").metricConflictsIndices());

response = client().prepareFieldCaps("old_index", "new_index").setFields("some_dimension", "some_metric").get();
assertIndices(response, "old_index", "new_index");
assertEquals(2, response.get().size());
assertTrue(response.get().containsKey("some_dimension"));
assertFalse(response.get().get("some_dimension").get("keyword").isDimension());
assertThat(response.get().get("some_dimension").get("keyword").nonDimensionIndices(), array(equalTo("new_index")));
}

public void testFailures() throws InterruptedException {
// in addition to the existing "old_index" and "new_index", create two where the test query throws an error on rewrite
assertAcked(prepareCreate("index1-error"));
Expand Down
Loading