# RumbleDB 2.1 "Cedrus Libani"

RumbleDB is a querying engine that allows you to query your large, messy datasets with ease and productivity. It covers the entire data pipeline: clean up, structure, normalize, validate, convert to an efficient binary format, and feed it right into Machine Learning estimators and models, all within the JSONiq language.

RumbleDB supports JSON-like datasets including JSON, JSON Lines, Parquet, Avro, SVM, CSV, ROOT as well as text files, of any size from kB to at least the two-digit TB range (we have not found the limit yet).

RumbleDB is both good at handling small amounts of data on your laptop (in which case it simply runs locally and efficiently in a single-thread) as well as large amounts of data by spreading computations on your laptop cores, or onto a large cluster (in which case it leverages Spark automagically).

RumbleDB can also be used to easily and efficiently convert data from a format to another, including from JSON to Parquet thanks to JSound validation.

It runs on many local or distributed filesystems such as HDFS, S3, Azure blob storage, and HTTP (read-only), and of course your local drive as well. You can use any of these file systems to store your datasets, but also to store and share your queries and functions as library modules with other users, worldwide or within your institution, who can import them with just one line of code. You can also output the results of your query or the log to these filesystems (as long as you have write access).

With RumbleDB, queries can be written in the tailor-made and expressive JSONiq language. Users can write their queries declaratively and start with just a few lines. No need for complex JSON parsing machinery as JSONiq supports the JSON data model natively.

The core of RumbleDB lies in JSONiq's FLWOR expressions, the semantics of which map beautifully to DataFrames and Spark SQL. Likewise expression semantics is seamlessly translated to transformations on RDDs or DataFrames, depending on whether a structure is recognized or not. Transformations are not exposed as function calls, but are completely hidden behind JSONiq queries, giving the user the simplicity of an SQL-like language and the flexibility needed to query heterogeneous, tree-like data that does not fit in DataFrames.

This documentation provides you with instructions on how to get started, examples of data sets and queries that can be executed locally or on a cluster, links to JSONiq reference and tutorials, notes on the function library implemented so far, and instructions on how to compile RumbleDB from scratch.

Please note that this is a (maturing) beta version. We welcome bug reports in the GitHub issues section.


# Ways to install and use

There are many ways to install and use RumbleDB. For example:

* By simply using one of our online sandboxes (Jupyter notebook or simple sandbox page)
* Our newest library: by installing a pip package (pip install jsoniq)
* By running the standalone RumbleDB jar with Java on your laptop
* By installing with homebrew
* By installing Spark yourself on your laptop (for more control on Spark parameters) and use a small RumbleDB jar with spark-submit
* By using our docker image on your laptop (go to the "Run with docker" section on the left menu)
* By uploading the small RumbleDB jar to an existing Spark cluster (such as AWS EMR)
* By running RumbleDB as an HTTP server in the background and connecting to it in a Jupyter notebook with the %%jsoniq magic.
* By installing it manually on your machine.

## Further steps

After installing RumbleDB, further steps could involve:

