Vega: Handle a situation where data-set returned is empty

When querying for data, there are situations where the returned data-set is empty for certain time ranges. Such a situation occurred in Vega (returned an empty array) and I tried to create a condition using expressions to check if the data-set is empty but it doesn't seem to work.

length(data('table')) == 0 ? '-' : datum.sum

Adding the above test condition in a formula transform says Undefined data set name: "table". Nothing seems to change when it is used inside a mark as a signal for a field.

How to check if the returned data-set is empty so that a condition can be applied to do a specific task accordingly? Your kind help would be appreciated.

Note: Vega v 4.3.0

It seems like you don't have the right name for your dataset. Are you sure that you named it table?

1 Like

Thank you for replying @wylie!

Yes, the name of the data-set is table. I can still debug it using the name table to find the results are empty. The message above,

comes when I add the condition inside formula transform only.

Interesting, would you mind sharing the full spec you're working with when you have missing data? https://www.elastic.co/guide/en/kibana/master/vega.html#asking-for-help-with-a-vega-spec

1 Like

The Inspect feature is disabled in the editor. It says This visualization doesn't support any inspectors. Could it be because it is the free version I'm using?

I see, that just means you're not using 7.10: we made Vega GA in 7.10 and added the Inspect feature.

In older versions, you can open the browser console and type VEGA_DEBUG.vegalite_spec. Copy that.

1 Like

Alright, I used the VEGA_DEBUG.vega_spec and mentioned below is the full spec it shows. I hope I did it right. :sweat_smile:

VEGA_DEBUG.vega_spec
{$schema: "https://vega.github.io/schema/vega/v5.json", width: 300, height: 300, autosize: "none", background: "#646464", …}$schema: "https://vega.github.io/schema/vega/v5.json"autosize: "none"background: "#646464"config: arc: {fill: "#54B399"}area: {fill: "#54B399"}line: {stroke: "#54B399"}path: {stroke: "#54B399"}range: category: {scheme: "elastic"}scheme: "elastic"__proto__: Object__proto__: Objectrect: {fill: "#54B399"}rule: {stroke: "#54B399"}shape: {stroke: "#54B399"}symbol: {fill: "#54B399"}trail: {fill: "#54B399"}__proto__: Objectdata: Array(1)0: format: {property: "hits.hits"}name: "table"transform: Array(4)0: {type: "project", fields: Array(1), as: Array(1)}1: {type: "formula", as: "elapsedTime", expr: "datum.elapsedTime/1000"}2: {type: "aggregate", fields: Array(1), ops: Array(1), as: Array(1)}3: {type: "formula", as: "sum", expr: "format(datum.sum, ',.3f')"}length: 4__proto__: Array(0)values: hits: hits: Array(0)length: 0__proto__: Array(0)max_score: nulltotal: 0__proto__: Objecttimed_out: falsetook: 2_shards: {total: 2, successful: 2, skipped: 1, failed: 0}__proto__: Object__proto__: Objectlength: 1__proto__: Array(0)description: "Metric"height: 300marks: Array(1)0: encode: enter: align: {value: "center"}baseline: {value: "middle"}fill: {value: "black"}fontSize: {value: 30}fontWeight: {value: "bold"}text: {signal: "length(data('table')) == 1 ? datum.sum : '-'"}x: {signal: "width/2"}y: {signal: "height*0.45"}__proto__: Object__proto__: Objectfrom: {data: "table"}name: "metric"type: "text"__proto__: Objectlength: 1__proto__: Array(0)width: 300__proto__: Object

Note: This spec is extracted by re-writing the same use-case using Elastic v 7.9.3 where the Vega version used is v 5.13.0. But still I cannot use this version either to handle a situation where there are no documents in the query results. However, it doesn't show the previous

message when length(data('table')) == 0 condition is used in a formula transform.

Use JSON.stringify(VEGA_DEBUG.vega_spec) and copy/paste the results.

1 Like

Yup, didn't know that. :sweat_smile:

"{"$schema":"https://vega.github.io/schema/vega/v5.json","width":300,"height":300,"autosize":"none","background":"#646464","description":"Metric","data":[{"name":"table","format":{"property":"hits.hits"},"transform":[{"type":"project","fields":["fields.elapsedTime"],"as":["elapsedTime"]},{"type":"formula","as":"elapsedTime","expr":"datum.elapsedTime/1000"},{"type":"aggregate","fields":["elapsedTime"],"ops":["sum"],"as":["sum"]},{"type":"formula","as":"sum","expr":"format(datum.sum, ',.3f')"}],"values":{"took":3,"timed_out":false,"_shards":{"total":2,"successful":2,"skipped":1,"failed":0},"hits":{"total":0,"max_score":null,"hits":[]}}}],"marks":[{"name":"metric","type":"text","from":{"data":"table"},"encode":{"enter":{"x":{"signal":"width/2"},"y":{"signal":"height*0.45"},"align":{"value":"center"},"baseline":{"value":"middle"},"text":{"signal":"length(data('table')) == 1 ? datum.sum : '-'"},"fontSize":{"value":30},"fontWeight":{"value":"bold"},"fill":{"value":"black"}}}}],"config":{"range":{"category":{"scheme":"elastic"}},"arc":{"fill":"#54B399"},"area":{"fill":"#54B399"},"line":{"stroke":"#54B399"},"path":{"stroke":"#54B399"},"rect":{"fill":"#54B399"},"rule":{"stroke":"#54B399"},"shape":{"stroke":"#54B399"},"symbol":{"fill":"#54B399"},"trail":{"fill":"#54B399"}}}"

