mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
1120 lines
24 KiB
Text
1120 lines
24 KiB
Text
[[vega]]
|
|
=== Vega
|
|
|
|
*Vega* and *Vega-Lite* are both grammars for creating custom visualizations. They are recommended for advanced users who are comfortable writing {es} queries manually.
|
|
*Vega-Lite* is a good starting point for users who are new to both grammars, but they are not compatible.
|
|
|
|
*Vega* and *Vega-Lite* panels can display one or more data sources, including {es}, Elastic Map Service,
|
|
URL, or static data, and support <<reference-for-kibana-extensions,{kib} extensions>> that allow you to embed the panels on your dashboard and add interactive tools.
|
|
|
|
Use *Vega* or *Vega-Lite* when you want to create visualizations with:
|
|
|
|
* Aggregations that use `nested` or `parent/child` mapping
|
|
* Aggregations without a {data-source}
|
|
* Queries that use custom time filters
|
|
* Complex calculations
|
|
* Extracted data from _source instead of aggregations
|
|
* Scatter charts, sankey charts, and custom maps
|
|
* An unsupported visual theme
|
|
|
|
These grammars have some limitations: they do not support tables, and can't run queries conditionally.
|
|
|
|
[role="screenshot"]
|
|
image::images/vega.png[Vega UI]
|
|
|
|
Both *Vega* and *Vega-Lite* use JSON, but {kib} has made this simpler to type by integrating
|
|
https://hjson.github.io/[HJSON]. HJSON supports the following:
|
|
|
|
* Optional quotes
|
|
* Double quotes or single quotes
|
|
* Optional commas
|
|
* Comments using // or /* syntax
|
|
* Multiline strings
|
|
|
|
[float]
|
|
==== Tutorials: Create custom panels
|
|
|
|
Learn how to connect *Vega-Lite* with {kib} filters and {es} data, then learn how to create more {kib} interaction using *Vega*.
|
|
|
|
As you edit the specs, work in small steps, and frequently save your work. Small changes can cause unexpected results. To save, click *Save* in the toolbar.
|
|
|
|
Before starting, add the eCommerce sample data that you'll use in your spec, then create the dashboard.
|
|
|
|
. On the home page, click *Try sample data*.
|
|
|
|
. Click *Other sample data sets*.
|
|
|
|
. On the *Sample eCommerce orders* card, click *Add data*.
|
|
|
|
. Open the main menu, then click *Dashboard*.
|
|
|
|
. On the *Dashboards* page, click *Create dashboard*.
|
|
|
|
[float]
|
|
===== Open and set up Vega-Lite
|
|
|
|
Open *Vega-Lite* and change the time range.
|
|
|
|
. On the dashboard, click *Select type*, then select *Custom visualization*.
|
|
+
|
|
A pre-populated line chart displays the total number of documents.
|
|
|
|
. Make sure the <<set-time-filter,time filter>> is *Last 7 days*.
|
|
|
|
[float]
|
|
[[vega-tutorial-create-a-stacked-area-chart]]
|
|
=== Tutorial: Create a stacked area chart from an {es} search query
|
|
|
|
Learn how to query {es} from *Vega-Lite*, displaying the results in a stacked area chart.
|
|
|
|
. In the *Vega-Lite* spec, replace `index: _all` with the following, then click *Update*:
|
|
|
|
```yaml
|
|
index: kibana_sample_data_ecommerce
|
|
```
|
|
|
|
A flat line appears with zero results.
|
|
|
|
To add the data fields from the *kibana_sample_data_ecommerce* {data-source}, replace the following, then click *Update*:
|
|
|
|
* `%timefield%: @timestamp` with `%timefield%: order_date`
|
|
|
|
* `field: @timestamp` with `field: order_date`
|
|
|
|
[float]
|
|
===== Add the aggregations
|
|
|
|
To create the stacked area chart, add the aggregations.
|
|
|
|
To check your work, open and use the <<console-kibana,*Console*>> on a separate browser tab.
|
|
|
|
. Open {kib} on a new tab.
|
|
|
|
. Open the main menu, then click *Dev Tools*.
|
|
|
|
. On the *Console* editor, enter the aggregation, then click *Click to send request*:
|
|
|
|
```js
|
|
POST kibana_sample_data_ecommerce/_search
|
|
{
|
|
"query": {
|
|
"range": {
|
|
"order_date": {
|
|
"gte": "now-7d"
|
|
}
|
|
}
|
|
},
|
|
"aggs": {
|
|
"time_buckets": {
|
|
"date_histogram": {
|
|
"field": "order_date",
|
|
"fixed_interval": "1d",
|
|
"extended_bounds": {
|
|
"min": "now-7d"
|
|
},
|
|
"min_doc_count": 0
|
|
}
|
|
}
|
|
},
|
|
"size": 0
|
|
}
|
|
```
|
|
|
|
Add the {ref}/search-aggregations-bucket-terms-aggregation.html[terms aggregation], then click *Click to send request*:
|
|
|
|
```js
|
|
POST kibana_sample_data_ecommerce/_search
|
|
{
|
|
"query": {
|
|
"range": {
|
|
"order_date": {
|
|
"gte": "now-7d"
|
|
}
|
|
}
|
|
},
|
|
"aggs": {
|
|
"categories": {
|
|
"terms": { "field": "category.keyword" },
|
|
"aggs": {
|
|
"time_buckets": {
|
|
"date_histogram": {
|
|
"field": "order_date",
|
|
"fixed_interval": "1d",
|
|
"extended_bounds": {
|
|
"min": "now-7d"
|
|
},
|
|
"min_doc_count": 0
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"size": 0
|
|
}
|
|
```
|
|
|
|
The response format is different from the first aggregation query:
|
|
|
|
```json
|
|
{
|
|
"aggregations" : {
|
|
"categories" : {
|
|
"doc_count_error_upper_bound" : 0,
|
|
"sum_other_doc_count" : 0,
|
|
"buckets" : [{
|
|
"key" : "Men's Clothing",
|
|
"doc_count" : 1661,
|
|
"time_buckets" : {
|
|
"buckets" : [{
|
|
"key_as_string" : "2020-06-30T00:00:00.000Z",
|
|
"key" : 1593475200000,
|
|
"doc_count" : 19
|
|
}, {
|
|
"key_as_string" : "2020-07-01T00:00:00.000Z",
|
|
"key" : 1593561600000,
|
|
"doc_count" : 71
|
|
}]
|
|
}
|
|
}]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
In the *Vega-Lite* spec, enter the aggregations, then click *Update*:
|
|
|
|
```yaml
|
|
data: {
|
|
url: {
|
|
%context%: true
|
|
%timefield%: order_date
|
|
index: kibana_sample_data_ecommerce
|
|
body: {
|
|
aggs: {
|
|
categories: {
|
|
terms: { field: "category.keyword" }
|
|
aggs: {
|
|
time_buckets: {
|
|
date_histogram: {
|
|
field: order_date
|
|
interval: {%autointerval%: true}
|
|
extended_bounds: {
|
|
min: {%timefilter%: "min"}
|
|
max: {%timefilter%: "max"}
|
|
}
|
|
min_doc_count: 0
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
size: 0
|
|
}
|
|
}
|
|
format: {property: "aggregations.categories.buckets" }
|
|
}
|
|
```
|
|
|
|
For information about the queries, refer to <<vega-queries, reference for writing {es} queries in Vega>>.
|
|
|
|
[float]
|
|
===== Debug the warning
|
|
|
|
To generate the data, *Vega-Lite* uses the `source_0` and `data_0`. `source_0` contains
|
|
the results from the {es} query, and `data_0` contains the visually encoded results that are shown on the chart.
|
|
To debug the warning, compare `source_0` and `data_0`.
|
|
|
|
. In the toolbar, click *Inspect*.
|
|
|
|
. From the *View* dropdown, select *Vega debug*.
|
|
|
|
. From the dropdown, select *source_0*.
|
|
+
|
|
[role="screenshot"]
|
|
image::images/vega_lite_tutorial_4.png[Table for data_0 with columns key, doc_count and array of time_buckets]
|
|
|
|
. To compare to the visually encoded data, select *data_0* from the dropdown.
|
|
+
|
|
[role="screenshot"]
|
|
image::images/vega_lite_tutorial_5.png[Table for data_0 where the key is NaN instead of a string]
|
|
+
|
|
*key* is unable to convert because the property is category (`Men's Clothing`, `Women's Clothing`, etc.) instead of a timestamp.
|
|
|
|
[float]
|
|
===== Add and debug the encoding block
|
|
|
|
In the *Vega-Lite* spec, add the `encoding` block:
|
|
|
|
```yaml
|
|
encoding: {
|
|
x: {
|
|
field: time_buckets.buckets.key
|
|
type: temporal
|
|
axis: { title: null }
|
|
}
|
|
y: {
|
|
field: time_buckets.buckets.doc_count
|
|
type: quantitative
|
|
axis: { title: "Document count" }
|
|
}
|
|
}
|
|
```
|
|
|
|
. Click *Inspect*, then select *Vega Debug* from the *View* dropdown.
|
|
|
|
. From the dropdown, select *data_0*.
|
|
+
|
|
[role="screenshot"]
|
|
image::images/vega_lite_tutorial_6.png[Table for data_0 showing that the column time_buckets.buckets.key is undefined]
|
|
|
|
*Vega-Lite* is unable to extract the `time_buckets.buckets` inner array.
|
|
|
|
[float]
|
|
===== Extract the `time_buckets.buckets` inner array
|
|
|
|
In {kib} 7.9 and later, use the *Vega-Lite* https://vega.github.io/vega-lite/docs/flatten.html[flatten transformation] to extract the `time_buckets.buckets` inner array.
|
|
|
|
If you are using {kib} 7.8 and earlier, the flatten transformation is available only in *Vega*.
|
|
|
|
In the *Vega-Lite* spec, add a `transform` block, then click *Update*:
|
|
|
|
```yaml
|
|
transform: [{
|
|
flatten: ["time_buckets.buckets"]
|
|
}]
|
|
```
|
|
|
|
. Click *Inspect*, then select *Vega Debug* from the *View* dropdown.
|
|
|
|
. From the dropdown, select *data_0*.
|
|
+
|
|
[role="screenshot"]
|
|
image::images/vega_lite_tutorial_7.png[Table showing data_0 with multiple pages of results, but undefined values in the column time_buckets.buckets.key]
|
|
+
|
|
Vega-Lite displays *undefined* values because there are duplicate names.
|
|
|
|
. To resolve the duplicate names, add the `transform` and `encoding` blocks, then click *Update*:
|
|
|
|
```yaml
|
|
transform: [{
|
|
flatten: ["time_buckets.buckets"],
|
|
as: ["buckets"]
|
|
}]
|
|
|
|
mark: area
|
|
|
|
encoding: {
|
|
x: {
|
|
field: buckets.key
|
|
type: temporal
|
|
axis: { title: null }
|
|
}
|
|
y: {
|
|
field: buckets.doc_count
|
|
type: quantitative
|
|
axis: { title: "Document count" }
|
|
}
|
|
color: {
|
|
field: key
|
|
type: nominal
|
|
}
|
|
}
|
|
```
|
|
|
|
[float]
|
|
===== Add hover states and tooltips
|
|
|
|
With the *Vega-Lite* spec, you can add hover states and tooltips to the stacked area chart with the `selection` block.
|
|
|
|
In the *Vega-Lite* spec, add the `encoding` block, then click *Update*:
|
|
|
|
```yaml
|
|
encoding: {
|
|
tooltip: [{
|
|
field: buckets.key
|
|
type: temporal
|
|
title: "Date"
|
|
}, {
|
|
field: key
|
|
type: nominal
|
|
title: "Category"
|
|
}, {
|
|
field: buckets.doc_count
|
|
type: quantitative
|
|
title: "Count"
|
|
}]
|
|
}
|
|
```
|
|
|
|
When you hover over the area series on the stacked area chart, a multi-line tooltip
|
|
appears, but is unable to indicate the nearest point. To
|
|
indicate the nearest point, add a second layer.
|
|
|
|
Add composite marks, then click *Update*:
|
|
|
|
```yaml
|
|
layer: [{
|
|
mark: area
|
|
}, {
|
|
mark: point
|
|
}]
|
|
```
|
|
|
|
The points are unable to stack and align with the stacked area chart.
|
|
|
|
Change the y `encoding`:
|
|
|
|
```yaml
|
|
y: {
|
|
field: buckets.doc_count
|
|
type: quantitative
|
|
axis: { title: "Document count" }
|
|
stack: true
|
|
}
|
|
```
|
|
|
|
Add a `selection` block inside `mark: point`:
|
|
|
|
```yaml
|
|
layer: [{
|
|
mark: area
|
|
}, {
|
|
mark: point
|
|
|
|
selection: {
|
|
pointhover: {
|
|
type: single
|
|
on: mouseover
|
|
clear: mouseout
|
|
empty: none
|
|
fields: ["buckets.key", "key"]
|
|
nearest: true
|
|
}
|
|
}
|
|
|
|
encoding: {
|
|
size: {
|
|
condition: {
|
|
selection: pointhover
|
|
value: 100
|
|
}
|
|
value: 5
|
|
}
|
|
fill: {
|
|
condition: {
|
|
selection: pointhover
|
|
value: white
|
|
}
|
|
}
|
|
}
|
|
}]
|
|
```
|
|
|
|
Move your cursor around the stacked area chart. The points are able to
|
|
indicate the nearest point.
|
|
|
|
[role="screenshot"]
|
|
image::images/vega_lite_tutorial_2.png[Vega-Lite tutorial selection enabled]
|
|
|
|
The selection is controlled by a signal. To view the signal, click *Inspect* in the toolbar.
|
|
|
|
.Expand final Vega-Lite spec
|
|
[%collapsible%closed]
|
|
====
|
|
[source,yaml]
|
|
----
|
|
{
|
|
$schema: https://vega.github.io/schema/vega-lite/v4.json
|
|
title: Event counts from ecommerce
|
|
data: {
|
|
url: {
|
|
%context%: true
|
|
%timefield%: order_date
|
|
index: kibana_sample_data_ecommerce
|
|
body: {
|
|
aggs: {
|
|
categories: {
|
|
terms: { field: "category.keyword" }
|
|
aggs: {
|
|
time_buckets: {
|
|
date_histogram: {
|
|
field: order_date
|
|
interval: {%autointerval%: true}
|
|
extended_bounds: {
|
|
min: {%timefilter%: "min"}
|
|
max: {%timefilter%: "max"}
|
|
}
|
|
min_doc_count: 0
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
size: 0
|
|
}
|
|
}
|
|
format: {property: "aggregations.categories.buckets" }
|
|
}
|
|
|
|
transform: [{
|
|
flatten: ["time_buckets.buckets"]
|
|
as: ["buckets"]
|
|
}]
|
|
|
|
encoding: {
|
|
x: {
|
|
field: buckets.key
|
|
type: temporal
|
|
axis: { title: null }
|
|
}
|
|
y: {
|
|
field: buckets.doc_count
|
|
type: quantitative
|
|
axis: { title: "Document count" }
|
|
stack: true
|
|
}
|
|
color: {
|
|
field: key
|
|
type: nominal
|
|
title: "Category"
|
|
}
|
|
tooltip: [{
|
|
field: buckets.key
|
|
type: temporal
|
|
title: "Date"
|
|
}, {
|
|
field: key
|
|
type: nominal
|
|
title: "Category"
|
|
}, {
|
|
field: buckets.doc_count
|
|
type: quantitative
|
|
title: "Count"
|
|
}]
|
|
}
|
|
|
|
layer: [{
|
|
mark: area
|
|
}, {
|
|
mark: point
|
|
|
|
selection: {
|
|
pointhover: {
|
|
type: single
|
|
on: mouseover
|
|
clear: mouseout
|
|
empty: none
|
|
fields: ["buckets.key", "key"]
|
|
nearest: true
|
|
}
|
|
}
|
|
|
|
encoding: {
|
|
size: {
|
|
condition: {
|
|
selection: pointhover
|
|
value: 100
|
|
}
|
|
value: 5
|
|
}
|
|
fill: {
|
|
condition: {
|
|
selection: pointhover
|
|
value: white
|
|
}
|
|
}
|
|
}
|
|
}]
|
|
}
|
|
----
|
|
|
|
====
|
|
|
|
[float]
|
|
[[vega-tutorial-update-kibana-filters-from-vega]]
|
|
=== Tutorial: Update {kib} filters from Vega
|
|
|
|
To build an area chart using an {es} search query, edit the *Vega* spec, then add click and drag handlers to update the {kib} filters.
|
|
|
|
In the *Vega* spec, enter the following, then click *Update*:
|
|
|
|
```yaml
|
|
{
|
|
$schema: "https://vega.github.io/schema/vega/v5.json"
|
|
data: [{
|
|
name: source_0
|
|
}]
|
|
|
|
scales: [{
|
|
name: x
|
|
type: time
|
|
range: width
|
|
}, {
|
|
name: y
|
|
type: linear
|
|
range: height
|
|
}]
|
|
|
|
axes: [{
|
|
orient: bottom
|
|
scale: x
|
|
}, {
|
|
orient: left
|
|
scale: y
|
|
}]
|
|
|
|
marks: [
|
|
{
|
|
type: area
|
|
from: {
|
|
data: source_0
|
|
}
|
|
encode: {
|
|
update: {
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
Add the {es} search query with the `data` block, then click *Update*:
|
|
|
|
```yaml
|
|
data: [
|
|
{
|
|
name: source_0
|
|
url: {
|
|
%context%: true
|
|
%timefield%: order_date
|
|
index: kibana_sample_data_ecommerce
|
|
body: {
|
|
aggs: {
|
|
time_buckets: {
|
|
date_histogram: {
|
|
field: order_date
|
|
fixed_interval: "3h"
|
|
extended_bounds: {
|
|
min: {%timefilter%: "min"}
|
|
max: {%timefilter%: "max"}
|
|
}
|
|
min_doc_count: 0
|
|
}
|
|
}
|
|
}
|
|
size: 0
|
|
}
|
|
}
|
|
format: { property: "aggregations.time_buckets.buckets" }
|
|
}
|
|
]
|
|
```
|
|
|
|
[float]
|
|
===== Change the x- and y-axes
|
|
|
|
Display labels for the x- and y-axes.
|
|
|
|
In the *Vega* spec, add the `scales` block, then click *Update*:
|
|
|
|
```yaml
|
|
scales: [{
|
|
name: x
|
|
type: time
|
|
range: width
|
|
domain: {
|
|
data: source_0
|
|
field: key
|
|
}
|
|
}, {
|
|
name: y
|
|
type: linear
|
|
range: height
|
|
domain: {
|
|
data: source_0
|
|
field: doc_count
|
|
}
|
|
}]
|
|
```
|
|
|
|
Add the `key` and `doc_count` fields as the X- and Y-axis values, then click *Update*:
|
|
|
|
```yaml
|
|
marks: [
|
|
{
|
|
type: area
|
|
from: {
|
|
data: source_0
|
|
}
|
|
encode: {
|
|
update: {
|
|
x: {
|
|
scale: x
|
|
field: key
|
|
}
|
|
y: {
|
|
scale: y
|
|
value: 0
|
|
}
|
|
y2: {
|
|
scale: y
|
|
field: doc_count
|
|
}
|
|
}
|
|
}
|
|
}
|
|
]
|
|
```
|
|
|
|
[role="screenshot"]
|
|
image::images/vega_tutorial_3.png[]
|
|
|
|
[float]
|
|
===== Add a block to the `marks` section
|
|
|
|
Show the clickable points on the area chart to filter for a specific date.
|
|
|
|
In the *Vega* spec, add to the `marks` block, then click *Update*:
|
|
|
|
```yaml
|
|
{
|
|
name: point
|
|
type: symbol
|
|
style: ["point"]
|
|
from: {
|
|
data: source_0
|
|
}
|
|
encode: {
|
|
update: {
|
|
x: {
|
|
scale: x
|
|
field: key
|
|
}
|
|
y: {
|
|
scale: y
|
|
field: doc_count
|
|
}
|
|
size: {
|
|
value: 100
|
|
}
|
|
fill: {
|
|
value: black
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
[float]
|
|
===== Create a signal
|
|
|
|
To make the points clickable, create a *Vega* signal. You can access the clicked `datum` in the expression used to update.
|
|
|
|
In the *Vega* spec, add a `signals` block to specify that the cursor clicks add a time filter with the three hour interval, then click *Update*:
|
|
|
|
```yaml
|
|
signals: [
|
|
{
|
|
name: point_click
|
|
on: [{
|
|
events: {
|
|
source: scope
|
|
type: click
|
|
markname: point
|
|
}
|
|
update: '''kibanaSetTimeFilter(datum.key, datum.key + 3 * 60 * 60 * 1000)'''
|
|
}]
|
|
}
|
|
]
|
|
```
|
|
|
|
The event uses the `kibanaSetTimeFilter` custom function to generate a filter that
|
|
applies to the entire dashboard on a click.
|
|
|
|
To make the area chart interactive, locate the `marks` block,
|
|
then update the `point` and add `cursor: { value: "pointer" }` to
|
|
`encoding`:
|
|
|
|
```yaml
|
|
{
|
|
name: point
|
|
type: symbol
|
|
style: ["point"]
|
|
from: {
|
|
data: source_0
|
|
}
|
|
encode: {
|
|
update: {
|
|
...
|
|
cursor: { value: "pointer" }
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
[float]
|
|
===== Add a drag interaction
|
|
|
|
To allow users to filter based on a time range, add a drag interaction, which requires additional signals and a rectangle overlay.
|
|
|
|
[role="screenshot"]
|
|
image::images/vega_tutorial_4.png[]
|
|
|
|
In the *Vega* spec, add a `signal` to track the X position of the cursor:
|
|
|
|
```yaml
|
|
{
|
|
name: currentX
|
|
value: -1
|
|
on: [{
|
|
events: {
|
|
type: mousemove
|
|
source: view
|
|
},
|
|
update: "clamp(x(), 0, width)"
|
|
}, {
|
|
events: {
|
|
type: mouseout
|
|
source: view
|
|
}
|
|
update: "-1"
|
|
}]
|
|
}
|
|
```
|
|
|
|
To indicate the current cursor position, add a `mark` block:
|
|
|
|
```yaml
|
|
{
|
|
type: rule
|
|
interactive: false
|
|
encode: {
|
|
update: {
|
|
y: {value: 0}
|
|
y2: {signal: "height"}
|
|
stroke: {value: "gray"}
|
|
strokeDash: {
|
|
value: [2, 1]
|
|
}
|
|
x: {signal: "max(currentX,0)"}
|
|
defined: {signal: "currentX > 0"}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
To track the selected time range, add a signal that updates
|
|
until the user releases their cursor or presses Return:
|
|
|
|
|
|
```yaml
|
|
{
|
|
name: selected
|
|
value: [0, 0]
|
|
on: [{
|
|
events: {
|
|
type: mousedown
|
|
source: view
|
|
}
|
|
update: "[clamp(x(), 0, width), clamp(x(), 0, width)]"
|
|
}, {
|
|
events: {
|
|
type: mousemove
|
|
source: window
|
|
consume: true
|
|
between: [{
|
|
type: mousedown
|
|
source: view
|
|
}, {
|
|
merge: [{
|
|
type: mouseup
|
|
source: window
|
|
}, {
|
|
type: keydown
|
|
source: window
|
|
filter: "event.key === 'Escape'"
|
|
}]
|
|
}]
|
|
}
|
|
update: "[selected[0], clamp(x(), 0, width)]"
|
|
}, {
|
|
events: {
|
|
type: keydown
|
|
source: window
|
|
filter: "event.key === 'Escape'"
|
|
}
|
|
update: "[0, 0]"
|
|
}]
|
|
}
|
|
```
|
|
|
|
There is a signal that tracks the time range from the user.
|
|
|
|
To indicate the range visually, add a mark that only appears conditionally:
|
|
|
|
```yaml
|
|
{
|
|
type: rect
|
|
name: selectedRect
|
|
encode: {
|
|
update: {
|
|
height: {signal: "height"}
|
|
fill: {value: "#333"}
|
|
fillOpacity: {value: 0.2}
|
|
x: {signal: "selected[0]"}
|
|
x2: {signal: "selected[1]"}
|
|
defined: {signal: "selected[0] !== selected[1]"}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Add a signal that updates the {kib} time filter when the cursor is released while
|
|
dragging:
|
|
|
|
```yaml
|
|
{
|
|
name: applyTimeFilter
|
|
value: null
|
|
on: [{
|
|
events: {
|
|
type: mouseup
|
|
source: view
|
|
}
|
|
update: '''selected[0] !== selected[1] ? kibanaSetTimeFilter(
|
|
invert('x',selected[0]),
|
|
invert('x',selected[1])) : null'''
|
|
}]
|
|
}
|
|
```
|
|
|
|
.Expand final Vega spec
|
|
[%collapsible%closed]
|
|
====
|
|
[source,yaml]
|
|
----
|
|
{
|
|
$schema: "https://vega.github.io/schema/vega/v5.json"
|
|
data: [
|
|
{
|
|
name: source_0
|
|
url: {
|
|
%context%: true
|
|
%timefield%: order_date
|
|
index: kibana_sample_data_ecommerce
|
|
body: {
|
|
aggs: {
|
|
time_buckets: {
|
|
date_histogram: {
|
|
field: order_date
|
|
fixed_interval: "3h"
|
|
extended_bounds: {
|
|
min: {%timefilter%: "min"}
|
|
max: {%timefilter%: "max"}
|
|
}
|
|
min_doc_count: 0
|
|
}
|
|
}
|
|
}
|
|
size: 0
|
|
}
|
|
}
|
|
format: { property: "aggregations.time_buckets.buckets" }
|
|
}
|
|
]
|
|
|
|
scales: [{
|
|
name: x
|
|
type: time
|
|
range: width
|
|
domain: {
|
|
data: source_0
|
|
field: key
|
|
}
|
|
}, {
|
|
name: y
|
|
type: linear
|
|
range: height
|
|
domain: {
|
|
data: source_0
|
|
field: doc_count
|
|
}
|
|
}]
|
|
|
|
axes: [{
|
|
orient: bottom
|
|
scale: x
|
|
}, {
|
|
orient: left
|
|
scale: y
|
|
}]
|
|
|
|
marks: [
|
|
{
|
|
type: area
|
|
from: {
|
|
data: source_0
|
|
}
|
|
encode: {
|
|
update: {
|
|
x: {
|
|
scale: x
|
|
field: key
|
|
}
|
|
y: {
|
|
scale: y
|
|
value: 0
|
|
}
|
|
y2: {
|
|
scale: y
|
|
field: doc_count
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{
|
|
name: point
|
|
type: symbol
|
|
style: ["point"]
|
|
from: {
|
|
data: source_0
|
|
}
|
|
encode: {
|
|
update: {
|
|
x: {
|
|
scale: x
|
|
field: key
|
|
}
|
|
y: {
|
|
scale: y
|
|
field: doc_count
|
|
}
|
|
size: {
|
|
value: 100
|
|
}
|
|
fill: {
|
|
value: black
|
|
}
|
|
cursor: { value: "pointer" }
|
|
}
|
|
}
|
|
},
|
|
{
|
|
type: rule
|
|
interactive: false
|
|
encode: {
|
|
update: {
|
|
y: {value: 0}
|
|
y2: {signal: "height"}
|
|
stroke: {value: "gray"}
|
|
strokeDash: {
|
|
value: [2, 1]
|
|
}
|
|
x: {signal: "max(currentX,0)"}
|
|
defined: {signal: "currentX > 0"}
|
|
}
|
|
}
|
|
},
|
|
{
|
|
type: rect
|
|
name: selectedRect
|
|
encode: {
|
|
update: {
|
|
height: {signal: "height"}
|
|
fill: {value: "#333"}
|
|
fillOpacity: {value: 0.2}
|
|
x: {signal: "selected[0]"}
|
|
x2: {signal: "selected[1]"}
|
|
defined: {signal: "selected[0] !== selected[1]"}
|
|
}
|
|
}
|
|
}
|
|
]
|
|
|
|
signals: [
|
|
{
|
|
name: point_click
|
|
on: [{
|
|
events: {
|
|
source: scope
|
|
type: click
|
|
markname: point
|
|
}
|
|
update: '''kibanaSetTimeFilter(datum.key, datum.key + 3 * 60 * 60 * 1000)'''
|
|
}]
|
|
}
|
|
{
|
|
name: currentX
|
|
value: -1
|
|
on: [{
|
|
events: {
|
|
type: mousemove
|
|
source: view
|
|
},
|
|
update: "clamp(x(), 0, width)"
|
|
}, {
|
|
events: {
|
|
type: mouseout
|
|
source: view
|
|
}
|
|
update: "-1"
|
|
}]
|
|
}
|
|
{
|
|
name: selected
|
|
value: [0, 0]
|
|
on: [{
|
|
events: {
|
|
type: mousedown
|
|
source: view
|
|
}
|
|
update: "[clamp(x(), 0, width), clamp(x(), 0, width)]"
|
|
}, {
|
|
events: {
|
|
type: mousemove
|
|
source: window
|
|
consume: true
|
|
between: [{
|
|
type: mousedown
|
|
source: view
|
|
}, {
|
|
merge: [{
|
|
type: mouseup
|
|
source: window
|
|
}, {
|
|
type: keydown
|
|
source: window
|
|
filter: "event.key === 'Escape'"
|
|
}]
|
|
}]
|
|
}
|
|
update: "[selected[0], clamp(x(), 0, width)]"
|
|
}, {
|
|
events: {
|
|
type: keydown
|
|
source: window
|
|
filter: "event.key === 'Escape'"
|
|
}
|
|
update: "[0, 0]"
|
|
}]
|
|
}
|
|
{
|
|
name: applyTimeFilter
|
|
value: null
|
|
on: [{
|
|
events: {
|
|
type: mouseup
|
|
source: view
|
|
}
|
|
update: '''selected[0] !== selected[1] ? kibanaSetTimeFilter(
|
|
invert('x',selected[0]),
|
|
invert('x',selected[1])) : null'''
|
|
}]
|
|
}
|
|
]
|
|
}
|
|
|
|
----
|
|
====
|
|
|
|
include::vega-reference.asciidoc[]
|