* Learning JSONiq. More details can be found in the JSONiq section of this documentation and in the [JSONiq specification](https://www.jsoniq.org/docs/JSONiq/webhelp/index.html) and [tutorials](https://colab.research.google.com/github/RumbleDB/rumble/blob/master/RumbleSandbox.ipynb).
* Storing some data on S3, creating a Spark cluster on Amazon EMR (or Azure blob storage and Azure, etc), and querying the data with RumbleDB. More details are found in the cluster section of this documentation.
* Using RumbleDB with Jupyter notebooks. For this, you can run RumbleDB as a server with a simple command, and get started by downloading the [main JSONiq tutorial as a Jupyter notebook](https://raw.githubusercontent.com/RumbleDB/rumble/master/RumbleSandbox.ipynb) and just clicking your way through it. More details are found in the Jupyter notebook section of this documentation. Jupyter notebooks work both locally and on a cluster.
* Write JSONiq code, and share it on the Web, as others can import it from HTTP in just one line from within their queries (no package publication or installation required) or specify an HTTP URL as an input query to RumbleDB!


# On the online sandbox

If you really want to start writing queries right now, there is a public sandbox [here](https://colab.research.google.com/github/RumbleDB/rumble/blob/master/RumbleSandbox.ipynb) that will just work and guide you. You only need to have a Google account to be able to execute them, as this exposes our Jupyter notebook via the Colab environment. You are also free to download and use this notebook with any other provider or even your own local Jupyter and it will work just the same: the queries are all shipped to our own, small public backend no matter what. However, this may require a bit of configuration (JAVA\_HOME pointing to Java 17 or 21, and if you have conflicting Spark installations in addition to pyspark, SPARK\_HOME pointing to a Spark 4.0 installation).

If you do not have a Google account, you can also use our simpler sandbox page without Jupyter, [here](http://public.rumbledb.org:9090/public.html) where you can type small queries and see the results.

With the sandboxes above, you can only inline your data in the query or access a dataset with an HTTP URL.

Once you want to take it to the next level and query your own data on your laptop, you will find instructions below to use RumbleDB on your own computer manually, which among others will allow you to query any files stored on your local disk. And then, you can take a leap of faith and use RumbleDB on a large cluster (Amazon EMR, your company's cluster, etc).


# As a pip package

You can use RumbleDB from within Python programmes by running

```bash
pip install jsoniq
```

## Java version

*Important note*: since the jsoniq package depends on pyspark 4, Java 17 or Java 21 is a requirement. If another version of Java is installed, the execution of a Python program attempting to create a RumbleSession will lead to an error message on stderr that contains explanations.

You can control your Java version with:

```bash
java -version
```

Information about how this package is used can be found [in this section](/writing-jsoniq-queries-in-python).

## Common issue: colliding Spark version

Some users who have already configured a Spark installation on their machine may encounter a version issue if SPARK\_HOME points to this alternate installation, and it is a different version of Spark (e.g., 3.5 or 3.4). The jsoniq package requires Spark 4.0.

If this happens, RumbleDB should output an informative error message. They are two ways to fix such conflicts:

* The easiest is remove the SPARK\_HOME environment variable completely. This will have RumbleDB fall back to the Spark 4.0 installation that ships with its pyspark dependency.
* Or you can instead change the value of SPARK\_HOME to point to a Spark 4.0 installation, if you have one. This would be for more advanced users who know what they are doing.

If you have another working Spark installation on your machine, you can see which version it is with

```
spark-submit --version
```

The above command is of course expected not to work for first-time users who only installed the jsoniq package and never installed Spark additionally on their machine.


# In jupyter notebooks

The Python edition of Rumble can be used to directly write JSONiq queries in Jupyter notebook cells. This is explained [here](/writing-jsoniq-queries-in-python/writing-queries-directly-in-jupyter-notebook-cells).  You first need to install the library as described [here](/getting-started/as-a-pip-package).


# With homebrew

##

It is also possible to use RumbleDB with brew, however there is currently no way to adjust memory usage. To install RumbleDB with brew, type the commands:

```
brew tap rumbledb/rumble
brew install --build-from-source rumble
```

You can test that it works with:

```
rumbledb run -q '1+1'
```

Then, launch a JSONiq shell with:

```
rumbledb repl
```

The RumbleDB shell appears:

```
    ____                  __    __     ____  ____ 
   / __ \__  ______ ___  / /_  / /__  / __ \/ __ )
  / /_/ / / / / __ `__ \/ __ \/ / _ \/ / / / __  |  The distributed JSONiq engine
 / _, _/ /_/ / / / / / / /_/ / /  __/ /_/ / /_/ /   2.1.0 "Cedrus Libani" beta
/_/ |_|\__,_/_/ /_/ /_/_.___/_/\___/_____/_____/  


Master: local[*]
Item Display Limit: 200
Output Path: -
Log Path: -
Query Path : -

rumble$
```

You can now start typing simple queries like the following few examples. Press *three times* the return key to execute a query.

```
"Hello, World"
```

or

```
 1 + 1
 
```

or

```
 (3 * 4) div 5
 
```


# Command line (java -jar)

## Java version (important)

You need to make sure that you have Java 11 or 17 and that, if you have several versions installed, JAVA\_HOME correctly points to Java 11 or 17.

RumbleDB works with both Java 11 and Java 17. You can check the Java version that is configured on your machine with:

```
java -version
```

If you do not have Java, you can download version 11 or 17 from [AdoptOpenJDK](https://adoptopenjdk.net/).

Do make sure it is not Java 8, which will not work.

## Download RumbleDB

RumbleDB is just a download and no installation is required.

In order to run RumbleDB, you simply need to download rumbledb-2.1.0-standalone.jar from the [download page](https://github.com/RumbleDB/rumble/releases) and put it in a directory of your choice, for example, right besides your data.

Make sure to use the corresponding jar name accordingly in all our instructions in lieu of rumbledb.jar.

You can test that it works with:

```
java -jar rumbledb-2.1.0-standalone.jar run -q '1+1'
```

or launch a JSONiq shell with:

```
java -jar rumbledb-2.1.0-standalone.jar repl
```

If you run out of memory, you can set allocate more memory to Java with an additional Java parameter, e.g., -Xmx10g

The RumbleDB shell appears:

```
    ____                  __    __     ____  ____ 
   / __ \__  ______ ___  / /_  / /__  / __ \/ __ )
  / /_/ / / / / __ `__ \/ __ \/ / _ \/ / / / __  |  The distributed JSONiq engine
 / _, _/ /_/ / / / / / / /_/ / /  __/ /_/ / /_/ /   2.1.0 "Cedrus Libani" beta
/_/ |_|\__,_/_/ /_/ /_/_.___/_/\___/_____/_____/  


Master: local[*]
Item Display Limit: 200
Output Path: -
Log Path: -
Query Path : -

rumble$
```

You can now start typing simple queries like the following few examples. Press *three times* the return key to execute a query.

```
"Hello, World"
```

or

```
 1 + 1
 
```

or

```
 (3 * 4) div 5
 
```

Javadoc

If you plan to add the jar to your Java environment to use RumbleDB in your Java programs, the JavaDoc documentation can be found [here](https://rumbledb.org/docs/latest/api/). The entry point is the class org.rumbledb.api.Rumble.


# Command line (with spark-submit and an existing Spark installation)

This method gives you more control about the Spark configuration than the experimental standalone jar, in particular you can increase the memory used, change the number of cores, and so on.

If you use Linux, Florian Kellner also kindly contributed an [installation script](https://github.com/fkellner/rumbledb-install-script) for Linux users that roughly takes care of what is described below for you.

{% hint style="info" %} <mark style="color:$warning;">Users of the Python edition (pip install jsoniq) should not have to install Spark manually because the pip package automatically installs pyspark and this contains a Spark 4 installation. However, advanced users who have multiple Spark installations or encounter a Spark version conflict in Python may find the information below useful.</mark>
{% endhint %}

### Install Spark (if you do not have installed already)

RumbleDB requires an Apache Spark installation on Linux, Mac or Windows. Important note: it needs to be either Spark 4, or the Scala 2.13 build of Spark 3.5.

It is straightforward to directly [download it](https://spark.apache.org/downloads.html), unpack it and put it at a location of your choosing. We recommend to pick Spark 4.0.0.

### SPARK\_HOME and PATH (you need to check even if you already have an existing installation)

You then need to point the SPARK\_HOME environment variable to this directory, and to additionally add the subdirectory "bin" within the unpacked directory to the PATH variable. On macOS this is done by adding.

{% hint style="info" %} <mark style="color:$warning;">Users of the Python edition who have additional Spark installations must ensure that SPARK\_HOME and PATH point to a Spark 4 installation. The Python edition does not work with Spark 3.5.</mark>  &#x20;
{% endhint %}

```
export SPARK_HOME=/path/to/spark-4.0.0-bin-hadoop3
export PATH=$SPARK_HOME/bin:$PATH
```

(with SPARK\_HOME appropriately set to match your unzipped Spark directory) to the file .zshrc in your home directory, then making sure to force the change with

```
. ~/.zshrc
```

in the shell. In Windows, changing the PATH variable is done in the control panel. In Linux, it is similar to macOS.

As an alternative, users who love the command line can also install Spark with a package management system instead, such as brew (on macOS) or apt-get (on Ubuntu). However, these might be less predictable than a raw download.

You can test that Spark was correctly installed with:

```
spark-submit --version
```

### Java version (important)

You need to make sure that you have Java 11 (for Spark 3.5) or 17 (for Spark 3.5 or 4.0) or 21 (for Spark 4.0) and that, if you have several versions installed, JAVA\_HOME correctly points to the correct Java installation. Spark only supports Java 11 or 17 or 21 depending on the version.

Spark 4+ is documented to work with both Java 17 and Java 21. If there is an issue with the Java version, RumbleDB will inform you with an appropriate error message. You can check the Java version that is configured on your machine with:

```
java -version
```

### Download the small version of the RumbleDB jar

Like Spark, RumbleDB is just a download and no installation is required.

In order to run RumbleDB, you simply need to download one of the small .jar files from the [download page](https://github.com/RumbleDB/rumble/releases) and put it in a directory of your choice, for example, right besides your data.

If you use Spark 4.0, use rumbledb-2.1.0-for-spark-4.0.jar.

If you use Spark 4.1, use rumbledb-2.1.0-for-spark-4.1.jar.

If you use Spark 4.2, use rumbledb-2.1.0-for-spark-4.2.jar.

These jars do not embed Spark, since you chose to set it up separately. They will work with your Spark installation with the spark-submit command.

Make sure to use the corresponding jar name accordingly in all our instructions in lieu of rumbledb.jar, replacing rumbledb.jar with the actual name of the jar file you downloaded.

In a shell, from the directory where the RumbleDB .jar lies, type, all on one line:

```
spark-submit rumbledb.jar repl
```

replacing rumbledb.jar with the actual name of the jar file you downloaded.

The RumbleDB shell appears:

```
    ____                  __    __     ____  ____ 
   / __ \__  ______ ___  / /_  / /__  / __ \/ __ )
  / /_/ / / / / __ `__ \/ __ \/ / _ \/ / / / __  |  The distributed JSONiq engine
 / _, _/ /_/ / / / / / / /_/ / /  __/ /_/ / /_/ /   2.1.0 "Cedrus Libani" beta
/_/ |_|\__,_/_/ /_/ /_/_.___/_/\___/_____/_____/  


Master: local[*]
Item Display Limit: 200
Output Path: -
Log Path: -
Query Path : -

rumble$
```

You can now start typing simple queries like the following few examples. Press *three times* the return key to execute a query.

```
"Hello, World"
```

or

```
 1 + 1
 
```

or

```
 (3 * 4) div 5
 
```


# With docker

<mark style="color:$info;">The docker installation is kindly contributed by Dr. Ingo Müller (Google).</mark>

## Known issue

On occasion, the docker version of RumbleDB used to throw a Kryo NoSuchMethodError on some systems. This should be fixed with version 2.1.0, let us know if this is not the case.

You can upgrade to the newest version with

```
docker pull rumbledb/rumble
```

## Running simple queries with Docker

Docker is the easiest way to get a standard environment that just works.

You can download Docker from [here](https://www.docker.com/).

Then, in a shell, type, all on one line:

```
docker run -i rumbledb/rumble repl
             
```

The first time, it might take some time to download everything, but this is all done automatically. Subsequent commands will run immediately.

When there are new RumbleDB versions, you can upgrade with:

```
docker pull rumbledb/rumble
```

The RumbleDB shell appears:

```
    ____                  __    __     ____  ____ 
   / __ \__  ______ ___  / /_  / /__  / __ \/ __ )
  / /_/ / / / / __ `__ \/ __ \/ / _ \/ / / / __  |  The distributed JSONiq engine
 / _, _/ /_/ / / / / / / /_/ / /  __/ /_/ / /_/ /   2.1.0 "Cedrus Libani" beta
/_/ |_|\__,_/_/ /_/ /_/_.___/_/\___/_____/_____/  


App name: spark-rumble-jar-with-dependencies.jar
Master: local[*]
Driver's memory: (not set)
Number of executors (only applies if running on a cluster): (not set)
Cores per executor (only applies if running on a cluster): (not set)
Memory per executor (only applies if running on a cluster): (not set)
Dynamic allocation: (not set)
Item Display Limit: 200
Output Path: -
Log Path: -
Query Path : -

RumbleDB$
```

You can now start typing simple queries like the following few examples. Press *three times* the return key to execute a query.

```
"Hello, World"
```

or

```
 1 + 1
 
```

or

```
 (3 * 4) div 5
 
```

The above queries do not actually use Spark. Spark is used when the I/O workload can be parallelized. The following query should output the file created above.

```
 json-lines("https://rumbledb.org/samples/products-small.json")
 
```

json-lines() reads its input in parallel, and thus will also work on your machine with MB or GB files (for TB files, a cluster will be preferable). You should specify a minimum number of partitions, here 10 (note that this is a bit ridiculous for our tiny example, but it is very relevant for larger files), as locally no parallelization will happen if you do not specify this number.

```
for $i in json-lines("https://rumbledb.org/samples/products-small.json", 10)
return $i
```

The above creates a very simple Spark job and executes it. More complex queries will create several Spark jobs. But you will not see anything of it: this is all done behind the scenes. If you are curious, you can go to [localhost:4040](http://localhost:4040) in your browser while your query is running (it will not be available once the job is complete) and look at what is going on behind the scenes.

Data can be filtered with the where clause. Again, below the hood, a Spark transformation will be used:

```
for $i in json-lines("https://rumbledb.org/samples/products-small.json", 10)
where $i.quantity gt 99
return $i
```

RumbleDB also supports grouping and aggregation, like so:

```
for $i in json-lines("https://rumbledb.org/samples/products-small.json", 10)
let $quantity := $i.quantity
group by $product := $i.product
return { "product" : $product, "total-quantity" : sum($quantity) }
```

RumbleDB also supports ordering. Note that clauses (where, let, group by, order by) can appear in any order. The only constraint is that the first clause should be a for or a let clause.

```
for $i in json-lines("https://rumbledb.org/samples/products-small.json", 10)
let $quantity := $i.quantity
group by $product := $i.product
let $sum := sum($quantity)
order by $sum descending
return { "product" : $product, "total-quantity" : $sum }
```

Finally, RumbleDB can also parallelize data provided within the query, exactly like Sparks' parallelize() creation:

```
for $i in parallelize((
 { "product" : "broiler", "store number" : 1, "quantity" : 20  },
 { "product" : "toaster", "store number" : 2, "quantity" : 100 },
 { "product" : "toaster", "store number" : 2, "quantity" : 50 },
 { "product" : "toaster", "store number" : 3, "quantity" : 50 },
 { "product" : "blender", "store number" : 3, "quantity" : 100 },
 { "product" : "blender", "store number" : 3, "quantity" : 150 },
 { "product" : "socks", "store number" : 1, "quantity" : 500 },
 { "product" : "socks", "store number" : 2, "quantity" : 10 },
 { "product" : "shirt", "store number" : 3, "quantity" : 10 }
), 10)
let $quantity := $i.quantity
group by $product := $i.product
let $sum := sum($quantity)
order by $sum descending
return { "product" : $product, "total-quantity" : $sum }
```

Mind the double parenthesis, as parallelize is a unary function to which we pass a sequence of objects.

## Running the RumbleDB docker as a server

You can also run the docker as a server like so:

```
docker run -p 8001:8001 --rm rumbledb/rumble serve -p 8001 -h 0.0.0.0
```

You can change the port to something else than 8001 at all three places it appears. Do not forget `-p 8001:8001` that forwards the port to the outside of the docker. Then, you can use a [jupyter notebook](https://github.com/RumbleDB/rumble/blob/master/RumbleSandbox.ipynb) connected to the RumbleDB docker server to write queries in it. Point the notebook to `http://localhost:8001/jsoniq` in the appropriate cell (or any other port).

## Querying local files with the docker version of RumbleDB

In order to query your local files, you need to mount a local directory to a directory within the docker. This is done with the `--mount` option, and the source path must be absolute. For the target, you can pick anything that makes sense to you.

For example, imagine you have a file products-small.json in the directory /path/to/my/directory. Then you need to run RumbleDB with:

```
docker run -t -i --mount type=bind,source=/path/to/my/directory,target=/home rumbledb/rumble repl
```

Then you can go ahead and use absolute paths in the target directory in input functions, like so:

```
for $i in json-lines("/home/products-small.json", 10)
where $i.quantity gt 99
return $i
```

You can also mount a local directory in this way running it as a server rather than a shell.


# Through the Java API with Maven

RumbleDB can also be used as a maven dependency. You can find it [here](https://central.sonatype.com/artifact/com.github.rumbledb/rumbledb).

The JavaDoc documentation is accessible [here](https://rumbledb.org/docs/latest/api/).


# On a Spark cluster (e.g., AWS EMR)

## Running RumbleDB on a cluster

After you have tried RumbleDB locally as explained in the getting started section, you can take RumbleDB to a real cluster simply by modifying the command line parameters as documented [here for spark-submit](https://spark.apache.org/docs/latest/submitting-applications.html).

{% hint style="info" %}
*<mark style="color:$warning;">Warning: EMR as of version 7.10 does not support Spark 4.0 yet, but we expect this will happen soon. In the meantime, you should use RumbleDB 1.22.</mark>*
{% endhint %}

## Creating a cluster

Creating a cluster is the easiest part, as most cloud providers today offer that with just a few clicks: Amazon EMR, Azure HDInsight, etc. You can start with 4-5 machines with a few CPUs each and a bit of memory, and increase later when you want to get serious on larger scales.

Maybe sure to select a cluster that has Apache Spark. On Amazon EMR, this is not the default and you need to make sure that you check the box that has Spark below the cluster version dropdown. We recommend taking the latest EMR version 6.5.0 and then picking Spark 3.1 in the software configuration. You will also need to create a public/private key pair if you do not already have one.

Wait for 5 or 6 minutes, and the cluster is ready.

Do not forget to terminate the cluster when you are done!

## How to tune the RumbleDB command

Next, you need to use ssh to connect to the master node of your cluster as the hadoop user and specifying your private key file. You will find the hostname of the machine on the EMR cluster page. The command looks like:

ssh -i \~/.ssh/yourkey.pem <hadoop@ec2-a-bunch-if-numbers.eu-central-1.compute.amazonaws.com>

If ssh hangs, then you may need to authorize your IP for incoming connections in the security group of your cluster.

And once you have connected with ssh and are on the shell, you can start using RumbleDB in a way similar to what you do on your laptop.

First you need to download it with wget (which is usually available by default on cloud virtual machines):

```
wget https://github.com/RumbleDB/rumble/releases/download/v2.1.0/rumbledb-2.1.0-for-spark-4.0.jar
```

This is all you need to do, since Apache Spark is already installed. If spark-submit does not work, you might want to wait for a few more minutes as it might be that the cluster is not fully prepared yet.

Often, the Spark cluster is running on yarn. The --master option can be changed from local\[\*] (which was for running on your laptop) to yarn compared to the getting started guide.

```
spark-submit --master yarn --deploy-mode client rumbledb-2.1.0-for-spark-4.0.jar repl
             
```

Most of the time, though (e.g., on Amazon EMR), it needs not be specified, as this is already set up in the environment. So the same command will do:

```
spark-submit rumbledb-2.1.0-for-spark-4.0.jar repl
             
```

When you are on a cluster, you can also adapt the number of executors, how many cores you want per executor, etc. It is recommended to use sqrt(n) cores per executor if a node has n cores. For the executor memory, it is just primary school math: you need to divide the memory on a machine with the number of executors per machine (which is also roughly sqrt(n)).

For example, if we have 6 worker nodes with each 16 cores and 64 GB, we can use 5 executores on each machine, with 3 cores and 10 GB per executor. This leaves a core and a bit of memory free for other cluster tasks.

```
spark-submit --num-executors 30 --executor-cores 3 --executor-memory 10g
             rumbledb-2.1.0-for-spark-4.0.jar repl
```

If necesasry, the size limit for materialization can be made higher with --materialization-cap or its shortcut -c (the default is 200). This affects the number of items displayed on the shells as an answer to a query. It also affects the maximum number of items that can be materialized from a large sequence into, say, an array. Warnings are issued if the cap is reached.

```
spark-submit --num-executors 30 --executor-cores 3 --executor-memory 10g
             rumbledb-2.1.0-for-spark-4.0.jar repl -c 10000
```

### Creation functions

json-lines() then takes an HDFS path and the host and port are optional if Spark is configured properly. A second parameter controls the minimum number of splits. By default, each HDFS block is a split if executed on a clustter. In a local execution, there is only one split by default.

The same goes for parallelize(). It is also possible to read text with text-file(), parquet files with parquet-file(), and it is also possible to read data on S3 rather than HDFS for all three functions json-lines(), text-file() and parquet-file().

### Bigger data sets

If you need a bigger data set out of the box, we recommend the [great language game](http://lars.yencken.org/datasets/languagegame/), which has 16 million objects. On Amazon EMR, we could even read several billion of objects on less than ten machines.

We tested this with each new release, and suggest the following queries to start with (we assume HDFS is the default file system, and that you copied over this dataset to HDFS with hadoop fs copyFromLocal):

```
for $i in json-lines(”/user/you/confusion−2014−03−02.json”, 300)
let $guess := $i.guess
let $target := $i.target
where $guess eq $target
where $target eq ”Russian”
return $i

for $i in json-lines(”/user/you/confusion−2014−03−02.json”, 300)
let $guess := $i.guess, $target := $i.target
where $guess eq $target
order by $target, $i.country descending, $i.date descending return $i

for $i in json-lines(”/user/you/confusion−2014−03−02.json”, 300)
let $country := $i.country, $target := $i.target
group by $target , $country
return {”Language”: $target ,
”Country” : $country , ”Guesses”: length($i)}
```

Note that by default only the first 200 items in the output will be displayed on the shell, but you can change it with the --materialization-cap parameter on the CLI.

### Execution of single queries and output to HDFS

RumbleDB also supports executing a single query from the command line, reading from HDFS and outputting the results to HDFS, with the query file being either local or on HDFS. For this, use the --query-path (optional as any text without parameter is recognized as a path in any case), --output-path (shortcut -o) and --log-path parameters.

```
spark-submit --num-executors 30 --executor-cores 3 --executor-memory 10g
             rumbledb-2.1.0-for-spark-4.0.jar run "hdfs:///user/me/query.jq"
             -o "hdfs:///user/me/results/output"
             --log-path "hdfs:///user/me/logging/mylog"
```

The query path, output path and log path can be any of the supported schemes (HDFS, file, S3, WASB...) and can be relative or absolute.

```
spark-submit --num-executors 30 --executor-cores 3 --executor-memory 10g
             rumbledb-2.1.0-for-spark-4.0.jar run "/home/me/my-local-machine/query.jq"
             -o "/user/me/results/output"
             --log-path "hdfs:///user/me/logging/mylog"
```


# Installing from source (for the adventurous)

We show here how to install RumbleDB from the GitHub repository and build it yourself if you wish to do so (for example, to use the latest master). However, the easiest way to use RumbleDB is to simply download the already compiled .jar files.

## Requirements

The following software is required:

* [Java SE](https://adoptium.net/temurin/releases/): the version of Java is important, as RumbleDB only works with Java 11 (Standalone or Spark 3.5), 17 (Standalone or Spark 3.5 or Spark 4 or Python) or 21 (Spark 4 or Python). The current master branch corresponds to Spark 4.0, meaning that Java 17 or 21 is required.
* [Spark](https://spark.apache.org/), version 4.0.0 (for example)
* [Ant](http://ant.apache.org/), version 1.10
* [Maven](https://maven.apache.org/) 3.9.9

### Checking the requirements

Type the following commands to check that the necessary commands are available. If not, you may need to either install the software, or make sure that it is on the PATH.

```
$ java -version

$ mvn --version

$ ant -version

$ spark-submit --version
```

## Checkout

You first need to download the rumble code to your local machine.

In the shell, go to the desired location:

```
$ cd some_directory
```

Clone the github repository:

```
$ git clone https://github.com/RumbleDB/rumble.git
```

Go to the root of this repository:

```
$ cd rumble
```

## Compile

You can compile the entire project like so:

```
$ mvn clean compile assembly:single
```

After successful completion, you can check the `target` directory, which should contain the compiled classes as well as the JAR file `rumbledb-2.1.0-jar-with-dependencies.jar`.

## Running locally

The most straightforward to test if the above steps were successful is to run the RumbleDB shell locally, like so:

```
$ spark-submit target/rumbledb-2.1.0-jar-with-dependencies.jar repl
```

The RumbleDB shell should start:

```
Using Spark's default log4j profile: org/apache/spark/log4j-defaults.properties

    ____                  __    __     ____  ____ 
   / __ \__  ______ ___  / /_  / /__  / __ \/ __ )
  / /_/ / / / / __ `__ \/ __ \/ / _ \/ / / / __  |  The distributed JSONiq engine
 / _, _/ /_/ / / / / / / /_/ / /  __/ /_/ / /_/ /   2.1.0 "Cedrus Libani" beta
/_/ |_|\__,_/_/ /_/ /_/_.___/_/\___/_____/_____/  

Master: local[2]
Item Display Limit: 1000
Output Path: -
Log Path: -
Query Path : -

rumble$
```

You can now start typing interactive queries. Queries can span over multiple lines. You need to press return 3 times to confirm.

```
rumble$ "Hello, world!"
```

This produces the following results (`>>>` show the extra, empty lines that appear on the first two presses of the return key).

```
rumble$ "Hello, world!"
>>> 
>>> 
Hello, world
```

You can try a few more queries.

```
rumble$ 2 + 2
>>> 
>>> 
4

rumble$ 1 to 10
>>> 
>>> 
( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
```

This is it. RumbleDB is setup and ready to go locally. You can now move on to a JSONiq tutorial. A RumbleDB tutorial will also follow soon.

## Running on a cluster

You can also try to run the RumbleDB shell on a cluster if you have one available and configured -- this is done with the same command, as the master and deployment mode are usually already set up in cloud-managed clusters. More details are provided in the rest of the documentation.


# As an HTTP server

{% hint style="info" %}
*<mark style="color:$warning;">Now that there is a pip package available, using it may appeal more to some users than this older approach based on running RumbleDB as a server (you can put your JSONiq queries in rumble.jsoniq() calls). We keep this documentation for any users interested in the server capabilities of RumbleDB.</mark>*
{% endhint %}

## Starting the HTTP server

RumbleDB can be run as an HTTP server that listens for queries. In order to do so, you can use the --server and --port parameters:

```
spark-submit rumbledb.jar serve -p 8001
```

This command will not return until you force it to (Ctrl+C on Linux and Mac). This is because the server has to run permanently to listen to incoming requests.

Most users will not have to do anything beyond running the above command. For most of them, the next step would be to open a Jupyter notebook that connects to this server automatically.

This HTTP server is built as a basic server for the single user use case, i.e., the user runs their own RumbleDB server on their laptop or cluster, and connects to it via their Jupyter notebook, one query at a time. Some of our users have more advanced needs, or have a larger user base, and typically prefer to implement their own HTTP server, lauching RumbleDB queries either via the public RumbleDB Java API (like the basic HTTP server does -- so its code can serve as a demo of the Java API) or via the RumbleDB CLI.

Caution! Launching a server always has consequences on security, especially as RumbleDB can read from and write to your disk; So make sure you activate your firewall. In later versions, we may support authentication tokens.

## Testing that it works (not necessary for most end users)

The HTTP server is meant not to be used directly by end users, but instead to make it possible to integrate RumbleDB in other languages and environments, such as Python and Jupyter notebooks.

To test that the server is running, you can try the following address in your browser, assuming you have a query stored locally at /tmp/query.jq. All queries have to go to the /jsoniq path.

```
http://localhost:8001/jsoniq?query-path=/tmp/query.jq
```

The request returns a JSON object, and the resulting sequence of items is in the values array.

```
{ "values" : [ "foo", "bar" ] }
```

Almost all parameters from the command line are exposed as HTTP parameters.

A query can also be submitted in the request body:

```
curl -X POST --data '1+1' http://localhost:8001/jsoniq
```

## Use with Jupyter notebooks

With the HTTP server running, if you have installed Python and Jupyter notebooks (for example with the Anaconda data science package that does all of it automatically), you can create a RumbleDB magic by just executing the following code in a cell:

```
!pip install rumbledb
%load_ext rumbledb
%env RUMBLEDB_SERVER=http://localhost:8001/jsoniq
```

Where, of course, you need to adapt the port (8001) to the one you picked previously.

Then, you can execute queries in subsequent cells with:

```
%jsoniq 1 + 1
```

or on multiple lines:

```
%%jsoniq
for $doc in json-lines("my-file")
where $doc.foo eq "bar"
return $doc

```

## Use with clusters

You can also let RumbleDB run as an HTTP server on the master node of a cluster, e.g. on Amazon EMR or Azure. You just need to:

* Create the cluster (it is usually just the push of a few buttons in Amazon or Azure)
* Wait for a few minutes
* Make sure that your own IP has incoming access to EMR machines by configuring the security group properly. You usually only need to do so the first time you set up a cluster (if your IP address remains the same), because the security group configuration will be reused for future EMR clusters.

Then there are two options

### With SSH tunneling

* Connect to the master with SSH with an extra parameter for securely tunneling the HTTP connection (for example `-L 8001:localhost:8001` or any port of your choosing)
* Download the RumbleDB jar to the master node

  wget <https://github.com/RumbleDB/rumble/releases/download/v1.24.0/rumbledb-1.24.0.jar>
* Launch the HTTP server on the master node (it will be accessible under `http://localhost:8001/jsoniq`).

  spark-submit rumbledb-1.24.0.jar serve -p 8001
* And then use Jupyter notebooks in the same way you would do it locally (it magically works because of the tunneling)

### With the EC2 hostname

There is also another way that does not need any tunnelling: you can specify the hostname of your EC2 machine (copied over from the EC2 dashboard) with the --host parameter. For example, with the placeholder :

```
spark-submit rumbledb.jar serve -p 8001 -h <ec2-hostname>
```

You also need to make sure in your EMR security group that the chosen port (e.g., 8001) is accessible from the machine in which you run your Jupyter notebook. Then, you can point your Jupyter notebook on this machine to `http://<ec2-hostname>:8001/jsoniq`.

Be careful not to open this port to the whole world, as queries can be sent that read and write to the EC2 machine and anything it has access to (like S3).


# First queries

This section assumes that you have installed RumbleDB with one of the proposed ways, and guides you through your first queries.

## Create some data set

Create, in the same directory as RumbleDB to keep it simple, a file data.json and put the following content inside. This is a small list of JSON objects in the JSON Lines format.

```
{ "product" : "broiler", "store number" : 1, "quantity" : 20  }
{ "product" : "toaster", "store number" : 2, "quantity" : 100 }
{ "product" : "toaster", "store number" : 2, "quantity" : 50 }
{ "product" : "toaster", "store number" : 3, "quantity" : 50 }
{ "product" : "blender", "store number" : 3, "quantity" : 100 }
{ "product" : "blender", "store number" : 3, "quantity" : 150 }
{ "product" : "socks", "store number" : 1, "quantity" : 500 }
{ "product" : "socks", "store number" : 2, "quantity" : 10 }
{ "product" : "shirt", "store number" : 3, "quantity" : 10 }
```

If you want to later try a bigger version of this data, you can also download a larger version with 100,000 objects from [here](https://rumbledb.org/samples/products-small.json). Wait, no, in fact you do not even need to download it: you can simply replace the file path in the queries below with "<https://rumbledb.org/samples/products-small.json>" and it will just work! RumbleDB feels just at home on the Web.

RumbleDB also scales without any problems to datasets that have millions or (on a cluster) billions of objects, although of course, for billions of objects HDFS or S3 are a better idea than the Web to store your data, for obvious reasons.

In the JSON Lines format that this simple dataset uses, you just need to make sure you have one object on each line (this is different from a plain JSON file, which has a single JSON value and can be indented). Of course, RumbleDB can read plain JSON files, too (with json-doc()), but below we will show you how to read JSON Line files, which is how JSON data scales.

## Running simple queries locally

Depending on your installation method, the JSONiq queries will go to:

* A cell in a jupyter notebook and with the %%jsoniq magic: a simple click is sufficient to execute.
* The shell: type the query, and finish by pressing Enter twice.
* In a Python program, inside a rumble.jsoniq() call of which you can exploit the output with more Python code.
* A JSONiq query file, which you can execute with the RumbleDB CLI interface.

Either way, the meaning of the queries is the same.

```
"Hello, World"
```

or

```
 1 + 1
 
```

or

```
 (3 * 4) div 5
 
```

The above queries do not actually use Spark. Spark is used when the I/O workload can be parallelized. The following query should output the file created above.

```
 json-lines("data.json")
 
```

json-lines() reads its input in parallel, and thus will also work on your machine with MB or GB files (for TB files, a cluster will be preferable). You should specify a minimum number of partitions, here 10 (note that this is a bit ridiculous for our tiny example, but it is very relevant for larger files), as locally no parallelization will happen if you do not specify this number.

```
for $i in json-lines("data.json", 10)
return $i
```

The above creates a very simple Spark job and executes it. More complex queries will create several Spark jobs. But you will not see anything of it: this is all done behind the scenes. If you are curious, you can go to [localhost:4040](http://localhost:4040) in your browser while your query is running (it will not be available once the job is complete) and look at what is going on behind the scenes.

Data can be filtered with the where clause. Again, below the hood, a Spark transformation will be used:

```
for $i in json-lines("data.json", 10)
where $i.quantity gt 99
return $i
```

RumbleDB also supports grouping and aggregation, like so:

```
for $i in json-lines("data.json", 10)
let $quantity := $i.quantity
group by $product := $i.product
return { "product" : $product, "total-quantity" : sum($quantity) }
```

RumbleDB also supports ordering. Note that clauses (where, let, group by, order by) can appear in any order. The only constraint is that the first clause should be a for or a let clause.

```
for $i in json-lines("data.json", 10)
let $quantity := $i.quantity
group by $product := $i.product
let $sum := sum($quantity)
order by $sum descending
return { "product" : $product, "total-quantity" : $sum }
```

Finally, RumbleDB can also parallelize data provided within the query, exactly like Sparks' parallelize() creation:

```
for $i in parallelize((
 { "product" : "broiler", "store number" : 1, "quantity" : 20  },
 { "product" : "toaster", "store number" : 2, "quantity" : 100 },
 { "product" : "toaster", "store number" : 2, "quantity" : 50 },
 { "product" : "toaster", "store number" : 3, "quantity" : 50 },
 { "product" : "blender", "store number" : 3, "quantity" : 100 },
 { "product" : "blender", "store number" : 3, "quantity" : 150 },
 { "product" : "socks", "store number" : 1, "quantity" : 500 },
 { "product" : "socks", "store number" : 2, "quantity" : 10 },
 { "product" : "shirt", "store number" : 3, "quantity" : 10 }
), 10)
let $quantity := $i.quantity
group by $product := $i.product
let $sum := sum($quantity)
order by $sum descending
return { "product" : $product, "total-quantity" : $sum }
```

Mind the double parenthesis, as parallelize is a unary function to which we pass a sequence of objects.


# Writing JSONiq queries in Python

You can use RumbleDB from within Python programmes by running

```bash
pip install jsoniq
```

## Java version

*Important note*: since the jsoniq package depends on pyspark 4, Java 17 or Java 21 is a requirement. If another version of Java is installed, the execution of a Python program attempting to create a RumbleSession will lead to an error message on stderr that contains explanations.

You can control your Java version with:

```bash
java -version
```

Information about how this package is used can be found [in this section](/writing-jsoniq-queries-in-python).

## Common issue: colliding Spark version

Some advanced users who have already configured a Spark installation on their machine may encounter a version issue if SPARK\_HOME points to this alternate installation, and it is a different version of Spark (e.g., 3.5 or 3.4). The jsoniq package requires Spark 4.0.

If this happens, RumbleDB should output an informative error message. They are two ways to fix such conflicts:

* The easiest is remove the SPARK\_HOME environment variable completely. This will have RumbleDB fall back to the Spark 4.0 installation that ships with its pyspark dependency.
* Or you can instead change the value of SPARK\_HOME to point to a Spark 4.0 installation, if you have one. This would be for more advanced users who know what they are doing.

If you have another working Spark installation on your machine, you can see which version it is with

```
spark-submit --version
```

The above command is of course expected not to work for first-time users who only installed the jsoniq package and never installed Spark additionally on their machine.

### High-level information on the library

A RumbleSession is a wrapper around a SparkSession that additionally makes sure the RumbleDB environment is in scope.

JSONiq queries are invoked with rumble.jsoniq() in a way similar to the way Spark SQL queries are invoked with spark.sql().

JSONiq variables can be bound to lists of JSON values (str, int, float, True, False, None, dict, list) or to Pyspark DataFrames. A JSONiq query can use as many variables as needed (for example, it can join between different collections).

It will later also be possible to read tables registered in the Hive metastore, similar to spark.sql(). Alternatively, the JSONiq query can also read many files of many different formats from many places (local drive, HTTP, S3, HDFS, ...) directly with simple [builtin function calls](/input) such as json-lines(), text-file(), parquet-file(), csv-file(), etc.

The resulting sequence of items can be retrieved as a list of JSON values, as a Pyspark DataFrame, or, for advanced users, as an RDD or with a streaming iteration over the items using the [RumbleDB Item API](https://github.com/RumbleDB/rumble/blob/master/src/main/java/org/rumbledb/api/Item.java).

It is also possible to write the sequence of items to the local disk, to HDFS, to S3, etc in a way similar to how DataFrames are written back by Pyspark.

The design goal is that it is possible to chain DataFrames between JSONiq and Spark SQL queries seamlessly. For example, JSONiq can be used to clean up very messy data and turn it into a clean DataFrame, which can then be processed with Spark SQL, spark.ml, etc.

Any feedback or error reports are very welcome.


# Your first programs

The syntax to start a session is similar to that of Spark. A RumbleSession is a SparkSession that additionally knows about RumbleDB. All attributes and methods of SparkSession are also available on RumbleSession.

Even though RumbleDB uses Spark internally, it can be used without any knowledge of Spark.

Executing a query is done with rumble.jsoniq() like so.

```python
from jsoniq import RumbleSession

rumble = RumbleSession.builder.getOrCreate();

items = rumble.jsoniq('1+1')
python_tup = items.json()
print(python_tup)
```

A query returns a sequence of items, here the sequence with just the integer item 2.

There are several ways to retrieve the results of the query. Calling the json() is just one of them. It retrieves the sequence of as a tuple of JSON values that Python can process. The detailed [type mapping for this is described here](/writing-jsoniq-queries-in-python/type-mapping). Other methods for [retrieving the output of a query are described here](/writing-jsoniq-queries-in-python/ways-to-get-and-process-the-output-of-a-jsoniq-query).

## More complex, standalone queries

Below are a few examples showing what is possible with JSONiq. You can [learn JSONiq with our interactive tutorial](https://colab.research.google.com/github/RumbleDB/rumble/blob/master/RumbleSandbox.ipynb). You will also find [a full language reference here](/the-jsoniq-language/jsoniq-specification) as well as a [list of builtin functions](/rumbledb-reference/function-library).

For complex queries, you can use Python's ability to spread strings over multiple lines, and with no need to escape special characters.

```python
seq = rumble.jsoniq("""

let $stores :=
[
  { "store number" : 1, "state" : "MA" },
  { "store number" : 2, "state" : "MA" },
  { "store number" : 3, "state" : "CA" },
  { "store number" : 4, "state" : "CA" }
]
let $sales := [
   { "product" : "broiler", "store number" : 1, "quantity" : 20  },
   { "product" : "toaster", "store number" : 2, "quantity" : 100 },
   { "product" : "toaster", "store number" : 2, "quantity" : 50 },
   { "product" : "toaster", "store number" : 3, "quantity" : 50 },
   { "product" : "blender", "store number" : 3, "quantity" : 100 },
   { "product" : "blender", "store number" : 3, "quantity" : 150 },
   { "product" : "socks", "store number" : 1, "quantity" : 500 },
   { "product" : "socks", "store number" : 2, "quantity" : 10 },
   { "product" : "shirt", "store number" : 3, "quantity" : 10 }
]
let $join :=
  for $store in $stores[], $sale in $sales[]
  where $store."store number" = $sale."store number"
  return {
    "nb" : $store."store number",
    "state" : $store.state,
    "sold" : $sale.product
  }
return [$join]
""");

print(seq.json());

seq = rumble.jsoniq("""
for $product in json-lines("http://rumbledb.org/samples/products-small.json", 10)
group by $store-number := $product.store-number
order by $store-number ascending
return {
    "store" : $store-number,
    "products" : [ distinct-values($product.product) ]
}
""");
print(seq.json());
```


# Ways to get and process the output of a JSONiq query

There are several ways to get back the output of the JSONiq query. There are many examples of use further down this page.

<table><thead><tr><th>Method</th><th>Description</th><th width="135.95703125">Requirement in availableOutputs()</th><th>Scale</th></tr></thead><tbody><tr><td>availableOutputs()</td><td>Returns a list that helps you understand which output methods you can call. The strings in this list can be Local, RDD, DataFrame, or PUL.</td><td></td><td>-</td></tr><tr><td>json()</td><td>Returns the results as a tuple containing dicts, lists, strs, ints, floats, booleans, Nones.</td><td>Local</td><td>Sequence length below the materialization cap. The default is 200 but it can be increased in the RumbleDB configuration.</td></tr><tr><td>df()</td><td>Returns the results as a pyspark data frame</td><td>DataFrame (i.e., RumbleDB was able to infer an output schema)</td><td>No limitation, but beyond a billion items, you should use a Spark cluster.</td></tr><tr><td>pdf()</td><td>Returns the results as a pandas data frame</td><td>DataFrame (i.e., RumbleDB was able to infer an output schema)</td><td>Should fit in your computer's memory.</td></tr><tr><td>rdd()</td><td>Returns the results as an RDD containing dicts, lists, strs, ints, floats, booleans, Nones (experimental)</td><td>RDD</td><td>No limitation, but beyond a billion items, you should use a Spark cluster.</td></tr><tr><td>items()</td><td>Returns the results as a list containing Java Item objects that can be queried with the RumbleDB Item API. Will contain more information and more accurate typing.</td><td>Local</td><td>Sequence length below the materialization cap. The default is 200 but it can be increased in the RumbleDB configuration.</td></tr><tr><td>open(), hasNext(), nextJSON(), close()</td><td>Allows streaming (with no limitation of length) through individuals items as dicts, lists, strs, ints, floats, booleans, Nones.</td><td>Local</td><td>No limitation, as long as you go through the stream without saving all past items.</td></tr><tr><td>open(), hasNext(), next(), close()</td><td>Allows streaming (with no limitation of length) through individuals items as Java Item objects that can be queried with the RumbleDB Item API. Will contain more information and more accurate typing.</td><td>Local</td><td>No limitation, as long as you go through the stream without saving all past items.</td></tr><tr><td>applyPUL()</td><td>Persists the Pending Update List produced by the query (to the Delta Lake or a table registered in the Hive metastore).</td><td>PUL</td><td>-</td></tr></tbody></table>


# Type mapping

Any expression in JSONiq returns a sequence of items. Any variable in JSONiq is bound to a sequence of items. Items can be objects, arrays, or atomic values (strings, integers, booleans, nulls, dates, binary, durations, doubles, decimal numbers, etc). A sequence of items can be a sequence of just one item, but it can also be empty, or it can be as large as to contain millions, billions or even trillions of items. Obviously, for sequence longer than a billion items, it is a better idea to use a cluster than a laptop. A relational table (or more generally a data frame) corresponds to a sequence of object items sharing the same schema. However, sequences of items are more general than tables or data frames and support heterogeneity seamlessly.

When passing Python values to JSONiq or getting them from a JSONiq queries, the mapping to and from Python is as follows:

| Python | JSONiq            |
| ------ | ----------------- |
| tuple  | sequence of items |
| dict   | object item       |
| list   | array item        |
| str    | string item       |
| int    | integer item      |
| bool   | boolean item      |
| None   | null item         |

Furthermore, other JSONiq types will be mapped to string literals. Users who want to preserve JSONiq types can use the Item API instead.

JSONiq is very powerful and expressive. You will find tutorials as well as a reference on JSONiq.org.


# Binding JSONiq variables to Python values

It is possible to bind a JSONiq variable to a tuple of native Python values and then use it in a query. JSONiq, variables are bound to sequences of items, just like the results of JSONiq queries are sequence of items. A Python tuple will be seamlessly converted to a sequence of items by the library.  Currently we only support strs, ints, floats, booleans, None, and (recursively) lists and dicts. But if you need more (like date, bytes, etc) we will add them without any problem. JSONiq has a rich type system.

Values can be passed with extra named parameters, like so.

```python
print(rumble.jsoniq("""
for $v in $c
let $parity := $v mod 2
group by $parity
return { switch($parity)
         case 0 return "even"
         case 1 return "odd"
         default return "?" : $v
}
""", c=(1,2,3,4, 5, 6)).json())

print(rumble.jsoniq("""
for $i in $c
return [
  for $j in $i
  return { "foo" : $j }
]
""", c=([1,2,3],[4,5,6])).json())


print(rumble.jsoniq('{ "results" : $c.foo[[2]] }',
    c=({"foo":[1,2,3]},{"foo":[4,{"bar":[1,False, None]},6]})).json())
```

It is also possible to bind variables more durably (across multiple jsoniq() calls) with bind().

```python
rumble.bind('$c', (1,2,3,4, 5, 6))
print(rumble.jsoniq("""
for $v in $c
let $parity := $v mod 2
group by $parity
return { switch($parity)
         case 0 return "even"
         case 1 return "odd"
         default return "?" : $v
}
""").json())

print(rumble.jsoniq("""
for $v in $c
let $parity := $v mod 2
group by $parity
return { switch($parity)
         case 0 return "gerade"
         case 1 return "ungerade"
         default return "?" : $v
}
""").json())

rumble.bind('$c', ([1,2,3],[4,5,6]))
print(rumble.jsoniq("""
for $i in $c
return [
  for $j in $i
  return { "foo" : $j }
]
""").json())

rumble.bind('$c', ({"foo":[1,2,3]},{"foo":[4,{"bar":[1,False, None]},6]}))
print(rumble.jsoniq('{ "results" : $c.foo[[2]] }').json())
```

It is possible to bind only one value. The it must be provided as a singleton tuple. This is because in JSONiq, an item is the same a sequence of one item.

```python
rumble.bind('$c', (42,))
print(rumble.jsoniq('for $i in 1 to $c return $i*$i').json())
```

For convenience and code readability, you can also use bindOne().

```python
rumble.bindOne('$c', 42)
print(rumble.jsoniq('for $i in 1 to $c return $i*$i').json())
```

A variable that was durably bound with bind() or bindOne() can be unbound with unbind().

```python
rumble.unbind('$c')
```


# Interacting with pandas DataFrames

RumbleDB can work out of the box with pandas DataFrames, both as input and (when the output has a schema) as output.

## Binding JSONiq variables to pandas DataFrames

bind() also accepts pandas dataframes

```python
data = {'Name': ['Alice', 'Bob', 'Charlie'],
        'Age': [30,25,35]};
pdf = pd.DataFrame(data);

rumble.bind('$a',pdf);
seq = rumble.jsoniq('$a.Name')
```

The same goes for extra named parameters.

```python
data = {'Name': ['Alice', 'Bob', 'Charlie'],
        'Age': [30,25,35]};
pdf = pd.DataFrame(data);

seq = rumble.jsoniq('$a.Name', a=pdf)
```

## Getting the results as a pandas DataFrame

It is also possible to get the results back as a pandas dataframe with pdf() (if the output has a schema, which you can check by calling availableOutputs() and seeing if "DataFrame" is in the returned list).

```
print(seq.pdf())
```


# Interacting with pyspark DataFrames

RumbleDB can work out of the box with pyspark DataFrames, both as input and (when the output has a schema) as output.

## Using Pyspark DataFrames with JSONiq

The power users can also interface our library with pyspark DataFrames. JSONiq sequences of items can have billions of items, and our library supports this out of the box: it can also run on clusters on AWS Elastic MapReduce for example. But your laptop is just fine, too: it will spread the computations on your cores. You can bind a DataFrame to a JSONiq variable. JSONiq will recognize this DataFrame as a sequence of object items.

Creating a data frame also similar to Spark (but using the rumble object).

```renpy
data = [("Alice", 30), ("Bob", 25), ("Charlie", 35)];
columns = ["Name", "Age"];
df = spark.createDataFrame(data, columns);
```

This is how to bind a JSONiq variable to a dataframe. You can bind as many variables as you want.

```python
rumble.bind('$a', df);
```

This is how to run a query. This is similar to spark.sql(). Since variable $a was bound to a DataFrame, it is automatically declared as an external variable and can be used in the query. In JSONiq, it is logically a sequence of objects.

```python
res = rumble.jsoniq('$a.Name');
```

You can also, instead of the bind() call, pass the pyspark DataFrame directly in jsoniq() with an extra named parameter:

```python
res = rumble.jsoniq('$a.Name', a=df);
```

There are several ways to collect the outputs, depending on the user needs but also on the query supplied. The following method returns a list containing one or several of "DataFrame", "RDD", "PUL", "Local".

If DataFrame is in the list, df() can be invoked.

If RDD is in the list, rdd() can be invoked.

If Local is the list, items() or json() can be invokved, as well as the local iterator API.

```python
modes = res.availableOutputs();
for mode in modes:
    print(mode)
```

## Manipulating DataFrames with SQL and JSONiq

If the output of the JSONiq query is structured (i.e., RumbleDB was able to detect a schema), then we can extract a regular data frame that can be further processed with spark.sql() or rumble.jsoniq().

```python
df = res.df();
df.show();
```

We are continuously working on the detection of schemas and RumbleDB will get better at it with them. JSONiq is a very powerful language and can also produce heterogeneous output "by design". Then you need to use rdd() instead of df(), or to collect the list of JSON values (see further down). Remember that availableOutputs() tells you what is at your disposal.

A DataFrame output by JSONiq can be reused as input to a Spark SQL query.

(Remember that rumble is a wrapper around a SparkSession object, so you can use rumble.sql() just like spark.sql())<br>

```python
df.createTempView("myview")
df2 = spark.sql("SELECT * FROM myview").toDF("name");
df2.show();
```

A DataFrame output by Spark SQL can be reused as input to a JSONiq query.

```python
rumble.bind('$b', df2);
seq2 = rumble.jsoniq("for $i in 1 to 5 return $b");
df3 = seq2.df();
df3.show();
```

And a DataFrame output by JSONiq can be reused as input to another JSONiq query.

```python
rumble.bind('$b', df3);
seq3 = rumble.jsoniq("$b[position() lt 3]");
df4 = seq3.df();
df4.show();
```


# More advanced output retrieval methods

This section shares more techniques for advanced users who want to make the most of RumbleDB in Python.

## RumbleDB Item API

JSONiq has a rich type system, and the [conversion to JSON values](/writing-jsoniq-queries-in-python/type-mapping) can lose type information.

An alternative consists of retrieving the sequence as a tuple of native items, which can be accessed with the [RumbleDB Item API](https://github.com/RumbleDB/rumble/blob/ccd9b91829510d7fbddf919d6d9ef7e45aa74905/src/main/java/org/rumbledb/api/Item.java).

```python
list = res.items();
for result in list:
    print(result.getStringValue())
```

## Streaming through the items

Sometimes, a sequence can be very long, and materializing it to a tuple of JSON values or a tuple of native items can fail because of the materialization cap. While it can be changed in the configuration to allow for larger tuples, this does not scale.

Another way to retrieve a sequence of arbitrary length is to use the iterator API to stream through the items one by one. If you do not keep previous values in memory, there is no limit to the sequence size than can be retrieved in this way (but it may take more time than using RDDs or DataFrames, which benefit from parallelism).

This is how to stream through the items converted to JSON one by one: &#x20;

```python
res.open();
while(res.hasNext()):
    print(res.nextJSON());
res.close();
```

This is how to stream through the native items, using the Item API:&#x20;

```python
res.open();
while (res.hasNext()):
    print(res.next().getStringValue());
res.close();
```

## Getting unstructured output as an RDD

Sometimes, it is not possible to retrieve the output sequence as a (pandas or pyspark) DataFrame because no schema could be inferred. This is notably the case if the output sequence is heterogeneous (such as a sequence of items mixing atomics, objects of various structures, arrays, etc).

And materializing or streaming may not be an option either if there are billions of items.

In this case, it is possible to obtain the output as an RDD instead. This gets an RDD of JSON values that can be processed by Python (using the [type mapping](/writing-jsoniq-queries-in-python/type-mapping)).

The rdd() method is experimental because we had to reverse-engineer how pyspark encodes RDDs for the Java Virtual Machine (pickling).

```python
rdd = res.rdd();
print(rdd.count());
for str in rdd.take(10):
    print(str);
```


# Writing queries directly in Jupyter notebook cells

The Python edition of RumbleDB comes out of the box with a JSONiq magic.&#x20;

If you are in a Jupyter notebook and have installed the jsoniq pip package, you can activate the jsoniq magic with:

```
%load_ext jsoniqmagic
```

Then, you can run JSONiq in standalone cells and see the results:

```
%%jsoniq
{"foobar":1} 
```

Of course, you can still continue to use rumble.jsoniq() calls and process the outputs as you see fit.

An example of the magic in action is available in our [upgraded online sandbox](https://colab.research.google.com/github/RumbleDB/rumble/blob/master/RumbleSandbox.ipynb).

Note: This is a different magic than the magic that works with the RumbleDB HTTP server. It is more modern and running a server is no longer needed with this different magic. It suffices to install the jsoniq Python package.&#x20;

## Change the behavior to output DataFrames

By default, the output will be in the form of serialized JSON values. If the output is structured, then you can change this default behavior to show it in the form of a DataFrame instead.

For a pandas DataFrame:

```notebook-python
%%jsoniq -pdf
for $i in 1 to 10000000
return { "foobar" : $i}
```

For a pyspark DataFrame:

```
%%jsoniq -df
for $i in 1 to 10000000
return { "foobar" : $i}
```

Note that it will not work in all cases. If the output is not fully structured or RumbleDB is unable to infer a DataFrame schema, you can specify the schema yourself. The schema language is called JSound and you will find a [tutorial here](https://github.com/ghislainfourny/jsound-tutorial).

```
%%jsoniq -pdf
declare type local:mytype as {
    "product" : "string",
    "store-number" : "int",
    "quantity" : "decimal"
};
validate type local:mytype* { 
    for $product in json-lines("http://rumbledb.org/samples/products-small.json", 10)
    where $product.quantity ge 995
    return $product
}
```

It is possible to measure the response time with the -t parameter:

```
%%jsoniq -t
for $i in 1 to 10000000
return { "foobar" : $i}
```


# Advanced configuration

## RumbleDB's specific configuration

It is possible to access RumbleDB's advanced configuration parameters with

```
conf = rumble.getRumbleConf()
```

Then, you can change the value of some parameters. For example, you can increase the number of JSON values that you can retrieve with a json() call:

```
conf.setResultSizeCap(1000)
```

You can also configure RumbleDB to output verbose information about the internal query plan, type and mode detection, and optimizations. This can be of interest to data engineers or researchers to understand how RumbleDB works.

```
conf.setPrintIteratorTree(True)
```

The complete API for configuring RumbleDB is accessible in our [JavaDoc](https://rumbledb.org/docs/latest/api/org/rumbledb/config/RumbleRuntimeConfiguration.html) pages. These methods are also callable in Python.

Warning: some of the configuration methods do not make sense in Python and are specific to the command line edition of RumbleDB (such as setting the query content or an output path and input/output format). Also, setting external variables in Python should not be done via the configuration, but with the bind() and unbind() functions or extra parameters in jsoniq() calls.

## Allocating more memory

If you get an out-of-memory error, it is possible to allocate memory when you build the Rumble session with a config() call. This is exactly the same way it is done when building a Spark session. The config() call can of course be used in combination with any other method calls that are part of the builder chain (withDelta(), appName(), config(), etc).

{% hint style="info" %}
This will only have an effect when done the first time the session is created. Spark and Java are not able to adjust the memory automatically after the session has been created. Subsequent calls of getOrCreate() do not create a new session, they only get the existing one.

In Jupyter, you can restart the Kernel before you create the session, to force a new session.&#x20;
{% endhint %}

For example:

```
rumble = RumbleSession.builder
.config("spark.driver.memory", "10g")
.getOrCreate()
```


# Write back to the disk (or data lake)

Generally, it is possible to write output by to disk using the pandas DataFrame API, the pyspark DataFrame API, or Python's library to write JSON values to disk.

For convenience, we provide a way to also directly do so with the sequence object output by the query.

it is possible to write the output to a file locally or on a cluster. The API is similar to that of Spark dataframes. Note that it creates a directory and stores the (potentially very large) output in a sharded directory. RumbleDB was already tested with up to 64 AWS machines and 100s of TBs of data.

Of course the examples below are so small that it makes more sense to process the results locally with Python, but this shows how GBs or TBs of data obtained from JSONiq can be written back to disk.

```python
seq = rumble.jsoniq("$a.Name");
seq.write().mode("overwrite").json("outputjson");
seq.write().mode("overwrite").parquet("outputparquet");

seq = rumble.jsoniq("1+1");
seq.write().mode("overwrite").text("outputtext");
```


# Data sources and formats

RumbleDB is able to read a variety of formats from a variety of file systems and database management systems.

We support functions to read JSON, JSON Lines, XML, Parquet, CSV, Text, ROOT, Delta files from various storage layers such as S3 and HDFS, Azure blob storage. We run most of our tests on Amazon EMR with S3 or HDFS, as well as locally on the local file system, but we welcome feedback on other setups.

We also support some ETL-based systems such as PostgreSQL, MongoDB and the Hive metastore.

## Supported formats

### JSON

A JSON file containing a single JSON object (or value) can be read with json-doc(). It will not spread access in any way, so that the files should be reasonably small. json-doc() can read JSON files even if the object or value is spread over multiple lines.

```
json-doc("file.json")
```

returns the (single) JSON value read from the supplied JSON file. This will also work for structures spread over multiple lines, as the read is local and not sharded.

json-doc() also works with an HTTP URI.

### JSON Lines

JSON Lines files are files that have one JSON object (or value) per line. Such files can thus become very large, up to billions or even trillions of JSON objects.

JSON Lines files are read with the json-lines() function (formerly called json-file()). json-lines() exists in unary and binary. The first parameter specifies the JSON file (or set of JSON files) to read. The second, optional parameter specifies the minimum number of partitions. It is recommended to use it in a local setup, as the default is only one partition, which does not fully use the parallelism. If the input is on HDFS, then blocks are taken as splits by default. This is also similar to Spark's textFile().

json-lines() also works with an HTTP URI, however, it will download the file completely and then parallelize, because HTTP does not support blocks. As a consequence, it can only be used for reasonable sizes.

Example of usage:

```
for $my-json in json-lines("hdfs://host:port/directory/file.json")
where $my-json.property eq "some value"
return $my-json
```

If a default host and port are set in the Hadoop configuration, you can directly specify an absolute path without host and port:

```
for $my-json in json-lines("/absolute/directory/file.json")
where $my-json.property eq "some value"
return $my-json
```

For a set of files:

```
for $my-json in json-lines("/absolute/directory/file-*.json")
where $my-json.property eq "some value"
return $my-json
```

If a working directory is set:

```
for $my-json in json-lines("file.json")
where $my-json.property eq "some value"
return $my-json
```

Several files or whole directories can be read with the same pattern syntax as in Spark.

```
for $my-json in json-lines("*.json")
where $my-json.property eq "some value"
return $my-json
```

In some cases, JSON Lines files are highly structured, meaning that all objects have the same fields and these fields are associated with values with the same types. In this case, RumbleDB will be faster navigating such files if you open them with the function structured-json-lines().

structured-json-lines() parses one or more json files that follow [JSON-lines](http://jsonlines.org/) format and returns a sequence of objects. This enables better performance with fully structured data and is recommended to use only when such data is available.

Warning: when the data has multiple types for the same field, this field and contained values will be treated as strings. This is also similar to Spark's spark.read.json().

Example of usage:

```
for $my-structured-json in structured-json-lines("hdfs://host:port/directory/structured-file.json")
where $my-structured-json.property eq "some value"
return $my-structured-json
```

### XML

XML files can be read into RumbleDB using the doc() function. The parameter specifies the XML file to read and return as a document node.

Example of usage:

```
doc("path/to/file.xml")
```

Additionally, RumbeDB provides the xml-files() function to read many XML files at once. xml-files() exists in unary and binary. The first parameter specifies the directory of XML files to read. The second, optional parameter specifies the minimum number of partitions. It is recommended to use it in a local setup, as the default is only one partition.

Example of usage:

```
xml-files("path/to/directory/*.xml", 10)
```

### Text

Text files can be read into a sequence of string items, one string per line. RumbleDB can open files that have billions or potentially even trillions of lines with the function text-file().

text-file() exists in unary and binary. The first parameter specifies the text file (or set of text files) to read and return as a sequence of strings.

The second, optional parameter specifies the minimum number of partitions. It is recommended to use it in a local setup, as the default is only one partition, which does not fully use the parallelism. If the input is on HDFS, then blocks are taken as splits by default. This is also similar to Spark's textFile().

Example of usage:

```
count(
  for $my-string in text-file("hdfs://host:port/directory/file.txt")
  for $token in tokenize($my-string, ";")
  where $token eq "some value"
  return $token
)
```

Several files or whole directories can be read with the same pattern syntax as in Spark.

(Also see examples for json-lines for host and port, sets of files and working directory).

There is also a function local-text-file() that reads locally, without parallelism. RumbleDB can stream through the file efficiently.

```
count(
  for $my-string in local-text-file("file:///home/me/file.txt")
  for $token in tokenize($my-string, ";")
  where $token eq "some value"
  return $token
)
```

RumbleDB supports also the W3C-standard functions unparsed-text and unparsed-text-lines. The output of the latter is automatically parallelized as a potentially large sequence of strings.

```
count(
  for $my-string in unparsed-text-lines("file:///home/me/file.txt")
  for $token in tokenize($my-string, ";")
  where $token eq "some value"
  return $token
)
```

```
count(
  let $text := unparsed-text("file:///home/me/file.txt")
  for $my-string in tokenize($text, "\n")
  for $token in tokenize($my-string, ";")
  where $token eq "some value"
  return $token
)
```

### Parquet

Parquet files can be opened with the function parquet-file().

Parses one or more parquet files and returns a sequence of objects. This is also similar to Spark's spark.read.parquet()

```
for $my-object in parquet-file("file.parquet")
where $my-object.property eq "some value"
return $my-json
```

Several files or whole directories can be read with the same pattern syntax as in Spark.

```
for $my-object in parquet-file("*.parquet")
where $my-object.property eq "some value"
return $my-json
```

### CSV

CSV files can be opened with the function csv-file().

Parses one or more csv files and returns a sequence of objects. This is also similar to Spark's spark.read.csv()

```
for $i in csv-file("file.csv")
where $i._c0 eq "some value"
return $i
```

Several files or whole directories can be read with the same pattern syntax as in Spark.

```
for $i in csv-file("*.csv")
where $i._c0 eq "some value"
return $i
```

Options can be given in the form of a JSON object. All available options can be found in the [Spark documentation](https://spark.apache.org/docs/latest/api/java/org/apache/spark/sql/DataFrameReader.html#csv-java.lang.String...-)

```
for $i in csv-file("file.csv", {"header": true, "inferSchema": true})
where $i.key eq "some value"
return $i
```

### PostgreSQL

{% hint style="info" %}
This functionality is currently only available in the Python edition (pip install jsoniq) as of 2.0.1+.
{% endhint %}

PostgreSQL tables can be opened with the function postgresql-table().

PostgreSQL is an OLTP system with its own storage system. Thus, unlike most other functions on this page, it uses a connection string rather than a path on a data lake.

It opens one table and returns it as a sequence of objects. The first argument is the connection string in the JDBC format, containing host, port, username, password, and database. The second argument is the name of the table to read.

```
for $i in postgresql-table("jdbc:postgresql://servername/dbname?user=postgres&password=example", "tablename")
where $i.attribute eq "some value"
return $i
```

The third parameter can be used to control the number of partitions.

```
for $i in postgresql-table("jdbc:postgresql://servername/dbname?user=postgres&password=example", "tablename", 10)
where $i.attribute eq "some value"
return $i
```

### MongoDB

{% hint style="info" %}
This functionality is currently only available in the Python edition (pip install jsoniq) as of 2.0.2+.
{% endhint %}

MongoDB collections can be opened with the function mongodb-collection().

MongoDB is an OLTP system with its own storage system. Thus, unlike most other functions on this page, it uses a connection string rather than a path on a data lake.

It opens one collection and returns it as a sequence of objects. The first argument is the connection string in the MongoDB format, containing host, port, database, collection, username, password. The second argument is the name of the collection to read.

```
for $i in mongodb-collection("mongodb://servername/dbname", "collection")
where $i.attribute eq "some value"
return $i
```

The third parameter can be used to control the number of partitions.

```
for $i in mongodb-collection("mongodb://servername/dbname", "collection", 10)
where $i.attribute eq "some value"
return $i
```

MongoDB does not work "out of the box" but requires some configuration as indicated on the MongoDB Spark connector website. In the Python edition, we simplified the process and all that is needed is to add withMongo() on the session building chain:

```python
RumbleSession.builder.withMongo().getOrCreate();
```

### Hive metastore

RumbleDB can connect to a table registered in the Hive metastore with the function table().

The Hive metastore manages its own storage system. Thus, unlike most other functions on this page, it uses a simple name rather than a path on a data lake.

```
for $i in table("mytable")
where $i.attribute eq "some value"
return $i
```

RumbleDB can also modify data in a Hive metastore table with the [JSONiq Update Facility](/the-jsoniq-language/jsoniq-update-facility).

### Delta files

Delta files, part of the [Delta Lake framework](https://delta.io/), can be opened with the function delta-file().

```
for $i in delta-file("hdfs://path/to/my/delta-file")
where $i.attribute eq "some value"
return $i
```

RumbleDB can also modify data in a delta file with the [JSONiq Update Facility](/the-jsoniq-language/jsoniq-update-facility).

Delta files do not work "out of the box" but require some configuration as indicated on the Delta Lake website (importing packages, configuring some parameters). In the Python edition, we simplified the process and all that is needed is to add withDelta() on the session building chain:

```python
RumbleSession.builder.withDelta().getOrCreate();
```

### AVRO

Avro files can be opened with the function avro-file().

Parses one or more avro files and returns a sequence of objects. This is similar to Spark's `spark.read().format("avro").load()`

```
for $i in avro-file("file.avro")
where $i._col1 eq "some value"
return $i
```

Several files or whole directories can be read with the same pattern syntax as in Spark.

```
for $i in avro-file("*.avro")
where $i._col1 eq "some value"
return $i
```

Options can be given in the form of a JSON object. All available options relevant for reading in avro data can be found in the [Spark documentation](https://spark.apache.org/docs/latest/sql-data-sources-avro.html#data-source-option)

```
for $i in avro-file("file.avro", {"ignoreExtension": true, "avroSchema": "/path/to/schema.avsc"})
where $i._col1 eq "some value"
return $i
```

### libSVM

libSVM files can be opened with the function libsvm-file().

Parses one or more libsvm files and returns a sequence of objects. This is similar to Spark's `spark.read().format("libsvm").load()`

```
for $i in libsvm-file("file.txt")
where $i._col1 eq "some value"
return $i
```

Several files or whole directories can be read with the same pattern syntax as in Spark.

```
for $i in libsvm-file("*.txt")
where $i._col1 eq "some value"
return $i
```

### ROOT

ROOT files can be open with the function root-file(). The second parameter specifies the path within the ROOT files (a ROOT file is like a mini-file system of its own). It is often `Events` or `tree`.

```
for $i in root-file("events.root", "Events")
where $i._c0 eq "some value"
return $i
```

## Creating your own big sequence

The function parallelize() can be used to create, on the fly, a big sequence of items in such a way that RumbleDB can spread its querying across cores and machines.

This function behaves like the Spark parallelize() you are familiar with and sends a large sequence to the cluster. The rest of the FLWOR expression is then evaluated with Spark transformations on the cluster.

```
for $i in parallelize(1 to 1000000)
where $i mod 1000 eq 0
return $i
```

There is also be a second, optional parameter that specifies the minimum number of partitions.

```
for $i in parallelize(1 to 1000000, 100)
where $i mod 1000 eq 0
return $i
```

## Supported file systems

As a general rule of thumb, RumbleDB can read from any file system that Spark can read from. The file system is inferred from the scheme used in the path used in any of the functions described above, with the exception of MongoDB, the Hive metastore, and PostgreSQL, which are ETL-based.

Note that the scheme is optional, in which case the default file system as configured in Hadoop and Spark is used. A relative path can also be provided, in which case the working directory (including its file system) as configured is used.

### Local file system

The scheme for the local file system is `file://`. Pay attention to the fact that for reading an absolute path, a third slash will follow the scheme.

Example:

```
file:///home/user/file.json
```

Warning! If you try to open a file from the local file system on a cluster of several machines, this might fail as the file is only on the machine that you are connected to. You need to pass additional parameters to `spark-submit` to make sure that any files read locally will be copied over to all machines.

If you use `spark-submit` locally, however, this will work out of the box, but we recommend specifying a number of partitions to avoid reading the file as a single partition.

For Windows, you need to use forward slashes, and if the local file system is set up as the default and you omit the file scheme, you still need a forward slash in front of the drive letter to not confuse it with a URI scheme:

```
file:///C:/Users/hadoop/file.json
file:/C:/Users/hadoop/file.json
/C:/Users/hadoop/file.json
```

In particular, the following will *not* work:

```
file://C:/Users/hadoop/file.json
C:/Users/hadoop/file.json
C:\Users\hadoop\file.json
file://C:\Users\hadoop\file.json
```

### HDFS

The scheme for the Hadoop Distributed File System is `hdfs://`. A host and port should also be specified, as this is required by Hadoop.

Example:

```
hdfs://www.example.com:8021/user/hadoop/file.json
```

If HDFS is already set up as the default file system as is often the case in managed Spark clusters, an absolute path suffices:

```
/user/hadoop/file.json
```

The following will *not* work:

```
hdfs:///user/hadoop/file.json
hdfs://user/hadoop/file.json
hdfs:/user/hadoop/file.json
```

### S3

There are three schemes for reading from S3: `s3://`, `s3n://` and `s3a://`.

Examples:

```
s3://my-bucket/directory/file.json
s3n://my-bucket/directory/file.json
s3a://my-bucket/directory/file.json
```

If you are on an Amazon EMR cluster, `s3://` is straightforward to use and will automatically authenticate. For more details on how to set up your environment to read from S3 and which scheme is most appropriate, we refer to the Amazon S3 documentation.

### Azure blob storage

The scheme for Azure blob storage is `wasb://`.

Example:

```
wasb://mycontainer@myaccount.blob.core.windows.net/directory/file.json
```


# The JSONiq language

JSONiq is a query and processing language specifically designed for the popular JSON data model. The main ideas behind JSONiq are based on lessons learned in more than 30 years of relational query systems and more than 15 years of experience with designing and implementing query languages for semi-structured data like XML and RDF.

The main source of inspiration behind JSONiq is XQuery, which has been proven so far a successful and productive query language for semi-structured data (in particular XML). JSONiq borrowed a large numbers of ideas from XQuery, like the structure and semantics of a FLWOR construct, the functional aspect of the language, the semantics of comparisons in the face of data heterogeneity, the declarative, snapshot-based updates. However, unlike XQuery, JSON is not concerned with the peculiarities of XML, like mixed content, ordered children, the confusion between attributes and elements, the complexities of namespaces and QNames, or the complexities of XML Schema, and so on.

The power of the XQuery's FLWOR construct and the functional aspect, combined with the simplicity of the JSON data model result in a clean, sleek and easy to understand data processing language. As a matter of fact, JSONiq is a language that can do more than queries: it can describe powerful data processing programs, from transformations, selections, joins of heterogeneous data sets, data enrichment, information extraction, information cleaning, and so on.

Technically, the main characteristics of JSONiq (and XQuery) are the following:

* It is a *set-oriented language*. While most programming languages are designed to manipulate one object at a time, JSONiq is designed to process sets (actually, sequences) of data objects.
* It is a *functional language*. A JSONiq program is an expression; the result of the program is the result of the evaluation of the expression. Expressions have fundamental role in the language: every language construct is an expression, and expressions are fully composable.
* It is a *declarative language*. A program specifies what is the result being calculated, and does not specify low level algorithms like the sort algorithm, the fact that an algorithm is executed in main memory or is external, on a single machine or parallelized on several machines, or what access patterns (aka indexes) are being used during the evaluation of the program. Such implementation decisions should be taken automatically, by an optimizer, based on the physical characteristics of the data, and of the hardware environment. Just like a traditional database would do. The language has been designed from day one with optimizability in mind.
* It is designed for *nested, heterogeneous, semi-structured data*. Data structures in JSON can be nested with arbitrary depth, do not have a specific type pattern (i.e. are heterogeneous), and may or may not have one or more schemas that describe the data. Even in the case of a schema, such a schema can be open, and/or simply partially describe the data. Unlike SQL, which is designed to query tabular, flat, homogeneous structures. JSONiq has been designed from scratch as a query for nested and heterogeneous data.


# JSONiq 1.0

JSONiq 1.0 is the first version of the JSONiq language, currently in use.

It is a cousin of the XQuery 3.0 language and was developed by W3C XML Query Working Group members as a proposal of how to integrate JSON support into the language, while making it appealing to the JSON community, and making it easy for an existing XQuery engine to implement.


# Introduction

In this specification, we detail the JSONiq language in version 1.0. Historically, JSONiq was first created as an extension to XQuery. Later, a separate core syntax was created which makes it 100% tailored for JSON. It is the JSONiq core syntax that is detailed in this document.

The functionality directly inherited from XQuery is described on a higher level and we explicitly refer for more in-depth details to the [W3C specification](https://www.w3.org/TR/xquery-30).

### Structure of a JSONiq program. <a href="#structureofajsoniqprogram.d12e158" id="structureofajsoniqprogram.d12e158"></a>

A JSONiq program can either be a main module, which contains a query that can be executed, or a library module, which defines functions and variables that can be used in other modules.

A main or library module can be optionally prefixed with a JSONiq declaration with a version (currently 1.0) and an encoding.

Module

![](/files/u8V3B4SlzZiaD44Uypnc)

### Main modules <a href="#mainmodules.d12e180" id="mainmodules.d12e180"></a>

A JSONiq main module is made of two parts: an optional prolog, and an expression, which is the main query.

MainModule

![](/files/7MNZViG2n9y1ZZewKqKO)

The result of the main JSONiq program is the result of its main query.

In the prolog, it is possible to declare global variables and functions. Mostly, you will recognize a prolog declaration by the semi-colon it ends with. The main query does not contain semi-colons (at least in core JSONiq).

Global variables and functions can use and call each other arbitrarily, even if the dependency is further down in the prolog. If there a cycle, an error is thrown.

JSONiq largely follows the W3C standard regarding modules. The detailed specification is found [here](https://www.w3.org/TR/xquery-30/#id-query-prolog).

### Library modules <a href="#librarymodules.d12e213" id="librarymodules.d12e213"></a>

Library modules do not contain any main query, just global variables and functions. They can be imported by other modules.

A library module is introduced with a module declaration, followed by the prolog containing its variables and functions.

LibraryModule

![](/files/86DQIoQlR9dwf5dF45Wc)

### Feature matrix <a href="#featurematrix.d12e236" id="featurematrix.d12e236"></a>

JSONiq is 99% reliant on XQuery, a W3C standard. For everything taken over from the W3C standard, a brief, non-normative explanation is provided with a link to the corresponding part in the W3C specification.

| Feature                                  | Specification status                                                                                                                                                 |
| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| JSONiq Data Model                        |                                                                                                                                                                      |
| Atomic items                             | W3C-conformant                                                                                                                                                       |
| Structured items                         | JSONiq-specific                                                                                                                                                      |
| Function items                           | W3C-conformant                                                                                                                                                       |
| Node items (XML)                         | Omitted (optional support by some engines)                                                                                                                           |
| JSONiq Type System                       |                                                                                                                                                                      |
| Atomic types                             | W3C-conformant, but support for xs:ID, xs:IDREF, xs:IDREFS, xs:Name, xs:NCName, xs:ENTITY, xs:ENTITIES, xs:NOTATION omitted (except for engines also supporting XML) |
| js:null type                             | JSONiq-specific                                                                                                                                                      |
| js:item, js:atomic types                 | JSONiq-specific synonyms for item() and xs:anyAtomicType                                                                                                             |
| Structured types                         | JSONiq-specific                                                                                                                                                      |
| Function types                           | W3C-conformant                                                                                                                                                       |
| Empty sequence type                      | JSONiq-specific notation () for empty-sequence()                                                                                                                     |
| XML node types                           | Omitted (optional support by engines supporting XML)                                                                                                                 |
| Concepts                                 |                                                                                                                                                                      |
| Effective boolean value                  | W3C-conformant, extended with object, array and null semantics                                                                                                       |
| Atomization                              | Omitted (optional support by engines supporting XML)                                                                                                                 |
| Expressions                              |                                                                                                                                                                      |
| Numeric literals                         | W3C-conformant                                                                                                                                                       |
| String literals                          | W3C-conformant, but escape is done with \ not with &                                                                                                                 |
| Boolean and null literals                | JSONiq-specific                                                                                                                                                      |
| Variable reference                       | W3C-conformant                                                                                                                                                       |
| Parenthesized expressions                | W3C-conformant                                                                                                                                                       |
| Context item expressions                 | W3C-conformant but $$ syntax instead of .                                                                                                                            |
| Static function calls                    | W3C-conformant                                                                                                                                                       |
| Named function reference                 | W3C-conformant                                                                                                                                                       |
| Inline function expressions              | W3C-conformant                                                                                                                                                       |
| Filter expressions                       | W3C-conformant                                                                                                                                                       |
| Dynamic function calls                   | W3C-conformant                                                                                                                                                       |
| Path expressions (XML)                   | Omitted (optional support by engines supporting XML, but relative paths must start with ./)                                                                          |
| Object lookup                            | JSONiq-specific                                                                                                                                                      |
| Array lookup                             | JSONiq-specific                                                                                                                                                      |
| Array unboxing                           | JSONiq-specific                                                                                                                                                      |
| Sequence expressions                     | W3C-conformant                                                                                                                                                       |
| Arithmetic expressions                   | W3C-conformant, no atomization needed (except for engines also supporting XML)                                                                                       |
| String concatenation expressions         | W3C-conformant                                                                                                                                                       |
| Comparison expressions                   | W3C-conformant, no need to atomize or convert from untyped and untypedAtomic (except for engines also supporting XML)                                                |
| Logical expressions                      | W3C-conformant                                                                                                                                                       |
| XML constructors                         | Omitted (optional support by engines supporting XML)                                                                                                                 |
| JSON (object and array) constructors     | JSONiq-specific                                                                                                                                                      |
| FLWOR expressions                        | W3C-conformant                                                                                                                                                       |
| Unordered and ordered expressions        | W3C-conformant                                                                                                                                                       |
| Conditional expressions                  | W3C-conformant                                                                                                                                                       |
| Switch expressions                       | W3C-conformant                                                                                                                                                       |
| Quantified expressions                   | W3C-conformant                                                                                                                                                       |
| Try-catch expressions                    | W3C-conformant                                                                                                                                                       |
| Instance-of expressions                  | W3C-conformant                                                                                                                                                       |
| Typeswitch expressions                   | W3C-conformant                                                                                                                                                       |
| Cast expressions                         | W3C-conformant                                                                                                                                                       |
| Castable expressions                     | W3C-conformant                                                                                                                                                       |
| Constructor functions                    | W3C-conformant, additional constructor function for null()                                                                                                           |
| Treat expressions                        | W3C-conformant                                                                                                                                                       |
| Simple map operator                      | W3C-conformant                                                                                                                                                       |
| Validate expressions                     | Omitted (optional support by engines supporting XML)                                                                                                                 |
| Extension expressions                    | W3C-conformant                                                                                                                                                       |
| Static context                           |                                                                                                                                                                      |
| XPath 1.0 compatibility mode             | Omitted (optional support by engines supporting XML)                                                                                                                 |
| Statically known namespaces              | W3C-conformant                                                                                                                                                       |
| Default element/type namespace           | W3C-conformant, strong recommendation for implementations to overwrite with the proxy namespace *<http://jsoniq.org/default-type-namespace>* to omit prefixes.       |
| Default function namespace               | W3C-conformant, strong recommendation for implementations to overwrite with *<http://jsoniq.org/default-function-namespace>* to omit prefixes.                       |
| In-scope schema definitions              | Omitted (optional support by engines supporting XML)                                                                                                                 |
| In-scope variables                       | W3C-conformant                                                                                                                                                       |
| Context item static type                 | W3C-conformant                                                                                                                                                       |
| Statically known function signatures     | W3C-conformant, augmented with all JSONiq builtin functions                                                                                                          |
| Statically known collations              | W3C-conformant                                                                                                                                                       |
| Default collation                        | W3C-conformant                                                                                                                                                       |
| Construction mode                        | Omitted (optional support by engines supporting XML)                                                                                                                 |
| Ordering mode                            | W3C-conformant                                                                                                                                                       |
| Default order for empty sequences        | W3C-conformant                                                                                                                                                       |
| Boundary-space policy                    | Omitted (optional support by engines supporting XML)                                                                                                                 |
| Copy-namespaces mode                     | Omitted (optional support by engines supporting XML)                                                                                                                 |
| Static Base URI                          | W3C-conformant                                                                                                                                                       |
| Statically known documents               | Omitted (optional support by engines supporting XML)                                                                                                                 |
| Statically known collections             | Omitted (optional support by engines supporting XML)                                                                                                                 |
| Statically known default collection type | Omitted (optional support by engines supporting XML)                                                                                                                 |
| Statically known decimal formats         | W3C-conformant                                                                                                                                                       |
| Dynamic context                          |                                                                                                                                                                      |
| Context item                             | W3C-conformant (but with syntax $$ not .)                                                                                                                            |
| Initial context item                     | W3C-conformant                                                                                                                                                       |
| Context position                         | W3C-conformant                                                                                                                                                       |
| Context size                             | W3C-conformant                                                                                                                                                       |
| Variable values                          | W3C-conformant                                                                                                                                                       |
| Named functions                          | W3C-conformant                                                                                                                                                       |
| Current dateTime                         | W3C-conformant                                                                                                                                                       |
| Implicit timezone                        | W3C-conformant                                                                                                                                                       |
| Default language                         | W3C-conformant                                                                                                                                                       |
| Default calendar                         | W3C-conformant                                                                                                                                                       |
| Default place                            | W3C-conformant                                                                                                                                                       |
| Available documents                      | Omitted (optional support by engines supporting XML)                                                                                                                 |
| Available text resources                 | W3C-conformant                                                                                                                                                       |
| Available node collections               | Omitted (optional support by engines supporting XML)                                                                                                                 |
| Default node collection                  | Omitted (optional support by engines supporting XML)                                                                                                                 |
| Available resource collections           | Omitted (optional support by engines supporting XML)                                                                                                                 |
| Default resource collection              | Omitted (optional support by engines supporting XML)                                                                                                                 |
| Environment variables                    | W3C-conformant                                                                                                                                                       |

### Namespaces <a href="#namespaces.d12e941" id="namespaces.d12e941"></a>

The namespace *<http://jsoniq.org/functions>* is used for JSONiq builtin functions defined by this specification. This namespace is exposed to the user and is bound by default to the prefix *jn*. For instance, the function name *jn:keys()* is in this namespace.

The namespace *<http://jsoniq.org/types>* is used for JSONiq builtin types defined by this specification (including synonyms for some XQuery types). This namespace is exposed to the user and is bound by default to the prefix *js*. For instance, the type name *js:null* is in this namespace.

The namespace *<http://jsoniq.org/default-function-namespace>* is a proxy namespace that maps to the *jn:* (JSONiq), *fn:* (XQuery) and *math:* (XQuery) namespaces. It is the default function namespace, allowing to call all these functions with no prefix.

The namespace *<http://jsoniq.org/default-type-namespace>* is a proxy namespace that maps to the *js:* (JSONiq) and *xs:* (XQuery) namespaces. It is the default type namespace, allowing to use all builtin types with no prefix.

Accessors used in JSONiq Data Model use the *jdm:* prefix. These functions are not exposed to the user and are for explanatory purposes of the data model within this document only. The *jdm:* prefix is not associated with a namespace.


# The JSONiq data model

JSONiq is a query language that was specifically designed for querying JSON, although its data model is powerful enough to handle more similar formats.

As stated on json.org, JSON is a "lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate."

A JSON document is made of the following building blocks: objects, arrays, strings, numbers, booleans and nulls.

JSONiq manipulates sequences of these building blocks, which are called items. Hence, a JSONiq value is a sequence of items.

Any JSONiq expression takes and returns sequences of items.

Comma-separated JSON-like building blocks is all you need to begin building your own sequences. You can mix and match, as JSONiq supports heterogeneous sequences seamlessly.

### A sequence

```

"foo", 2, true, { "foo", "bar" }, null, [ 1, 2, 3 ]
      
```

Result:foo 2 true foo bar null \[ 1, 2, 3 ]

Sequences are flat and cannot be nested. This makes streaming possible, which is very powerful.

### Sequences are flat

```

( ("foo", 2), ( (true, 4, null), 6 ) )
      
```

Result:foo 2 true 4 null 6

A sequence can be empty. The empty sequence can be constructed with empty parentheses.

### The empty sequence

```

()
      
```

Result:

A sequence of just one item is considered the same as just this item. Whenever we say that an expression returns or takes one item, we really mean that it takes a singleton sequence of one item.

### A sequence of one item

```

("foo")
      
```

Result:foo

JSONiq classifies the items mentioned above in three categories:

* Atomic items: strings, numbers, booleans and nulls, but also many other supported atomic values such as dates, binary, etc.
* Structured items: objects and arrays.
* Function items: items that can take parameters and, upon evaluation, return sequences of items.

The JSONiq data model follows the [W3C specification](https://www.w3.org/TR/xpath-datamodel-30/#sequences), but, in core JSONiq, does not include XML nodes, and includes instead JSON objects and arrays. Engines are free, however, to optionally support XML nodes in addition to JSON objects and arrays.

### Atomic items <a href="#atomicitems.d12e1084" id="atomicitems.d12e1084"></a>

An atomic is a non-structured value that is annotated with a type.

JSONiq atomic values follow the [W3C specification](https://www.w3.org/TR/xpath-datamodel-30/#AtomicValue).

JSONiq supports most atomic values available in the [W3C specification](https://www.w3.org/TR/xpath-datamodel-30/#types-hierarchy). They are described in Chapter [The JSONiq type system](#chapter-type-system). JSONiq furthermore defines an additional atomic value, *null*, with a type of its own, *jn:null*, which does not exist in the W3C specification.

In particular, JSONiq supports all core JSON values. Note that JSON numbers correspond to three different types in JSONiq.

* *string*: all JSON strings.
* *integer*: all JSON numbers that are integers (no dot, no exponent), infinite range.
* *decimal*: all JSON numbers that are decimals (no exponent), infinite range.
* *double*: IEEE double-precision 64-bit floating point numbers (corresponds to JSON numbers with an exponent).
* *boolean*: the JSON booleans true and false.
* *null*: the JSON null.

### Structured items <a href="#structureditems.d12e1164" id="structureditems.d12e1164"></a>

Structured items in JSONiq do not follow the XQuery 3.1 standard but are specific to JSONiq.

In JSONiq, an object represents a JSON object, i.e., a collection of string/items pairs.

Objects have the following property:

* pairs. A set of pairs. Each pair consists of an atomic value of type *xs:string* and of an item.

  \[ Consistency constraint: no two pairs have the same name (using fn:codepoint-equal). ]

The XQuery data model uses accessors to explain the data model. Accessors are not exposed to the user and are only used for convenience in this specification. Objects have the following accessors:

* *jdm:object-keys($o as js:object) as xs:string\**: returns all keys in the object $o.
* *jdm:object-value($o as js:object, $s as xs:string) as js:item*: returns the value associated with $s in the object $o.

An object does not have a typed value.

In JSONiq, an array represents a JSON array, i.e., a ordered list of items.

Objects have the following property:

* members. An ordered list of items.

Arrays have the following accessors:

* *jdm:array-size($a as js:array) as xs:nonNegativeInteger*: returns the number of values in the array $a.
* *jdm:array-value($a as js:array, $i as xs:positiveInteger) as js:item*: returns the value at position $i in the array $a..

An array does not have a typed value.

Unlike in the XQuery 3.1 standard, the values in arrays and objects are single items (which disallows the empty sequence or a sequence of more than one item). Also, object keys must be strings (which disallows any other atomic value).

### Function items <a href="#section-function-items" id="section-function-items"></a>

JSONiq also supports function items, also known as higher-order functions. A function item can be passed parameters and evaluated.

A function item has an optional name and an arity. It also has a signature, which consists of the sequence type of each one of its parameters (as many as its arity), and the sequence type of the values it returns.

The fact that functions are items means that they can be returned by expressions, and passed as parameters to other functions. This is why they are also often called higher-order functions.

JSONiq function items follow the [W3C specification](https://www.w3.org/TR/xpath-datamodel-30/#function-items).


# Input datasets (examples)

Even though you can build your own JSON values with JSONiq by copying-and-pasting JSON documents, most of the time, your JSON data will come from an external input dataset.

How this dataset is access depends on the JSONiq implementation and of the context. Some engines can read the data from a file located on a file system, local or distributed (HDFS, S3); some others get data from the Web; some others are full-fledged datastores and have collections that can be created, queried, modified and persisted.

It is up to each engine to document which functions should be used, and how, in order to read datasets into a JSONiq Data Model instance. These functions will take implementation-defined parameters and typically return sequences of objects, or sequences of strings, or sequences of items, etc.

For the purpose of examples given in this specification, we assume that a hypothetical engine has collections that are sequences of objects, identified by a name which is a string. We assume that there is a collection() function that returns all objects associated with the provided collection name.

We assume in particular that there are three example collections, shown below.

### Collection 1

```

collection("one-object")
    
```

Result

```
{ "foo" : "bar" }
```

### Collection 2

```

collection("captains")
    
```

Result

```

{ "name" : "James T. Kirk", "series" : [ "The original series" ], "century" : 23 }
{ "name" : "Jean-Luc Picard", "series" : [ "The next generation" ], "century" : 24 }
{ "name" : "Benjamin Sisko", "series" : [ "The next generation", "Deep Space 9" ], "century" : 24 }
{ "name" : "Kathryn Janeway", "series" : [ "The next generation", "Voyager" ], "century" : 24  }
{ "name" : "Jonathan Archer", "series" : [ "Entreprise" ], "century" : 22 }
{ "codename" : "Emergency Command Hologram", "surname" : "The Doctor", "series" : [ "Voyager" ], "century" : 24 }
{ "name" : "Samantha Carter", "series" : [ ], "century" : 21 }
      
```

### Collection 3

```
collection("films")
```

Result

```

{ "id" : "I", "name" : "The Motion Picture", "captain" : "James T. Kirk" }
{ "id" : "II", "name" : "The Wrath of Kahn", "captain" : "James T. Kirk" }
{ "id" : "III", "name" : "The Search for Spock", "captain" : "James T. Kirk" }
{ "id" : "IV", "name" : "The Voyage Home", "captain" : "James T. Kirk" }
{ "id" : "V", "name" : "The Final Frontier", "captain" : "James T. Kirk" }
{ "id" : "VI", "name" : "The Undiscovered Country", "captain" : "James T. Kirk" }
{ "id" : "VII", "name" : "Generations", "captain" : [ "James T. Kirk", "Jean-Luc Picard" ] }
{ "id" : "VIII", "name" : "First Contact", "captain" : "Jean-Luc Picard" }
{ "id" : "IX", "name" : "Insurrection", "captain" : "Jean-Luc Picard" }
{ "id" : "X", "name" : "Nemesis", "captain" : "Jean-Luc Picard" }
{ "id" : "XI", "name" : "Star Trek", "captain" : "Spock" }
{ "id" : "XII", "name" : "Star Trek Into Darkness", "captain" : "Spock" }
      
```


# The JSONiq type system

This section describes JSONiq types as well as the sequence type syntax.

JSONiq manipulates semi-structured data: in general, JSONiq allows you, but does not require you to specify types. So you have as much or as little type verification as you wish.

JSONiq is still strongly typed, so that you will be told if there is a type inconsistency or mismatch in your programs.

Whenever you do not specify the type of a variable or the type signature of a function, the most general type for any sequence of items, item\*, is assumed.

Section [Expressions dealing with types](#section-type-expressions) introduces expressions which work with values of these types, as well as type operations (variable types, casts, ...).

### Sequence types <a href="#sequencetypes.d12e1331" id="sequencetypes.d12e1331"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-sequencetype-syntax) regarding sequence occurrence indicators. The following explanations, provided as an informal summary for convenience, are non-normative.

A sequence is an ordered list of items.

All sequences match the sequence type *js:item\**.

A sequence type is made of an item type followed by an occurrence indicator:

* The symbol \* (star) stands for a sequence of any length (zero or more)
* The symbol + (plus) stands for a non-empty sequence (one or more)
* The symbol ? (question mark) stands for an empty or a singleton sequence (zero or one)
* The absence of indicator stands for a singleton sequence (one).

Examples:

* string matches any singleton sequence containing a string.
* item+ matches any non-empty sequence.
* object? matches the empty sequence and any sequence containing one object.

JSONiq defines the syntax *()* for the empty sequence, rather than *empty-sequence()*.

SequenceType

![](/files/x6duVQ6LVSC8tsk0eW7Y)

### Item types <a href="#itemtypes.d12e1415" id="itemtypes.d12e1415"></a>

Item types are the first component of a sequence type, together with the cardinality indicator. Thus, an item type matches (or not) a single item. For example, "foo" matches the item type xs:string.

There are three categories of item types:

* Atomic types (W3C-conformant, additional js:null and js:atomic)
* Structured types (JSONiq-specific)
* Function types (W3C-conformant)

JSONiq uses a JSONiq-specific, implementation-defined default type namespace that acts as a proxy namespace to all types (xs: or js:). As a consequence, buitin atomic types do not need to be prefixed in the JSONiq syntax (*integer* instead of *xs:integer*, *null* instead of *js:null*).

All items match the item type *js:item*, which is a JSONiq-specific synonym for the W3C-confirmant *item()*.

ItemType

![](/files/jUnazbxSdpsosiTHYJCg)

#### Atomic types <a href="#atomictypes.d12e1473" id="atomictypes.d12e1473"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-types) for atomic types except for modifications in the list of available atomic types and a simplified syntax for xs:anyAtomicType. The following explanations, provided as an informal summary for convenience, are non-normative.

Atomic types are organized in a tree hierarchy.

JSONiq defines the following build-in types that have a direct relation with JSON:

* *xs:string*: the value space is all strings made of Unicode characters.

  All string literals build an atomic which matches string.
* *xs:integer* (W3C-conformant): the value space is that of all mathematical integral numbers (N), with an infinite range. This is a subtype of *decimal*, so that all integers also match the item type *decimal*.

  All integer literals build an atomic which matches integer.
* *xs:decimal* (W3C-conformant): the value space is that of all mathematical decimal numbers (D), with an infinite range.

  All decimal literals build an atomic which matches decimal.
* *xs:double* (W3C-conformant): the value space is that of all IEEE double-precision 64-bit floating point numbers.

  All double literals build an atomic which matches double.
* *xs:boolean* (W3C-conformant): the value space contains the booleans true and false.

  All boolean literals build an atomic which matches boolean.
* *js:null* (JSONiq-specific): the value space is a singleton and only contains null.

  All null literals build an atomic which matches null.
* *js:atomic* (JSONiq-specific synonym of, and W3C-conformant with, *xs:anyAtomicType*): all atomic types.

  All literals build an atomic which matches atomic.

JSONiq also supports further atomic types, which are conformant with [XML Schema](http://www.w3.org/TR/xmlschema11-2/#built-in-datatypes).

These datatypes are already used as a set of atomic datatypes by the other two semi-structured data formats of the Web: XML and RDF, as well as by the corresponding query languages: XQuery and SPARQL, so it is natural for a complete JSON data model to reuse them.

* Further number types: xs:float, xs:long, xs:int, xs:short, xs:byte, xs:float, xs:positiveInteger, xs:negativeInteger, xs:nonPositiveInteger, xs:nonNegativeInteger, xs:unsignedLong, xs:unsignedInt, xs:unsignedShort, xs:unsignedByte.
* Date or time types: xs:date, xs:dateTime, xs:dateTimeStamp, xs:gDay, xs:gMonth, xs:gMonthDay, xs:gYear, xs:xs:gYearMonth, xs:time.
* Duration types: xs:duration, xs:dayTimeDuration, xs:yearMonthDuration.
* Binary types: xs:base64Binary, xs:hexBinary.
* An URI type: xs:anyURI.

The support of xs:ID, xs:IDREF, xs:IDREFS, xs:NOTATION, xs:Name, xs:NCName, xs:NMTOKEN, xs:NMTOKENS, xs:ENTITY, xs:ENTITIES is not required by JSONiq, although engines that also support XML can support them.

AtomicType

![](/files/V7LOXIPcHotKEzlSQjZE)

#### Structured types <a href="#structuredtypes.d12e1620" id="structuredtypes.d12e1620"></a>

JSONiq introduces four more types for matching objects and arrays. Like atomic types, they do not need the *js:* prefix in the syntax (*object* instead of *js:object*, etc.).

All objects match the item type *js:object*.

All arrays match the item type *js:array*.

All objects and arrays match the item type *js:json-item*.

For engines that also support optionally XML, *js:structured-item* matches both XML nodes and JSON objects and arrays.

StructuredType

![](/files/I6zOvX2R8Ipfvolk4tgS)

#### Function types <a href="#functiontypes.d12e1668" id="functiontypes.d12e1668"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-function-test) regarding function types. The following explanations are non-normative.

FunctionType

![](/files/VZ7JAeoJNLmorxX68DuF)

AnyFunctionType

![](/files/RcaxAyFf72tnwAAHKldG)

TypedFunctionType

![](/files/LYDDS0vstFlDvdtBoB6N)


# Expressions

### Construction of items <a href="#chapter-construction" id="chapter-construction"></a>

In JSONiq, objects, arrays and basic atomic values (string, number, boolean, null) are constructed exactly as they are constructed in JSON. Any JSON document is also a valid JSONiq query which just "returns itself".

Because JSONiq expressions are fully composable, however, in objects and arrays constructors, it is possible to put any JSONiq expression and not only atomic literals, object constructors and array constructors. Furthermore, JSONiq supports the construction of other W3C-standardized builtin types (date, hexBinary, etc).

The following examples are a few of many operators available in JSONiq: "to" for creating arithmetic sequences, "||" for concatenating strings, "+" for adding numbers, "," for appending sequences.

In an array, the operand expression will evaluated to a sequence of items, and these items will be copied and become members of the newly created array.

#### Composable array constructors

```

  [ 1 to 10 ]
    
```

Result:\[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

In an object, the expression you use for the key must evaluate to an atomic - if it is not a string, it will just be cast to it.

#### Composable object keys

```

  { "foo" || "bar" : true }
      
```

Result:{ "foobar" : true }

An error is raised if the key expressions is not an atomic.

#### Non-atomic object keys

```

  { [ 1, 2 ] : true }
      
```

Result:An error was raised: can not atomize an array item: an array has probably been passed where an atomic value is expected (e.g., as a key, or to a function expecting an atomic item)

If the value expression is empty, null will be used as a value, and if it contains two items or more, they will be wrapped into an array.

If the colon is preceded with a question mark, then the pair will be omitted if the value expression evaluates to the empty sequence.

#### Composable object values

```

  { "foo" : 1 + 1 }
      
```

Result:{ "foo" : 2 }

#### Composable object values and automatic conversion

```

  { "foo" : (), "bar" : (1, 2) }
      
```

Result:{ "foo" : null, "bar" : \[ 1, 2 ] }

#### Optional pair (not implemented yet in Zorba)

```

  { "foo" ?: (), "bar" : (1, 2) }
      
```

Result:An error was raised: invalid expression: syntax error, unexpected "?", expecting "end of file" or "," or "}"

The {| |} syntax can be used to merge several objects.

#### Merging object constructor

```

  {| { "foo" : "bar" }, { "bar" : "foo" } |}
      
```

Result:{ "foo" : "bar", "bar" : "foo" }

An error is raised if the operand expression does not evaluate to a sequence of objects.

#### Merging object constructor with a type error

```

  {| 1 |}
      
```

Result:An error was raised: xs:integer can not be treated as type object()\*

#### Numbers <a href="#numbers.d12e1832" id="numbers.d12e1832"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-literals) for constructing numbers. The following explanations, provided as an informal summary for convenience, are non-normative.

Literal

![](/files/UQDAD2NbnuWDaaDlHSVw)

NumericLiteral

![](/files/xx2CMQ2Tw8qRUir5bN5L)

IntegerLiteral

![](/files/1vmDCNPkaej79uDfxArx)

DecimalLiteral

![](/files/iipECUVEBL89Q2rw4Bdg)

DoubleLiteral

![](/files/Unjn1817S3wgQljjPszW)

The syntax for creating numbers is identical to that of JSON (it is actually a more flexible superset, for example leading 0s are allowed, and a decimal literal can begin with a dot). Note that JSONiq distinguishes between integers (no dot, no scientific notation), decimals (dot but no scientific notation) and doubles (scientific notation). As expected, an integer literal creates an atomic of type integer, and so on.

**Integer literals**

```

42
      
```

Result:42

**Decimal literals**

```

3.14
      
```

Result:3.14

**Double literals**

```

+6.022E23
      
```

Result:6.022E23

#### Strings <a href="#strings.d12e1941" id="strings.d12e1941"></a>

The syntax for creating string items is conformant to [JSON](https://www.ecma-international.org/publications/standards/Ecma-404.htm) rather than to the W3C standard for string literals. This means concretely that escaping is done with backslashes and not with ampersands. Also, like in JSON, double quotes are required and single quotes are forbidden.

StringLiteral

![](/files/nOGhdqXobabtdUtllpxh)

**String literals**

```

  "foo"
        
```

Result:foo

**String literals with escaping**

```

  "This is a line\nand this is a new line"
        
```

Result:This is a line and this is a new line

**String literals with Unicode character escaping**

```

  "\u0001"
        
```

Result:\&#x1;

**String literals with a nested quote**

```

  "This is a nested \"quote\""
        
```

Result:This is a nested "quote"

#### Booleans and null <a href="#booleansandnull.d12e2006" id="booleansandnull.d12e2006"></a>

JSONiq also introduces three more literals for constructing booleans and nulls: true, false and null. This makes in particular the functions true() and false() superfluous.

BooleanLiteral

![](/files/8R0lrjVXKuTvnW0SYC8g)

NullLiteral

![](/files/hJMTIydT3CI7OslzSztD)

**Boolean literals (true)**

```

true
      
```

Result:true

**Boolean literals (false)**

```

false
      
```

Result:false

**Null literals**

```

null
      
```

Result:null

#### Other atomic values <a href="#otheratomicvalues.d12e2070" id="otheratomicvalues.d12e2070"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-constructor-functions) for constructing most atomic values with constructors. In JSONiq, the *xs* prefix is optional.

#### Objects <a href="#objects.d12e2084" id="objects.d12e2084"></a>

Expressions constructing objects are JSONiq-specific and introduced in this specification.

ObjectConstructor

![](/files/82r8JkghswXElQIVhTWI)

PairConstructor

![](/files/5xT8hwAabONVJfgqnBxo)

The syntax for creating objects is identical to that of JSON. You can use for an object key any string literal, and for an object value any literal, object constructor or array constructor.

**Empty object constructors**

```

{}
      
```

Result:{ }

**Object constructors 1**

```

{ "foo" : "bar" }
      
```

Result:{ "foo" : "bar" }

**Object constructors 2**

```

{ "foo" : [ 1, 2, 3, 4, 5, 6 ] }
      
```

Result:{ "foo" : \[ 1, 2, 3, 4, 5, 6 ] }

**Object constructors 3**

```

{ "foo" : true, "bar" : false }
      
```

Result:{ "foo" : true, "bar" : false }

**Nested object constructors**

```

{ "this is a key" : { "value" : "a value" } }
      
```

Result:{ "this is a key" : { "value" : "a value" } }

As in JavaScript, if your key is simple enough (like alphanumerics, underscores, dashes, this kind of things), the quotes can be omitted. The strings for which quotes are not mandatory are called NCNames. This class of strings can be used for unquoted keys, for variable and function names, and for module aliases.

**Object constructors with unquoted key 1**

```

{ foo : "bar" }
      
```

Result:{ "foo" : "bar" }

**Object constructors with unquoted key 2**

```

{ foo : [ 1, 2, 3, 4, 5, 6 ] }
      
```

Result:{ "foo" : \[ 1, 2, 3, 4, 5, 6 ] }

**Object constructors with unquoted key 3**

```

{ foo : "bar", bar : "foo" }
      
```

Result:{ "foo" : "bar", "bar" : "foo" }

**Object constructors with needed quotes around the key**

```

{ "but you need the quotes here" : null }
    
```

Result:{ "but you need the quotes here" : null }

Objects can be constructed more dynamically (e.g., dynamic keys) by constructing and merging smaller objects. Duplicate key names throw an error.

**Object constructors with needed quotes around the key**

```

{|
  for $i in 1 to 3
  return { "foo" || $i : $i }
|}
    
```

Result:{ "foo1" : 1, "foo2" : 2, "foo3" : 3 }

#### Arrays <a href="#arrays.d12e2225" id="arrays.d12e2225"></a>

Expressions constructing arrays are JSONiq-specific and introduced in this specification.

ArrayConstructor

![](/files/8O4L1gu7lgoyBxIi4VzQ)

Expr

![](/files/PUUYtigDOvBkkVbR3bI2)

The syntax for creating arrays is identical to that of JSON: square brackets, comma separated literals, object constructors and arrays constructors.

**Empty array constructors**

```

[]
      
```

Result:\[ ]

**Array constructors**

```

[ 1, 2, 3, 4, 5, 6 ]
      
```

Result:\[ 1, 2, 3, 4, 5, 6 ]

**Nested array constructors**

```

[ "foo", 3.14, [ "Go", "Boldly", "When", "No", "Man", "Has", "Gone", "Before" ], { "foo" : "bar" }, true, false, null ]
      
```

Result:\[ "foo", 3.14, \[ "Go", "Boldly", "When", "No", "Man", "Has", "Gone", "Before" ], { "foo" : "bar" }, true, false, null ]

Square brackets are mandatory. Do not push it.

#### Functions <a href="#functions.d12e2293" id="functions.d12e2293"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-inline-func) for constructing function items with inline expressions or [named function references](https://www.w3.org/TR/xquery-30/#id-named-function-ref). The following explanations, provided as an informal summary for convenience, are non-normative.

Function items can be constructed in two ways: by definining its body directly (inline function expression), or by referring by name to a function declared in a prolog.

FunctionItemExpr

![](/files/fIQcoTYwjpecGWAaH2tw)

**Inline function expression**

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-inline-func) for constructing function items with inline expressions. The following explanations, provided as an informal summary for convenience, are non-normative.

A function can be built directly by specifying its parameters and its body as expression. Types are optional and by default, assumed to be item\*.

Function items can also be produced with a partial function application.

**Inline function expression**

```

           function ($x as integer, $y as integer) as integer { $x + 2 },
           function ($x) { $x + 2 }
       
```

Result(two function items)

InlineFunctionExpr

![](/files/ISp8enee6q1NiEC0oLAT)

ParamList

![](/files/fy2YJxWKTJzUaaXha4Uo)

**Named function reference**

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-named-function-ref) for constructing function items with named function references. The following explanations, provided as an informal summary for convenience, are non-normative.

If a function is builtin or declared in a prolog, in the same module or imported, then it is also possible to build a function item by referring to its name and arity.

**Named function reference**

```

           declare function local:sum($x as integer, $y as integer) as integer
           {
             $x + 2
           };
           local:sum#2
       
```

Result(a function items)

NamedFunctionRef

![](/files/JdCr8PAbPmx9qOdBEadw)

### Manipulating atomic values <a href="#chapter-basic-operations" id="chapter-basic-operations"></a>

We now introduce the expressions that manipulate atomic values: arithmetics, logics, comparison, string concatenation.

#### Arithmetics <a href="#arithmetics.d12e2420" id="arithmetics.d12e2420"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-arithmetic) for arithmetic expressions, and naturally extends to return errors for null values. The following explanations, provided as an informal summary for convenience, are non-normative.

JSONiq supports the basic four operations, integer division and modulo.

Multiplicative operations have precedence over additive operations. Parentheses can override it.

**Basic arithmetic operations with precedence override**

```

1 * ( 2 + 3 ) + 7 idiv 2 - (-8) mod 2
      
```

Result (run with Zorba):8

Dates, times and durations are also supported in a natural way.

**Using basic operations with dates.**

```

date("2013-05-01") - date("2013-04-02")
      
```

Result (run with Zorba):P29D

If any of the operands is a sequence of more than one item, an error is raised.

**Sequence of more than one number in an addition**

```

(1, 2) + 3
      
```

Result (run with Zorba):An error was raised: sequence of more than one item can not be promoted to parameter type xs:anyAtomicType? of function add()

If any of the operands is not a number, a date, a time or a duration, an error is raised, which seamlessly includes raising errors for null with no need to extend the specification.

**Null in an addition**

```

1 + null
      
```

Result (run with Zorba):An error was raised: arithmetic operation not defined between types "xs:integer" and "js:null"

If one of the operands evaluates to the empty sequence, then the operation results in the empty sequence.

If the two operands do not have the same number type, JSONiq will do the adequate conversions.

**Basic arithmetic operations with an empty sequence**

```

() + 2
      
```

Result (run with Zorba):

AdditiveExpr

![](/files/K7N8mxjcJydqXN7IqBjW)

MultiplicativeExpr

![](/files/D5DVQ37Z7FgYJbWCLA01)

UnaryExpr

![](/files/XbcxGH7iDrE5YsU1zM4E)

#### String concatenation <a href="#stringconcatenation.d12e2536" id="stringconcatenation.d12e2536"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-string-concat-expr) for string concatenation. The following explanations, provided as an informal summary for convenience, are non-normative.

Two strings or more can be concatenated using the concatenation operator.

**String concatenation**

```

"Captain" || " " || "Kirk"
      
```

Result (run with Zorba):Captain Kirk

An empty sequence is treated like an empty string.

**String concatenation with the empty sequence**

```

"Captain" || () || "Kirk"
      
```

Result (run with Zorba):CaptainKirk

StringConcatExpr

![](/files/eX0iQt5sRiTqOTVCH9Pn)

#### Comparison <a href="#comparison.d12e2585" id="comparison.d12e2585"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-comparisons) for comparison, and only extends its semantics to null values as follows.

null can be compared for equality or inequality to anything - it is only equal to itself so that false is returned when comparing if for equality with any non-null atomic. True is returned when comparing it with non-equality with any non-null atomic.

**Equality and non-equality comparison with null**

```

1 eq null, "foo" ne null, null eq null
      
```

Result (run with Zorba):false true true

For ordering operators (lt, le, gt, ge), null is considered the smallest possible value (like in JavaScript).

**Ordering comparison with null**

```

1 lt null
      
```

Result (run with Zorba):false

The following explanations, provided as an informal summary for convenience, are non-normative.

ComparisonExpr

![](/files/Cyju4KjtGhEnA2whamUY)

Atomics can be compared with the usual six comparison operators (equality, non-equality, lower-than, greater-than, lower-or-equal, greater-or-equal), and with the same two-letter symbols as in MongoDB.

**Equality comparison**

```

1 + 1 eq 2, 1 lt 2
      
```

Result (run with Zorba):true true

Comparison is only possible between two compatible types, otherwise, an error is raised.

**Comparisons with a type mismatch**

```

"foo" eq 1
      
```

Result (run with Zorba):An error was raised: "xs:string": invalid type: can not compare for equality to type "xs:integer"

Like for arithmetic operations, if an operand is the empty sequence, the empty sequence is returned as well.

**Comparison with the empty sequence**

```

() eq 1
      
```

Result (run with Zorba):

Comparisons and logic operators are fundamental for a query language and for the implementation of a query processor as they impact query optimization greatly. The current comparison semantics for them is carefully chosen to have the right characteristics as to enable optimization.

#### Logics <a href="#logics.d12e2678" id="logics.d12e2678"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-logical-expressions) for logical expressions; it introduces a prefix unary not operator as a synonym for fn:not, and extends the semantics of effective boolean values to objects, arrays and nulls. The following explanations, provided as an informal summary for convenience, are non-normative.

OrExpr

![](/files/hyCsLZz6OyMAUSWXGuTv)

AndExpr

![](/files/HacuKYDJ3l5E3JQbjnXS)

NotExpr

![](/files/RBN3EstSPQt75V7l1LE3)

JSONiq logics support is based on two-valued logics: just true and false.

Non-boolean operands get automatically converted to either true or false, or an error is raised. The boolean() function performs a manual conversion.

* An empty sequence is converted to false.
* A singleton sequence of one null is converted to false.
* A singleton sequence of one string is converted to true except the empty string which is converted to false.
* A singleton sequence of one number is converted to true except zero or NaN which are converted to false.
* An operand singleton sequence whose first item is an object or array is converted to true.
* Other operand sequences cannot be converted and an error is raised.

JSONiq supports the most famous three boolean operations: conjunction, disjunction and negation. Negation has the highest precedence, then conjunction, then disjunction. Parentheses can override.

**Logics with booleans**

```

true and ( true or not true )
      
```

Result (run with Zorba):true

**Logics with comparing operands**

```

1 + 1 eq 2 or 1 + 1 eq 3
      
```

Result (run with Zorba):true

**Conversion of the empty sequence to false**

```

boolean(())
      
```

Result (run with Zorba):false

**Conversion of null to false**

```

boolean(null)
      
```

Result (run with Zorba):false

**Conversion of a string to true**

```

boolean("foo"), boolean("")
      
```

Result (run with Zorba):true false

**Conversion of a number to false**

```

0 and true, not (not 1e42)
      
```

Result (run with Zorba):false true

**Conversion of an object to a boolean (not implemented in Zorba at this point)**

```

{ "foo" : "bar" } or false
      
```

Result (run with Zorba):true

If the input sequence has more than one item, and the first item is not an object or array, an error is raised.

**Error upon conversion of a sequence of more than one item, not beginning with a JSON item, to a boolean**

```

( 1, 2, 3 ) or false
      
```

Result (run with Zorba):An error was raised: invalid argument type for function fn:boolean(): effective boolean value not defined for sequence of more than one item that starts with "xs:integer"

Unlike in C++ or Java, you cannot rely on the order of evaluation of the operands of a boolean operation. The following query may return true or may return an error.

**Non-determinism in presence of errors.**

```

true or (1 div 0)
      
```

Result (run with Zorba):true

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-quantified-expressions) for quantified expressions. The following explanations, provided as an informal summary for convenience, are non-normative.

QuantifiedExpr

![](/files/rCTdrS68T8XEYMMagKUj)

It is possible to perform a conjunction or a disjunction on a predicate for each item in a sequence.

**Universal quantifier**

```

every $i in 1 to 10 satisfies $i gt 0
      
```

Result (run with Zorba):true

**Existential quantifier on several variables**

```

some $i in -5 to 5, $j in 1 to 10 satisfies $i eq $j
      
```

Result (run with Zorba):true

Variables can be annotated with a type. If no type is specified, item\* is assumed. If the type does not match, an error is raised.

**Existential quantifier with type checking**

```

some $i as integer in -5 to 5, $j as integer in 1 to 10 satisfies $i eq $j
      
```

Result (run with Zorba):true

### Manipulating sequences <a href="#chapter-manipulating-sequences" id="chapter-manipulating-sequences"></a>

JSONiq can create sequences with concatenation (comma) or with a range. Parentheses can be used for overriding precedence.

#### Comma operator <a href="#commaoperator.d12e2926" id="commaoperator.d12e2926"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#construct_seq) for the concatenation of sequences with commas. The following explanations, provided as an informal summary for convenience, are non-normative.

Expr

![](/files/PUUYtigDOvBkkVbR3bI2)

Use a comma to concatenate two sequences, or even single items. This operator has the lowest precedence of all.

**Comma**

```

1, 2, 3, 4, 5, 6, 7, 8, 9, 10
  
```

Result (run with Zorba):1 2 3 4 5 6 7 8 9 10

**Comma**

```

{ "foo" : "bar" }, [ 1 ]
  
```

Result (run with Zorba):{ "foo" : "bar" } \[ 1 ]

Sequences do not nest. You need to use arrays in order to nest.

#### Range operator <a href="#rangeoperator.d12e2983" id="rangeoperator.d12e2983"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#construct_seq) for range expressions. The following explanations, provided as an informal summary for convenience, are non-normative.

RangeExpr

![](/files/JTQWW39ZE4DyjrLHTQvO)

With the binary operator "to", you can generate larger sequences with just two integer operands.

**Range operator**

```

1 to 10
  
```

Result (run with Zorba):1 2 3 4 5 6 7 8 9 10

If one operand evaluates to the empty sequence, then the range operator returns the empty sequence.

**Range operator with the empty sequence**

```

() to 10, 1 to ()
  
```

Result (run with Zorba):

Otherwise, if an operand evaluates to something else than a single integer or an empty sequence, an error is raised.

**Range operator with a type inconsistency**

```

(1, 2) to 10
  
```

Result (run with Zorba):An error was raised: sequence of more than one item can not be promoted to parameter type xs:integer? of function to()

#### Parenthesized expression <a href="#parenthesizedexpression.d12e3056" id="parenthesizedexpression.d12e3056"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-paren-expressions) for parenthesized expressions. The following explanations, provided as an informal summary for convenience, are non-normative.

ParenthesizedExpr

![](/files/MVZqjfSxNt8Qm2NP1hPO)

Use parentheses to override the precedence of expressions.

If the parentheses are empty, the empty sequence is produced.

**Empty sequence**

```

()
      
```

Result (run with Zorba):

### Calling functions <a href="#chapter-function-calls" id="chapter-function-calls"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-eval-function-call) for function calls. The following explanations, provided as an informal summary for convenience, are non-normative.

Function calls in JSONiq can either be made statically, with a named function, or dynamically, by passing a function item on the fly.

The syntax for function calls is similar to many other languages. JSONiq supports four sorts of functions:

* Builtin functions: these have no prefix and can be called without any import.
* Local functions: they are defined in the prolog, to be used in the main query. They have the prefix *local:*. Chapter [Prologs](#chapter-prolog) describes how to define your own local functions.
* Imported functions: they are defined in a library module. They have the prefix corresponding to the alias to which the imported module has been bound to. Chapter [Modules](#chapter-modules) describes how to define your own modules.
* Anonymous functions: they are defined on the fly, by inline function expressions or partial evaluation.

The first three are named functions and can be called statictically. All four can be called dynamically, as a named function can be also passed as an item with a named function reference.

#### Static function calls <a href="#staticfunctioncalls.d12e3148" id="staticfunctioncalls.d12e3148"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-function-calls) for static function calls. The following explanations, provided as an informal summary for convenience, are non-normative.

A static function call consists of the name of the function and of expressions returning its parameters. An error is thrown if no function with the corresponding name and arity is found.

**A builtin function call.**

```

       keys({ "foo" : "bar", "bar" : "foo" })
     
```

Result:foo bar

**A builtin function call.**

```

       concat("foo", "bar")
     
```

Result:foobar

An error is raised if the actual types do not match the expected types.

**A type error in a function call.**

```

       sum({ "foo" : "bar" })
     
```

Result:An error was raised: can not atomize an object item: an object has probably been passed where an atomic value is expected (e.g., as a key, or to a function expecting an atomic item)

JSONiq static function calls follow the [W3C specification](https://www.w3.org/TR/xquery-30/#id-function-calls).

FunctionCall

![](/files/WSat2H0CBlI0FDdnbsqG)

#### Dynamic function calls <a href="#dynamicfunctioncalls.d12e3213" id="dynamicfunctioncalls.d12e3213"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-dynamic-function-invocation) for dynamic function calls. The following explanations, provided as an informal summary for convenience, are non-normative.

A dynamic function call is a postfix expression. Its left-hand-side is an expression that must return a single function item (see in the data model [Function items](#section-function-items)). Its right-hand side is a list of parameters, each one of which is an arbitrary expression providing a sequence of items, one such sequence for each parameter.

**A dynamic function call.**

```

       let $f := function($x) { $x + 1 }
       return $f(2)
     
```

Result:3

If the number of parameters does not match the arity of the function, an error is raised. An error is also raised if an argument value does not match the corresponding type in the function signature.

Otherwise, the function is evaluated with the supplied parameters. If the result matches the return type of the function, it is returned, otherwise an error is raised.

**A dynamic function call with signature**

```

       let $f := function($x as integer) as integer { $x + 1 }
       return $f(2)
     
```

Result:3

JSONiq dynamic function calls follow the [W3C specification](https://www.w3.org/TR/xquery-30/#id-dynamic-function-invocation).

PostfixExpr

![](/files/gwH7HGgEqAB87zCGRLSx)

ArgumentList

![](/files/x0ya9RvcaKHieC0b2kTc)

Argument

![](/files/YRniLeNuW3Re1VygRgoD)

#### Partial application <a href="#partialapplication.d12e3301" id="partialapplication.d12e3301"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#dt-partial-function-application) for partial application. The following explanations, provided as an informal summary for convenience, are non-normative.

A static or dynamic function call also have placeholder parameters, represented with a question mark in the syntax. When this is the case, the function call returns a function item that is the partial application of the original function, and its arity is the number of remaining placeholders.

**A partial application.**

```

       let $f := function($x as integer, $y as integer) as integer { $x + $y }
       let $g := $f(?, 2)
       return $g(2)
     
```

Result:4

JSONiq dynamic function calls follow the [W3C specification](https://www.w3.org/TR/xquery-30/#dt-partial-function-application).

### Navigating objects <a href="#chapter-selectors" id="chapter-selectors"></a>

Like in JavaScript, it is possible to navigate through objects and arrays. This is a specific JSONiq extension.

JSONiq also allows to filter sequences with a predicate and predicates are fully W3C-conformant.

JSONiq supports filtering items from a sequence, looking up the value associated with a given key in an object, looking up the item at a given position in an array, and looking up all items in an array.

PostfixExpr

![](/files/gwH7HGgEqAB87zCGRLSx)

#### Object field selector <a href="#objectfieldselector.d12e3356" id="objectfieldselector.d12e3356"></a>

ObjectLookup

![](/files/1OfX2BnoOn5ASyMqjZIc)

The simplest way to navigate in an object is similar to JavaScript, using a dot. This will work as soon as you do not push it too much: alphanumerical characters, dashes, underscores - just like unquoted keys in object constructors, any NCName is allowed.

**Object lookup**

```

{ "foo" : "bar" }.foo
      
```

Result (run with Zorba):bar

Since JSONiq expressions are composable, you can also use any expression for the left-hand side. You might need parentheses depending on the precedence.

**Lookup on a single-object collection.**

```

collection("one-object").foo
      
```

Result (run with Zorba):bar

The dot operator does an implicit mapping on the left-hand-side, i.e., it applies the lookup in turn on each item. Lookup on an object returns the value associated with the supplied key, or the empty sequence if there is none. Lookup on any item which is not an object (arrays and atomics) results in the empty sequence.

**Object lookup with an iteration on several objects**

```

({ "foo" : "bar" }, { "foo" : "bar2" }, { "bar" : "foo" }).foo
        
```

Result (run with Zorba):bar bar2

**Object lookup with an iteration on a collection**

```

collection("captains").name
      
```

Result (run with Zorba):James T. Kirk Jean-Luc Picard Benjamin Sisko Kathryn Janeway Jonathan Archer Samantha Carter

**Object lookup on a mixed sequence**

```

({ "foo" : "bar1" }, [ "foo", "bar" ], { "foo" : "bar2" }, "foo").foo
      
```

Result (run with Zorba):bar1 bar2

Of course, unquoted keys will not work for strings that are not NCNames, e.g., if the field contains a dot or begins with a digit. Then you will need quotes.

**Quotes for object lookup**

```

{ "foo bar" : "bar" }."foo bar"
      
```

Result (run with Zorba):bar

If you use an expression on the right side of the dot, it must always have parentheses. The result of the right-hand-side expression is cast to a string. An error is raised if the cast fails.

**Object lookup with a nested expression**

```

{ "foobar" : "bar" }.("foo" || "bar")
      
```

Result (run with Zorba):bar

**Object lookup with a nested expression**

```

{ "foobar" : "bar" }.("foo", "bar")
      
```

Result (run with Zorba):An error was raised: sequence of more than one item can not be treated as type xs:string

**Object lookup with a nested expression**

```

{ "1" : "bar" }.(1)
      
```

Result (run with Zorba):bar

Variables, or a context item reference, do not need parentheses. Variables are introduced later, but here is a sneak peek:

**Object lookup with a variable**

```

let $field := "foo" || "bar"
return { "foobar" : "bar" }.$field
      
```

Result (run with Zorba):bar

#### Array member selector <a href="#arraymemberselector.d12e3489" id="arraymemberselector.d12e3489"></a>

ArrayLookup

![](/files/9Zdru0TbWyiEblxAL9XC)

Array lookup uses double square brackets.

**Array lookup**

```

[ "foo", "bar" ] [[2]]
      
```

Result (run with Zorba):bar

Since JSONiq expressions are composable, you can also use any expression for the left-hand side. You might need parentheses depending on the precedence.

**Array lookup after an object lookup**

```

{ field : [ "one",  { "foo" : "bar" } ] }.field[[2]].foo
      
```

Result (run with Zorba):bar

The array lookup operator does an implicit mapping on the left-hand-side, i.e., it applies the lookup in turn on each item. Lookup on an array returns the item at that position in the array, or the empty sequence if there is none (position larger than size or smaller than 1). Lookup on any item which is not an array (objects and atomics) results in the empty sequence.

**Array lookup with an iteration on several arrays**

```

([ 1, 2, 3 ], [ 4, 5, 6 ])[[2]]
        
```

Result (run with Zorba):2 5

**Array lookup with an iteration on a collection**

```

collection("captains").series[[1]]
      
```

Result (run with Zorba):The original series The next generation The next generation The next generation Entreprise Voyager

**Array lookup on a mixed sequence**

```

([ 1, 2, 3 ], [ 4, 5, 6 ], { "foo" : "bar" }, true)[[3]]
      
```

Result (run with Zorba):3 6

The expression inside the double-square brackets may be any expression. The result of evaluating this expression is cast to an integer. An error is raised if the cast fails.

**Array lookup with a right-hand-side expression**

```

[ "foo", "bar" ] [[ 1 + 1 ]]
      
```

Result (run with Zorba):bar

ArrayUnboxing

![](/files/vtsZp6wwYRMnzUWl9s0U)

You can also extract all items from an array (i.e., as a sequence) with the \[] syntax. The \[] operator also implicitly iterates on the left-hand-side, returning the empty sequence for non-arrays.

**Extracting all items from an array**

```

[ "foo", "bar" ][]
      
```

Result (run with Zorba):foo bar

**Extracting all items from arrays in a mixed sequence**

```

([ "foo", "bar" ], { "foo" : "bar" }, true, [ 1, 2, 3 ] )[]
      
```

Result (run with Zorba):foo bar 1 2 3

#### Sequence predicates <a href="#sequencepredicates.d12e3612" id="sequencepredicates.d12e3612"></a>

Predicate

![](/files/nof5h8nONYCsgkxEo90E)

A predicate allows filtering a sequence, keeping only items that fulfill it.

The predicate is evaluated once for each item in the left-hand-side sequence, with the context item set to that item. The predicate expression can use $$ to access this context item.

ContextItemExpr

![](/files/Qchurp7StzeRYVw41Aui)

If the predicate evaluates to an integer, it is matched against the item position in the left-hand side sequence automatically

**Predicate expression**

```

(1 to 10)[2]
      
```

Result (run with Zorba):2

Otherwise, the result of the predicate is converted to a boolean.

All items for which the converted predicate result evaluates to true are then output.

**Predicate expression**

```

(1 to 10)[$$ mod 2 eq 0]
      
```

Result (run with Zorba):2 4 6 8 10

### Control flow expressions <a href="#chapter-control-flow" id="chapter-control-flow"></a>

JSONiq supports control flow expressions such as if-then-else, switch and typeswitch following the W3C standard.

#### Conditional expressions <a href="#conditionalexpressions.d12e3681" id="conditionalexpressions.d12e3681"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-conditionals) for conditional expressions. The following explanations, provided as an informal summary for convenience, are non-normative.

IfExpr

![](/files/ZfSZFDAtsmKkj7X2cKyd)

A conditional expressions allows you to pick one or another value depending on a boolean value.

**A conditional expression**

```

if (1 + 1 eq 2) then { "foo" : "yes" } else { "foo" : "false" }
      
```

Result (run with Zorba):{ "foo" : "yes" }

The behavior of the expression inside the if is similar to that of logical operations (two-valued logics), meaning that non-boolean values get converted to a boolean.

**A conditional expression**

```

if (null) then { "foo" : "yes" } else { "foo" : "no" }
      
```

Result (run with Zorba):{ "foo" : "no" }

**A conditional expression**

```

if (1) then { "foo" : "yes" } else { "foo" : "no" }
      
```

Result (run with Zorba):{ "foo" : "yes" }

**A conditional expression**

```

if (0) then { "foo" : "yes" } else { "foo" : "no" }
        
```

Result (run with Zorba):{ "foo" : "no" }

**A conditional expression**

```

if ("foo") then { "foo" : "yes" } else { "foo" : "no" }
      
```

Result (run with Zorba):{ "foo" : "yes" }

**A conditional expression**

```

if ("") then { "foo" : "yes" } else { "foo" : "no" }
        
```

Result (run with Zorba):{ "foo" : "no" }

**A conditional expression**

```

if (()) then { "foo" : "yes" } else { "foo" : "no" }
        
```

Result (run with Zorba):{ "foo" : "no" }

**A conditional expression**

```

if (({ "foo" : "bar" }, [ 1, 2, 3, 4])) then { "foo" : "yes" } else { "foo" : "no" }
        
```

Result (run with Zorba):{ "foo" : "yes" }

Note that the else clause is mandatory (but can be the empty sequence)

**A conditional expression**

```

if (1+1 eq 2) then { "foo" : "yes" } else ()
        
```

Result (run with Zorba):{ "foo" : "yes" }

#### Switch expressions <a href="#switchexpressions.d12e3839" id="switchexpressions.d12e3839"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-switch) for switch expressions. The following explanations, provided as an informal summary for convenience, are non-normative.

SwitchExpr

![](/files/1L1uKC4FEtskeGhHIXH8)

SwitchCaseClause

![](/files/1aTh9yTHK5GcSLEY3guK)

A switch expression evaluates the expression inside the switch. If it is an atomic, it compares it in turn to the provided atomic values (with the semantics of the eq operator) and returns the value associated with the first matching case clause.

Note that if there is an object or array in the base switch expression or any case expression, a JSONiq-specific type error JNTY0004 will be raised, because objects and arrays cannot be atomized and the W3C standard requires atomization of the base and case expressions.

**A switch expression**

```

switch ("foo")
case "bar" return "foo"
case "foo" return "bar"
default return "none"
        
```

Result (run with Zorba):bar

If it is not an atomic, an error is raised.

**A switch expression**

```

switch ({ "foo" : "bar" })
case "bar" return "foo"
case "foo" return "bar"
default return "none"
        
```

Result (run with Zorba):An error was raised: can not atomize an object item: an object has probably been passed where an atomic value is expected (e.g., as a key, or to a function expecting an atomic item)

If no value matches, the default is used.

**A switch expression**

```

switch ("no-match")
case "bar" return "foo"
case "foo" return "bar"
default return "none"
        
```

Result (run with Zorba):none

The case clauses support composability of expressions as well.

**A switch expression**

```

switch (2)
case 1 + 1 return "foo"
case 2 + 2 return "bar"
default return "none"
        
```

Result (run with Zorba):foo

**A switch expression**

```

switch (true)
case 1 + 1 eq 2 return "1 + 1 is 2"
case 2 + 2 eq 5 return "2 + 2 is 5"
default return "none of the above is true"
        
```

Result (run with Zorba):1 + 1 is 2

#### Try-catch expressions <a href="#trycatchexpressions.d12e3958" id="trycatchexpressions.d12e3958"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-try-catch) for try-catch expressions. The following explanations, provided as an informal summary for convenience, are non-normative.

TryCatchExpr

![](/files/UsBXyYWQt9hpQVFJ2H4J)

A try catch expression evaluates the expression inside the try block and returns its resulting value.

However, if an error is raised dynamically, the catch clause is evaluated and its result value returned.

**A try catch expression**

```

try { 1 div 0 } catch * { "division by zero!" } 
      
```

Result (run with Zorba):division by zero!

Only errors raised within the lexical scope of the try block are caught.

**A try catch expression**

```

let $x := 1 div 0
return try { $x }
       catch * { "division by zero!" } 
      
```

Result (run with Zorba):An error was raised: division by zero

Errors that are detected statically within the try block are still reported statically.

**A try catch expression**

```

try { x } catch * { "syntax error" } 
      
```

Result (run with Zorba):syntax error

### FLWOR expressions <a href="#chapter-flwor" id="chapter-flwor"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-flwor-expressions) for FLWOR expressions. The following explanations, provided as an informal summary for convenience, are non-normative.

FLWORExpr

![](/files/FQpE8EDUqBmrx01UWNzq)

FLWOR expressions are probably the most powerful JSONiq construct and correspond to SQL's SELECT-FROM-WHERE statements, but they are more general and more flexible. In particular, clauses can almost appear in any order (apart that it must begin with a for or let clause, and end with a return clause).

Here is a bit of theory on how it works.

A clause binds values to some variables according to its own semantics, possibly several times. Each time, a tuple of variable bindings (mapping variable names to sequences) is passed on to the next clause.

This goes all the way down, until the return clause. The return clause is eventually evaluated for each tuple of variable bindings, resulting in a sequence of items for each tuple.

These sequences of items are concatenated, in the order of the incoming tuples, and the obtained sequence is returned by the FLWOR expression.

We are now giving practical examples with a hint on how it maps to SQL.

#### For clauses <a href="#forclauses.d12e4070" id="forclauses.d12e4070"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-xquery-for-clause) for for clauses. The following explanations, provided as an informal summary for convenience, are non-normative.

ForClause

![](/files/dk3VSoRH2sjc4y8lcUpW)

For clauses allow iteration on a sequence.

For each incoming tuple, the expression in the for clause is evaluated to a sequence. Each item in this sequence is in turn bound to the for variable. A tuple is hence produced for each incoming tuple, and for each item in the sequence produced by the for clause for this tuple.

The order in which items are bound by the for clause can be relaxed with unordered expressions, as described later in this section.

The following query, using a for and a return clause, is the counterpart of SQL's "SELECT name FROM captains". $x is bound in turn to each item in the captains collection.

**A for clause.**

```

for $x in collection("captains")
return $x.name
      
```

Result (run with Zorba):James T. Kirk Jean-Luc Picard Benjamin Sisko Kathryn Janeway Jonathan Archer Samantha Carter

For clause expressions are composable, there can be several of them.

**Two for clauses.**

```

for $x in ( 1, 2, 3 )
for $y in ( 1, 2, 3 )
return 10 * $x + $y
      
```

Result (run with Zorba):11 12 13 21 22 23 31 32 33

**A for clause.**

```

for $x in ( 1, 2, 3 ), $y in ( 1, 2, 3 )
return 10 * $x + $y
      
```

Result (run with Zorba):11 12 13 21 22 23 31 32 33

A for variable is visible to subsequence bindings.

**A for clause.**

```

for $x in ( [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ), $y in $x[]
return $y
      
```

Result (run with Zorba):1 2 3 4 5 6 7 8 9

**A for clause.**

```

for $x in collection("captains"), $y in $x.series[]
return { "captain" : $x.name, "series" : $y }
      
```

Result (run with Zorba):{ "captain" : "James T. Kirk", "series" : "The original series" } { "captain" : "Jean-Luc Picard", "series" : "The next generation" } { "captain" : "Benjamin Sisko", "series" : "The next generation" } { "captain" : "Benjamin Sisko", "series" : "Deep Space 9" } { "captain" : "Kathryn Janeway", "series" : "The next generation" } { "captain" : "Kathryn Janeway", "series" : "Voyager" } { "captain" : "Jonathan Archer", "series" : "Entreprise" } { "captain" : null, "series" : "Voyager" }

It is also possible to bind the position of the current item in the sequence to a variable.

**A for clause.**

```

for $x at $position in collection("captains")
return { "captain" : $x.name, "id" : $position }
        
```

Result (run with Zorba):{ "captain" : "James T. Kirk", "id" : 1 } { "captain" : "Jean-Luc Picard", "id" : 2 } { "captain" : "Benjamin Sisko", "id" : 3 } { "captain" : "Kathryn Janeway", "id" : 4 } { "captain" : "Jonathan Archer", "id" : 5 } { "captain" : null, "id" : 6 } { "captain" : "Samantha Carter", "id" : 7 }

JSONiq supports joins. For example, the counterpart of "SELECT c.name AS captain, m.name AS movie FROM captains c JOIN movies m ON c.name = m.name" is:

**A join**

```

for $captain in collection("captains"), $movie in collection("movies")[ try { $$.captain eq $captain.name } catch * { false } ]
return { "captain" : $captain.name, "movie" : $movie.name }
        
```

Result (run with Zorba):{ "captain" : "James T. Kirk", "movie" : "The Motion Picture" } { "captain" : "James T. Kirk", "movie" : "The Wrath of Kahn" } { "captain" : "James T. Kirk", "movie" : "The Search for Spock" } { "captain" : "James T. Kirk", "movie" : "The Voyage Home" } { "captain" : "James T. Kirk", "movie" : "The Final Frontier" } { "captain" : "James T. Kirk", "movie" : "The Undiscovered Country" } { "captain" : "Jean-Luc Picard", "movie" : "First Contact" } { "captain" : "Jean-Luc Picard", "movie" : "Insurrection" } { "captain" : "Jean-Luc Picard", "movie" : "Nemesis" }

Note how JSONiq handles semi-structured data in a flexible way.

Outer joins are also possible with "allowing empty", i.e., output will also be produced if there is no matching movie for a captain. The following query is the counterpart of "SELECT c.name AS captain, m.name AS movie FROM captains c LEFT JOIN movies m ON c.name = m.captain".

**A join**

```

for $captain in collection("captains"), $movie allowing empty in collection("movies")[ try { $$.captain eq $captain.name } catch * { false } ]
return { "captain" : $captain.name, "movie" : $movie.name }
        
```

Result (run with Zorba):{ "captain" : "James T. Kirk", "movie" : "The Motion Picture" } { "captain" : "James T. Kirk", "movie" : "The Wrath of Kahn" } { "captain" : "James T. Kirk", "movie" : "The Search for Spock" } { "captain" : "James T. Kirk", "movie" : "The Voyage Home" } { "captain" : "James T. Kirk", "movie" : "The Final Frontier" } { "captain" : "James T. Kirk", "movie" : "The Undiscovered Country" } { "captain" : "Jean-Luc Picard", "movie" : "First Contact" } { "captain" : "Jean-Luc Picard", "movie" : "Insurrection" } { "captain" : "Jean-Luc Picard", "movie" : "Nemesis" } { "captain" : "Benjamin Sisko", "movie" : null } { "captain" : "Kathryn Janeway", "movie" : null } { "captain" : "Jonathan Archer", "movie" : null } { "captain" : null, "movie" : null } { "captain" : "Samantha Carter", "movie" : null }

#### Where clauses <a href="#whereclauses.d12e4197" id="whereclauses.d12e4197"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-where) for where clauses. The following explanations, provided as an informal summary for convenience, are non-normative.

WhereClause

![](/files/bhbCy5OBmhYo2vtimRoF)

Where clauses are used for filtering (selection operator in the relational algebra).

For each incoming tuple, the expression in the where clause is evaluated to a boolean (possibly converting an atomic to a boolean). if this boolean is true, the tuple is forwarded to the next clause, otherwise it is dropped.

The following query corresponds to "SELECT series FROM captains WHERE name = 'Kathryn Janeway'".

**A where clause.**

```

for $x in collection("captains")
where $x.name eq "Kathryn Janeway"
return $x.series
      
```

Result (run with Zorba):\[ "The next generation", "Voyager" ]

#### Order clauses <a href="#orderclauses.d12e4239" id="orderclauses.d12e4239"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-order-by-clause) for order by clauses. The following explanations, provided as an informal summary for convenience, are non-normative.

OrderByClause

![](/files/QheF4dJZeSqYcLD3UbLf)

Order clauses are for reordering tuples.

For each incoming tuple, the expression in the where clause is evaluated to an atomic. The tuples are then sorted based on the atomics they are associated with, and then forwarded to the next clause.

Like for ordering comparisons, null values are always considered the smallest.

The following query is the counterpart of SQL's "SELECT \* FROM captains ORDER BY name".

**An order by clause.**

```

for $x in collection("captains")
order by $x.name
return $x
      
```

Result (run with Zorba):{ "name" : "Benjamin Sisko", "series" : \[ "The next generation", "Deep Space 9" ], "century" : 24 } { "name" : "James T. Kirk", "series" : \[ "The original series" ], "century" : 23 } { "name" : "Jean-Luc Picard", "series" : \[ "The next generation" ], "century" : 24 } { "name" : "Jonathan Archer", "series" : \[ "Entreprise" ], "century" : 22 } { "name" : "Kathryn Janeway", "series" : \[ "The next generation", "Voyager" ], "century" : 24 } { "name" : "Samantha Carter", "series" : \[ ], "century" : 21 } { "codename" : "Emergency Command Hologram", "surname" : "The Doctor", "series" : \[ "Voyager" ], "century" : 24 }

Multiple sorting criteria can be given - they are treated like a lexicographic order (most important criterium first).

**An order by clause.**

```

for $x in collection("captains")
order by size($x.series), $x.name
return $x
      
```

Result (run with Zorba):{ "name" : "Samantha Carter", "series" : \[ ], "century" : 21 } { "name" : "James T. Kirk", "series" : \[ "The original series" ], "century" : 23 } { "name" : "Jean-Luc Picard", "series" : \[ "The next generation" ], "century" : 24 } { "name" : "Jonathan Archer", "series" : \[ "Entreprise" ], "century" : 22 } { "codename" : "Emergency Command Hologram", "surname" : "The Doctor", "series" : \[ "Voyager" ], "century" : 24 } { "name" : "Benjamin Sisko", "series" : \[ "The next generation", "Deep Space 9" ], "century" : 24 } { "name" : "Kathryn Janeway", "series" : \[ "The next generation", "Voyager" ], "century" : 24 }

It can be specified whether the order is ascending or descending. Empty sequences are allowed and it can be chosen whether to put them first or last.

**An order by clause.**

```

for $x in collection("captains")
order by $x.name descending empty greatest
return $x
      
```

Result (run with Zorba):{ "codename" : "Emergency Command Hologram", "surname" : "The Doctor", "series" : \[ "Voyager" ], "century" : 24 } { "name" : "Samantha Carter", "series" : \[ ], "century" : 21 } { "name" : "Kathryn Janeway", "series" : \[ "The next generation", "Voyager" ], "century" : 24 } { "name" : "Jonathan Archer", "series" : \[ "Entreprise" ], "century" : 22 } { "name" : "Jean-Luc Picard", "series" : \[ "The next generation" ], "century" : 24 } { "name" : "James T. Kirk", "series" : \[ "The original series" ], "century" : 23 } { "name" : "Benjamin Sisko", "series" : \[ "The next generation", "Deep Space 9" ], "century" : 24 }

An error is raised if the expression does not evaluate to an atomic or the empty sequence.

**An order by clause.**

```

for $x in collection("captains")
order by $x
return $x.name
      
```

Result (run with Zorba):An error was raised: can not atomize an object item: an object has probably been passed where an atomic value is expected (e.g., as a key, or to a function expecting an atomic item)

Collations can be used to give a specific way of how strings are to be ordered. A collation is identified by a URI.

**Use of a collation in an order by clause.**

```

for $x in collection("captains")
order by $x.name collation "http://www.w3.org/2005/xpath-functions/collation/codepoint"
return $x.name
      
```

Result (run with Zorba):Benjamin Sisko James T. Kirk Jean-Luc Picard Jonathan Archer Kathryn Janeway Samantha Carter

#### Group clauses <a href="#groupclauses.d12e4331" id="groupclauses.d12e4331"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-group-by) for group by clauses. The following explanations, provided as an informal summary for convenience, are non-normative.

GroupByClause

![](/files/xTZHNOGaxmoad7rDdMba)

Grouping is also supported, like in SQL.

For each incoming tuple, the expression in the group clause is evaluated to an atomic (a grouping key). The incoming tuples are then grouped according to the key they are associated with.

For each group, a tuple is output, with a binding from the grouping variable to the key of the group.

**A group by clause.**

```

for $x in collection("captains")
group by $century := $x.century
return { "century" : $century  }
      
```

Result (run with Zorba):{ "century" : 21 } { "century" : 22 } { "century" : 23 } { "century" : 24 }

As for the other (non-grouping) variables, their values within one group are all concatenated, keeping the same name. Aggregations can be done on these variables.

The following query is equivalent to "SELECT century, COUNT(\*) FROM captains GROUP BY century".

**A group by clause.**

```

for $x in collection("captains")
group by $century := $x.century
return { "century" : $century, "count" : count($x) }
      
```

Result (run with Zorba):{ "century" : 21, "count" : 1 } { "century" : 22, "count" : 1 } { "century" : 23, "count" : 1 } { "century" : 24, "count" : 4 }

JSONiq's group by is more flexible than SQL and is fully composable.

**A group by clause.**

```

for $x in collection("captains")
group by $century := $x.century
return { "century" : $century, "captains" : [ $x.name ] }
      
```

Result (run with Zorba):{ "century" : 21, "captains" : \[ "Samantha Carter" ] } { "century" : 22, "captains" : \[ "Jonathan Archer" ] } { "century" : 23, "captains" : \[ "James T. Kirk" ] } { "century" : 24, "captains" : \[ "Jean-Luc Picard", "Benjamin Sisko", "Kathryn Janeway" ] }

Unlike SQL, JSONiq does not need a having clause, because a where clause works perfectly after grouping as well.

The following query is the counterpart of "SELECT century, COUNT(\*) FROM captains GROUP BY century HAVING COUNT(\*) > 1"

**A group by clause.**

```

for $x in collection("captains")
group by $century := $x.century
where count($x) gt 1
return { "century" : $century, "count" : count($x) }
      
```

Result (run with Zorba):{ "century" : 24, "count" : 4 }

#### Let clauses <a href="#letclauses.d12e4413" id="letclauses.d12e4413"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-xquery-let-clause) for let clauses. The following explanations, provided as an informal summary for convenience, are non-normative.

LetClause

![](/files/3XBEJtVyZGVp5FlNJ9qg)

Let bindings can be used to define aliases for any sequence, for convenience.

For each incoming tuple, the expression in the let clause is evaluated to a sequence. A binding is added from this sequence to the let variable in each tuple. A tuple is hence produced for each incoming tuple.

**A let clause.**

```

for $x in collection("captains")
let $century := $x.century
group by $century
let $number := count($x)
where $number gt 1
return { "century" : $century, "count" : $number }
      
```

Result (run with Zorba):{ "century" : 24, "count" : 4 }

Note that it is perfectly fine to reuse a variable name and hide a variable binding.

**A let clause.**

```

for $x in collection("captains")
let $century := $x.century
group by $century
let $number := count($x)
let $number := count(distinct-values(for $series in $x.series
                                     return typeswitch($series)
                                            case array return $series()
                                            default return $series ))
where $number gt 1
return { "century" : $century, "number of series" : $number }
      
```

Result (run with Zorba):{ "century" : 24, "number of series" : 3 }

#### Count clauses <a href="#countclauses.d12e4464" id="countclauses.d12e4464"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-count) for count clauses. The following explanations, provided as an informal summary for convenience, are non-normative.

CountClause

![](/files/SQ3IfZht9IYXKXr36lv3)

For each incoming tuple, a binding from the position of this tuple in the tuple stream to the count variable is added. The new tuple is then forwarded to the next clause.

**A count clause.**

```

for $x in collection("captains")
order by $x.name
count $c
return { "id" : $c, "captain" : $x }
      
```

Result (run with Zorba):{ "id" : 1, "captain" : { "name" : "Benjamin Sisko", "series" : \[ "The next generation", "Deep Space 9" ], "century" : 24 } } { "id" : 2, "captain" : { "name" : "James T. Kirk", "series" : \[ "The original series" ], "century" : 23 } } { "id" : 3, "captain" : { "name" : "Jean-Luc Picard", "series" : \[ "The next generation" ], "century" : 24 } } { "id" : 4, "captain" : { "name" : "Jonathan Archer", "series" : \[ "Entreprise" ], "century" : 22 } } { "id" : 5, "captain" : { "name" : "Kathryn Janeway", "series" : \[ "The next generation", "Voyager" ], "century" : 24 } } { "id" : 6, "captain" : { "name" : "Samantha Carter", "series" : \[ ], "century" : 21 } } { "id" : 7, "captain" : { "codename" : "Emergency Command Hologram", "surname" : "The Doctor", "series" : \[ "Voyager" ], "century" : 24 } }

#### Map operator <a href="#mapoperator.d12e4500" id="mapoperator.d12e4500"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-map-operator) for the map operator, except that it changes the syntax for the context item to *$$* instead of the *.* syntax.

The following explanations, provided as an informal summary for convenience, are non-normative.

SimpleMapExpr

![](/files/BEbOJFDP3QND3eB9jIDR)

ContextItemExpr

![](/files/Qchurp7StzeRYVw41Aui)

JSONiq provides a shortcut for a for-return construct, automatically binding each item in the left-hand-side sequence to the context item.

**A simple map**

```

(1 to 10) ! ($$ * 2)
      
```

Result (run with Zorba):2 4 6 8 10 12 14 16 18 20

**An equivalent query**

```

for $i in 1 to 10
return $i * 2
      
```

Result (run with Zorba):2 4 6 8 10 12 14 16 18 20

#### Variable references <a href="#variablereferences.d12e4566" id="variablereferences.d12e4566"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-variables) for variable references, except that it disallows the character *.* in variable names, which is instead used for object lookup.

#### Composing FLWOR expressions <a href="#composingflworexpressions.d12e4580" id="composingflworexpressions.d12e4580"></a>

Like all other expressions, FLWOR expressions can be composed. In the following examples, a FLWOR is nested in a function call, nested in a FLWOR, nested in an array constructor:

**Nested FLWORs**

```

        [
          for $c in collection("captains")
          where exists(for $m in collection("movies")
                       where some $moviecaptain in let $captain := $m.captain
                                                   return typeswitch ($captain)
                                                          case array return $captain()
                                                          default return $captain
                             satisfies
                             $moviecaptain eq $c.name
                       return $m)
          return $c.name
        ]
      
```

Result (run with Zorba):\[ "James T. Kirk", "Jean-Luc Picard" ]

#### Ordered and Unordered expressions <a href="#orderedandunorderedexpressions.d12e4597" id="orderedandunorderedexpressions.d12e4597"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-unordered-expressions) for ordered and unordered expressions. The following explanations, provided as an informal summary for convenience, are non-normative.

OrderedExpr

![](/files/fM12Ka5tVq3lVbFdWEyD)

UnorderedExpr

![](/files/ZtP8OMHZQy5LCYBngjU7)

By default, the order in which a for clause binds its items is important.

This behaviour can be relaxed in order give the optimizer more leeway. An unordered expression relaxes ordering by for clauses within its operand scope:

**An unordered expression.**

```

unordered {
  for $captain in collection("captains")
  where $captain.century eq 24
  return $captain
}
      
```

Result (run with Zorba):{ "name" : "Jean-Luc Picard", "series" : \[ "The next generation" ], "century" : 24 } { "name" : "Benjamin Sisko", "series" : \[ "The next generation", "Deep Space 9" ], "century" : 24 } { "name" : "Kathryn Janeway", "series" : \[ "The next generation", "Voyager" ], "century" : 24 } { "codename" : "Emergency Command Hologram", "surname" : "The Doctor", "series" : \[ "Voyager" ], "century" : 24 }

An ordered expression can be used to reactivate ordering behaviour in a subscope.

**An ordered expression.**

```

unordered {
  for $captain in collection("captains")
  where ordered { exists(for $movie at $i in collection("movies")
                         where $i eq 5
                         where $movie.captain eq $captain.name
                         return $movie) }
  return $captain
}
      
```

Result (run with Zorba):{ "name" : "James T. Kirk", "series" : \[ "The original series" ], "century" : 23 }

### Expressions dealing with types <a href="#section-type-expressions" id="section-type-expressions"></a>

This section describes JSONiq types as well as the sequence type syntax.

#### Instance-of expressions <a href="#instanceofexpressions.d12e4668" id="instanceofexpressions.d12e4668"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-instance-of) for ordered and unordered expressions. The following explanations, provided as an informal summary for convenience, are non-normative.

InstanceofExpr

![](/files/QxyLzs1ajtyJG4CyqZCe)

An instance expression can be used to tell whether a JSONiq value matches a given sequence type.

**Instance of expression**

```

1 instance of integer
      
```

Result (run with Zorba):true

**Instance of expression**

```

1 instance of string
      
```

Result (run with Zorba):false

**Instance of expression**

```

"foo" instance of string
      
```

Result (run with Zorba):true

**Instance of expression**

```

{ "foo" : "bar" } instance of object
      
```

Result (run with Zorba):true

**Instance of expression**

```

({ "foo" : "bar" }, { "bar" : "foo" }) instance of json-item+
      
```

Result (run with Zorba):true

**Instance of expression**

```

[ 1, 2, 3 ] instance of array?
      
```

Result (run with Zorba):true

**Instance of expression**

```

() instance of ()
      
```

Result (run with Zorba):true

#### Treat expressions <a href="#treatexpressions.d12e4765" id="treatexpressions.d12e4765"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-treat) for ordered and unordered expressions. The following explanations, provided as an informal summary for convenience, are non-normative.

TreatExpr

![](/files/Mn9O9E7RA8ZIarXNUQ2e)

A treat expression checks that a JSONiq value matches a given sequence type. If it is not the case, an error is raised.

**Treat as expression**

```

1 treat as integer
      
```

Result (run with Zorba):1

**Treat as expression**

```

1 treat as string
      
```

Result (run with Zorba):An error was raised: "xs:integer" cannot be treated as type xs:string

**Treat as expression**

```

"foo" treat as string
      
```

Result (run with Zorba):foo

**Treat as expression**

```

{ "foo" : "bar" } treat as object
      
```

Result (run with Zorba):{ "foo" : "bar" }

**Treat as expression**

```

({ "foo" : "bar" }, { "bar" : "foo" }) treat as json-item+
      
```

Result (run with Zorba):{ "foo" : "bar" } { "bar" : "foo" }

**Treat as expression**

```

[ 1, 2, 3 ] treat as array?
      
```

Result (run with Zorba):\[ 1, 2, 3 ]

**Treat as expression**

```

() treat as ()
      
```

Result (run with Zorba):

#### Castable expressions <a href="#castableexpressions.d12e4862" id="castableexpressions.d12e4862"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-castable) for ordered and unordered expressions. The following explanations, provided as an informal summary for convenience, are non-normative.

CastableExpr

![](/files/A1MRCCNR25dV3nZZg6bh)

A castable expression checks whether a JSONiq value can be cast to a given atomic type and returns true or false accordingly. It can be used before actually casting to that type.

**Castable as expression**

```

"1" castable as integer
      
```

Result (run with Zorba):true

**Castable as expression**

```

"foo" castable as integer
      
```

Result (run with Zorba):false

**Castable as expression**

```

"2013-04-02" castable as date
      
```

Result (run with Zorba):true

**Castable as expression**

```

() castable as date
      
```

Result (run with Zorba):false

**Castable as expression**

```

("2013-04-02", "2013-04-03") castable as date
      
```

Result (run with Zorba):false

The question mark allows for an empty sequence.

**Castable as expression**

```

() castable as date?
      
```

Result (run with Zorba):true

#### Cast expressions <a href="#castexpressions.d12e4952" id="castexpressions.d12e4952"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-cast) for ordered and unordered expressions. The following explanations, provided as an informal summary for convenience, are non-normative.

CastExpr

![](/files/shjCKtPJ03MCGraLG8oh)

A cast expression casts a JSONiq value to a given atomic type. The resulting value is annotated with this type.

**Cast as expression**

```

"1" cast as integer
      
```

Result (run with Zorba):1

**Cast as expression**

```

"foo" cast as integer
      
```

Result (run with Zorba):An error was raised: "foo": value of type xs:string is not castable to type xs:integer

**Cast as expression**

```

"2013-04-02" cast as date
      
```

Result (run with Zorba):2013-04-02

**Cast as expression**

```

() cast as date
      
```

Result (run with Zorba):An error was raised: empty sequence can not be cast to type with quantifier '1'

**Cast as expression**

```

("2013-04-02", "2013-04-03") cast as date
      
```

Result (run with Zorba):An error was raised: sequence of more than one item can not be cast to type with quantifier '1' or '?'

The question mark allows for an empty sequence.

**Cast as expression**

```

() cast as date?
      
```

Result (run with Zorba):

**Cast as expression**

```

"2013-04-02" cast as date?
      
```

Result (run with Zorba):2013-04-02

#### Typeswitch expressions <a href="#typeswitchexpressions.d12e5052" id="typeswitchexpressions.d12e5052"></a>

JSONiq follows the [W3C standard](https://www.w3.org/TR/xquery-30/#id-typeswitch) for ordered and unordered expressions. The following explanations, provided as an informal summary for convenience, are non-normative.

TypeswitchExpr

![](/files/1kAE9cPrx1Y9lzDcQ6q1)

CaseClause

![](/files/sssqFj4gfYGOZTILIjjP)

A typeswitch expressions tests if the value resulting from the first operand matches a given list of types. The expression corresponding to the first matching case is finally evaluated. If there is no match, the expression in the default clause is evaluated.

**Typeswitch expression**

```

typeswitch("foo")
case integer return "integer"
case string return "string"
case object return "object"
default return "other"
      
```

Result (run with Zorba):string

In each clause, it is possible to bind the value of the first operand to a variable.

**Typeswitch expression**

```

typeswitch("foo")
case $i as integer return $i + 1
case $s as string return $s || "foo"
case $o as object return [ $o ]
default $d return $d
      
```

Result (run with Zorba):foofoo

The vertical bar can be used to allow several types in the same case clause.

**Typeswitch expression**

```

typeswitch("foo")
case $a as integer | string return { "integer or string" : $a }
case $o as object return [ $o ]
default $d return $d
      
```

Result (run with Zorba):{ "integer or string" : "foo" }


# Prologs

This section introduces prologs, which allows declaring functions and global variables that can then be used in the main query. A prolog also allows setting some default behaviour.

MainModule

![](/files/7MNZViG2n9y1ZZewKqKO)

Prolog

![](/files/dKmgwmyasCGLcWSldCVZ)

The prolog appears before the main query and is optional. It can contain setters and module imports, followed by function and variable declarations.

Module imports are explained in the next chapter.

### Setters <a href="#setters.d12e5165" id="setters.d12e5165"></a>

Setters allow to specify a default behaviour for various aspects of the language.

#### Default collation <a href="#defaultcollation.d12e5173" id="defaultcollation.d12e5173"></a>

DefaultCollationDecl

![](/files/3cHfpB3D3ao5FQjVxx8C)

This specifies the default collation used for grouping and ordering clauses in FLWOR expressions. It can be overriden with a collation directive in these clauses.

#### Default ordering mode <a href="#defaultorderingmode.d12e5193" id="defaultorderingmode.d12e5193"></a>

OrderingModeDecl

![](/files/hEANS7ma4HmOGF4kZUJz)

This specifies the default behaviour of from clauses, i.e., if they bind tuples in the order in which items occur in the binding sequence. It can be overriden with ordered and unordered expressions.

#### Default ordering behaviour for empty sequences <a href="#defaultorderingbehaviourforemptysequences.d12e5214" id="defaultorderingbehaviourforemptysequences.d12e5214"></a>

EmptyOrderDecl

![](/files/aFFcm05UiYIOV9IgkxNd)

This specifies whether empty sequences come first or last in an ordering clause. It can be overriden by the corresponding directives in such clauses.

#### Default decimal format <a href="#defaultdecimalformat.d12e5234" id="defaultdecimalformat.d12e5234"></a>

DecimalFormatDecl

![](/files/F1T7C0vJgh3s8XDSvmje)

DFPropertyName

![](/files/wb2TpzqEBK5m996VnnU5)

This specifies a default decimal format for the builtin function format-number().

### Global variables <a href="#globalvariables.d12e5268" id="globalvariables.d12e5268"></a>

VarDecl

![](/files/dTPvOEZCXMIwCp1SSDZJ)

Variables can be declared global. Global variables are declared in the prolog.

#### Global variable

```

  declare variable $obj := { "foo" : "bar" };
  $obj
      
```

Result (run with Zorba):{ "foo" : "bar" }

#### Global variable

```

  declare variable $numbers := (1, 2, 3, 4, 5);
  [ $numbers ]
      
```

Result (run with Zorba):\[ 1, 2, 3, 4, 5 ]

You can specify a type for a variable. If the type does not match, an error is raised. Types will be explained later. In general, you do not need to worry too much about variable types except if you want to make sure that what you bind to a variable is really what you want. In most cases, the engine will take care of types for you.

#### Global variable with a type

```

  declare variable $obj as object := { "foo" : "bar" };
  $obj
      
```

Result (run with Zorba):{ "foo" : "bar" }

An external variable allows you to pass a value from the outside environment, which can be very useful. Each implementation can choose their own way of passing a value to an external variable. A default value for an external variable can also be supplied in case none is provided outside.

#### An external global variable

```

  declare variable $obj external;
  $obj
      
```

Result (run with Zorba):An error was raised: "obj": variable has no value

#### An external global variable with a default value

```

  declare variable $obj external := { "foo" : "bar" };
  $obj
      
```

Result (run with Zorba):{ "foo" : "bar" }

### Functions <a href="#functions.d12e5343" id="functions.d12e5343"></a>

VarDecl

![](/files/MlbExneKUXW6lKAMZi3W)

You can define your own functions in the prolog. These user-defined functions must be prefixed with *local:*, both in the declaration and when called.

Remember than types are optional, and if you do not specify any, item\* is assumed, both for parameters and for the return type.

#### An external global variable with a default value

```

declare function local:say-hello($x) { "Hello, " || $x || "!" };
local:say-hello("Mister Spock")
      
```

Result (run with Zorba):Hello, Mister Spock!

#### An external global variable with a default value

```

declare function local:say-hello($x as string) { "Hello, " || $x || "!" };
local:say-hello("Mister Spock")
      
```

Result (run with Zorba):Hello, Mister Spock!

#### An external global variable with a default value

```

declare function local:say-hello($x as string) as string { "Hello, " || $x || "!" };
local:say-hello("Mister Spock")
      
```

Result (run with Zorba):Hello, Mister Spock!

If you do specify types, an error is raised in case of a mismatch

#### An external global variable with a default value

```

declare function local:say-hello($x) { "Hello, " || $x || "!" }; 
local:say-hello(1)
      
```

Result (run with Zorba):Hello, 1!


# Modules

Module

![](/files/u8V3B4SlzZiaD44Uypnc)

You can group functions and variables in separate library modules.

MainModule

![](/files/7MNZViG2n9y1ZZewKqKO)

Up to now, everything we encountered were main modules, i.e., a prolog followed by a main query.

LibraryModule

![](/files/86DQIoQlR9dwf5dF45Wc)

A library module does not contain any query - just functions and variables that can be imported by other modules.

A library module must be assigned to a namespace. For convenience, this namespace is bound to an alias in the module declaration. All variables and functions in a library module must be prefixed with this alias.

### A library module

```

module namespace my = "http://www.example.com/my-module";
declare variable $my:variable := { "foo" : "bar" };
declare variable $my:n := 42;
declare function my:function($i as integer) { $i * $i };
    
```

ModuleImport

![](/files/IGvGHRBxkX33WBWFJ7FB)

Here is a main module which imports the former library module. An alias is given to the module namespace (my). Variables and functions from that module can be accessed by prefixing their names with this alias. The alias may be different than the internal alias defined in the imported module.

### An importing main module

```

import module namespace other= "http://www.example.com/my-module";
other:function($other:n)
    
```

Result (run with Zorba):1764


# Function library

## Function Library <a href="#chapter-functions" id="chapter-functions"></a>

JSONiq provides a rich set of functions.

### JSON specific functions. <a href="#jsonspecificfunctions.d12e5506" id="jsonspecificfunctions.d12e5506"></a>

Some functions are specific to JSON.

#### keys <a href="#keys.d12e5512" id="keys.d12e5512"></a>

This function returns the distinct keys of all objects in the supplied sequence, in an implementation-dependent order.

`keys($o as item*) as string*`

**Getting all distinct key names in the supplied objects, ignoring non-objects.**

```

let $o := ("foo", [ 1, 2, 3 ], { "a" : 1, "b" : 2 }, { "a" : 3, "c" : 4 })
return keys($o)
        
```

Result (run with Zorba):a b c

**Retrieving all Pairs from an Object:**

```

let $map := { "eyes" : "blue", "hair" : "fuchsia" }
for $key in keys($map)
return { $key : $map.$key }
        
```

Result (run with Zorba):{ "eyes" : "blue" } { "hair" : "fuchsia" }

#### members <a href="#members.d12e5544" id="members.d12e5544"></a>

This functions returns all members of all arrays of the supplied sequence.

`members($a as item*) as item*`

**Retrieving the members of all supplied arrays, ignoring non-arrays.**

```

let $planets :=  ( "foo", { "foo" : "bar "}, [ "mercury", "venus", "earth", "mars" ], [ 1, 2, 3 ])
return members($planets)
        
```

Result (run with Zorba):mercury venus earth mars 1 2 3

#### null <a href="#null.d12e5566" id="null.d12e5566"></a>

This function returns the JSON null.

`null() as null`

#### parse-json <a href="#parsejson.d12e5579" id="parsejson.d12e5579"></a>

This function parses its first parameter (a string) as JSON, and returns the resulting sequence of objects and arrays.

`parse-json($arg as string?) as json-item*`

`parse-json($arg as string?, $options as object) as json-item*`

The object optionally supplied as the second parameter may contain additional options:

* `jsoniq-multiple-top-level-items` (boolean): indicates whether parsing to zero, or several objects is allowed. An error is raised if this value is false and there is not exactly one object that was parsed.

If parsing is not successful, an error is raised. Parsing is considered in particular to be non-successful if the boolean associated with "jsoniq-multiple-top-level-items" in the additional parameters is false and there is extra content after parsing a single abject or array.

**Parsing a JSON document**

```

parse-json("{ \"foo\" : \"bar\" }", { "jsoniq-multiple-top-level-items" : false })
        
```

Result (run with Zorba):{ "foo" : "bar" }

**Parsing multiple, whitespace-separated JSON documents**

```

parse-json("{ \"foo\" : \"bar\" } { \"bar\" : \"foo\" }")
        
```

Result (run with Zorba):{ "foo" : "bar" } { "bar" : "foo" }

#### size <a href="#size.d12e5631" id="size.d12e5631"></a>

This function returns the size of the supplied array, or the empty sequence if the empty sequence is provided.

`size($a as array?) as integer?`

**Retrieving the size of an array**

```

let $a := [1 to 10]
return size($a)
        
```

Result (run with Zorba):10

#### accumulate <a href="#accumulate.d12e5653" id="accumulate.d12e5653"></a>

This function dynamically builds an object, like the {| |} syntax, except that it does not throw an error upon pair collision. Instead, it accumulates them, wrapping into an array if necessary. Non-objects are ignored.

```

declare function accumulate($seq as item*) as object
{
  {|
    keys($seq) ! { $$ : $seq.$$ }
  |}
};
      
```

#### descendant-arrays <a href="#descendantarrays.d12e5662" id="descendantarrays.d12e5662"></a>

This function returns all arrays contained within the supplied items, regardless of depth.

```

declare function descendant-arrays($seq as item*) as array*
{
  for $i in $seq
  return typeswitch ($i)
  case array return ($i, descendant-arrays($i[])
  case object return descendant-arrays(values($i))
  default return ()
};
      
```

#### descendant-objects <a href="#descendantobjects.d12e5671" id="descendantobjects.d12e5671"></a>

This function returns all objects contained within the supplied items, regardless of depth.

```

declare function descendant-objects($seq as item*) as object*
{
  for $i in $seq
  return typeswitch ($i)
  case object return ($i, descendant-objects(values($i)))
  case array return descendant-objects($i[])
  default return ()
};
      
```

#### descendant-pairs <a href="#descendantpairs.d12e5680" id="descendantpairs.d12e5680"></a>

This function returns all descendant pairs within the supplied items.

```

declare function descendant-pairs($seq as item*)
{
  for $i in $seq
  return typeswitch ($i)
  case object return
    for $k in keys($o)
    let $v := $o.$k
    return ({ $k : $v }, descendant-pairs($v))
  case array return descendant-pairs($i[])
  default return ()
};
      
```

**Accessing all descendant pairs**

```

let $o := 
{
  "first" : 1,
  "second" : { 
    "first" : "a", 
    "second" : "b" 
  }
}
return descendant-pairs($o)
        
```

Result (run with Zorba):An error was raised: "descendant-pairs": function with arity 1 not declared

#### flatten <a href="#flatten.d12e5700" id="flatten.d12e5700"></a>

This function recursively flattens arrays in the input sequence, leaving non-arrays intact.

```

declare function flatten($seq as item*) as item*
{
  for $value in $seq
  return typeswitch ($value)
         case array return flatten($value[])
         default return $value
};
	  
```

#### intersect <a href="#intersect.d12e5709" id="intersect.d12e5709"></a>

This function returns the intersection of the supplied objects, and aggregates values corresponding to the same name into an array. Non-objects are ignored.

```

declare function intersect($seq as item*)
{
  {|
    let $objects := $seq[. instance of object()]
    for $key in keys(head($objects))
    where every $object in tail($objects)
          satisfies exists(index-of(keys($object), $key))
    return { $key : $objects.$key }
  |}
};
      
```

#### project <a href="#project.d12e5718" id="project.d12e5718"></a>

This function iterates on the input sequence. It projects objects by filtering their pairs and leaves non-objects intact.

```

declare function project($seq as item*, $keys as string*) as item*
{
  for $item in $seq
  return typeswitch ($item)
         case $object as object return
         {|
           for $key in keys($object)
           where some $to-project in $keys satisfies $to-project eq $key
           let $value := $object.$key
           return { $key : $value }
         |}
         default return $item
};
      
```

**Projecting an object 1**

```

let $o := {
  "Captain" : "Kirk",
  "First Officer" : "Spock",
  "Engineer" : "Scott"
  }
return project($o, ("Captain", "First Officer"))
        
```

Result (run with Zorba):{ "Captain" : "Kirk", "First Officer" : "Spock" }

**Projecting an object 2**

```

let $o := {
  "Captain" : "Kirk",
  "First Officer" : "Spock",
  "Engineer" : "Scott"
  }
return project($o, "XQuery Evangelist")
        
```

Result (run with Zorba):{ }

#### remove-keys <a href="#removekeys.d12e5747" id="removekeys.d12e5747"></a>

This function iterates on the input sequence. It removes the pairs with the given keys from all objects and leaves non-objects intact.

```

declare function remove-keys($seq as item*, $keys as string*) as item*
{
  for $item in $seq
  return typeswitch ($item)
         case $object as object return
         {|
           for $key in keys($object)
           where every $to-remove in $keys satisfies $to-remove ne $key
           let $value := $object.$key
           return { $key : $value }
         |}
         default return $item
};
      
```

**Removing keys from an object (not implemented yet)**

```

let $o := {
  "Captain" : "Kirk",
  "First Officer" : "Spock",
  "Engineer" : "Scott"
  }
return remove-keys($o, ("Captain", "First Officer"))
        
```

Result (run with Zorba):An error was raised: "remove-keys": function with arity 2 not declared

#### values <a href="#values.d12e5766" id="values.d12e5766"></a>

This function returns all values in the supplied objects. Non-objects are ignored.

```

declare function values($seq as item*) as item* {
  for $i in $seq
  for $k in jn:keys($i)
  return $i($k)
};
      
```

#### encode-for-roundtrip <a href="#encodeforroundtrip.d12e5775" id="encodeforroundtrip.d12e5775"></a>

This function encodes any sequence of items, even containing non-JSON types, to a sequence of JSON items that can be serialized as pure JSON, in a way that it can be parsed and decoded back using decode-from-roundtrip. JSON features are left intact, while atomic items annotated with a non-JSON type are converted to objects embedding all necessary information.

`encode-for-roundtrip($items as item*) as json-item*`

#### decode-from-roundtrip <a href="#decodefromroundtrip.d12e5788" id="decodefromroundtrip.d12e5788"></a>

This function decodes a sequence previously encoded with encode-for-roundtrip.

`decode-from-roundtrip($items as json-item*) as item*`

### Functions taken from XQuery <a href="#functionstakenfromxquery.d12e5801" id="functionstakenfromxquery.d12e5801"></a>

* Access to the external environment: [collection#1](https://www.w3.org/TR/xpath-functions-30/#func-collection)
* Function to turn atomics into booleans for use in two-valued logics: [boolean#1](https://www.w3.org/TR/xpath-functions-30/#func-boolean)
* Raising errors: [error#0](https://www.w3.org/TR/xpath-functions-30/#func-error), [error#1](https://www.w3.org/TR/xpath-functions-30/#func-error), [error#2](https://www.w3.org/TR/xpath-functions-30/#func-error), [error#3](https://www.w3.org/TR/xpath-functions-30/#func-error).
* Functions on numeric values: [abs#1](https://www.w3.org/TR/xpath-functions-30/#func-abs), [ceilingabs#1](https://www.w3.org/TR/xpath-functions-30/#func-ceilingabs), [floorabs#1](https://www.w3.org/TR/xpath-functions-30/#func-floorabs), [roundabs#1](https://www.w3.org/TR/xpath-functions-30/#func-roundabs), [round-half-to-even#1](https://www.w3.org/TR/xpath-functions-30/#func-round-half-to-even)
* Parsing numbers: [number#0](https://www.w3.org/TR/xpath-functions-30/#func-number), [number#1](https://www.w3.org/TR/xpath-functions-30/#func-number)
* Formatting integers: [format-integer#2](https://www.w3.org/TR/xpath-functions-30/#func-format-integer), [format-integer#3](https://www.w3.org/TR/xpath-functions-30/#func-format-integer)
* Formatting numbers: [format-numberreturn r#2](https://www.w3.org/TR/xpath-functions-30/#func-format-number), [format-number#3](https://www.w3.org/TR/xpath-functions-30/#func-format-number)
* Trigonometric and exponential functions: [pi#0](https://www.w3.org/TR/xpath-functions-30/#func-pi), [exp#1](https://www.w3.org/TR/xpath-functions-30/#func-exp), [exp10#1](https://www.w3.org/TR/xpath-functions-30/#func-exp10), [log#1](https://www.w3.org/TR/xpath-functions-30/#func-log), [log10#1](https://www.w3.org/TR/xpath-functions-30/#func-log10), [pow#2](https://www.w3.org/TR/xpath-functions-30/#func-pow), [sqrt#1](https://www.w3.org/TR/xpath-functions-30/#func-sqrt), [sin#1](https://www.w3.org/TR/xpath-functions-30/#func-sin), [cos#1](https://www.w3.org/TR/xpath-functions-30/#func-cos), [tan#1](https://www.w3.org/TR/xpath-functions-30/#func-tan), [asin#1](https://www.w3.org/TR/xpath-functions-30/#func-asin), [acos#1](https://www.w3.org/TR/xpath-functions-30/#func-acos), [atan#1](https://www.w3.org/TR/xpath-functions-30/#func-atan), [atan2#1](https://www.w3.org/TR/xpath-functions-30/#func-atan2)
* Functions to assemble and disassemble strings: [codepoints-to-string#1](https://www.w3.org/TR/xpath-functions-30/#func-codepoints-to-string), [string-to-codepoints#1](https://www.w3.org/TR/xpath-functions-30/#func-string-to-codepoints)
* Comparison of strings: [compare#2](https://www.w3.org/TR/xpath-functions-30/#func-compare), [compare#3](https://www.w3.org/TR/xpath-functions-30/#func-compare), [codepoint-equal#2](https://www.w3.org/TR/xpath-functions-30/#func-codepoint-equal)
* Functions on string values: [concat#2](https://www.w3.org/TR/xpath-functions-30/#func-concat), [string-join#1](https://www.w3.org/TR/xpath-functions-30/#func-string-join), [string-join#2](https://www.w3.org/TR/xpath-functions-30/#func-string-join), [substring#2](https://www.w3.org/TR/xpath-functions-30/#func-substring), [substring#3](https://www.w3.org/TR/xpath-functions-30/#func-substring), [string-length#0](https://www.w3.org/TR/xpath-functions-30/#func-string-length), [string-length#1](https://www.w3.org/TR/xpath-functions-30/#func-string-length), [normalize-space#0](https://www.w3.org/TR/xpath-functions-30/#func-normalize-space), [normalize-space#1](https://www.w3.org/TR/xpath-functions-30/#func-normalize-space), [normalize-unicode#1](https://www.w3.org/TR/xpath-functions-30/#func-normalize-unicode), [normalize-unicode#2](https://www.w3.org/TR/xpath-functions-30/#func-normalize-unicode), [upper-case#1](https://www.w3.org/TR/xpath-functions-30/#func-upper-case), [lower-case#1](https://www.w3.org/TR/xpath-functions-30/#func-lower-case), [translate#3](https://www.w3.org/TR/xpath-functions-30/#func-translate)
* Functions based on substring matching: [contains#2](https://www.w3.org/TR/xpath-functions-30/#func-contains), [contains#3](https://www.w3.org/TR/xpath-functions-30/#func-contains), [starts-with#2](https://www.w3.org/TR/xpath-functions-30/#func-starts-with), [starts-with#3](https://www.w3.org/TR/xpath-functions-30/#func-starts-with), [ends-with#2](https://www.w3.org/TR/xpath-functions-30/#func-ends-with), [ends-with#3](https://www.w3.org/TR/xpath-functions-30/#func-ends-with), [substring-before#2](https://www.w3.org/TR/xpath-functions-30/#func-substring-before), [substring-before#3](https://www.w3.org/TR/xpath-functions-30/#func-substring-before), [substring-after#2](https://www.w3.org/TR/xpath-functions-30/#func-substring-after), [substring-after#3](https://www.w3.org/TR/xpath-functions-30/#func-substring-after)
* String functions that use regular expressions: [matches#2](https://www.w3.org/TR/xpath-functions-30/#func-matches), [matches#3](https://www.w3.org/TR/xpath-functions-30/#func-matches), [replace#3](https://www.w3.org/TR/xpath-functions-30/#func-replace), [replace#4](https://www.w3.org/TR/xpath-functions-30/#func-replace), [tokenize#2](https://www.w3.org/TR/xpath-functions-30/#func-tokenize), [tokenize#3](https://www.w3.org/TR/xpath-functions-30/#func-tokenize)
* Functions that manipulate URIs: [resolve-uri#1](https://www.w3.org/TR/xpath-functions-30/#func-resolve-uri), [resolve-uri#2](https://www.w3.org/TR/xpath-functions-30/#func-resolve-uri), [encode-for-uri#1](https://www.w3.org/TR/xpath-functions-30/#func-encode-for-uri), [iri-to-uri#1](https://www.w3.org/TR/xpath-functions-30/#func-iri-to-uri), [escape-html-uri#1](https://www.w3.org/TR/xpath-functions-30/#func-escape-html-uri)
* General functions on sequences: [empty#1](https://www.w3.org/TR/xpath-functions-30/#func-empty), [exists#1](https://www.w3.org/TR/xpath-functions-30/#func-exists), [head#1](https://www.w3.org/TR/xpath-functions-30/#func-head), [tail#1](https://www.w3.org/TR/xpath-functions-30/#func-tail), [insert-before#3](https://www.w3.org/TR/xpath-functions-30/#func-insert-before), [remove#2](https://www.w3.org/TR/xpath-functions-30/#func-remove), [reverse#1](https://www.w3.org/TR/xpath-functions-30/#func-reverse), [subsequence#2](https://www.w3.org/TR/xpath-functions-30/#func-subsequence), [subsequence#3](https://www.w3.org/TR/xpath-functions-30/#func-subsequence), [unordered#1](https://www.w3.org/TR/xpath-functions-30/#func-unordered)
* Function that compare values in sequences: [distinct-values#1](https://www.w3.org/TR/xpath-functions-30/#func-distinct-values), [distinct-values#2](https://www.w3.org/TR/xpath-functions-30/#func-distinct-values), [index-of#2](https://www.w3.org/TR/xpath-functions-30/#func-index-of), [index-of#3](https://www.w3.org/TR/xpath-functions-30/#func-index-of), [deep-equal#2](https://www.w3.org/TR/xpath-functions-30/#func-deep-equal). [deep-equal#3](https://www.w3.org/TR/xpath-functions-30/#func-deep-equal)
* Functions that test the cardinality of sequences: [zero-or-one#1](https://www.w3.org/TR/xpath-functions-30/#func-zero-or-one), [one-or-more#1](https://www.w3.org/TR/xpath-functions-30/#func-one-or-more), [exactly-one#1](https://www.w3.org/TR/xpath-functions-30/#func-exactly-one)
* Aggregate functions: [count#1](https://www.w3.org/TR/xpath-functions-30/#func-count), [avg#1](https://www.w3.org/TR/xpath-functions-30/#func-avg), [max#1](https://www.w3.org/TR/xpath-functions-30/#func-max), [min#1](https://www.w3.org/TR/xpath-functions-30/#func-min), [sum#1](https://www.w3.org/TR/xpath-functions-30/#func-sum)
* Serializing functions: [serialize#1](https://www.w3.org/TR/xpath-functions-30/#func-serialize) (unary)
* Context information: [current-dateTime#1](https://www.w3.org/TR/xpath-functions-30/#func-current-dateTime), [current-date#1](https://www.w3.org/TR/xpath-functions-30/#func-current-date), [current-time#1](https://www.w3.org/TR/xpath-functions-30/#func-current-time), [implicit-timezone#1](https://www.w3.org/TR/xpath-functions-30/#func-implicit-timezone), [default-collation#1](https://www.w3.org/TR/xpath-functions-30/#func-default-collation)
* Constructor functions: for all builtin types, with the name of the builtin type and unary. Equivalent to a cast expression.


# Equality and identity

As in most language, one can distinguish between physical equality and logical equality.

Atomics can only be compared logically. Their physically identity is totally opaque to you.

### Logical comparison of two atomics

```

1 eq 1
    
```

Result (run with Zorba):true

### Logical comparison of two atomics

```

1 eq 2
    
```

Result (run with Zorba):false

### Logical comparison of two atomics

```

"foo" eq "bar"
    
```

Result (run with Zorba):false

### Logical comparison of two atomics

```

"foo" ne "bar"
    
```

Result (run with Zorba):true

Two objects or arrays can be tested for logical equality as well, using deep-equal(), which performs a recursive comparison.

### Logical comparison of two JSON items

```

deep-equal({ "foo" : "bar" }, { "foo" : "bar" })
    
```

Result (run with Zorba):true

### Logical comparison of two JSON items

```

deep-equal({ "foo" : "bar" }, { "bar" : "foo" })
    
```

Result (run with Zorba):false

The physical identity of objects and arrays is not exposed to the user in the core JSONiq language itself. Some library modules might be able to reveal it, though.


# Notes

### Sequences vs. Arrays <a href="#sequencesvsarrays.d12e6309" id="sequencesvsarrays.d12e6309"></a>

Even though JSON supports arrays, JSONiq uses a different construct as its first class citizens: sequences. Any value returned by or passed to an expression is a sequence.

The main difference between sequences and arrays is that sequences are completely flat, meaning they cannot contain other sequences.

Since sequences are flat, expressions of the JSONiq language just concatenate them to form bigger sequences.

This is crucial to allow streaming results, for example through an HTTP session.

#### Flat sequences

```

( (1, 2), (3, 4) )
      
```

Result (run with Zorba):1 2 3 4

Arrays on the other side can contain nested arrays, like in JSON.

#### Nesting arrays

```

[ [ 1, 2 ], [ 3, 4 ] ]
      
```

Result (run with Zorba):\[ \[ 1, 2 ], \[ 3, 4 ] ]

Many expressions return single items - actually, they really return a singleton sequence, but a singleton sequence of one item is considered the same as this item.

#### Singleton sequences

```

1 + 1
      
```

Result (run with Zorba):2

This is different for arrays: a singleton array is distinct from its unique member, like in JSON.

#### Singleton sequences

```

[ 1 + 1 ]
      
```

Result (run with Zorba):\[ 2 ]

An array is a single item. A (non-singleton) sequence is not. This can be observed by counting the number of items in a sequence.

#### count() on an array

```

count([ 1, "foo", [ 1, 2, 3, 4 ], { "foo" : "bar" } ])
      
```

Result (run with Zorba):1

#### count() on a sequence

```

count( ( 1, "foo", [ 1, 2, 3, 4 ], { "foo" : "bar" } ) )
      
```

Result (run with Zorba):4

Other than that, arrays and sequences can contain exactly the same members (atomics, arrays, objects).

#### Members of an array

```

[ 1, "foo", [ 1, 2, 3, 4 ], { "foo" : "bar" } ]
      
```

Result (run with Zorba):\[ 1, "foo", \[ 1, 2, 3, 4 ], { "foo" : "bar" } ]

#### Members of an sequence

```

( 1, "foo", [ 1, 2, 3, 4 ], { "foo" : "bar" } )
      
```

Result (run with Zorba):1 foo \[ 1, 2, 3, 4 ] { "foo" : "bar" }

Arrays can be converted to sequences, and vice-versa.

#### Converting an array to a sequence

```

[ 1, "foo", [ 1, 2, 3, 4 ], { "foo" : "bar" } ] []
      
```

Result (run with Zorba):1 foo \[ 1, 2, 3, 4 ] { "foo" : "bar" }

#### Converting a sequence to an array

```

[ ( 1, "foo", [ 1, 2, 3, 4 ], { "foo" : "bar" } ) ]
      
```

Result (run with Zorba):\[ 1, "foo", \[ 1, 2, 3, 4 ], { "foo" : "bar" } ]

### Null vs. empty sequence <a href="#nullvsemptysequence.d12e6437" id="nullvsemptysequence.d12e6437"></a>

Null and the empty sequence are two different concepts.

Null is an item (an atomic value), and can be a member of an array or of a sequence, or the value associated with a key in an object. Sequences cannot, as they represent the absence of any item.

#### Null values in an array

```

[ null, 1, null, 2 ]
      
```

Result (run with Zorba):\[ null, 1, null, 2 ]

#### Null values in an object

```

{ "foo" : null }
      
```

Result (run with Zorba):{ "foo" : null }

#### Null values in a sequence

```

(null, 1, null, 2)
      
```

Result (run with Zorba):null 1 null 2

If an empty sequence is found as an object value, it is automatically converted to null.

#### Automatic conversion to null.

```

{ "foo" : () }
      
```

Result (run with Zorba):{ "foo" : null }

In an arithmetic opration or a comparison, if an operand is an empty sequence, an empty sequence is returned. If an operand is a null, an error is raised except for equality and inequality.

#### Empty sequence in an arithmetic operation.

```

() + 2
      
```

Result (run with Zorba):

#### Null in an arithmetic operation.

```

null + 2
      
```

Result (run with Zorba):An error was raised: arithmetic operation not defined between types "js:null" and "xs:integer"

#### Null and empty sequence in an arithmetic operation.

```

null + ()
      
```

Result (run with Zorba):

#### Empty sequence in a comparison.

```

() eq 2
      
```

Result (run with Zorba):

#### Null in a comparison.

```

null eq 2
      
```

Result (run with Zorba):false

#### Null in a comparison.

```

null lt 2
      
```

Result (run with Zorba):true

#### Null and the empty sequence in a comparison.

```

null eq ()
      
```

Result (run with Zorba):

#### Null and the empty sequence in a comparison.

```

null lt ()
      
```

Result (run with Zorba):


# Open Issues

The [JSON update syntax](http://jsoniq.org/docs/JSONiqExtensionToXQuery/html/section-json-updates.html) was not integrated yet into the core language. This is planned, and the syntax will be simplified (no json keyword, dot lookup allowed here as well).

The semantics for the [JSON serialization method](http://jsoniq.org/docs/JSONiqExtensionToXQuery/html/section-json-serialization.html) is the same as in the JSONiq Extension to XQuery. It is still under discussion how to escape special characters with the Text output method.


# JSONiq 3.1

JSONiq 3.1 is an initiative of the RumbleDB team that aligns JSONiq more closely with XQuery 3.1, which has now become a W3C recommendation, but keeping what makes it JSONiq: the flagship feature being the ability to copy-paste JSON into a JSONiq query and with a navigation syntax conducive to Petabyte-scale querying and that appeals to the JSON community.

JSONiq 3.1 does not require a distinct data model (JDM) since XQuery 3.1 support maps and arrays. As a result, JSONiq 3.1 objects are a special case of XQuery 3.1 maps and JSONiq 3.1 arrays are a special case of XQuery 3.1 arrays.

JSONiq 3.1 does not require a separate serialization mechanism, since XQuery 3.1 supports the JSON output method.

JSONiq 3.1 benefits from all the map and object builtin functions defined in XQuery 3.1.

JSONiq 3.1 is fully interoperable with XQuery 3.1 and can execute on the same virtual machine (similar to Scala and Java). Concretely, XQuery and JSONiq modules can be imported and used in the same (XQuery or JSONiq) query.

This also paves the way for JSONiq 4.0 which will also be aligned with XQuery 4.0 as much as is technically possible.

As a result, the specification for JSONiq 3.1 is even more minimal than that of JSONiq 1.0. This makes it easy to support for any existing XQuery engine to step into the JSON community.

RumbleDB is slowly deploying the use of JSONiq 3.1 but it will take some time as we make sure to sweep in all corners.

## How JSONiq 3.1 amends XQuery 3.1

### Context item

In JSONiq 3.1, the context item is obtained through $$ and not through a dot.

{% hint style="info" %}
The dot is used for JSONiq's large-scale object lookup syntax.
{% endhint %}

### No dot in names

The . character is disallowed in variable names, function names and QName/NCName literals.

{% hint style="info" %}
The dot is used for JSONiq's large-scale object lookup syntax.
{% endhint %}

### Escaping in strings

String literals use JSON escaping instead of XML escaping (backslash, not ampersand).

{% hint style="info" %}
JSON-like escaping is more natural to the JSON community. This allows in particular copy-pasting (nested) JSON content directly into the query without modification.
{% endhint %}

### Map constructors

In map (object) constructors, the "map" keyword in front is optional.

{% hint style="info" %}
This is forward compatible with XQuery 4.0. This allows copy-pasting (nested) JSON content directly into the query without modification.
{% endhint %}

### Constraints on XPath

A name test on any of the element names true, false, or null must be prefixed with $$/ and cannot stand on its own.

{% hint style="info" %}
This allows for the parsing of true, false and null literals, allowing copy-pasting JSON content directly into the query without modification.
{% endhint %}

### True, null, and false literals

true and false exist as literals and do not have to be obtained through function calls (true(), false()).

null exists as a literal representing a special item. It is distinct from the empty sequence.

{% hint style="info" %}
The null literal is forward compatible with XQuery 4.0's semantics for json-doc() and parse-doc(), which enable the representation of JSON null literals as a special QName item rather than an empty sequence with the "null" parameter.
{% endhint %}

### Array constructors

The \[ ... ] array constructor has the semantics of XQuery 3.1's array { ... } constructor, in the sense that each item in the child expression's sequence becomes a member of the array.

{% hint style="info" %}
This behavior is familiar to the JSONiq community and is preserved.
{% endhint %}

### Navigation

The dot ., the double square brackets \[\[ ]], and the array unboxing syntax \[] are retained and follow the semantics of JSONiq 1.0. They come in addition to the XQuery 3.1's ? postfix and unary lookup syntax.

{% hint style="info" %}
The difference with ? lookup syntax is that . \[] and \[\[ ]] are semantically optimized for working flexibly on large-scale sequences of items, e.g., billions of items.&#x20;
{% endhint %}

## How JSONiq 3.1 differs from JSONiq 1.0

The data model standardized by the W3C working group is more generic and allows for atomic object keys that are not necessarily strings (dates, etc). Also, an object value or an array value can be a sequence of items and does not need to be a single item. The particular case in which object keys are strings and values are single items (or empty) corresponds to the JSON use.

Unquoted keys are not allowed in JSONiq 3.1 and are considered element name tests.

There are other minor changes in semantics that correspond to the alignment with XQuery 3.1 such as Effective Boolean Values, comparison, etc.


# JSONiq Update Facility

JSONiq follows the [XQuery Update Facility standard](https://www.w3.org/TR/xquery-update-30) and introduces update primitives and update expressions specific to JSON data.

In JSONiq, updates are not immediately applied. Rather, a snapshot of the current data is taken, and a list of updates, called the Pending Update List, is collected. Then, upon explicit request by the user (via specific expressions), the Pending Update List is applied atomically, leading to a new snapshot. It is also possible for an engine to persist (to the local disk, to a database management system, to a data lake...) the resulting Pending Update List after a query has been completed.

<br>


# JSON update primitives

A Pending Update List is an unordered list of update primitives. Update primitives are internal and do not appear in the syntax. Each kind of update primitive models one individual update to an object or an array.

A Pending Update List can by analogy be seen as the diff between two git revisions, and a single update primitive can be seen, with this same analogy, as the difference between two single lines of code. Thus, the JSONiq Update Facility is to trees what git is to lines of text: a "tree diff" language.

JSONiq adds the following new update primitives, specific to JSON. They are similar to those defined by the XQuery Update Facility for XML.

Update primitives within a PUL are applied with strict snapshot semantics. For examples, the positions are resolved against the array before the updates. Names are resolved on the object before the updates.

### Update primitive for objects and arrays (in collections or in memory)

<table><thead><tr><th width="282.91796875">Update primitive</th><th>Description</th></tr></thead><tbody><tr><td><p><em>jupd:insert-into-object(</em></p><p>  <em>$target as object(),</em></p><p>  <em>$content as object())</em></p></td><td>Inserts all pairs of the object $content into the object $target.</td></tr><tr><td><p><em>jupd:insert-into-array(</em></p><p>  <em>$target as array(),</em></p><p>  <em>$position as xs:integer,</em></p><p>  <em>$content as item()*)</em></p></td><td>Inserts all items in the sequence $content before position $position into the array $target.</td></tr><tr><td><p><em>jupd:delete-from-object(</em></p><p>  <em>$target as object(),</em></p><p>  <em>$keys as xs:string*)</em></p></td><td>Removes the pairs the names of which appear in $keys from the object $target.</td></tr><tr><td><p><em>jupd:delete-from-array(</em></p><p>  <em>$target as array(),</em></p><p>  <em>$position as xs:integer)</em></p></td><td>Removes the item at position $position from the array $target (causes all following items in the array to move one position to the left).</td></tr><tr><td><p><em>jupd:replace-in-array(</em></p><p>  <em>$target as array(),</em></p><p>  <em>$position as xs:integer,</em></p><p>  <em>$content as item())</em></p></td><td>Replaces the item at position $position in the array $target with the item $content (do nothing if $position is not comprised between 1 and jdm:size($target)).</td></tr><tr><td><p><em>jupd:replace-in-object(</em></p><p>  <em>$target as object(),</em></p><p>  <em>$key as xs:string,</em></p><p>  <em>$content as item())</em></p></td><td>Replaces the value of the pair named $key in the object $target with the item $content (do nothing if there is no such pair).</td></tr><tr><td><p><em>jupd:rename-in-object(</em></p><p>  <em>$target as object(),</em></p><p>  <em>$key as xs:string,</em></p><p>  <em>$content as xs:string)</em></p></td><td>Renames the pair originally named $key in the object $target as $content (do nothing if there is no such pair).</td></tr></tbody></table>

### Update primitives at the collection level

*Credits: Dwij Dixit/Ghislain Fourny (student project at ETH)*

<table><thead><tr><th width="283.4296875">Update primitive</th><th>Description</th></tr></thead><tbody><tr><td><p><em>jupd:create-collection(</em></p><p>  <em>$name as string,</em></p><p>  <em>$mode as string,</em></p><p>  <em>$content as item()*)</em></p></td><td>Creates a collection initialized with the provided items. Mode determines the kind of collection (e.g., a Hive metastore table, a delta lake file, etc).</td></tr><tr><td><p><em>jupd:truncate-collection(</em></p><p>  <em>$name as string,</em></p><p>  <em>$mode as string)</em></p></td><td>Deletes the specified collection.</td></tr><tr><td><p><em>jupd:edit(</em></p><p>  <em>$target as item(),</em></p><p>  <em>$content as item())</em></p></td><td>Modifies an item in a collection into another item, preserving its identity and location.</td></tr><tr><td><p><em>jupd:delete-in-collection(</em></p><p>  <em>$target as item())</em></p></td><td>Deletes the provided item from its collection.</td></tr><tr><td><p><em>jupd:insert-first-into-collection(</em></p><p>  <em>$name as string,</em></p><p>  <em>$mode as string,</em></p><p>  <em>$content as item()*)</em></p></td><td>Inserts the provided items at the very beginning of the specified collection.</td></tr><tr><td><p><em>jupd:insert-last-into-collection(</em></p><p>  <em>$name as string,</em></p><p>  <em>$mode as string,</em></p><p>  <em>$content as item()*)</em></p></td><td>Inserts the provided items at the very end of the specified collection.</td></tr><tr><td><p><em>jupd:insert-before-into-collection(</em></p><p>  <em>$target as item,</em></p><p>  <em>$content as item()*)</em></p></td><td>Inserts the provided items before the specified item in its collection.</td></tr><tr><td><p><em>jupd:insert-last-into-collection(</em></p><p>  <em>$target as item,</em></p><p>  <em>$content as item()*)</em></p></td><td>Inserts the provided items after the specified item in its collection.</td></tr></tbody></table>

<br>


# Merging updates

In the middle of a program, several PULs can be produced against the same snapshot. They are then merged with upd:mergeUpdates (part of the XQuery Update Facility standard), which is extended as follows.

* Several deletes on the same object are replaced with a unique delete on that object, with a list of all selectors (names) to be deleted, where duplicates have been eliminated.
* Several deletes on the same array and selector (position) are replaced with a unique delete on that array and with that selector.
* Several inserts on the same array and selector (position) are equivalent to a unique insert on that array and selector with the content of those original inserts appended in an implementation-dependent order (like XQUF).
* Several inserts on the same object are equivalent to a unique insert where the objects containing the pairs to insert are merged. An error jerr:JNUP0005 is raised if a collision occurs.
* Several replaces on the same object or array and with the same selector raise an error jerr:JNUP0009.
* Several renames on the same object and with the same selector raise an error jerr:JNUP0010.
* If there is a replace and a delete on the same object or array and with the same selector, the replace is omitted in the merged PUL.
* If there is a rename and a delete on the same object or array and with the same selector, the rename is omitted in the merged PUL.

<br>


# Applying updates

At the end of an updating program, the resulting PUL is applied with upd:applyUpdates (part of the XQuery Update Facility standard), which is extended as follows:

* First, before applying any update, each update primitive (except the jupd:insert-into-object primitives, which do not have any target) locks onto its target by resolving the selector on the object or array it updates. If the selector is resolved to the empty sequence, the update primitive is ignored in step 2. After this operation, each of these update primitives will contain a reference to either the pair (for an object) or the value (for an array) on or relatively to which it operates.
* Then each update primitive is applied, using the target references that were resolved at step 1. The order in which they are applied is not relevant and does not affect the final instance of the data model. After applying all updates, an error jerr:JNUP0006 is raised upon pair name collision within the same object.


# The transform expression

<br>

<figure><img src="https://www.jsoniq.org/docs/JSONiq/webhelp/images/CopyModifyExpr.png" alt=""><figcaption></figcaption></figure>

Updates can be applied to a clone of an existing instance with the [copy-modify-return](https://www.w3.org/TR/xquery-update-30/#id-copy-modify) expression.

The content of the modify clause may build a complex Pending Update List with multiple updates. Remember that, with snapshot semantics, each update is applied against the initial snapshot, and updates do not see each other's effects.

Updating expression can also be combined with conditional expressions (in the then and else clauses), switch expressions (in the return clauses), FLWOR expressions (in the return clause), etc for more powerful queries based on patterns in the available data (from any source visible to the JSONiq query).

The updates generated inside the modify clause may only target the cloned object, i.e., the variable specified in the copy clause.

**Example 191. JSON copy-modify-return expression**

```
copy $obj := { "foo" : "bar", "bar" : [ 1,2,3 ] }
modify (
  insert json { "bar" : 123, "foobar" : [ true, false ] } into $obj,
  delete json $obj.bar,
  replace value of json $obj.foo with true
)
return $obj
      
```

**Result:** { "foo" : true, "bar" : 123, "foobar" : \[ true, false ] }

<br>

In the remainder of this chapter, we showcase the individual updating expressions one by one, inside a copy-modify-return expression.

Update expressions can also appear outside of a copy-modify-return expression, in which case they propagate and/or persist directly to their targets, to the extent that the context makes it meaningful and possible.

<br>


# Primary updating expressions

Update expressions are the visible part of JSONiq Updates in the language. Each primary updating expression contributes an update primitive to the Pending Update List being built.

## Nested updates (memory or persistent)

These expressions may appear in a copy-modify-return (transform) expression (for in-memory updates on cloned values), or outside (for persistent updates to an underlying storage).

### **Inserting values into an object or array**

<br>

<figure><img src="https://www.jsoniq.org/docs/JSONiq/webhelp/images/JSONInsertExpr.png" alt=""><figcaption></figcaption></figure>

A JSON insert expression is used to insert new pairs into an object. It produces a *jupd:insert-into-object* update primitive. If the target is not an object, JNUP0008 is raised. If the content is not a sequence of objects, JNUP0019 is raised. These objects are merged prior to inserting the pairs into the target, and JNDY0003 is raised if the content to be inserted has colliding keys.

**Example**&#x20;

```
copy $obj := { "foo" : "bar" }
modify insert json { "bar" : 123, "foobar" : [ true, false ] } into $obj
return $obj
      
```

**Result:** { "foo" : "bar", "bar" : 123, "foobar" : \[ true, false ] }

A JSON insert expression is also used to insert a new member into an array. It produces a *jupd:insert-into-array* update primitive. If the target is not an array, JNUP0008 is raised. If the position is not an integer, JNUP0007 is raised.

**Example**

```
copy $arr := { "foo" : [1,2,3,4] }
modify insert json 5 into $arr.foo at position 3
return $arr
      
```

**Result:** { "foo" : \[ 1, 2, 5, 3, 4 ] }

### **Deleting values in an object or array**

<br>

<figure><img src="https://www.jsoniq.org/docs/JSONiq/webhelp/images/JSONDeleteExpr.png" alt=""><figcaption></figcaption></figure>

A JSON delete expression is used to remove a pair from an object. It produces a *jupd:delete-from-object* update primitive. If the key is not a string, JNUP0007 is raised. If the key does not exist, JNUP0016 is raised.

**Example**&#x20;

```
copy $obj := { "foo" : "bar", "bar" : 123 }
modify delete json $obj.foo
return $obj
      
```

**Result:** { "bar" : 123 }

A JSON delete expression is also used to remove a member from an array. It produces a *jupd:insert-from-array* update primitive. If the position is not an integer, JNUP0007 is raised. If the position is out of range, JNUP0016 is raised.

**Example**&#x20;

```
copy $arr := [1,2,3,4,5,6]
modify delete json $arr[[3]]
return $arr
      
```

**Result:** \[ 1, 2, 4, 5, 6 ]

<br>

### **Renaming a key**

<br>

<figure><img src="https://www.jsoniq.org/docs/JSONiq/webhelp/images/JSONRenameExpr.png" alt=""><figcaption></figcaption></figure>

A JSON rename expression is used to rename a key in an object. It produces a *jupd:rename-in-object* update primitive. If the sequence on the left of the dot is not a single object, JNUP0008 is raised. If the new name is not a single string, JNUP0007 is raised. If the old key does not exist, JNUP0016 is raised.

**Example 196. JSON rename expression**

```
copy $obj := { "foo" : "bar", "bar" : 123 }
modify rename json $obj.foo as "foobar"
return $obj
      
```

**Result:** { "foobar" : "bar", "bar" : 123 }

### **Appending values to an array**

<br>

<figure><img src="https://www.jsoniq.org/docs/JSONiq/webhelp/images/JSONAppendExpr.png" alt=""><figcaption></figcaption></figure>

A JSON append expression is used to add a new member at the end of an array. It produces a *jupd:insert-into-array* update primitive. JNUP0008 is raised if the target is not an array.

**Example 197. JSON append expression**

```
copy $obj := { "foo" : "bar", "bar" : [1,2,3] }
modify append json 4 into $obj.bar
return $obj
      
```

**Result:** { "foo" : "bar", "bar" : \[ 1, 2, 3, 4 ] }<br>

### **Replacing a value in an object or array.**<br>

<figure><img src="https://www.jsoniq.org/docs/JSONiq/webhelp/images/JSONReplaceExpr.png" alt=""><figcaption></figcaption></figure>

A JSON replace expression is used to replace the value associated with a certain key in an object. It produces a *jupd:replace-in-object* update primitive. JNUP0007 is raised if the selector is not a single string. If the selector key does not exist, JNUP0016 is raised.

**Example**&#x20;

```
copy $obj := { "foo" : "bar", "bar" : [1,2,3] }
modify replace value of json $obj.foo with { "nested" : true }
return $obj
      
```

**Result:** { "foo" : { "nested" : true }, "bar" : \[ 1, 2, 3 ] }

A JSON replace expression is also used to replace a member in an array. It produces a *jupd:insert-in-array* update primitive. JNUP0007 is raised if the selector is not a single position. If the selector position is out of range, JNUP0016 is raised.

**Example**

```
copy $obj := { "foo" : "bar", "bar" : [1,2,3] }
modify replace value of json $obj.bar[[2]] with "two"
return $obj
      
```

**Result:** { "foo" : "bar", "bar" : \[ 1, "two", 3 ] }

## Update expressions at the collection top-level (persistent only)

These expressions may not appear in a copy-modify-return (transform) expression because they can only be used for persistent updates to an underlying storage (document store, data lakehouse, etc).

### Creating a collection

<figure><img src="/files/qJgC5JzfcVispod670Pq" alt=""><figcaption></figcaption></figure>

This expression creates an update primitive that creates a collection.

**Example**&#x20;

```
create collection table("mytable") with ({"foo":1},{"foo":2}),
create collection delta-file("/path/to/file.delta") with ({"foo":1},{"foo":2})
```

### Deleting a collection

<figure><img src="/files/3JgR0ELXUfHubeooYsho" alt=""><figcaption></figcaption></figure>

This expression creates an update primitive that deletes a collection.

**Example**&#x20;

```
delete collection table("mytable"),
delete collection delta-file("/path/to/file.delta")
```

### Inserting into a collection

<figure><img src="/files/L75WIyWyqKZ3wQ4NuWK1" alt=""><figcaption></figcaption></figure>

This expression creates an update primitive that inserts values at the beginning or end of a collection, or before or after specific values in that collection.

**Example**

```
insert {"foo":3} first into collection table("mytable"),
insert {"foo":4} last into collection delta-file("/path/to/file.delta"),
insert {"foo":3} before table("mytable")[3] into collection,
insert {"foo":3} after delta-file("/path/to/file.delta")[3] into collection
```

### Editing a value in a collection

<figure><img src="/files/TKNFwNAqxAcmOLBwGNuJ" alt=""><figcaption></figcaption></figure>

This expression creates an update primitive that modifies a value in a collection into the other supplied value.

**Example**

```
edit table("mytable")[1] into {"foo":3} in collection
```

### Deleting a value from a collection

<figure><img src="/files/7E0hw5ICE4Y6u83CSM6h" alt=""><figcaption></figcaption></figure>

This expression creates an update primitive that deletes a specified value from its collection.

**Example**

```
delete table("mytable")[1] from collection
```


# RumbleDB Reference


# JSONiq coverage

RumbleDB relies on the JSONiq language.

## JSONiq reference

The complete specification can be found [here](/the-jsoniq-language/jsoniq-specification) and on the [JSONiq.org](http://www.jsoniq.org) website. The implementation is now in a very advanced stage and there remain only few unsupported core JSONiq features.

## JSONiq tutorial

A tutorial can be found [here](https://github.com/ghislainfourny/jsoniq-tutorial). All queries in this tutorial will work with RumbleDB.

## JSONiq tutorial for Python users

A tutorial aimed at Python users can be found [here](https://github.com/ghislainfourny/jsoniq-tutorial-python). Please keep in mind, though, that examples using not supported features may not work (see below).

## Nested FLWOR expressions

FLWOR expressions now support nestedness, for example like so:

```
let $x := for $x in json-lines("file.json")
          where $x.field eq "foo"
          return $x
return count($x)
```

However, keep in mind that parallelization cannot be nested in Spark (there cannot be a job within a job), that is, the following will not work:

```
for $x in json-lines("file1.json")
let $z := for $y in json-lines("file2.json")
          where $y.foo eq $x.fbar
          return $y
return count($z)
```

## Expressions pushed down to Spark

Many expressions are pushed down to Spark out of the box. For example, this will work on a large file leveraging the parallelism of Spark:

```
count(json-lines("file.json")[$$.field eq "foo"].bar[].foo[[1]])
```

What is pushed down so far is:

* FLWOR expressions (as soon as a for clause is encountered, binding a variable to a sequence generated with json-lines() or parallelize())
* aggregation functions such as count
* JSON navigation expressions: object lookup (as well as keys() call), array lookup, array unboxing, filtering predicates
* predicates on positions, include use of context-dependent functions position() and last(), e.g.,
* type checking (instance of, treat as)
* many builtin function calls (head, tail, exist, etc)

```
json-lines("file.json")[position() ge 10 and position() le last() - 2]
```

More expressions working on sequences will be pushed down in the future, prioritized on the feedback we receive.

We also started to push down some expressions to DataFrames and Spark SQL (obtained via structured-json-lines, csv-file and parquet-file calls). In particular, keys() pushes down the schema lookup if used on parquet-file() and structured-json-lines(). Likewise, count() as well as object lookup, array unboxing and array lookup is also pushed down on DataFrames.

When an expression does not support pushdown, it will materialize automaticaly. To avoid issues, the materializion is capped by default at 200 items, but this can be changed on the command line with --materialization-cap. A warning is issued if a materialization happened and the sequence was truncated on screen. An error is thrown if this happens within a query.

## External global variables.

Prologs with user-defined functions and global variables are supported. Global external variables are supported (use "--variable:foo bar" on the command line to assign values to them). If the declared type is not string, then the literal supplied on the command line is cast. If the declared type is anyURI, the path supplied on the command line is also resolved against the working directory to an absolute URI. Thus, anyURI should be used to supply paths dynamically through an external variable.

Context item declarations are supported and a global context item value can be passed with the "--context-item" or "-I" parameter on the command line.

## Library modules

Library modules are now supported (experimental, please report bugs), and their namespace URI is used for resolution. If it is relative, it is resolved against the importing module location.

The same schemes are supported as for reading queries and data: file, hdfs, and so on. HTTP is also supported: you can import modules from the Web!

Example of library module (the file name is library-module.jq):

```
module namespace m = "library-module.jq";

declare variable $m:x := 2;

declare function mod:func($v) {
  $m:x + $v
);
```

Example of importing module (assuming it is in the same directory):

```
import module namespace mod = "library-module.jq";

mod:func($mod:x)
```

### Try/catch

Try/catch expressions are supported. Error codes are in the default, RumbleDB namespace and do not need prefixes.

```
try { 1 div 0 } catch FOAR0001 { "Division by zero!" }
```

### Supported types

The JSONiq type system is fully supported. Below is a complete list of JSONiq types and their support status. All builtin types are in the default type namespace, so that no prefix is needed. These types are defined in the XML Schema standard. Note that some types specific to XML (e.g., NOTATION, NMTOKENS, NMTOKEN, ID, IDREF, ENTITY, etc) are not part of the JSONiq standard and not supported by RumbleDB.

| Type               | Status          |
| ------------------ | --------------- |
| atomic             | JSONiq 1.0 only |
| anyAtomicType      | supported       |
| anyURI             | supported       |
| base64Binary       | supported       |
| boolean            | supported       |
| byte               | supported       |
| date               | supported       |
| dateTime           | supported       |
| dateTimeStamp      | supported       |
| dayTimeDuration    | supported       |
| decimal            | supported       |
| double             | supported       |
| duration           | supported       |
| float              | supported       |
| gDay               | supported       |
| gMonth             | supported       |
| gYear              | supported       |
| gYearMonth         | supported       |
| hexBinary          | supported       |
| int                | supported       |
| integer            | supported       |
| long               | supported       |
| negativeInteger    | supported       |
| nonPositiveInteger | supported       |
| nonNegativeInteger | supported       |
| numeric            | supported       |
| positiveInteger    | supported       |
| short              | supported       |
| string             | supported       |
| time               | supported       |
| unsignedByte       | supported       |
| unsignedInt        | supported       |
| unsignedLong       | supported       |
| unsignedShort      | supported       |
| yearMonthDuration  | supported       |

## Unsupported/Unimplemented features (beta release)

Most core features of JSONiq are now in place, and we are working on getting the last (less used) ones into RumbleDB as well. We prioritize their implementation on user requests.

### Prolog

Some prolog settings (base URI, ordering mode, decimal format, namespace declarations) are not supported yet.

Location hints for the resolution of modules are not supported yet.

### FLWOR features

Window clauses are not supported, because they are not compatible with the Spark execution model.

### Function types

Function type syntax is supported.

Function annotations are not supported (%public, %private...), but this is planned.

### Builtin functions

Most JSONiq and XQuery builtin functions are now supported (see function documentation), except XML-specific functions. A few are still missing, do not hesitate to reach out if you need them.

Constructors for atomic types are fully supported.

Buitin functions cannot yet be used with named function reference expressions (example: concat#2).

### Error variables

Error variables ($err:code, ...) for inside catch blocks are not supported.

### Updates and scripting

There are future plans to support JSONiq updates and scripting.


# Function library

We list here the most important functions supported by RumbleDB, and introduce them by means of examples. Highly detailed specifications can be found in the [underlying W3C standard](https://www.w3.org/TR/xpath-functions-30/#func-floor), unless the function is marked as specific to JSON or RumbleDB, in which case it can be found [here](http://www.jsoniq.org/docs/JSONiq/html-single/index.html#idm34604304). JSONiq and RumbleDB intentionally do not support builtin functions on XML nodes, NOTATION or QNames. RumbleDB supports almost all other W3C-standardized functions, please contact us if you are still missing one.

For the sake of ease of use, all W3C standard builtin functions and JSONiq builtin functions are in the RumbleDB namespace, which is the default function namespace and does not require any prefix in front of function names.

It is recommended that user-defined functions are put in the local namespace, i.e., their name should have the local: prefix (which is predefined). Otherwise, there is the risk that your code becomes incompatible with subsequent releases if new (unprefixed) builtin functions are introduced.

## Errors and diagnostics

### Diagnostic tracing

### trace

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-trace)

Fully implemented

```
trace(1 to 3)
```

returns (1, 2, 3) and logs it in the log-path if specified

## Functions and operators on numerics

### Functions on numeric values

### abs

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-abs)

Fully implemented

```
abs(-2)
```

returns 2.0

### ceiling

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-ceiling)

Fully implemented

```
ceiling(2.3)
```

returns 3.0

### floor

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-floor)

Fully implemented

```
floor(2.3)
```

returns 2.0

### round

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-round)

Fully implemented

```
round(2.3)
```

returns 2.0

```
round(2.2345, 2)
```

returns 2.23

### round-half-to-even

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-round-half-to-even)

Fully implemented

```
round-half-to-even(2.2345, 2), round-half-to-even(2.2345)
```

### Parsing numbers

### number

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-number)

Fully implemented

```
number("15")
```

returns 15 as a double

```
number("foo")
```

returns NaN as a double

```
number(15)
```

returns 15 as a double

### Formatting integers

### format-integer

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-format-integer)

Not implemented

\##Formatting numbers

### format-number

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-format-number)

Not implemented

\##Trigonometric and exponential functions

\###pi [W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-pi)

Fully implemented

```
pi()
```

returns 3.141592653589793

\###exp [W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-exp)

Fully implemented

```
exp(10)
```

\###exp10 [W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-exp10)

Fully implemented

```
exp10(10)
```

### log

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-log)

Fully implemented

```
log(100)
```

### log10

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-log10)

Fully implemented

```
log10(100)
```

### pow

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-pow)

Fully implemented

```
pow(10, 2)
```

### sqrt

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-sqrt)

Fully implemented

```
sqrt(4)
```

returns 2

### sin

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-sin)

Fully implemented

```
sin(pi())
```

### cos

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-cos)

Fully implemented

```
cos(pi())
```

### cosh

JSONiq-specific. Fully implemented

```
cosh(pi())
```

### sinh

JSONiq-specific. Fully implemented

```
sinh(pi())
```

### tan

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-tan)

Fully implemented

```
tan(pi())
```

### asin

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-asin)

Fully implemented

```
asin(1)
```

### acos

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-acos)

Fully implemented

```
acos(1)
```

### atan

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-atan)

Fully implemented

```
atan(1)
```

### atan2

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-atan2)

Fully implemented

```
atan2(1)
```

### Random numbers

### random-number-generator

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-random-number-generator)

Not implemented

## Functions on strings

### Functions to assemble and disassemble strings

### string-to-codepoint

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-string-to-codepoint)

Fully implemented

```
string-to-codepoints("Thérèse")
```

returns (84, 104, 233, 114, 232, 115, 101)

```
string-to-codepoints("")
```

returns ()

### codepoints-to-string

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-codepoints-to-string)

Fully implemented

```
codepoints-to-string((2309, 2358, 2378, 2325))
```

returns "अशॊक"

```
codepoints-to-string(())
```

returns ""

### Comparison of strings

### compare

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-compare)

Fully implemented

```
compare("aa", "bb")
```

returns -1

### codepoint-equal

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-codepoint-equal)

Fully implemented

```
codepoint-equal("abcd", "abcd")
```

returns true

```
codepoint-equal("", ())
```

returns ()

### collation-key

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-collation-key)

Not implemented

### contains-token

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-contains-token)

Not implemented

### Functions on string values

### concat

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-concat)

Fully implemented

```
concat("foo", "bar", "foobar")
```

returns "foobarfoobar"

### string-join

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-string-join)

Fully implemented

```
string-join(("foo", "bar", "foobar"))
```

returns "foobarfoobar"

```
string-join(("foo", "bar", "foobar"), "-")
```

returns "foo-bar-foobar"

### substring

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-substring)

Fully implemented

```
substring("foobar", 4)
```

returns "bar"

```
substring("foobar", 4, 2)
```

returns "ba"

### string-length

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-string-length)

Fully implemented

Returns the length of the supplied string, or 0 if the empty sequence is supplied.

```
string-length("foo")
```

returns 3.

```
string-length(())
```

returns 0.

\###normalize-space [W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-normalize-space)

Fully implemented

Normalization of spaces in a string.

```
normalize-space(" The    wealthy curled darlings                                         of    our    nation. "),
```

returns "The wealthy curled darlings of our nation."

### normalize-unicode

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-normalize-unicode)

Fully implemented

Returns the value of the input after applying Unicode normalization.

```
normalize-unicode("hello world", "NFC")
```

returns the unicode-normalized version of the input string. Normalization forms NFC, NFD, NFKC, and NFKD are supported. "FULLY-NORMALIZED" though supported, should be used with caution as only the composition exclusion characters supported FULLY-NORMALIZED are which are uncommented in the [following file](https://www.unicode.org/Public/UCD/latest/ucd/CompositionExclusions.txt).

### upper-case

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-upper-case)

Fully implemented

```
upper-case("abCd0")
```

returns "ABCD0"

### lower-case

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-lower-case)

Fully implemented

```
lower-case("ABc!D")
```

returns "abc!d"

### translate

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-translate)

Fully implemented

```
translate("bar","abc","ABC")
```

returns "BAr"

```
translate("--aaa--","abc-","ABC")
```

returns "AAA"

### Functions based on substring matching

### contains

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-contains)

Fully implemented

```
contains("foobar", "ob")
```

returns true.

### starts-with

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-starts-with)

Fully implemented

```
starts-with("foobar", "foo")
```

returns true

### ends-with

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-ends-with)

Fully implemented

```
ends-with("foobar", "bar")
```

returns true.

### substring-before

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-substring-before)

Fully implemented

```
substring-before("foobar", "bar")
```

returns "foo"

```
substring-before("foobar", "o")
```

returns "f"

### substring-after

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-substring-after)

Fully implemented

```
substring-after("foobar", "foo")
```

returns "bar"

```
substring-after("foobar", "r")
```

returns ""

### String functions that use regular expressions

### matches

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-matches)

Arity 2 implemented, arity 3 is not.

Regular expression matching. The semantics of regular expressions are those of Java's Pattern class.

```
matches("foobar", "o+")
```

returns true.

```
matches("foobar", "^fo+.*")
```

returns true.

### replace

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-replace)

Arity 3 implemented, arity 4 is not.

Regular expression matching and replacing. The semantics of regular expressions are those of Java's Pattern class.

```
replace("abracadabra", "bra", "*")
```

returns "a\*cada\*"

```
replace("abracadabra", "a(.)", "a$1$1")
```

returns "abbraccaddabbra"

### tokenize

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-tokenize)

Arity 2 implemented, arity 3 is not.

```
tokenize("aa bb cc dd")
```

returns ("aa", "bb", "cc", "dd")

```
tokenize("aa;bb;cc;dd", ";")
```

returns ("aa", "bb", "cc", "dd")

### analyze-string

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-analyze-string)

Not implemented

## Functions that manipulate URIs

### resolve-uri

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-resolve-uri)

Fully implemented

```
string(resolve-uri("examples","http://www.examples.com/"))
```

returns <http://www.examples.com/examples>

### encode-for-uri

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-encode-for-uri)

Fully implemented

```
encode-for-uri("100% organic")
```

returns 100%25%20organic

### iri-to-uri

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-iri-to-uri)

Not implemented

### escape-html-uri

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-escape-html-uri)

Not implemented

## Functions and operators on Boolean values

### Boolean constant functions

### true

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-true)

Fully implemented

```
fn:true()
```

returns true

### false

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-false)

Fully implemented

```
fn:false()
```

returns false

### boolean

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-boolean)

Fully implemented

```
boolean(9)
```

returns true

```
boolean("")
```

returns false

### not

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-not)

Fully implemented

```
not(9)
```

returns false

```
boolean("")
```

returns true

## Functions and operators on durations

### Component extraction functions on durations

### years-from-duration

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-years-from-duration)

Fully implemented

```
years-from-duration(duration("P2021Y6M"))
```

returns 2021.

### months-from-duration

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-months-from-duration)

Fully implemented

```
months-from-duration(duration("P2021Y6M"))
```

returns 6.

### days-from-duration

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-days-from-duration)

Fully implemented

```
days-from-duration(duration("P2021Y6M17D"))
```

returns 17.

### hours-from-duration

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-hours-from-duration)

Fully implemented

```
hours-from-duration(duration("P2021Y6M17DT12H35M30S"))
```

returns 12.

### minutes-from-duration

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-minutes-from-duration)

Fully implemented

```
minutes-from-duration(duration("P2021Y6M17DT12H35M30S"))
```

returns 35.

### seconds-from-duration

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-seconds-from-duration)

Fully implemented

```
minutes-from-duration(duration("P2021Y6M17DT12H35M30S"))
```

returns 30.

## Functions and operators on dates and times

### Constructing a DateTime

### dateTime

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-dateTime)

Fully implemented

```
dateTime("2004-04-12T13:20:00+14:00")
```

returns 2004-04-12T13:20:00+14:00

### Component extraction functions on dates and times

### year-from-dateTime

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-year-from-dateTime)

Fully implemented

```
year-from-dateTime(dateTime("2021-04-12T13:20:32.123+02:00"))
```

returns 2021.

### month-from-dateTime

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-month-from-dateTime)

Fully implemented

```
month-from-dateTime(dateTime("2021-04-12T13:20:32.123+02:00"))
```

returns 04.

### day-from-dateTime

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-day-from-dateTime)

Fully implemented

```
day-from-dateTime(dateTime("2021-04-12T13:20:32.123+02:00"))
```

returns 12.

### hours-from-dateTime

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-hours-from-dateTime)

Fully implemented

```
hours-from-dateTime(dateTime("2021-04-12T13:20:32.123+02:00"))
```

returns 13.

### minutes-from-dateTime

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-minutes-from-dateTime)

Fully implemented

```
minutes-from-dateTime(dateTime("2021-04-12T13:20:32.123+02:00"))
```

returns 20.

### seconds-from-dateTime

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-seconds-from-dateTime)

Fully implemented

```
seconds-from-dateTime(dateTime("2021-04-12T13:20:32.123+02:00"))
```

returns 32.

### timezone-from-dateTime

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-timezone-from-dateTime)

Fully implemented

```
timezone-from-dateTime(dateTime("2021-04-12T13:20:32.123+02:00"))
```

returns PT2H.

### year-from-date

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-year-from-date)

Fully implemented

```
year-from-date(date("2021-06-04"))
```

returns 2021.

### month-from-date

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-month-from-date)

Fully implemented

```
month-from-date(date("2021-06-04"))
```

returns 6.

### day-from-date

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-day-from-date)

Fully implemented

```
day-from-date(date("2021-06-04"))
```

returns 4.

### timezone-from-date

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-timezone-from-date)

Fully implemented

```
timezone-from-date(date("2021-06-04-14:00"))
```

returns -PT14H.

### hours-from-time

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-hours-from-time)

Fully implemented

```
hours-from-time(time("13:20:32.123+02:00"))
```

returns 13.

### minutes-from-time

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-minutes-from-time)

Fully implemented

```
minutes-from-time(time("13:20:32.123+02:00"))
```

returns 20.

### seconds-from-time

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-seconds-from-time)

Fully implemented

```
seconds-from-time(time("13:20:32.123+02:00"))
```

returns 32.123.

### timezone-from-time

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-timezone-from-time)

Fully implemented

```
timezone-from-time(time("13:20:32.123+02:00"))
```

returns PT2H.

### Timezone adjustment functions on dates and time values

### adjust-dateTime-to-timezone

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-adjust-dateTime-to-timezone)