Unless I am missing something, I don't see the data in this. Your data request looks like it's empty. You might have to update it again to ensure you are getting data back first.

      "values": {
        "took": 3,
        "timed_out": false,
        "_shards": {"total": 2, "successful": 2, "skipped": 1, "failed": 0},
        "hits": {"total": 0, "max_score": null, "hits": []}
      }
1 Like

That's the problem @aaron-nimocks. I want to figure out a way to check if there is no data is returned from the query. That's why I tried to use,

in the text mark and display a dash when there are no results returned in the query. But, it doesn't seem to work.

Sorry I didn't even read the initial question. :slight_smile:

The way I understand it is you can't do it from where you are testing.

    {
      "name": "metric",
      "type": "text",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"signal": "width/2"},
          "y": {"signal": "height*0.45"},
          "align": {"value": "center"},
          "baseline": {"value": "middle"},
          "text": {"signal": "length(data('table')) == 1 ? datum.sum : '-'"},
          "fontSize": {"value": 30},
          "fontWeight": {"value": "bold"},
          "fill": {"value": "blue"}
        }
      }
    }

The from data table line means loop through every row of the data table and do the below. If the data table is empty then it will never process any of those marks to even check. Maybe I'm wrong but that's always how I thought it works.

If so, the solution would be to create the data table so it always exists and have your query that gets your real data add to it.

1 Like

Try this @ege

Create a signal that will check to see if data is in the table. It will be an empty array if empty.

"signals": [{"name": "$tableLoaded", "init": "data('table')"}],

Then just duplicate your text mark. Have one loop through the data and the other not. Put a conditional on the one that doesn't use the data to see if the signal $tableLoaded is empty or not and put your - mark if false.

    {
      "name": "metric",
      "type": "text",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"signal": "width/2"},
          "y": {"signal": "height*0.45"},
          "align": {"value": "center"},
          "baseline": {"value": "middle"},
          "text": {"signal": "datum.sum //or whatever the real value is"},
          "fontSize": {"value": 30},
          "fontWeight": {"value": "bold"},
          "fill": {"value": "blue"}
        }
      }
    },
    {
      "type": "text",
      "encode": {
        "enter": {
          "x": {"signal": "width/2"},
          "y": {"signal": "height*0.45"},
          "align": {"value": "center"},
          "baseline": {"value": "middle"},
          "text": {"signal": "$tableLoaded == '[]' ? '' : '-'"},
          "fontSize": {"value": 30},
          "fontWeight": {"value": "bold"},
          "fill": {"value": "blue"}
        }
      }
    }
1 Like

WOohOOOOOO! IT WORKED!!! :partying_face: :partying_face: :partying_face:

Thank you so much @aaron-nimocks! I had to adjust it a little bit, but, your approach worked! :smiley:

Eventhough the length of the queried data-set cannot be checked using length(data('table')) for some reason, the length of the array generated through the signal can be checked. So, here's what I did:

  "signals": [{"name": "$tableLoaded", "init": "data('table')"}],
  "marks": [
    {
      "name": "metric",
      "type": "text",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"signal": "width/2"},
          "y": {"signal": "height*0.45"},
          "align": {"value": "center"},
          "baseline": {"value": "middle"},
          "text": {"signal": "datum.sum"},
          "fontSize": {"value": 30},
          "fontWeight": {"value": "bold"},
          "fill": {"value": "black"}
        }
      }
    },
    {
      "type": "text",
      "encode": {
        "enter": {
          "x": {"signal": "width/2"},
          "y": {"signal": "height*0.45"},
          "align": {"value": "center"},
          "baseline": {"value": "middle"},
          "text": {"signal": "length($tableLoaded) == 0 ? '-' : ''"},
          
          "fontSize": {"value": 30},
          "fontWeight": {"value": "bold"},
          "fill": {"value": "blue"}
        }
      }
    }
  ]

I think you are right about this

and that's why length(data('table')) is not working.

That solves a problem I had my head burning on for the past coupe of days. :grin:

Thank you again @wylie and @aaron-nimocks for your great support! :heart_eyes: :heart_eyes: :heart_eyes:

2 Likes

Getting back to the Vega version where the problem occured, Vega v 4.3.0, the above solution should be adjusted a little bit in order to work. Since init property in signals is only available after Vega v 4.4, I had to change the code as below, by adding update property instead of init.

  "signals": [{"name": "$tableLoaded", "update": "data('table')"}],
  "marks": [
    {
      "name": "metric",
      "type": "text",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"signal": "width/2"},
          "y": {"signal": "height*0.45"},
          "align": {"value": "center"},
          "baseline": {"value": "middle"},
          "text": {"signal": "datum.sum"},
          "fontSize": {"value": 30},
          "fontWeight": {"value": "bold"},
          "fill": {"value": "black"}
        }
      }
    },
    {
      "type": "text",
      "encode": {
        "enter": {
          "x": {"signal": "width/2"},
          "y": {"signal": "height*0.45"},
          "align": {"value": "center"},
          "baseline": {"value": "middle"},
          "text": {"signal": "length($tableLoaded) == 0 ? '-' : ''"},
          
          "fontSize": {"value": 30},
          "fontWeight": {"value": "bold"},
          "fill": {"value": "blue"}
        }
      }
    }
  ]

Cheers! :blush:

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.