Menu
Open source

UQL Parser

UQL (Unstructured Query Language) is an advanced query format in the Infinity data source which consolidates JSON, CSV, XML and GraphQL formats. UQL also provides the ability to customize results.

UQL is an opinionated query language designed for in-memory operations. A UQL query consists of a list of commands joined by |, with each command on its own line. Most of the time, fields are referred to within double quotes, with single quotes used for string literals. UQL was inspired by the Kusto query language and uses similar syntax.

Note

UQL is still in beta but used widely. If you encounter any issues with UQL, create a bug in GitHub.

If your data looks like this:

JSON
[
  {
    "id": 1,
    "name": { "firstName": "john", "lastName": "doe" },
    "dob": "1985-01-01",
    "city": "chennai"
  },
  {
    "id": 2,
    "name": { "firstName": "alice", "lastName": "bob" },
    "dob": "1990-12-31",
    "city": "london"
  }
]

then the following UQL query:

SQL
parse-json
| extend "full name"=strcat("name.firstName",' ',"name.lastName"), "dob"=todatetime("dob")
| project-away "name"
| order by "full name" asc

will produce a four column table (id, dob, city, full name).

Basic UQL commands

The following commands are available in all versions of the plugin unless specified otherwise.

project

project is used to select columns to include in the results. If you want to select a property inside a nested object, you can use dot notation. Optionally, you can also alias the field names.

SQL
parse-json
| project "id", "name.firstName", "date of birth"="dob"

project-away

project-away is the opposite of project. It drops specific columns from the data. It doesn’t support aliases or the dot notation selector.

SQL
parse-json
| project-away "id", "city"

order by

order by sorts the input based on any column. The sort order can be specified and should be either asc (ascending) or desc (descending).

SQL
parse-json
| order by "full name" asc

extend

extend is similar to project but instead of selecting columns, it adds or replaces columns in existing data. extend expects an alias and a function.

SQL
parse-json
| extend "dob"=todatetime("dob"), "city"=toupper("city")

Functions that can be used with extend are described in the table below.

function keywordsyntaxdescriptionavailable from
trimtrim("name")trims both the start and end of the string0.8.0
trim_starttrim_start("name")trims the start of the string0.8.0
trim_endtrim_end("name")trims the end of the string0.8.0
tonumbertonumber("age")converts a string into a number0.8.0
tostringtostring("age")converts a number into a string0.8.0
todatetimetodatetime("age")converts a datetime string into a datetime0.8.0
unixtime_seconds_todatetimeunixtime_seconds_todatetime("dob")converts unix epoch (seconds) timestamp to a datetime0.8.0
unixtime_nanoseconds_todatetimeunixtime_nanoseconds_todatetime("dob")converts unix epoch (nanoseconds) timestamp to a datetime0.8.0
unixtime_milliseconds_todatetimeunixtime_milliseconds_todatetime("dob")converts unix epoch (milliseconds) timestamp to datetime0.8.0
unixtime_microseconds_todatetimeunixtime_microseconds_todatetime("dob")converts unix epoch (microseconds) timestamp to datetime0.8.0
format_datetimeformat_datetime("dob",'DD/MM/YYYY')converts a datetime to a string in a specific format0.8.0
add_datetimeadd_datetime("dob",'-1d')adds a duration to a datetime field0.8.0
startofminutestartofminute("dob")rounds the datetime field to the start of the minute0.8.0
startofhourstartofhour("dob")rounds the datetime field to the start of the hour0.8.0
startofdaystartofday("dob")rounds the datetime field to the start of the day0.8.0
startofmonthstartofmonth("dob")rounds the datetime field to the start of the month0.8.0
startofweekstartofweek("dob")rounds the datetime field to the start of the week0.8.0
startofyearstartofyear("dob")rounds the datetime field to the star of the year0.8.0
extractextract('regex',index,"col1")extracts part of the string field using regex and match index (0/1/..)1.0.0
sumsum("col1","col2")sum of two or more columns0.8.0
diffdiff("col1","col2")difference between two columns0.8.0
mulmul("col1","col2")multiplication of two columns0.8.0
divdiv("col1","col2")division of two columns (col1/col2)0.8.0
percentagepercentage("col1","col2")percentage of two columns ((col1/col2)*100)1.0.0
strcatstrcat("col1","col2")concatenates two or more columns0.8.0
splitsplit("col1",'delimiter')splits a string using a delimiter1.0.0
replace_stringreplace_string("col1",'src','replacer')replace a portion of string with another1.0.0
reversereverse("col1")reverse a string1.0.0
floorfloor("col1")calculates the floor value of given numeric field0.8.7
ceilceil("col1")calculates the ceil value of given numeric field0.8.7
roundround("col1")calculates the round value of given numeric field0.8.7
signsign("col1")calculates the sign value of given numeric field0.8.7
powpow("col1",3)calculates the pow value of given numeric field0.8.7
sinsin("col1")calculates the sin value of given numeric field0.8.7
coscos("col1")calculates the cos value of given numeric field0.8.7
tantan("col1")calculates the tan value of given numeric field0.8.7
loglog("col1")calculates the log value of given numeric field0.8.7
log2log2("col1")calculates the log2 value of given numeric field0.8.7
log10log10("col1")calculates the log10 value of given numeric field0.8.7
parse_urlparse_url("col1")parses the column as a URL0.8.6
parse_url("col1",'pathname')returns the pathname of the URL. Options are host,hash,origin,href,protocol and search0.8.6
parse_url("col1",'search','key1')returns the query string value for key1. 2nd arg is always search0.8.6
atobatob("col1")returns atob value of a string column. (reference)1.3.0
btoabtoa("col1")returns btoa value of a string column. (reference)1.3.0
substringsubstring("col1",1,5)returns substring value of a string column. (reference)1.3.0