Fully implemented

```
adjust-dateTime-to-timezone(dateTime("2004-04-12T13:20:15+14:00"), dayTimeDuration("PT4H5M"))
```

returns 2004-04-12T03:25:15+04:05.

### adjust-date-to-timezone

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-adjust-date-to-timezone)

Fully implemented

```
adjust-date-to-timezone(date("2014-03-12"), dayTimeDuration("PT4H"))
```

returns 2014-03-12+04:00.

### adjust-time-to-timezone

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-adjust-time-to-timezone)

Fully implemented

```
adjust-time-to-timezone(time("13:20:00-05:00"), dayTimeDuration("-PT14H"))
```

returns 04:20:00-14:00.

### Formatting dates and times functions

The functions in this section accept a simplified version of the picture string, in which a variable marker accepts only:

* One of the following component specifiers: Y, M, d, D, F, H, m, s, P
* A first presentation modifier, for which the value can be:
  * Nn, for all supported component specifiers, besides P
  * N, if the component specifier is P
  * a format token that indicates a numbering sequence of the the following form: '0001'
* A second presentation modifier, for which the value can be t or c, which are also the default values
* A width modifier, both minimum and maximum values

### format-dateTime

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-format-dateTime)

Fully implemented

```
format-dateTime(dateTime("2004-04-12T13:20:00"), "[m]-[H]-[D]-[M]-[Y]")
```

returns 20-13-12-4-2004

### format-date

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-format-date)

Fully implemented

```
format-date(date("2004-04-12"), "[D]-[M]-[Y]")
```

returns 12-4-2004

### format-time

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-format-time)

Fully implemented

```
format-time(time("13:20:00"), "[H]-[m]-[s]")
```

returns 13-20-0

## Functions related to QNames

Not implemented

## Functions and operators on sequences

### General functions and operators on sequences

### empty

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-empty)

Fully implemented

Returns a boolean whether the input sequence is empty or not.

```
empty(1 to 10)
```

returns false.

### exists

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-exists)

Fully implemented

Returns a boolean whether the input sequence has at least one item or not.

```
exists(1 to 10)
```

returns true.

```
exists(())
```

returns false.

This is pushed down to Spark and works on big sequences.

```
exists(json-lines("file.json"))
```

### head

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-head)

Fully implemented

Returns the first item of a sequence, or the empty sequence if it is empty.

```
head(1 to 10)
```

returns 1.

```
head(())
```

returns ().

This is pushed down to Spark and works on big sequences.

```
head(json-lines("file.json"))
```

### tail

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-tail)

Fully implemented

Returns all but the last item of a sequence, or the empty sequence if it is empty.

```
tail(1 to 5)
```

returns (2, 3, 4, 5).