For example, running this UQL query:

SQL
parse-json
| project "a", "triple"=sum("a","a","a"),"thrice"=mul("a",3), sum("a","b"),  diff("a","b"), mul("a","b")

over the data

[ { "a": 12, "b" : 20 }, { "a" : 6, "b": 32} ]

yields the following output:

atriplethricesumdiffmul
12363632-8240
6181838-26192

To apply multiple transformations over a field, repeat them with the same field name. For example, the UQL query:

extend "name"=tolower("name"), "name"=trim("name")

applies the tolower function and then the trim function over the name field.

When working with data in arrays, there are a few other extend/project functions available as follows.

pack

pack converts an array of key value pairs into a map. Example:

extend "foo"=pack('key1',"value1",'key1',"value2")

yields an object:

{key1:value1,key2:value2}

array_from_entries

array_from_entries builds an array of objects from an array. Example:

extend "foo"=array_from_entries('timestamp',[2010,2020,2030])

yields an array:

[{timestamp:2010},{timestamp:2020},{timestamp:2030}]

array_to_map

array_to_map converts an array of entries to a map. Optionally, one can provide aliases for keys instead of their index. Example:

extend "foo"=array_to_map(['chennai','india'],'city','country')

yields

{ 'city': 'chennai', 'country':'india'}

summarize

summarize aggregates the data by a string column. summarize expects alias, summarize by fields and summarize function arguments. The available summarization functions are listed in the table.

function keywordsyntaxdescriptionavailable from
countcount()count of values0.8.0
sumsum("age")sum of age0.8.0
minmin("population")min of population0.8.0
maxmax("foo")max of foo0.8.0
meanmean("foo")mean of foo0.8.0

For example, given the following data:

JSON
[
  { "city": "tokyo", "country": "japan", "population": 200 },
  { "city": "newyork", "country": "usa", "population": 60 },
  { "city": "oslo", "country": "usa", "population": 40 },
  { "city": "new delhi", "country": "india", "population": 180 },
  { "city": "mumbai", "country": "india", "population": 150 }
]

Running this UQL query