```
tail(())
```

returns ().

This is pushed down to Spark and works on big sequences.

```
tail(json-lines("file.json"))
```

### insert-before

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-insert-before)

Fully implemented

```
insert-before((3, 4, 5), 0, (1, 2))
```

returns (1, 2, 3, 4, 5).

### remove

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-remove)

Fully implemented

```
remove((1, 2, 10), 3)
```

returns (1, 2).

### reverse

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-reverse)

Fully implemented

```
remove((1, 2, 3))
```

returns (3, 2, 1).

### subsequence

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-subsequence)

Fully implemented

```
subsequence((1, 2, 3), 2, 5)
```

returns (2, 3).

### unordered

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-unordered)

Fully implemented

```
unordered((1, 2, 3))
```

returns (1, 2, 3).

### Functions that compare values in sequences

### distinct-values

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-distinct-values)

Fully implemented

Eliminates duplicates from a sequence of atomic items.

```
distinct-values((1, 1, 4, 3, 1, 1, "foo", 4, "foo", true, 3, 1, true, 5, 3, 1, 1))
```

returns (1, 4, 3, "foo", true, 5).

This is pushed down to Spark and works on big sequences.

```
distinct-values(json-lines("file.json").foo)
```

```
distinct-values(text-file("file.txt"))
```

### index-of

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-index-of)

Fully implemented

```
index-of((10, 20, 30, 40), 30)
```

returns 3.

```
index-of((10, 20, 30, 40), 35)
```

returns "".

### deep-equal

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-deep-equal)

Fully implemented

```
deep-equal((10, 20, "a"), (10, 20, "a"))
```

returns true.

```
deep-equal(("b", "0"), ("b", 0))
```

returns false.

### Functions that test the cardinality of sequences

### zero-or-one

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-zero-or-one)

Fully implemented

```
zero-or-one(("a"))
```

returns "a".

```
zero-or-one(("a", "b"))
```

returns an error.

### one-or-more

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-one-or-more)

Fully implemented

```
one-or-more(("a"))
```

returns "a".

```
one-or-more(())
```

returns an error.

### exactly-one

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-exactly-one)

Fully implemented

```
exactly-one(("a"))
```

returns "a".

```
exactly-one(("a", "b"))
```

returns an error.

### Aggregate functions

### count

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-count)

Fully implemented

```
let $x := (1, 2, 3, 4)
return count($x)
```

returns 4.

Count calls are pushed down to Spark, so this works on billions of items as well:

```
count(json-lines("file.json"))
```

```
count(
  for $i in json-lines("file.json")
  where $i.foo eq "bar"
  return $i
)
```