SQL
parse-json
| summarize "number of cities"=count(), "total population"=sum("population") by "country"
| extend "country"=toupper("country")
| order by "total population" desc

produces output like this:

countrynumber of citiestotal population
INDIA2330
JAPAN1200
USA2100

pivot

The pivot command performs pivot operations over the data. It accepts 3 arguments.

  • The 1st argument is the summarization function. Example: count("id) or sum("salary")
  • The 2nd argument is the row field name. Example: "country"
  • The 3rd argument is the column field name. Example: "occupation"

Pivot example 1

Given this CSV data:

csv
name,age,country,occupation,salary
Leanne Graham,38,USA,Devops Engineer,3000
Ervin Howell,27,USA,Software Engineer,2300
Clementine Bauch,17,Canada,Student,
Patricia Lebsack,42,UK,Software Engineer,2800
Leanne Bell,38,USA,Senior Software Engineer,4000
Chelsey Dietrich,32,USA,Software Engineer,3500

Running the following query:

SQL
parse-csv
| extend "salary"=tonumber("salary")
| pivot sum("salary"), "country", "occupation"

produces this output:

countryDevops EngineerSoftware EngineerStudentSenior Software Engineer
USA3000580004000
CANADA0000
UK0280000

Whereas the following summarize query:

SQL
parse-csv
| extend "salary"=tonumber("salary")
| summarize "salary"=sum("salary") by "country", "occupation"

will produce:

countryoccupationsalary
USADevops Engineer3000
USASoftware Engineer5800
CanadaStudent0
UKSoftware Engineer2800
UKSenior Software Engineer4000

Choose either summarize or pivot according to your needs.

parse-json

parse-json parses the response as JSON.

parse-csv

parse-csv parses the response as CSV.

parse-xml

parse-xml parses the response as XML.

parse-yaml

parse-yaml parses the response as YAML.

count

count returns the number of results.

SQL
parse-json
| count

limit

limit restricts the number of results returned. For example, the following query returns only the first 10 results:

SQL
parse-json
| limit 10

scope

scope sets the context (document root) of the output data. It is useful when the results are inside a nested JSON object.

example

JSON
{
  "meta": { "last-updated": "2021-08-09" },
  "count": 2,
  "users": [{ "name": "foo" }, { "name": "bar" }]
}

and the following UQL query returns only the contents of the “users” key, ignoring the other root level properties.

SQL
parse-json
| scope "users"

mv-expand

mv-expand expands multi-value properties into their own records. For example, running the command mv-expand "user"="users" over this data:

JSON
[
  { "group": "A", "users": ["user a1", "user a2"] },
  { "group": "B", "users": ["user b1"] }
]

Produces results like this:

JSON
[
  { "group": "A", "user": "user a1" },
  { "group": "A", "user": "user a2" },
  { "group": "B", "user": "user b1" }
]

mv-expand should also work for non-string arrays.

project kv()

project kv() is used to convert the given object into key-value pairs.

Example, given the data:

{ "a": {"name":"a1"}, "b": {"name":"b1"}, "c": {"name":"c1"} }

and the query

parse-json | project kv()

the following table is returned:

keyvalue
a{"name":"a1"}
b{"name":"b1"}
c{"name":"c1"}

This command can be also used with arguments. For example, given the data

{ "data": { "a": {"name":"a1"}, "b": {"name":"b1"}, "c": {"name":"c1"} } }

and the query

parse-json | project kv("data")

the same results as the previous example are returned.

project kv() is available from version 0.8.7 of the plugin

JSONata

jsonata accepts a JSONata query to run over the output of the previous command.

SQL
parse-json
| scope "library"
| jsonata "library.loans@$L.books@$B[$L.isbn=$B.isbn].customers[$L.customer=id].{ 'customer': name, 'book': $B.title, 'due': $L.return}"
| count

Like any other command, the jsonata command can be combined/piped with multiple commands. You can use JSONata for filtering the data as well.

JSONata support is available from version 0.8.8 of the plugin

Comments

Any new line that starts with # will be treated as a comment.