### avg

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-avg)

Fully implemented

```
let $x := (1, 2, 3, 4)
return avg($x)
```

returns 2.5.

Avg calls are pushed down to Spark, so this works on billions of items as well:

```
avg(json-lines("file.json").foo)
```

### max

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-max)

Fully implemented

```
let $x := (1, 2, 3, 4)
return max($x)
```

returns 4.

```
for $i in 1 to 3
return max($i)
```

returns (1, 2, 3).

Max calls are pushed down to Spark, so this works on billions of items as well:

```
max(json-lines("file.json").foo)
```

### min

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-min)

Fully implemented

```
let $x := (1, 2, 3, 4)
return min($x)
```

returns 1.

```
for $i in 1 to 3
return min($i)
```

returns (1, 2, 3).

Min calls are pushed down to Spark, so this works on billions of items as well:

```
min(json-lines("file.json").foo)
```

### sum

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-sum)

Fully implemented

```
let $x := (1, 2, 3, 4)
return sum($x)
```

returns 10.

Sum calls are pushed down to Spark, so this works on billions of items as well:

```
sum(json-lines("file.json").foo)
```

### Functions giving access to external information

### doc

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-doc)

Fully implemented

```
doc("path/to/file.xml")
```

Returns the corresponding document node

### collection

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-collection)

Not implemented

### Parsing and serializing

### serialize

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-serialize)

Fully implemented

Serializes the supplied input sequence, returning the serialized representation of the sequence as a string

```
serialize({hello: "world"})
```

returns { "hello" : "world" }

## Context Functions

### position

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-position)

Fully implemented

```
(1 to 10)[position() eq 5]
```

returns 5

### last

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-last)

Fully implemented

```
(1 to 10)[position() eq last()]
```

returns 10

```
(1 to 10)[last()]
```

returns 10

### current-dateTime

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-current-dateTime)

Fully implemented

```
current-dateTime()
```

returns 2020-02-26T11:22:48.423+01:00

### current-date

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-current-date)

Fully implemented

```
current-date()
```

returns 2020-02-26Europe/Zurich

### current-time

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-current-time)

Fully implemented

```
current-time()
```

returns 11:24:10.064+01:00

### implicit-timezone

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-implicit-timezone)

Fully implemented

```
implicit-timezone()
```

returns PT1H.

### default-collation

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-default-collation)

Fully implemented

```
default-collation()
```

returns <http://www.w3.org/2005/xpath-functions/collation/codepoint>.

## High order functions

### Functions on functions

### function-lookup

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-function-lookup)

Not implemented

### function-name

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-function-name)

Not implemented

### function-arity

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-function-arity)

Not implemented

### Basic higher-order functions

### for-each

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-function-for-each)

Not implemented

### filter

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-function-filter)

Not implemented

### fold-left

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-function-fold-left)

Not implemented

### fold-right

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-function-fold-right)

Not implemented

### for-each-pair

[W3C specification](https://www.w3.org/TR/xpath-functions-31/#func-function-for-each-pair)

Not implemented

## JSONiq functions

### keys

[JSONiq specification](https://www.jsoniq.org/docs/JSONiq/webhelp/index.html#ch08s01s01.html)

Fully implemented

```
keys({"foo" : "bar", "bar" : "foobar"})
```

returns ("foo", "bar"). Also works on an input sequence, eliminating duplicates

```
keys(({"foo" : "bar", "bar" : "foobar"}, {"foo": "bar2"}))
```

Keys calls are pushed down to Spark, so this works on billions of items as well:

```
keys(json-lines("file.json"))
```

### members

[JSONiq specification](https://www.jsoniq.org/docs/JSONiq/webhelp/index.html#ch08s01s02.html)

Fully implemented

```
members([1 to 100])
```

This function returns the members as an array, but not recursively, i.e., nested arrays are not unboxed.

Returns the first 100 integers as a sequence. Also works on an input sequence, in a distributive way.

```
members(([1 to 100], [ 300 to 1000 ]))
```

### null

[JSONiq specification](https://www.jsoniq.org/docs/JSONiq/webhelp/index.html#ch08s01s03.html)

Fully implemented

```
null()
```

Returns a JSON null (also available as the literal null).

### parse-json

[JSONiq specification](https://www.jsoniq.org/docs/JSONiq/webhelp/index.html#ch08s01s04.html)

Fully implemented

### size

[JSONiq specification](https://www.jsoniq.org/docs/JSONiq/webhelp/index.html#ch08s01s05.html)

Fully implemented

```
size([1 to 100])
```

returns 100. Also works if the empty sequence is supplied, in which case it returns the empty sequence.

```
size(())
```

### accumulate

[JSONiq specification](https://www.jsoniq.org/docs/JSONiq/webhelp/index.html#ch08s01s06.html)

Fully implemented

```
accumulate(({ "b" : 2 }, { "c" : 3 }, { "b" : [1, "abc"] }, {"c" : {"d" : 0.17}}))
```

returns

```
{ "b" : [ 2, [ 1, "abc" ] ], "c" : [ 3, { "d" : 0.17 } ] }
```

### descendant-arrays

[JSONiq specification](https://www.jsoniq.org/docs/JSONiq/webhelp/index.html#ch08s01s06.html)

Fully implemented

```
descendant-arrays(([0, "x", { "a" : [1, {"b" : 2}, [2.5]], "o" : {"c" : 3} }]))
```

returns

```
[ 0, "x", { "a" : [ 1, { "b" : 2 }, [ 2.5 ] ], "o" : {"c" : 3} } ]
[ 1, { "b" : 2 }, [ 2.5 ] ]
[ 2.5 ]
```

### descendant-objects

[JSONiq specification](https://www.jsoniq.org/docs/JSONiq/webhelp/index.html#ch08s01s07.html)

Fully implemented

```
descendant-objects(([0, "x", { "a" : [1, {"b" : 2}, [2.5]], "o" : {"c" : 3} }]))
```

returns

```
{ "a" : [ 1, { "b" : 2 }, [ 2.5 ] ], "o" : { "c" : 3 } }
{ "b" : 2 }
{ "c" : 3 }
```

### descendant-pairs

[JSONiq specification](https://www.jsoniq.org/docs/JSONiq/webhelp/index.html#ch08s01s08.html)

Fully implemented

```
descendant-pairs(({ "a" : [1, {"b" : 2}], "d" : {"c" : 3} }))
```

returns

```
{ "a" : [ 1, { "b" : 2 } ] }
{ "b" : 2 }
{ "d" : { "c" : 3 } }
{ "c" : 3 }
```

### flatten

[JSONiq specification](https://www.jsoniq.org/docs/JSONiq/webhelp/index.html#ch08s01s09.html)

Fully implemented

```
flatten(([1, 2], [[3, 4], [5, 6]], [7, [8, 9]]))
```

Unboxes arrays recursively, stopping the recursion when any other item is reached (object or atomic). Also works on an input sequence, in a distributive way.

Returns (1, 2, 3, 4, 5, 6, 7, 8, 9).

### intersect

[JSONiq specification](https://www.jsoniq.org/docs/JSONiq/webhelp/index.html#ch08s01s10.html)

Fully implemented

```
intersect(({"a" : "abc", "b" : 2, "c" : [1, 2], "d" : "0"}, { "a" : 2, "b" : "ab", "c" : "foo" }))
```

returns

```
{ "a" : [ "abc", 2 ], "b" : [ 2, "ab" ], "c" : [ [ 1, 2 ], "foo" ] }
```

### project

[JSONiq specification](https://www.jsoniq.org/docs/JSONiq/webhelp/index.html#ch08s01s11.html)

Fully implemented

```
project({"foo" : "bar", "bar" : "foobar", "foobar" : "foo" }, ("foo", "bar"))
```

returns the object {"foo" : "bar", "bar" : "foobar"}. Also works on an input sequence, in a distributive way.

```
project(({"foo" : "bar", "bar" : "foobar", "foobar" : "foo" }, {"foo": "bar2"}), ("foo", "bar"))
```

### remove-keys

[JSONiq specification](https://www.jsoniq.org/docs/JSONiq/webhelp/index.html#ch08s01s12.html)

Fully implemented

```
remove-keys({"foo" : "bar", "bar" : "foobar", "foobar" : "foo" }, ("foo", "bar"))
```

returns the object {"foobar" : "foo"}. Also works on an input sequence, in a distributive way.

```
remove-keys(({"foo" : "bar", "bar" : "foobar", "foobar" : "foo" }, {"foo": "bar2"}), ("foo", "bar"))
```

### values

[JSONiq specification](https://www.jsoniq.org/docs/JSONiq/webhelp/index.html#ch08s01s13.html)

Fully implemented

```
values({"foo" : "bar", "bar" : "foobar"})
```

returns ("bar", "foobar"). Also works on an input sequence, in a distributive way.

```
values(({"foo" : "bar", "bar" : "foobar"}, {"foo" : "bar2"}))
```

Values calls are pushed down to Spark, so this works on billions of items as well:

```
values(json-lines("file.json"))
```

### encode-for-roundtrip

[JSONiq specification](https://www.jsoniq.org/docs/JSONiq/webhelp/index.html#ch08s01s13.html)

Not implemented

### decode-from-roundtrip

[JSONiq specification](https://www.jsoniq.org/docs/JSONiq/webhelp/index.html#ch08s01s14.html)

Not implemented

### json-doc

```
json-doc("/Users/sheldon/object.json")
```

returns the (unique) JSON value parsed from a local JSON (but not necessarily JSON Lines) file where this value may be spread over multiple lines.


# User-defined types

RumbleDB now supports user-defined array and object types both with the JSound compact syntax and the JSound verbose syntax.

## JSound Schema Compact syntax

RumbleDB user-defined types can be defined with the JSound syntax. A tutorial for the JSound syntax can be found [here](https://github.com/ghislainfourny/jsound-tutorial).

For now, RumbleDB only allows the definition of user-defined types for objects and arrays. User-defined atomic types and union types will follow soon. The @ (primary key) and ? (nullable) shortcuts are supported as of version 2.0.5. The behavior of nulls with absent vs. nullable fields can be tweaked in the configuration (e.g., if a null is present in an optional, non-nullable field, RumbleBD can be lenient and simply remove it instead of throwing an error).

The implementation is still experimental and bugs are still expected, which we will appreciate to be informed of.

## Type declaration

A new type can be declared in the prolog, at the same location where you also define global variables and user-defined functions.

```
declare type local:my-type as {
  "foo" : "string",
  "bar" : "integer"
};

{ "foo" : "this is a string", "bar" : 42 }
```

In the above query, although the type is defined, the query returns an object that was not validated against this type.

## Type declaration

To validate and annotate a sequence of objects, you need to use the validate-type expression, like so:

```
declare type local:my-type as {
  "foo" : "string",
  "bar" : "integer"
};

validate type local:my-type* {
  { "foo" : "this is a string", "bar" : 42 }
}
```

You can use user-defined types wherever other types can appear: as type annotation for FLWOR variables or global variables, as function parameter or return types, in instance-of or treat-as expressions, etc.

```
declare type local:my-type as {
  "foo" : "string",
  "bar" : "integer"
};

declare function local:proj($x as local:my-type+) as string*
{
  $x.foo
};

let $a as local:my-type* := validate type local:my-type* {
  { "foo" : "this is a string", "bar" : 42 }
}
return if($a instance of local:my-type*)
       then local:proj($a)
       else "Not an instance."
```

You can validate larger sequences

```
declare type local:my-type as {
  "foo" : "string",
  "bar" : "integer"
};

validate type local:my-type* {
  { "foo" : "this is a string", "bar" : 42 },
  { "foo" : "this is another string", "bar" : 1 },
  { "foo" : "this is yet another string", "bar" : 2 },
  { "foo" : "this is a string", "bar" : 12 },
  { "foo" : "this is a string", "bar" : 42345 },
  { "foo" : "this is a string", "bar" : 42 }
}
```

You can also validate, in parallel, an entire JSON Lines file, like so:

```
declare type local:my-type as {
  "foo" : "string",
  "bar" : "integer"
};

validate type local:my-type* {
  json-lines("hdfs:///directory-file.json")
}
```

## Optional vs. required fields

By defaults, fields are optional:

```
declare type local:my-type as {
  "foo" : "string",
  "bar" : "integer"
};

validate type local:my-type* {
  { "foo" : "this is a string", "bar" : 42 },
  { "bar" : 1 },
  { "foo" : "this is yet another string", "bar" : 2 },
  { "foo" : "this is a string" },
  { "foo" : "this is a string", "bar" : 42345 },
  { "foo" : "this is a string", "bar" : 42 }
}
```

You can, however, make a field required by adding a ! in front of its name:

```
declare type local:my-type as {
  "foo" : "string",
  "!bar" : "integer"
};

validate type local:my-type* {
  { "foo" : "this is a string", "bar" : 42 },
  { "bar" : 1 },
  { "foo" : "this is yet another string", "bar" : 2 },
  { "foo" : "this is a string", "bar" : 1234 },
  { "foo" : "this is a string", "bar" : 42345 },
  { "foo" : "this is a string", "bar" : 42 }
}
```

Or you can provide a default value with the equal sign:

```
declare type local:my-type as {
  "foo" : "string=foobar",
  "!bar" : "integer"
};

validate type local:my-type* {
  { "foo" : "this is a string", "bar" : 42 },
  { "bar" : 1 },
  { "foo" : "this is yet another string", "bar" : 2 },
  { "foo" : "this is a string", "bar" : 1234 },
  { "foo" : "this is a string", "bar" : 42345 },
  { "foo" : "this is a string", "bar" : 42 }
}
```

## Extra fields

Extra fields will be rejected. However, the verbose version of JSound supports allowing extra fields (open objects) and will be supported in a future version of RumbleDB.

## Nested arrays

With the JSound comptact syntax, you can easily define nested array structures:

```
declare type local:my-type as {
  "foo" : "string",
  "!bar" : [ "integer" ]
};

validate type local:my-type* {
  { "foo" : "this is a string", "bar" : [ 42, 1234 ] },
  { "bar" : [ 1 ] },
  { "foo" : "this is yet another string", "bar" : [ 2 ] },
  { "foo" : "this is a string", "bar" : [ ] },
  { "foo" : "this is a string", "bar" : [ 1, 2, 3, 4, 5, 6 ] },
  { "foo" : "this is a string", "bar" : [ 42 ] }
}
```

You can even further nest objects:

```
declare type local:my-type as {
  "foo" : { "bar" : "integer" },
  "!bar" : [ { "first" : "string", "last" : "string" } ]
};

validate type local:my-type* {
  {
    "foo" : { "bar" : 1 },
    "bar" : [
      { "first" : "Albert", "last" : "Einstein" },
      { "first" : "Erwin", "last" : "Schrodinger" }
    ]
  },
  {
    "foo" : { "bar" : 2 },
    "bar" : [
      { "first" : "Alan", "last" : "Turing" },
      { "first" : "John", "last" : "Von Neumann" }
    ]
  },
  {
    "foo" : { "bar" : 3 },
    "bar" : [
    ]
  }
}
```

Or split your definitions into several types that refer to each other:

```
declare type local:person as {
  "first" : "string",
  "last" : "string"
};

declare type local:my-type as {
  "foo" : { "bar" : "integer" },
  "!bar" : [ "local:person" ]
};

validate type local:my-type* {
  {
    "foo" : { "bar" : 1 },
    "bar" : [
      { "first" : "Albert", "last" : "Einstein" },
      { "first" : "Erwin", "last" : "Schrodinger" }
    ]
  },
  {
    "foo" : { "bar" : 2 },
    "bar" : [
      { "first" : "Alan", "last" : "Turing" },
      { "first" : "John", "last" : "Von Neumann" }
    ]
  },
  {
    "foo" : { "bar" : 3 },
    "bar" : [
    ]
  }
}
```

## DataFrames

In fact, RumbleDB will internally convert the sequence of objects to a Spark DataFrame, leading to faster execution times.

In other words, the JSound Compact Schema Syntax is perfect for defining DataFrames schema!

## Verbose syntax

For advanced JSound features, such as open object types or subtypes, the verbose syntax must be used, like so:

```
declare type local:x as jsound verbose {
  "kind" : "object",
  "baseType" : "object",
  "content" : [
    { "name" : "foo", "type" : "integer" }
  ],
  "closed" : false
};

declare type local:y as jsound verbose {
  "kind" : "object",
  "baseType" : "local:x",
  "content" : [
    { "name" : "bar", "type" : "date" }
  ],
  "closed" : true
};
```

The JSound type system, as its name indicates, is sound: you can only make subtypes more restrictive than the super type. The complete specification of both syntaxes is available on the [JSound website](https://www.jsound-spec.org/).

In the feature, RumbleDB will support user-defined atomic types and union types via the verbose syntax.

## What's next?

Once you have validated your data as a dataframe with a user-defined type, you are all set to use the RumbleDB ML Machine Learning library and feed it through ML pipelines!


# Configuration parameters

The parameters that can be used on the command line as well as on the planned HTTP server are shown below. They are also accessible via the Java API and via Python through the [RumbleRuntimeConfiguration](https://rumbledb.org/docs/latest/api/org/rumbledb/config/RumbleRuntimeConfiguration.html) class.

RumbleDB runs in three modes. You can select the mode passing a verb as the first parameter. For example:

```
   spark-submit rumbledb.jar run file.jq -o output-dir -P 1
   spark-submit rumbledb.jar run -q '1+1'
   spark-submit rumbledb.jar serve -p 8001
   spark-submit rumbledb.jar repl -c 10
```

Previous parameters (--shell, --query-path, --server) work in a backward compatible fashion, however we do recommend to start using the new verb-based format.

| Shell parameter                                   | Shortcut                                                 | HTTP parameter                                  | example values                                                   | Semantics                                                                                                                                                                                                                                                                                      |
| ------------------------------------------------- | -------------------------------------------------------- | ----------------------------------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| --shell                                           | repl                                                     | N/A                                             | yes, no                                                          | yes runs the interactive shell. No executes a query specified with --query-path                                                                                                                                                                                                                |
| --shell-filter                                    | N/A                                                      | N/A                                             | jq .                                                             | Post-processes the output of JSONiq queries on the shell with the specified command (reading the RumbleDB output via stdin)                                                                                                                                                                    |
| --query                                           | -q                                                       | query                                           | 1+1                                                              | A JSONiq query directly provided as a string.                                                                                                                                                                                                                                                  |
| --query-path                                      | (any text without -- or - is recognized as a query path) | query-path                                      | file:///folder/file.jq                                           | A JSONiq query file to read from (from any file system, even the Web!).                                                                                                                                                                                                                        |
| --output-path                                     | -o                                                       | output-path                                     | file:///folder/output                                            | Where to output to (if the output is large, it will create a sharded directory, otherwise it will create a file)                                                                                                                                                                               |
| --output-format                                   | -f                                                       | N/A                                             | json, csv, avro, parquet, or any other format supported by Spark | An output format to use for the output. Formats other than json can only be output if the query outputs a highly structured sequence of objects (you can nest your query in an annotate() call to specify a schema if it does not).                                                            |
| --output-format-option:foo                        | N/A                                                      | N/A                                             | bar                                                              | Options to further specify the output format (example: separator character for CSV, compression format...)                                                                                                                                                                                     |
| --overwrite                                       | -O (meaning --overwrite yes)                             | overwrite                                       | yes, no                                                          | Whether to overwrite to --output-path. No throws an error if the output file/folder exists.                                                                                                                                                                                                    |
| --materialization-cap                             | -c                                                       | materialization-cap                             | 100000                                                           | A cap on the maximum number of items to materialize during the query execution for large sequences within a query. For example, when nesting an expression producing a large sequence of items (and that RumbleDB chose to physically store as an RDD or DataFrame) into an array constructor. |
| --result-size                                     |                                                          | result-size                                     | 10                                                               | A cap on the maximum number of items to output on the screen or to a local list.                                                                                                                                                                                                               |
| --number-of-output-partitions                     | -P                                                       | N/A                                             | ad hoc                                                           | How many partitions to create in the output, i.e., the number of files that will be created in the output path directory.                                                                                                                                                                      |
| --log-path                                        | N/A                                                      | log-path                                        | file:///folder/log.txt                                           | Where to output log information                                                                                                                                                                                                                                                                |
| --print-iterator-tree                             | N/A                                                      | N/A                                             | yes, no                                                          | For debugging purposes, prints out the expression tree and runtime interator tree.                                                                                                                                                                                                             |
| --show-error-info                                 | -v (meaning --show-error-info yes)                       | show-error-info                                 | yes, no                                                          | For debugging purposes. If you want to report a bug, you can use this to get the full exception stack. If no, then only a short message is shown in case of error.                                                                                                                             |
| --static-typing                                   | -t (meaning --static-typing yes)                         | static-typing                                   | yes, no                                                          | Activates static type analysis, which annotates the expression tree with inferred types at compile time and enables more optimizations (experimental). Deactivated by default.                                                                                                                 |
| --server                                          | serve                                                    | N/A                                             | yes, no                                                          | yes runs RumbleDB as a server on port 8001. Run queries with <http://localhost:8001/jsoniq?query-path=/folder/foo.json>                                                                                                                                                                        |
| --port                                            | -p                                                       | N/A                                             | 8001 (default)                                                   | Changes the port of the RumbleDB HTTP server to any of your liking                                                                                                                                                                                                                             |
| --host                                            | -h                                                       | N/A                                             | localhost (default)                                              | Changes the host of the RumbleDB HTTP server to any of your liking                                                                                                                                                                                                                             |
| --variable:foo                                    | N/A                                                      | variable:foo                                    | bar                                                              | --variable:foo bar initialize the global variable $foo to "bar". The query must contain the corresponding global variable declaration, e.g., "declare variable $foo external;"                                                                                                                 |
| --context-item                                    | -I                                                       | context-item                                    | bar                                                              | initializes the global context item $$ to "bar". The query must contain the corresponding global variable declaration, e.g., "declare context item external;"                                                                                                                                  |
| --context-item-input                              | -i                                                       | context-item-input                              | -                                                                | reads the context item value from the standard input                                                                                                                                                                                                                                           |
| --context-item-input-format                       | N/A                                                      | context-item-input-format                       | text or json                                                     | sets the input format to use for parsing the standard input (as text or as a serialized json value)                                                                                                                                                                                            |
| --dates-with-timezone                             | N/A                                                      | dates-with-timezone                             | yes or no                                                        | activates timezone support for the type xs:date (deactivated by default)                                                                                                                                                                                                                       |
| --lax-json-null-valication                        | N/A                                                      | lax-json-null-validation                        | yes or no                                                        | Allows conflating JSON nulls with absent values when validating nillable object fields for more flexibility (activated by default).                                                                                                                                                            |
| --optimize-general-comparison-to-value-comparison | N/A                                                      | optimize-general-comparison-to-value-comparison | yes or no                                                        | activates automatic conversion of general comparisons to value comparisons when applicable (activated by default)                                                                                                                                                                              |
| --function-inlining                               | N/A                                                      | function-inlining                               | yes or no                                                        | activates function inlining for non-recursive functions (activated by default)                                                                                                                                                                                                                 |
| --parallel-execution                              | N/A                                                      | parallel-execution                              | yes or no                                                        | activates parallel execution when possible (activated by default)                                                                                                                                                                                                                              |
| --native-execution                                | N/A                                                      | native-execution                                | yes or no                                                        | activates native (Spark SQL) execution when possible (activated by default)                                                                                                                                                                                                                    |
| --default-language                                | N/A                                                      | N/A                                             | jsoniq10, jsoniq31, xquery31                                     | specifies the query language to be used                                                                                                                                                                                                                                                        |
| --optimize-steps                                  | N/A                                                      | N/A                                             | yes or no                                                        | allows RumbleDB to optimize steps, might violate stability of document order (activated by default)                                                                                                                                                                                            |
| --optimize-steps-experimental                     | N/A                                                      | N/A                                             | yes or no                                                        | experimentally optimizes steps more by skipping uniqueness and sorting in some cases. correctness is not yet verified (disabled by default)                                                                                                                                                    |
| --optimize-parent-pointers                        | N/A                                                      | N/A                                             | yes or no                                                        | allows RumbleDB to remove parent pointers from items if no steps requiring parent pointers are detected statically (activated by default)                                                                                                                                                      |
| --static-base-uri                                 | N/A                                                      | N/A                                             | "../data/"                                                       | sets the static base uri for the execution. This option overwrites module location but is overwritten by declaration inside query                                                                                                                                                              |


# RumbleML

## RumbleDB ML

RumbleDB ML is a Machine Learning library built on top of the RumbleDB engine that makes it more productive and easier to perform ML tasks thanks to the abstraction layer provided by JSONiq.

The machine learning capabilities are exposed through JSONiq function items. The concepts of "estimator" and "transformer", which are core to Machine Learning, are naturally function items and fit seamlessly in the JSONiq data model.

Training sets, test sets, and validation sets, which contain features and labels, are exposed through JSONiq sequences of object items: the keys of these objects are the features and labels.

The names of the estimators and of the transformers, as well as the functionality they encapsulate, are directly inherited from the [SparkML](https://spark.apache.org/docs/latest/ml-guide.html) library which RumbleDB ML is based on: we chose not to reinvent the wheel.

### Transformers

A **transformer** is a function item that maps a sequence of objects to a sequence of objects.

It is an abstraction that either performs a feature transformation or generates predictions based on trained models. For example:

* *Tokenizer* is a feature transformer that receives textual input data and splits it into individual terms (usually words), which are called tokens.
* *KMeansModel* is a trained model and a transformer that can read a dataset containing features and generate predictions as its output.

### Estimators

An **estimator** is a function item that maps a sequence of objects to a transformer (yes, you got it right: that's a function item returned by a function item. This is why they are also called higher-order functions!).

Estimators abstract the concept of a Machine Learning algorithm or any algorithm that fits or trains on data. For example, a learning algorithm such as *KMeans* is implemented as an Estimator. Calling this estimator on data essentially trains a KMeansModel, which is a Model and hence a Transformer.

### Parameters

Transformers and estimators are function items in the RumbleDB Data Model. Their first argument is the sequence of objects that represents, for example, the training set or test set. Parameters can be provided as their second argument. This second argument is expected to be an object item. The machine learning parameters form the fields of the said object item as key-value pairs.

### Type Annotations

RumbleDB ML works on highly structured data, because it requires full type information for all the fields in the training set or test set. It is on our development plan to automate the detection of these types when the sequence of objects gets created in the fly.

RumbleDB supports a user-defined type system with which you can validate and annotate datasets against a JSound schema.

This annotation is required to be applied on any dataset that must be used as input to RumbleDB ML, but it is superfluous if the data was directly read from a structured input format such as Parquet, CSV, Avro, SVM or ROOT.

### Examples

* Tokenizer Example:

```

declare type local:id-and-sentence as {
  "id": "integer",
  "sentence": "string"
};


let $local-data := (
    {"id": 1, "sentence": "Hi I heard about Spark"},
    {"id": 2, "sentence": "I wish Java could use case classes"},
    {"id": 3, "sentence": "Logistic regression models are neat"}
)
let $df-data := validate type local:id-and-sentence* { $local-data }

let $transformer := get-transformer("Tokenizer")
for $i in $transformer(
    $df-data,
    {"inputCol": "sentence", "outputCol": "output"}
)
return $i

// returns
// { "id" : 1, "sentence" : "Hi I heard about Spark", "output" : [ "hi", "i", "heard", "about", "spark" ] }
// { "id" : 2, "sentence" : "I wish Java could use case classes", "output" : [ "i", "wish", "java", "could", "use", "case", "classes" ] }
// { "id" : 3, "sentence" : "Logistic regression models are neat", "output" : [ "logistic", "regression", "models", "are", "neat" ] }
```

* KMeans Example:

```
declare type local:col-1-2-3 as {
  "id": "integer",
  "col1": "decimal",
  "col2": "decimal",
  "col3": "decimal"
};

let $vector-assembler := get-transformer("VectorAssembler")(
  ?,
  { "inputCols" : [ "col1", "col2", "col3" ], "outputCol" : "features" }
)

let $local-data := (
    {"id": 0, "col1": 0.0, "col2": 0.0, "col3": 0.0},
    {"id": 1, "col1": 0.1, "col2": 0.1, "col3": 0.1},
    {"id": 2, "col1": 0.2, "col2": 0.2, "col3": 0.2},
    {"id": 3, "col1": 9.0, "col2": 9.0, "col3": 9.0},
    {"id": 4, "col1": 9.1, "col2": 9.1, "col3": 9.1},
    {"id": 5, "col1": 9.2, "col2": 9.2, "col3": 9.2}
)
let $df-data := validate type local:col-1-2-3* {$local-data }
let $df-data := $vector-assembler($df-data)

let $est := get-estimator("KMeans")
let $tra := $est(
    $df-data,
    {"featuresCol": "features"}
)

for $i in $tra(
    $df-data,
    {"featuresCol": "features"}
)
return $i

// returns
// { "id" : 0, "col1" : 0, "col2" : 0, "col3" : 0, "prediction" : 0 }
// { "id" : 1, "col1" : 0.1, "col2" : 0.1, "col3" : 0.1, "prediction" : 0 }
// { "id" : 2, "col1" : 0.2, "col2" : 0.2, "col3" : 0.2, "prediction" : 0 }
// { "id" : 3, "col1" : 9, "col2" : 9, "col3" : 9, "prediction" : 1 }
// { "id" : 4, "col1" : 9.1, "col2" : 9.1, "col3" : 9.1, "prediction" : 1 }
// { "id" : 5, "col1" : 9.2, "col2" : 9.2, "col3" : 9.2, "prediction" : 1 }
```

## RumbleDB ML Functionality Overview:

### RumblDB eML - Catalogue of Estimators:

#### [AFTSurvivalRegression](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/regression/AFTSurvivalRegression.html)

**Parameters:**

```
- aggregationDepth: integer
- censorCol: string
- featuresCol: string
- fitIntercept: boolean
- labelCol: string
- maxIter: integer
- predictionCol: string
- quantileProbabilities: array (of double)
- quantilesCol: string
- tol: double
```

#### [ALS](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/recommendation/ALS.html)

**Parameters:**

```
- alpha: double
- checkpointInterval: integer
- coldStartStrategy: string
- finalStorageLevel: string
- implicitPrefs: boolean
- intermediateStorageLevel: string
- itemCol: string
- maxIter: integer
- nonnegative: boolean
- numBlocks: integer
- numItemBlocks: integer
- numUserBlocks: integer
- predictionCol: string
- rank: integer
- ratingCol: string
- regParam: double
- seed: double
- userCol: string
```

#### [BisectingKMeans](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/clustering/BisectingKMeans.html)

**Parameters:**

```
- distanceMeasure: string
- featuresCol: string
- k: integer
- maxIter: integer
- minDivisibleClusterSize: double
- predictionCol: string
- seed: double
```

#### [BucketedRandomProjectionLSH](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/BucketedRandomProjectionLSH.html)

**Parameters:**

```
- bucketLength: double
- inputCol: string
- numHashTables: integer
- outputCol: string
- seed: double
```

#### [ChiSqSelector](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/ChiSqSelector.html)

**Parameters:**

```
- fdr: double
- featuresCol: string
- fpr: double
- fwe: double
- labelCol: string
- numTopFeatures: integer
- outputCol: string
- percentile: double
- selectorType: string
```

#### [CountVectorizer](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/CountVectorizer.html)

**Parameters:**

```
- binary: boolean
- inputCol: string
- maxDF: double
- minDF: double
- minTF: double
- outputCol: string
- vocabSize: integer
```

#### [CrossValidator](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/tuning/CrossValidator.html)

**Parameters:**

```
- collectSubModels: boolean
- estimator: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- numFolds: integer
- parallelism: integer
- seed: double
```

#### [DecisionTreeClassifier](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/classification/DecisionTreeClassifier.html)

**Parameters:**

```
- cacheNodeIds: boolean
- checkpointInterval: integer
- featuresCol: string
- impurity: string
- labelCol: string
- maxBins: integer
- maxDepth: integer
- maxMemoryInMB: integer
- minInfoGain: double
- minInstancesPerNode: integer
- predictionCol: string
- probabilityCol: string
- rawPredictionCol: string
- seed: double
- thresholds: array (of double)
```

#### [DecisionTreeRegressor](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/regression/DecisionTreeRegressor.html)

**Parameters:**

```
- cacheNodeIds: boolean
- checkpointInterval: integer
- featuresCol: string
- impurity: string
- labelCol: string
- maxBins: integer
- maxDepth: integer
- maxMemoryInMB: integer
- minInfoGain: double
- minInstancesPerNode: integer
- predictionCol: string
- seed: double
- varianceCol: string
```

#### [FPGrowth](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/fpm/FPGrowth.html)

**Parameters:**

```
- itemsCol: string
- minConfidence: double
- minSupport: double
- numPartitions: integer
- predictionCol: string
```

#### [GBTClassifier](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/classification/GBTClassifier.html)

**Parameters:**

```
- cacheNodeIds: boolean
- checkpointInterval: integer
- featuresCol: string
- featureSubsetStrategy: string
- impurity: string
- labelCol: string
- lossType: string
- maxBins: integer
- maxDepth: integer
- maxIter: integer
- maxMemoryInMB: integer
- minInfoGain: double
- minInstancesPerNode: integer
- predictionCol: string
- probabilityCol: string
- rawPredictionCol: string
- seed: double
- stepSize: double
- subsamplingRate: double
- thresholds: array (of double)
- validationIndicatorCol: string
```

#### [GBTRegressor](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/regression/GBTRegressor.html)

**Parameters:**

```
- cacheNodeIds: boolean
- checkpointInterval: integer
- featuresCol: string
- featureSubsetStrategy: string
- impurity: string
- labelCol: string
- lossType: string
- maxBins: integer
- maxDepth: integer
- maxIter: integer
- maxMemoryInMB: integer
- minInfoGain: double
- minInstancesPerNode: integer
- predictionCol: string
- seed: double
- stepSize: double
- subsamplingRate: double
- validationIndicatorCol: string
```

#### [GaussianMixture](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/clustering/GaussianMixture.html)

**Parameters:**

```
- featuresCol: string
- k: integer
- maxIter: integer
- predictionCol: string
- probabilityCol: string
- seed: double
- tol: double
```

#### [GeneralizedLinearRegression](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/regression/GeneralizedLinearRegression.html)

**Parameters:**

```
- family: string
- featuresCol: string
- fitIntercept: boolean
- labelCol: string
- link: string
- linkPower: double
- linkPredictionCol: string
- maxIter: integer
- offsetCol: string
- predictionCol: string
- regParam: double
- solver: string
- tol: double
- variancePower: double
- weightCol: string
```

#### [IDF](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/IDF.html)

**Parameters:**

```
- inputCol: string
- minDocFreq: integer
- outputCol: string
```

#### [Imputer](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/Imputer.html)

**Parameters:**

```
- inputCols: array (of string)
- missingValue: double
- outputCols: array (of string)
- strategy: string
```

#### [IsotonicRegression](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/regression/IsotonicRegression.html)

**Parameters:**

```
- featureIndex: integer
- featuresCol: string
- isotonic: boolean
- labelCol: string
- predictionCol: string
- weightCol: string
```

#### [KMeans](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/clustering/KMeans.html)

**Parameters:**

```
- distanceMeasure: string
- featuresCol: string
- initMode: string
- initSteps: integer
- k: integer
- maxIter: integer
- predictionCol: string
- seed: double
- tol: double
```

#### [LDA](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/clustering/LDA.html)

**Parameters:**

```
- checkpointInterval: integer
- docConcentration: double
- docConcentration: array (of double)
- featuresCol: string
- k: integer
- keepLastCheckpoint: boolean
- learningDecay: double
- learningOffset: double
- maxIter: integer
- optimizeDocConcentration: boolean
- optimizer: string
- seed: double
- subsamplingRate: double
- topicConcentration: double
- topicDistributionCol: string
```

#### [LinearRegression](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/regression/LinearRegression.html)

**Parameters:**

```
- aggregationDepth: integer
- elasticNetParam: double
- epsilon: double
- featuresCol: string
- fitIntercept: boolean
- labelCol: string
- loss: string
- maxIter: integer
- predictionCol: string
- regParam: double
- solver: string
- standardization: boolean
- tol: double
- weightCol: string
```

#### [LinearSVC](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/classification/LinearSVC.html)

**Parameters:**

```
- aggregationDepth: integer
- featuresCol: string
- fitIntercept: boolean
- labelCol: string
- maxIter: integer
- predictionCol: string
- rawPredictionCol: string
- regParam: double
- standardization: boolean
- threshold: double
- tol: double
- weightCol: string
```

#### [LogisticRegression](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/classification/LogisticRegression.html)

**Parameters:**

```
- aggregationDepth: integer
- elasticNetParam: double
- family: string
- featuresCol: string
- fitIntercept: boolean
- labelCol: string
- lowerBoundsOnCoefficients: object (of object of double)
- lowerBoundsOnIntercepts: object (of double)
- maxIter: integer
- predictionCol: string
- probabilityCol: string
- rawPredictionCol: string
- regParam: double
- standardization: boolean
- threshold: double
- thresholds: array (of double)
- tol: double
- upperBoundsOnCoefficients: object (of object of double)
- upperBoundsOnIntercepts: object (of double)
- weightCol: string
```

#### [MaxAbsScaler](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/MaxAbsScaler.html)

**Parameters:**

```
- inputCol: string
- outputCol: string
```

#### [MinHashLSH](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/MinHashLSH.html)

**Parameters:**

```
- inputCol: string
- numHashTables: integer
- outputCol: string
- seed: double
```

#### [MinMaxScaler](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/MinMaxScaler.html)

**Parameters:**

```
- inputCol: string
- max: double
- min: double
- outputCol: string
```

#### [MultilayerPerceptronClassifier](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/classification/MultilayerPerceptronClassifier.html)

**Parameters:**

```
- blockSize: integer
- featuresCol: string
- initialWeights: object (of double)
- labelCol: string
- layers: array (of integer)
- maxIter: integer
- predictionCol: string
- probabilityCol: string
- rawPredictionCol: string
- seed: double
- solver: string
- stepSize: double
- thresholds: array (of double)
- tol: double
```

#### [NaiveBayes](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/classification/NaiveBayes.html)

**Parameters:**

```
- featuresCol: string
- labelCol: string
- modelType: string
- predictionCol: string
- probabilityCol: string
- rawPredictionCol: string
- smoothing: double
- thresholds: array (of double)
- weightCol: string
```

#### [OneHotEncoder](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/OneHotEncoder.html)

**Parameters:**

```
- dropLast: boolean
- handleInvalid: string
- inputCols: array (of string)
- outputCols: array (of string)
```

#### [OneVsRest](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/classification/OneVsRest.html)

**Parameters:**

```
- featuresCol: string
- labelCol: string
- parallelism: integer
- predictionCol: string
- rawPredictionCol: string
- weightCol: string
```

#### [PCA](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/PCA.html)

**Parameters:**

```
- inputCol: string
- k: integer
- outputCol: string
```

#### [Pipeline](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/Pipeline.html)

**Parameters:**

```
```

#### [QuantileDiscretizer](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/QuantileDiscretizer.html)

**Parameters:**

```
- handleInvalid: string
- inputCol: string
- inputCols: array (of string)
- numBuckets: integer
- numBucketsArray: array (of integer)
- outputCol: string
- outputCols: array (of string)
- relativeError: double
```

#### [RFormula](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/RFormula.html)

**Parameters:**

```
- featuresCol: string
- forceIndexLabel: boolean
- formula: string
- handleInvalid: string
- labelCol: string
- stringIndexerOrderType: string
```

#### [RandomForestClassifier](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/classification/RandomForestClassifier.html)

**Parameters:**

```
- cacheNodeIds: boolean
- checkpointInterval: integer
- featuresCol: string
- featureSubsetStrategy: string
- impurity: string
- labelCol: string
- maxBins: integer
- maxDepth: integer
- maxMemoryInMB: integer
- minInfoGain: double
- minInstancesPerNode: integer
- numTrees: integer
- predictionCol: string
- probabilityCol: string
- rawPredictionCol: string
- seed: double
- subsamplingRate: double
- thresholds: array (of double)
```

#### [RandomForestRegressor](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/regression/RandomForestRegressor.html)

**Parameters:**

```
- cacheNodeIds: boolean
- checkpointInterval: integer
- featuresCol: string
- featureSubsetStrategy: string
- impurity: string
- labelCol: string
- maxBins: integer
- maxDepth: integer
- maxMemoryInMB: integer
- minInfoGain: double
- minInstancesPerNode: integer
- numTrees: integer
- predictionCol: string
- seed: double
- subsamplingRate: double
```

#### [StandardScaler](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/StandardScaler.html)

**Parameters:**

```
- inputCol: string
- outputCol: string
- withMean: boolean
- withStd: boolean
```

#### [StringIndexer](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/StringIndexer.html)

**Parameters:**

```
- handleInvalid: string
- inputCol: string
- outputCol: string
- stringOrderType: string
```

#### [TrainValidationSplit](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/tuning/TrainValidationSplit.html)

**Parameters:**

```
- collectSubModels: boolean
- estimator: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- parallelism: integer
- seed: double
- trainRatio: double
```

#### [VectorIndexer](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/VectorIndexer.html)

**Parameters:**

```
- handleInvalid: string
- inputCol: string
- maxCategories: integer
- outputCol: string
```

#### [Word2Vec](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/Word2Vec.html)

**Parameters:**

```
- inputCol: string
- maxIter: integer
- maxSentenceLength: integer
- minCount: integer
- numPartitions: integer
- outputCol: string
- seed: double
- stepSize: double
- vectorSize: integer
- windowSize: integer
```

### RumbleDB ML - Catalogue of Transformers:

#### [AFTSurvivalRegressionModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/regression/AFTSurvivalRegressionModel.html)

**Parameters:**

```
- featuresCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- predictionCol: string
- quantileProbabilities: array (of double)
- quantilesCol: string
```

#### [ALSModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/recommendation/ALSModel.html)

**Parameters:**

```
- coldStartStrategy: string
- itemCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- predictionCol: string
- userCol: string
```

#### [Binarizer](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/Binarizer.html)

**Parameters:**

```
- inputCol: string
- outputCol: string
- threshold: double
```

#### [BisectingKMeansModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/clustering/BisectingKMeansModel.html)

**Parameters:**

```
- featuresCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- predictionCol: string
```

#### [BucketedRandomProjectionLSHModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/BucketedRandomProjectionLSHModel.html)

**Parameters:**

```
- inputCol: string
- outputCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
```

#### [Bucketizer](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/Bucketizer.html)

**Parameters:**

```
- handleInvalid: string
- inputCol: string
- inputCols: array (of string)
- outputCol: string
- outputCols: array (of string)
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- splits: array (of double)
- splitsArray: array (of array of double)
```

#### [ChiSqSelectorModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/ChiSqSelectorModel.html)

**Parameters:**

```
- featuresCol: string
- outputCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
```

#### [ColumnPruner](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/ColumnPruner.html)

**Parameters:**

```
```

#### [CountVectorizerModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/CountVectorizerModel.html)

**Parameters:**

```
- binary: boolean
- inputCol: string
- minTF: double
- outputCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
```

#### [CrossValidatorModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/tuning/CrossValidatorModel.html)

**Parameters:**

```
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
```

#### [DCT](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/DCT.html)

**Parameters:**

```
- inputCol: string
- inverse: boolean
- outputCol: string
```

#### [DecisionTreeClassificationModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/classification/DecisionTreeClassificationModel.html)

**Parameters:**

```
- cacheNodeIds: boolean
- checkpointInterval: integer
- featuresCol: string
- impurity: string
- maxBins: integer
- maxDepth: integer
- maxMemoryInMB: integer
- minInfoGain: double
- minInstancesPerNode: integer
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- predictionCol: string
- probabilityCol: string
- rawPredictionCol: string
- seed: double
- thresholds: array (of double)
```

#### [DecisionTreeRegressionModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/regression/DecisionTreeRegressionModel.html)

**Parameters:**

```
- cacheNodeIds: boolean
- checkpointInterval: integer
- featuresCol: string
- impurity: string
- maxBins: integer
- maxDepth: integer
- maxMemoryInMB: integer
- minInfoGain: double
- minInstancesPerNode: integer
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- predictionCol: string
- seed: double
- varianceCol: string
```

#### [DistributedLDAModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/clustering/DistributedLDAModel.html)

**Parameters:**

```
- featuresCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- seed: double
- topicDistributionCol: string
```

#### [ElementwiseProduct](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/ElementwiseProduct.html)

**Parameters:**

```
- inputCol: string
- outputCol: string
- scalingVec: object (of double)
```

#### [FPGrowthModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/fpm/FPGrowthModel.html)

**Parameters:**

```
- itemsCol: string
- minConfidence: double
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- predictionCol: string
```

#### [FeatureHasher](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/FeatureHasher.html)

**Parameters:**

```
- categoricalCols: array (of string)
- inputCols: array (of string)
- numFeatures: integer
- outputCol: string
```

#### [GBTClassificationModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/classification/GBTClassificationModel.html)

**Parameters:**

```
- cacheNodeIds: boolean
- checkpointInterval: integer
- featuresCol: string
- featureSubsetStrategy: string
- impurity: string
- maxBins: integer
- maxDepth: integer
- maxIter: integer
- maxMemoryInMB: integer
- minInfoGain: double
- minInstancesPerNode: integer
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- predictionCol: string
- probabilityCol: string
- rawPredictionCol: string
- seed: double
- stepSize: double
- subsamplingRate: double
- thresholds: array (of double)
```

#### [GBTRegressionModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/regression/GBTRegressionModel.html)

**Parameters:**

```
- cacheNodeIds: boolean
- checkpointInterval: integer
- featuresCol: string
- featureSubsetStrategy: string
- impurity: string
- maxBins: integer
- maxDepth: integer
- maxIter: integer
- maxMemoryInMB: integer
- minInfoGain: double
- minInstancesPerNode: integer
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- predictionCol: string
- seed: double
- stepSize: double
- subsamplingRate: double
```

#### [GaussianMixtureModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/clustering/GaussianMixtureModel.html)

**Parameters:**

```
- featuresCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- predictionCol: string
- probabilityCol: string
```

#### [GeneralizedLinearRegressionModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/regression/GeneralizedLinearRegressionModel.html)

**Parameters:**

```
- featuresCol: string
- linkPredictionCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- predictionCol: string
```

#### [HashingTF](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/HashingTF.html)

**Parameters:**

```
- binary: boolean
- inputCol: string
- numFeatures: integer
- outputCol: string
```

#### [IDFModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/IDFModel.html)

**Parameters:**

```
- inputCol: string
- outputCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
```

#### [ImputerModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/ImputerModel.html)

**Parameters:**

```
- inputCols: array (of string)
- outputCols: array (of string)
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
```

#### [IndexToString](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/IndexToString.html)

**Parameters:**

```
- inputCol: string
- labels: array (of string)
- outputCol: string
```

#### [Interaction](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/Interaction.html)

**Parameters:**

```
- inputCols: array (of string)
- outputCol: string
```

#### [IsotonicRegressionModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/regression/IsotonicRegressionModel.html)

**Parameters:**

```
- featureIndex: integer
- featuresCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- predictionCol: string
```

#### [KMeansModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/clustering/KMeansModel.html)

**Parameters:**

```
- featuresCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- predictionCol: string
```

#### [LinearRegressionModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/regression/LinearRegressionModel.html)

**Parameters:**

```
- featuresCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- predictionCol: string
```

#### [LinearSVCModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/classification/LinearSVCModel.html)

**Parameters:**

```
- featuresCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- predictionCol: string
- rawPredictionCol: string
- threshold: double
- weightCol: double
```

#### [LocalLDAModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/clustering/LocalLDAModel.html)

**Parameters:**

```
- featuresCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- seed: double
- topicDistributionCol: string
```

#### [LogisticRegressionModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/classification/LogisticRegressionModel.html)

**Parameters:**

```
- featuresCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- predictionCol: string
- probabilityCol: string
- rawPredictionCol: string
- threshold: double
- thresholds: array (of double)
```

#### [MaxAbsScalerModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/MaxAbsScalerModel.html)

**Parameters:**

```
- inputCol: string
- outputCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
```

#### [MinHashLSHModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/MinHashLSHModel.html)

**Parameters:**

```
- inputCol: string
- outputCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
```

#### [MinMaxScalerModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/MinMaxScalerModel.html)

**Parameters:**

```
- inputCol: string
- max: double
- min: double
- outputCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
```

#### [MultilayerPerceptronClassificationModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/classification/MultilayerPerceptronClassificationModel.html)

**Parameters:**

```
- featuresCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- predictionCol: string
- probabilityCol: string
- rawPredictionCol: string
- thresholds: array (of double)
```

#### [NGram](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/NGram.html)

**Parameters:**

```
- inputCol: string
- n: integer
- outputCol: string
```

#### [NaiveBayesModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/classification/NaiveBayesModel.html)

**Parameters:**

```
- featuresCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- predictionCol: string
- probabilityCol: string
- rawPredictionCol: string
- thresholds: array (of double)
```

#### [Normalizer](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/Normalizer.html)

**Parameters:**

```
- inputCol: string
- outputCol: string
- p: double
```

#### [OneHotEncoder](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/OneHotEncoder.html)

**Parameters:**

```
- dropLast: boolean
- inputCol: string
- outputCol: string
```

#### [OneHotEncoderModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/OneHotEncoderModel.html)

**Parameters:**

```
- dropLast: boolean
- handleInvalid: string
- inputCols: array (of string)
- outputCols: array (of string)
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
```

#### [OneVsRestModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/classification/OneVsRestModel.html)

**Parameters:**

```
- featuresCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- predictionCol: string
- rawPredictionCol: string
```

#### [PCAModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/PCAModel.html)

**Parameters:**

```
- inputCol: string
- outputCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
```

#### [PipelineModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/PipelineModel.html)

**Parameters:**

```
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
```

#### [PolynomialExpansion](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/PolynomialExpansion.html)

**Parameters:**

```
- degree: integer
- inputCol: string
- outputCol: string
```

#### [RFormulaModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/RFormulaModel.html)

**Parameters:**

```
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
```

#### [RandomForestClassificationModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/classification/RandomForestClassificationModel.html)

**Parameters:**

```
- cacheNodeIds: boolean
- checkpointInterval: integer
- featuresCol: string
- featureSubsetStrategy: string
- impurity: string
- maxBins: integer
- maxDepth: integer
- maxMemoryInMB: integer
- minInfoGain: double
- minInstancesPerNode: integer
- numTrees: integer
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- predictionCol: string
- probabilityCol: string
- rawPredictionCol: string
- seed: double
- subsamplingRate: double
- thresholds: array (of double)
```

#### [RandomForestRegressionModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/regression/RandomForestRegressionModel.html)

**Parameters:**

```
- cacheNodeIds: boolean
- checkpointInterval: integer
- featuresCol: string
- featureSubsetStrategy: string
- impurity: string
- maxBins: integer
- maxDepth: integer
- maxMemoryInMB: integer
- minInfoGain: double
- minInstancesPerNode: integer
- numTrees: integer
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
- predictionCol: string
- seed: double
- subsamplingRate: double
```

#### [RegexTokenizer](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/RegexTokenizer.html)

**Parameters:**

```
- gaps: boolean
- inputCol: string
- minTokenLength: integer
- outputCol: string
- pattern: string
- toLowercase: boolean
```

#### [SQLTransformer](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/SQLTransformer.html)

**Parameters:**

```
- statement: string
```

#### [StandardScalerModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/StandardScalerModel.html)

**Parameters:**

```
- inputCol: string
- outputCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
```

#### [StopWordsRemover](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/StopWordsRemover.html)

**Parameters:**

```
- caseSensitive: boolean
- inputCol: string
- locale: string
- outputCol: string
- stopWords: array (of string)
```

#### [StringIndexerModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/StringIndexerModel.html)

**Parameters:**

```
- handleInvalid: string
- inputCol: string
- outputCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
```

#### [Tokenizer](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/Tokenizer.html)

**Parameters:**

```
- inputCol: string
- outputCol: string
```

#### [TrainValidationSplitModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/tuning/TrainValidationSplitModel.html)

**Parameters:**

```
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
```

#### [VectorAssembler](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/VectorAssembler.html)

**Parameters:**

```
- handleInvalid: string
- inputCols: array (of string)
- outputCol: string
```

#### [VectorAttributeRewriter](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/VectorAttributeRewriter.html)

**Parameters:**

```
```

#### [VectorIndexerModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/VectorIndexerModel.html)

**Parameters:**

```
- inputCol: string
- outputCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
```

#### [VectorSizeHint](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/VectorSizeHint.html)

**Parameters:**

```
- handleInvalid: string
- inputCol: string
- size: integer
```

#### [VectorSlicer](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/VectorSlicer.html)

**Parameters:**

```
- indices: array (of integer)
- inputCol: string
- names: array (of string)
- outputCol: string
```

#### [Word2VecModel](https://spark.apache.org/docs/3.0.0/api/java/org/apache/spark/ml/feature/Word2VecModel.html)

**Parameters:**

```
- inputCol: string
- outputCol: string
- parent: estimator (i.e., function(object*, object) as function(object*, object) as object*)
```


# Frequently asked questions and common issues

## Out of memory error

By default, the memory allocated is limited. This depends on whether you run RumbleDB with the standalone jar or as the thin jar in a Spark environment.

If you run RumbleDB with a standalone jar, then your laptop will allocate by default one quarter of your total working memory. You can check this with:

```
java -XX:+PrintFlagsFinal -version | grep -iE 'MaxHeapSize'   
```

In order to increase the memory, you can use `-Xmx10g` (for 10 GB, but you can use any other value):

```
java -jar -Xmx10g rumbledb-2.1.0-standalone.jar ...
```

If you run RumbleDB on your laptop (or a single machine) with the thin jar, then by default this is limited to around 2 GB, and you can change this with `--driver-memory`:

```
spark-submit --driver-memory 10G rumbledb-2.1.0-for-spark-4.0.jar ...
```

If you run RumbleDB on a cluster, then the memory needs to be allocated to the executors, not the driver:

```
spark-submit --executor-memory 10G rumbledb-2.1.0-for-spark-4.0.jar ...
```

Setting things up on a cluster requires more thinking because setting the executor memory should be done in conjunction with setting the total number of executors and the number of cores per executor. This highly depends on your cluster hardware.

## Paths with whitespaces

RumbleDB does not currently support paths with a whitespace. Make sure to put your data and modules at paths without whitespaces.

## "Hadoop bin directory does not exist" on Windows

If this happens, you can download winutils.exe to solve the issue as explained [here](https://phoenixnap.com/kb/install-spark-on-windows-10).

## "java.lang.NoSuchMethodError: com.esotericsoftware.kryo.serializers. FieldSerializer.setIgnoreSyntheticFields" with docker

This is a known issue under investigation. It is related to a version conflict between Kryo 4 and Kryo 5 that occasionally happens on some docker installations. We recommend trying a local installation instead, as described in the Getting Started section.

## Java version

A very common issue leading to some errors is using the wrong Java version. With Spark 3.5, only Java 11 or 17 is supported. With Spark 4, Java 17 or 21 are supported.

You should make sure in particular you are not using a more recent Java version. Multiple Java versions can normally co-habit on the same machine but you need to make sure to set the JAVA\_HOME variable appropriately.

It is easy to check the Java version with:

```
java -version
```


# Error codes

* \[FOAR0001] - Division by zero.
* \[FOAR0002] - Numeric operation overflow/underflow
* \[FOCA0002] - A value that is not lexically valid for a particular type has been encountered.
* \[FOCH0001] - Raised by fn:codepoints-to-string if the input contains an integer that is not the codepoint of a valid XML character.
* \[FOCH0003] - Raised by fn:normalize-unicode if the requested normalization form is not supported by the implementation.
* \[FODC0002] - Error retrieving resource.
* \[FODT0001] - Overflow/underflow in date/time operation.
* \[FODT0002] - Overflow/underflow in duration operation.
* \[FOFD1340] -This error is raised if the picture string or calendar supplied to fn:format-date, fn:format-time, or fn:format-dateTime has invalid syntax.
* \[FOFD1350] - This error is raised if the picture string supplied to fn:format-date selects a component that is not present in a date, or if the picture string supplied to fn:format-time selects a component that is not present in a time.
* \[FOTY0012] - The argument has no typed value (objects, arrays, functions cannot be atomized).
* \[JNTY0004] - Unexpected non-atomic element. Raised when objects or arrays are supplied where an atomic element is expected.
* \[JNTY0024] - Error getting the string value for array and object items
* \[JNTY0018] - Invalid selector error code. It is a type error if there is not exactly one supplied parameter for an object or array selector.
* \[RBDY0005] - Materialization Error: the sequence is too big to be materialized. Use --materialization-cap to increase the maximum materialization size, or add an output path to write to.
* \[RBML0001] - Unrecognized RumbleDB ML Class Reference An unrecognized classname is used in query while accessing the RumbleDB ML API.
* \[RBML0002] - Unrecognized RumbleDB ML Param Reference An unrecognized parameter is used in query while operating with a RumbleDB ML class.
* \[RBML0003] - Invalid RumbleDB ML Param Provided parameter does not match the expected type or value for the referenced RumbleDB ML class.
* \[RBML0004] - Input is not a DataFrame Provided input of items does not form a DataFrame as expected by RumbleDB ML.
* \[RBML0005] - Invalid schema for DataFrame in annotate() The provided schema can not be applied to the item data while converting the data to a DataFrame
* \[RBST0001] - CLI error. Raised when invalid parameters are supplied at launch.
* \[RBST0002] - Unimplemented feature error. Raised when a JSONiq feature that is not yet implemented in RumbleDB is used.
* \[RBST0003] - Invalid for clause expression error. Raised when an expression produces a different, big sequence of items for each binding within a big tuple, which would lead to a data flow explosion and to a nesting of jobs on the Spark cluster.
* \[RBST0004] - Implementation Error.
* \[SENR0001] - Serialization error. Function items can not be serialized.
* \[XPDY0002] - It is a dynamic error if evaluation of an expression relies on some part of the dynamic context that is absent.
* \[XPDY0050] - Dynamic type treat error. It is a dynamic error if the dynamic type of the operand of a treat expression does not match the sequence type specified by the treat expression. This error might also be raised by a path expression beginning with "/" or "//" if the context node is not in a tree that is rooted at a document node. This is because a leading "/" or "//" in a path expression is an abbreviation for an initial step that includes the clause treat as document-node().
* \[XPDY0130] - Generic runtime exception \[check error message].
* \[XPST0003] - Parsing error. Invalid syntax or unsupported feature in query.
* \[XPST0008] - Undefined element reference. It is a static error if an expression refers to an element name, attribute name, schema type name, namespace prefix, or variable name that is not defined in the static context, except for an ElementName in an ElementTest or an AttributeName in an AttributeTest.
* \[XPST0017] - Invalid function call error. It is a static error if the expanded QName and number of arguments in a static function call do not match the name and arity of a function signature in the static context.
* \[XPST0080] - Invalid cast error - It is a static error if the target type of a cast or castable expression is NOTATION anySimpleType, or anyAtomicType.
* \[XPST0081] - Unknown namespace prefix - It is a static error if a QName used in a query contains a namespace prefix that cannot be expanded into a namespace URI by using the statically known namespaces.
* \[XPTY0004] - Unexpected Type Error. It is a type error if, during the static analysis phase, an expression is found to have a static type that is not appropriate for the context in which the expression occurs, or during the dynamic evaluation phase, the dynamic type of a value does not match a required type. Example: using subtraction on strings.
* \[XQDY0054] - It is a dynamic error if a cycle is encountered in the definition of a module's dynamic context components, for example because of a cycle in variable declarations.
* \[XQTY0024] - Attribute After Non Attribute Error - It is a type error if the content sequence in an element constructor contains an attribute node following a node that is not an attribute node.
* \[XQDY0025] - Duplicate Attribute Error - It is a dynamic error if any attribute of a constructed element does not have a name that is distinct from the names of all other attributes of the constructed element.
* \[XQDY0074] - Invalid Element Name Error - It is a dynamic error if the value of the name expression in a computed element or attribute constructor cannot be converted to an expanded QName (for example, because it contains a namespace prefix not found in statically known namespaces.)
* \[XQDY0096] - Invalid Node Name Error - It is a dynamic error if the node-name of a node constructed by a computed element constructor has any of the following properties: 1. Its namespace prefix is xmlns. 2. Its namespace URI is <http://www.w3.org/2000/xmlns/>. 3. Its namespace prefix is xml and its namespace URI is not <http://www.w3.org/XML/1998/namespace>. 4. Its namespace prefix is other than xml and its namespace URI is <http://www.w3.org/XML/1998/namespace>.
* \[XQDY0137] - Duplicate pair name. It is a dynamic error if two pairs in an object constructor or in a simple object union have the same name.
* \[XQST0016] - Module declaration error. Current implementation does not support the Module Feature raises a static error if it encounters a module declaration or a module import.
* \[XQST0031] - Invalid JSONiq version. It is a static error if the version number specified in a version declaration is not supported by the implementation. For now, only version 1.0 is supported.
* \[XQST0033] - Namespace prefix bound twice. It is a static error if a module contains multiple bindings for the same namespace prefix.
* \[XQST0034] - Function already exists. It is a static error if multiple functions declared or imported by a module have the same number of arguments and their expanded QNames are equal (as defined by the eq operator).
* \[XQST0038] - It is a static error if a Prolog contains more than one default collation declaration, or the value specified by a default collation declaration is not present in statically known collations.
* \[XQST0039] - Duplicate parameter name. It is a static error for a function declaration or an inline function expression to have more than one parameter with the same name.
* \[XQST0047] - It is a static error if multiple module imports in the same Prolog specify the same target namespace.
* \[XQST0048] - It is a static error if a function or variable declared in a library module is not in the target namespace of the library module.
* \[XQST0049] - It is a static error if two or more variables declared or imported by a module have the same name.
* \[XQST0052] - Simple type error. The type must be the name of a type defined in the in-scope schema types, and the {variety} of the type must be simple.
* \[XQST0059] - It is a static error if an implementation is unable to process a schema or module import by finding a schema or module with the specified target namespace.
* \[XQST0069] - A static error is raised if a Prolog contains more than one empty order declaration.
* \[XQST0088] - It is a static error if the literal that specifies the target namespace in a module import or a module declaration is of zero length.
* \[XQST0089] - It is a static error if a variable bound in a for or window clause of a FLWOR expression, and its associated positional variable, do not have distinct names (expanded QNames).
* \[XQST0094] - Invalid variable in group-by clause. The name of each grouping variable must be equal (by the eq operator on expanded QNames) to the name of a variable in the input tuple stream.
* \[XQST0118] - In a direct element constructor, the name used in the end tag must exactly match the name used in the corresponding start tag, including its prefix or absence of a prefix.


# Licenses

RumbleDB uses the following software:

* ANTLR v4 Framework - BSD License
* Apache Commons Text - Apache License
* Apache Commons Lang - Apache License
* Apache Commons IO - Apache License
* Apache HTTP client - Apache License
* gson - Apache License
* JLine terminal framework - BSD License
* Kryo serialization framework - BSD License
* Laurelin (ROOT parser) - BSD-3
* Spark Libraries - Apache License

As well as the JSONiq language - CC BY-SA 3.0 License


