JSONiq The SQL of NoSQL-published
JSONiq The SQL of NoSQL-published
Ghislain Fourny
Table of Contents
1. Introduction ................................................................................................. 1
NoSQL - Why Are Relational Databases Not Good Enough? ............................ 1
Why JSONiq? .......................................................................................... 2
How to Run the Queries in This Book? ........................................................ 3
Acknowledgements ................................................................................... 3
I. JSON and the JSONiq Data Model ................................................................... 5
2. The JSON Syntax ................................................................................. 7
JSON Strings ................................................................................... 7
JSON Numbers ................................................................................ 8
JSON Booleans ................................................................................ 9
JSON Null ...................................................................................... 9
JSON Objects .................................................................................. 9
3. The JSONiq Data Model ...................................................................... 11
JSONiq Values: Items and Sequences ................................................. 11
Objects .......................................................................................... 13
Arrays ........................................................................................... 13
Atomics ........................................................................................ 14
4. The JSONiq Type System ..................................................................... 15
Item Types .................................................................................... 16
Atomic Types ......................................................................... 16
JSON Item Types : Object Types and Array Types ........................ 18
The Most General Item Type. ................................................... 19
Sequence Types .............................................................................. 20
II. Construction of Items and JSON Navigation .................................................... 23
5. Construction of Items ........................................................................... 25
Atomic Literals .............................................................................. 25
String Literals ........................................................................ 25
Number Literals. ..................................................................... 25
Boolean and Null Literals ......................................................... 27
Object Constructors ......................................................................... 27
Array Constructors .......................................................................... 29
Composing Constructors .................................................................. 29
6. Collections ......................................................................................... 33
Collections Used Throughout This Book ............................................. 33
7. JSON Navigation ................................................................................ 37
Object Navigation ........................................................................... 37
Array Unboxing .............................................................................. 40
Sequence Filtering .......................................................................... 41
Array Navigation ............................................................................ 42
iii
JSONiq
iv
45
47
47
47
48
48
49
50
50
52
52
53
54
55
57
57
59
60
63
63
64
70
72
76
81
83
84
85
86
89
89
89
90
91
92
95
97
97
97
98
99
99
JSONiq
100
101
105
107
115
117
119
121
125
129
vi
Chapter 1. Introduction
The possible solutions to a given problem emerge as the leaves of a tree, each node
representing a point of deliberation and decision.
Niklaus Wirth
Why JSONiq?
Why JSONiq?
NoSQL has a very broad meaning and, while the general principles are similar between data
stores, each data store has a specific format for the values (or trees) and a query language
tailored for the data store.
JSONiq was developed with the idea that many data stores share the same design principles
(e.g., collections of trees) so that it should be possibly to query them in a unified and portable
way.
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 results 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. Neither does it specify
Acknowledgements
The design and implementation of JSONiq is a team effort involving Dana Florescu (Oracle),
Jonathan Robie (EMC), Matthias Brantner (28msec), Markos Zaharioudakis (Oracle), Till
Westmann (Oracle) and myself (28msec).
This book was carefully reviewed by Matthias Brantner, Federico Cavalieri (28msec) and
Paul J. Lucas (28msec). Many thanks to them!
A significant part of the introduction ("Why JSONiq?") was written by Dana Florescu.
JSON Strings
Strings are double-quoted. To put it simply, they are sequences of Unicode characters with
absolutely no restriction:
"foo",
"What NoSQL solutions are out there?"
However, syntactically, some of these characters must be escaped with backslashes (escape
sequence). This includes double quotes, escaped as \" -- because otherwise they could be
confused with the end of a string -- and backslahes themselves, escaped as \\ -- because
otherwise you would not know if you mean a backslash character, or if you are escaping the
following character.
Finally, all Unicode control characters (null, new line, form feed, delete...) are not allowed
directly and must be built with an escape sequence. Any Unicode character, including control
characters, can be built with \u followed by the four hexadecimal digits that identify it within
Unicode. The most frequent control characters even have their own shortcuts: \n (new line),
\t (tab), \r (carriage return), \b (backspace), \f (form feed). The slash can also be obtained with
\/, although it is fine too if it appears alone. This is useful in JSON-hosting environments
where slashes are special.
JSON Numbers
JSON Numbers
Numbers cover the entire decimal space. There is no range restriction. Although there is
no formal distinction in JSON, numbers can be grouped into three subcategories. These
subcategories play an important role in JSONiq.
Integers, possibly with a negative sign and not beginning with a leading 0 (except 0 itself):
0
9
42
-96
123456789012345678901234567890123456789012345
"Plain" decimals, with a dot, both followed and preceded by at least by one digit (no
leading dot):
0.3
9.6
42.2346902834
-96.01345023400
Decimals in scientific notation, i.e., a plain decimal followed by an E (case does not matter)
and by a power of ten (an integer with an optional sign):
0.3e0
9.6E+24
42.2346902834e-2
-96.01345023400E-02345
JSON Booleans
JSON Booleans
Booleans cover the two logical truth values true and false, unquoted. There is not much more
to say about them...
true
false
JSON Null
Null is a special value that can be used to denote the absence of value.
null
JSON Objects
Objects are unordered sets of key/value pairs. A key is any JSON string as described above.
A value is any JSON building block.
According to the JSON RFC, keys (the strings) should be unique within the same object -and JSONiq does consider them unique.
You can see in the following examples that values can be also nested objects or arrays.
{
"_id" : "511C7C5C9A277C22D138802D",
"question_id" : 4419499,
"last_edit_date" : "2012-12-17T00:02:31",
"creation_date" : "2010-12-11T23:15:19",
"last_activity_date" : "2012-12-17T00:02:31",
"score" : 15,
"accepted_answer_id" : 4421601,
"title" : "MySQL and NoSQL: Help me to choose the right o
ne",
"tags" : [ "php", "mysql", "nosql", "cassandra" ],
JSON Objects
"view_count" : 3972,
"owner" : {
"user_id" : 279538,
"display_name" : "cedivad",
"reputation" : 430,
"user_type" : "registered",
"profile_image" : "http://www.gravatar.com/avatar/b77fa
dd2ba791134ac40a9c184be1eda?d=identicon&r=PG",
"link" : "http://stackoverflow.com/users/279538/cedivad
",
"accept_rate" : 74
},
"link" : "http://stackoverflow.com/questions/4419499/mysq
l-and-nosql-help-me-to-choose-the-right-one",
"is_answered" : true
}
In the NoSQL world, top-level JSON objects are often referred to as JSON documents.
10
11
JSONiq Values:
Items and Sequences
Commas are all you need to begin building your own sequences. You can mix and match!
12
Objects
()
Results:
Rule #3: A sequence of just one item is considered the same as this item itself. Whenever
we say that an expression returns or takes one item, we really mean that it takes a singleton
sequence of one item.
Results:
"foo"
JSONiq classifies the items mentioned above in three categories:
Objects: the counterparts of the syntactic JSON objects.
Arrays: the counterparts of the syntactic JSON arrays.
Atomics: the counterparts of JSON strings, JSON numbers, JSON booleans and JSON
nulls - but with a very rich type system which includes dates, for example.
Objects
An object represents a JSON object: an unordered collection of string/item pairs.
Each pair consists of an atomic of type string and of an item which can be in any category.
No two pairs have the same name. Because of this, the word field is also used to refer to pairs.
Arrays
An array represents a JSON array: an ordered list of items -- items in any category.
An array can be seen as a sequence that is wrapped in one single item. And since an array
is an item, arrays can nest -- like in JSON.
13
Atomics
Atomics
An atomic is a non-structured value that is annotated with a type.
JSONiq defines many useful builtin atomic types. For now, let us introduce those that have a
JSON counterpart. 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.
JSONiq also offers many other types of atomics. Here is a little appetizer that showcases
constructing a date and a duration (365 days), and adding them.
Results:
"2014-06-21"
14
Results:
256
Like in JavaScript, it is possible to create a variable without explicitly giving any static type.
JSONiq is still strongly typed, so that you will be told if there is a type inconsistency or
mismatch in your programs.
Results:
256
Variables are explained in the section called Variables in Chapter 10, FLWOR Expressions
more in details.
JSONiq supports types at the sequence level. They are called sequence types, and the syntax
for designing types is called the sequence type syntax. The type "integer" that was shown in
15
Item Types
Example 4.1, Specifying a type. matches singleton sequences of one atomic item of type
integer.
We say that a sequence matches a sequence type (or that a sequence type matches a sequence)
if the sequence is in the value space of the sequence type. Since an item is a particular
(singleton) sequence, we also can say that an item matches an item type or conversely.
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. But it is not forbidden for
the processor to be smart and warn you if it can detect that a type issue can arise at runtime.
There are many JSONiq expressions (cast, instance of, ...) which perform type operations and
that make use of the sequence type syntax. In the remainder of this section, we will introduce
sequence types using an "instance of" expression that returns true or false depending on
whether or not the type on the right side is matched by the value on the left side -- like in Java.
Results:
true
Item Types
Atomic Types
Atomic types are organized in a tree hierarchy.
JSONiq defines the following build-in types that have a direct relation with JSON:
string: the value space is all strings made of Unicode characters.
All string literals build an atomic that matches string.
integer: 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 that matches integer.
16
Atomic Types
decimal: the value space is that of all mathematical decimal numbers (D), with an infinite
range.
All decimal literals build an atomic that matches decimal.
double: the value space is that of all IEEE double-precision 64-bit floating point numbers.
All double literals build an atomic that matches double.
boolean: the value space contains the booleans true and false.
All boolean literals build an atomic that matches boolean.
null: the value space is a singleton and only contains null.
All null literals build an atomic that matches null.
atomic: all atomic types.
All literals build an atomic that matches atomic.
Results:
true
true
true
true
true
true
true
true
17
Results:
true
true
true
true
18
of object,
of json-item,
array,
json-item
Results:
true
true
true
true
true
Results:
true
true
true
true
true
19
Sequence Types
true
Sequence Types
All sequences match the sequence type item*.
Results:
true
true
true
But sequence types can be much more precise than that. In general, a sequence type is made of
an item type, as presented above, followed by an occurrence indicator among the following:
* stands for a sequence of any length (zero or more)
+ stands for a non-empty sequence (one or more)
? stands for an empty or a singleton sequence (zero or one)
The absence of indicator stands for a singleton sequence (one).
20
Sequence Types
Results:
true
true
true
true
true
true
There is also a special type that matches only empty sequences, denoted () as well:
Results:
true
21
22
Chapter 5. Construction of
Items
As we just saw, the items (objects, arrays, strings, ...) mentioned in Chapter 2, The JSON
Syntax are constructed exactly as they are constructed in JSON. In a way, any JSON building
block is also a well-formed JSONiq query which just "returns itself" (more precisely: its
counterpart in the JSONiq Data Model).
Atomic Literals
String Literals
The syntax for creating strings is identical to that of JSON. No surprise here. JSON's
backslash escaping is supported, and like in JSON, double quotes are required and single
quotes are forbidden.
Results:
"foo"
"This is a line
and this is a new line"
""
"This is a nested "quote""
Number Literals.
The syntax for creating numbers is identical to that of JSON.
25
Number Literals.
Results:
42
3.14
-6.022E23
Well, not quite. Actually, JSONiq allows a more flexible superset. In particular:
leading 0s are allowed
a decimal literal can begin or end with a dot
a number may begin with a + sign
Results:
42
0.1415926535
42
6.022E23
Remember 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. No surprises either.
26
Results:
true
false
null
Object Constructors
The syntax for creating objects is also 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.
: "bar" },
: [ 1, 2, 3, 4, 5, 6 ] },
: true, "bar" : false },
is a key" : { "value" : "a value" } }
Results:
{
}
{
"foo" : "bar"
}
27
Object Constructors
{
"foo" : [ 1, 2, 3, 4, 5, 6 ]
}
{
"foo" : true,
"bar" : false
}
{
"this is a key" : {
"value" : "a value"
}
}
Again, JSONiq is more flexible here. Like in JavaScript, if your key is simple enough (like
alphanumerics, underscores, dashes, these kinds of things), you are welcome to omit the
quotes. The strings for which quotes are not mandatory are called unquoted names. This class
of strings can be used for unquoted keys, but also in later sections for variable and function
names, and for module aliases.
foo : "bar" },
foo : [ 1, 2, 3, 4, 5, 6 ] },
foo : "bar", bar : "foo" },
"but you need the quotes here" : null }
Results:
{
"foo" : "bar"
}
{
"foo" : [ 1, 2, 3, 4, 5, 6 ]
}
{
"foo" : "bar",
"bar" : "foo"
}
{
"but you need the quotes here" : null
28
Array Constructors
Array Constructors
The syntax for creating arrays is identical to that of JSON (do you sense a growing feeling
that we are repeating ourselves? But it feels so good to say it): square brackets, comma
separated values.
Results:
[ ]
[ 1, 2, 3, 4, 5, 6 ]
[ "foo", [ 3.14, "Go" ], { "foo" : "bar" }, true ]
Square brackets are mandatory. Things can only be pushed so far.
Composing Constructors
Of course, JSONiq would not be very interesting if all you could do is copy and paste JSON
documents. So now is time to get to the meat.
Because JSONiq expressions are fully composable, in objects and arrays constructors, you
can put way more than just atomic literals, object constructors and array constructors: you can
put any JSONiq expression. An expression is the JSONiq building block. You already know
some (literals, constructors, comma, cast, instance of) and plenty more will be introduced
in the next part (arithmetics, logic, comparison, if-then-else, try-catch, FLWORS that allow
you to join, select, group, filter, project, stream in windows, ...)
In order to illustrate composability, the following examples use a few of the many operators
you can use:
"to" for creating sequences of consecutive integers,
"||" for concatenating strings,
29
Composing Constructors
Results:
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
[ "foobar", 1, 2, 3, 4 ]
In an object, the expression you use for the key must evaluate to an atomic - if it is not a
string, it will just get cast to it.
An error is raised if the key expression is not an atomic.
Results:
{
"foobar" : true
}
{
"2" : "foo"
}
And do not worry about the value expression: if it is empty, null will be used as a value, and
if it contains two items or more, they will be wrapped into an array.
30
Composing Constructors
Results:
{
"foo" : 2
}
{
"foo" : null,
"bar" : [ 1, 2 ]
}
The {| |} constructor can be used to merge several objects.
Results:
{
"foo" : "bar",
"bar" : "foo"
}
An error is raised if the operand expression does not evaluate to a sequence of objects.
31
32
Chapter 6. Collections
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 be in a collection.
We now introduce collections, because collections are perfect to illustrate the JSON
navigation syntax which will be introduced in the next section.
Collections are sequences of objects, identified by a name which is a string.
Adding or deleting collections from the set of known collections to a query processor, and
loading the data in a collection are implementation-dependent and outside of the scope of
this book.
We will just assume that there is a function named collection() that returns all objects
associated with the provided collection name.
Results:
{
"question" : "What NoSQL technology should I use?"
}
33
Collections Used
Throughout This Book
Results:
{
"question" : "What NoSQL technology should I use?"
}
collection("faqs") - this is a collection of StackOverflow FAQs.
Results:
{
"_id" : "511C7C5C9A277C22D138802D",
"question_id" : 4419499,
"last_edit_date" : "2012-12-17T00:02:31",
"creation_date" : "2010-12-11T23:15:19",
"last_activity_date" : "2012-12-17T00:02:31",
"score" : 15,
"accepted_answer_id" : 4421601,
"title" : "MySQL and NoSQL: Help me to choose the right o
ne",
"tags" : [ "php", "mysql", "nosql", "cassandra" ],
"view_count" : 3972,
"owner" : {
"user_id" : 279538,
"display_name" : "cedivad",
"reputation" : 430,
"user_type" : "registered",
"profile_image" : "http://www.gravatar.com/avatar/b77fa
dd2ba791134ac40a9c184be1eda?d=identicon&r=PG",
"link" : "http://stackoverflow.com/users/279538/cedivad
",
"accept_rate" : 74
},
"link" : "http://stackoverflow.com/questions/4419499/mysq
34
Collections Used
Throughout This Book
l-and-nosql-help-me-to-choose-the-right-one",
"is_answered" : true
}
collection("answers") - this is a collection of StackOverflow answers (to the previous
FAQs).
Results:
{
"_id" : "511C7C5D9A277C22D13880C3",
"question_id" : 37823,
"answer_id" : 37841,
"creation_date" : "2008-09-01T12:14:38",
"last_activity_date" : "2008-09-01T12:14:38",
"score" : 7,
"is_accepted" : false,
"owner" : {
"user_id" : 2562,
"display_name" : "Ubiguchi",
"reputation" : 1871,
"user_type" : "registered",
"profile_image" : "http://www.gravatar.com/avatar/00b87
a917ec763c0c051dc6b8c06f402?d=identicon&r=PG",
"link" : "http://stackoverflow.com/users/2562/ubiguchi"
}
}
Many queries in this book can be directly input into 28.io's try-it-now sandbox, as these
collections are preloaded (this is real-world data).
35
36
Object Navigation
The simplest way to navigate an object is similar to JavaScript. This will work as soon as
you do not push it too much: alphanumerical characters, dashes, underscores. The rule for
unquoted names is similar to keys in object constructions, and to variable names. The empty
sequence is returned if no key is found with the specified name.
37
Object Navigation
collection("one-object").question
Results:
"What NoSQL technology should I use?"
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 any item which is not an object (arrays and atomics) results
in the empty sequence.
Results:
"bar"
"bar2"
{
"ids" : [ 4419499, 282783, 4720508, 5453872, 6183352 ]
}
Results:
"What NoSQL technology should I use?"
"answer"
38
Object Navigation
Of course, unquoted keys will not work for strings that are not unquoted names, e.g., if the
field contains a dot or begins with a digit. Then you will need quotes. If you use a more
general expression on the right-hand side of the dot, it must always have parentheses.
Results:
"What NoSQL technology should I use?"
"What NoSQL technology should I use?"
The value returned by the right-hand side expression is cast to string. An error is raised upon
failure. This value may be the empty sequence, in which case the object lookup also returns
the empty sequence.
Results:
"What NoSQL technology should I use?"
"What NoSQL technology should I use?"
39
Array Unboxing
Variables, or a context item reference, do not need parentheses. Variables are introduced in
the section called Variables, but here is a sneak peek:
Array Unboxing
The items in an array (which is an item) can be extracted as a sequence of items with the
[] postfix operator.
The argument must be (a singleton sequence of) one array or the empty sequence (in which
case the empty sequence is returned as well.
40
Sequence Filtering
"mysql"
"nosql"
"cassandra"
"sql"
"database"
"nosql"
"non-relational-database"
"nosql"
"couchdb"
"cassandra"
"redis"
"database"
"full-text-search"
"nosql"
"couchdb"
"riak"
"database"
"view"
"nosql"
"couchdb"
Sequence Filtering
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. The predicate
expression can use $$ to refer to the item being processed, called the context item.
If the predicate evaluates to an integer, it is matched against the item position in the lefthand side sequence automatically.
Results:
41
Array Navigation
5
"What is the bottleneck in MapReduce?"
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.
Results:
"What NoSQL technology should I use?"
2
4
6
8
10
Array Navigation
Once you know how to unbox an array and to filter a sequence, array lookup comes for free.
It feels very much like opening a box of Swiss chocolate and then picking your favorite:
Unbox the array with [].
Pick the $i-th item in the sequence using a predicate with an integer [$i].
42
Array Navigation
Results:
"answer"
"What is the bottleneck in MapReduce?"
43
44
Construction of Sequences
Comma Operator
The comma allows you to concatenate two sequences, or even single items. This operator has
the lowest precedence of all, so do not forget the parentheses if you would like to change this.
Also, the comma operator is associative -- in particular, sequences do not nest. You need to
use arrays in order to nest.
],
Results:
1
2
3
4
5
{
"foo" : "bar"
}
[ 1 ]
2
4
1
2
47
Range Operator
3
4
5
Range Operator
With the binary operator "to", you can generate larger sequences with just two integer
operands.
If the left operand is greater than the right operand, an empty sequence is returned.
If an operand evaluates to something else than a single integer, an error is raised. There is one
exception with the empty sequence, which behaves in a particular way for most operations
(see below).
Results:
1
2
3
4
5
6
7
8
9
10
Parenthesized Expressions
Expressions take precedence on one another. For example, addition has a higher precedence
than the comma. Parentheses allow you to change precedence.
If the parentheses are empty, the empty sequence is produced.
48
Arithmetics
Results:
25
Arithmetics
JSONiq supports the basic four operations, as well integer division and modulo. You should
keep in mind that, as is the case in most programming languages, multiplicative operations
have precedence over additive operations. Parentheses can override it, as explained above.
Results:
8
Dates, times and durations are also supported in a natural way.
Results:
"P29D"
If any of the operands is a sequence of more than one item, an error is raised.
If any of the operands is not a number, a date, a time or a duration, or if the operands are not
compatible (say a number and a time), an error is raised.
49
String Concatenation
Do not worry if the two operands do not have the same number type, JSONiq will do the
adequate conversions.
String Concatenation
Two strings or more can be concatenated using the concatenation operator. An empty
sequence is treated like an empty string.
Comparison
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.
Comparison is only possible between two compatible types, otherwise, an error is raised.
50
Comparison
1 + 1 eq 2,
1 lt 2
Results:
true
true
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.
Results:
false
true
true
For ordering operators (lt, le, gt, ge), null is considered the smallest possible value (like in
JavaScript).
Results:
true
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
51
comparison semantics for them is carefully chosen to have the right characteristics as to
enable optimization.
Logic
JSONiq logics support is based on two-valued logics: there is just true and false and nothing
else.
Non-boolean operands get automatically converted to either true or false, or an error is
raised. The boolean() function performs a manual conversion. The rules for conversion were
designed in such a way that it feels "natural". Here they are:
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.
Operand singleton sequences of any other item cannot be converted and an error is raised.
Operand sequences of more than one item cannot be converted and an error is raised.
52
Propositional Logic
Results:
{
"empty-sequence" : false,
"null" : false,
"non-empty-string" : true,
"empty-string" : false,
"zero" : false,
"not-zero" : true
}
false
Propositional Logic
JSONiq supports the most famous three boolean operations: conjunction, disjunction,
and negation. Negation has the highest precedence, then conjunction, then disjunction.
Comparisons have a higher precedence than all logical operations. Parentheses can override.
Results:
true
53
First-Order Logic
(Quantified Variables)
true
A sequence with more than one item, or singleton objects and arrays cannot be converted to
a boolean. An error is raised if it is attempted.
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 raise an error.
Results:
true
Results:
true
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.
54
Builtin Functions
Builtin Functions
The syntax for function calls is similar to many other languages.
Like in C++ (namespaces) or Java (packages, classes), functions live in namespaces that are
URIs.
Although it is possible to fully write the name of a function, namespace included, it can be
cumbersome. Hence, for convenience, a namespace can be associated with a prefix that acts
as a shortcut.
JSONiq supports three 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 12, Prologs 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 13,
Modules describes how to define your own modules.
For now, we only introduce how to call builtin functions -- these are the simplest, since they
do not need any prefix or explicit namespace.
55
Builtin Functions
"bar"
"foobar"
Some builtin functions perform aggregation and are particularly convenient:
Results:
5050
50.5
20
Remember that JSONiq is a strongly typed language. Functions have signatures, for example
sum() expects a sequence of numbers. An error is raised if the actual types do not match the
expected types.
Also, calling a function with two parameters is different from calling a function with one
parameter that is a sequence with two items. For the latter, extra parentheses must be added
to make sure that the sequence is taken as a single parameter.
Results:
4
56
Conditional Expressions
A conditional expression allows you to pick the one or the other value depending on a boolean
value.
57
Conditional Expressions
Results:
{
"foo" : "no"
}
{
"foo" : "yes"
}
{
"foo" : "no"
}
{
"foo" : "yes"
}
{
"foo" : "no"
}
{
"foo" : "no"
}
{
"foo" : "yes"
}
Note that the else clause is mandatory (but can be the empty sequence)
Results:
{
58
Switch expressions
"foo" : "yes"
}
Switch expressions
Switch expressions are very similar to C++. 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.
59
Try-Catch expressions
Results:
"foo"
"1 + 1 is 2"
Try-Catch expressions
A try catch expression evaluates the expression inside the try block and returns its resulting
value.
However, if an error is raised during this evaluation, the catch clause is evaluated and its
result value returned.
Results:
"Caught!"
Only errors raised within the lexical scope of the try block are caught.
60
Try-Catch expressions
return try { $x }
catch * { "Caught!" }
Error:
division by zero
Errors that are detected statically within the try block, for example syntax errors, are still
reported statically.
Note that this applies also if the engine is capable of detecting a type error statically, while
another engine might only discover it at runtime and catch it. You should keep this in mind,
and only use try-catch expressions as a safety net.
Error:
invalid expression: syntax error, a path expression cannot
begin with an axis step
Example 9.10. A try catch expression with a type error (no guarantee of
failure or success).
try { "foo" + "bar" } catch * { "Caught!" }
Results:
"Caught!"
61
62
Variables
Values can be bound to variables within a certain scope. Variable references always begin
with a dollar sign: $foo.
The scope of a variable declared in a FLWOR clause comprises all further clauses of the
FLWOR expression up to the return clause.
Variables are immutables, but variable bindings can be hidden with a binding to a variable
with the same name.
Variables can be declared by FLWOR expressions as shown in this chapter, but also as global
variables (the section called Global Variables) or in typeswitch expressions (the section
called Typeswitch Expressions).
There is a special variable which is called the context item and which is denoted with $$.
You already saw it in the section called Sequence Filtering in Chapter 7, JSON Navigation.
63
For Clauses
For Clauses
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.
For example, the following for clause:
for $x in 1 to 3
...
produces the following stream of tuples. The tuples themselves are for explanatory purposes,
they are not part of the data model. The syntax is also ad-hoc and is used for illustrating.
$x : 1
$x : 2
$x : 3
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
display_name FROM answers". $x is bound in turn to each item in the answers collection.
Results:
"Ubiguchi"
"Rob Wells"
"Victor Nicollet"
"descent89"
64
For Clauses
"JasonSmith"
"JasonSmith"
"JasonSmith"
"JasonSmith"
For clause expressions are composable, there can be several of them.
Results:
11
12
13
21
22
23
31
32
33
Results:
11
12
13
21
22
23
31
65
For Clauses
32
33
A for variable is visible to subsequent bindings.
Results:
1
2
3
4
5
6
7
8
9
{
"id" : 5453872,
"tag" : "database"
}
{
"id" : 5453872,
"tag" : "full-text-search"
}
{
"id" : 5453872,
66
For Clauses
"tag" : "nosql"
}
{
"id" : 5453872,
"tag" : "couchdb"
}
{
"id" : 5453872,
"tag" : "riak"
}
It is also possible to bind the position of the current item in the sequence to a variable.
at $position in collection("answers")
{
id" : $x.answer_id,
id" : $position
Results:
{
"old id" : 37841,
"new id" : 1
}
{
"old id" : 37844,
"new id" : 2
}
{
"old id" : 4419542,
"new id" : 3
}
{
"old id" : 4419578,
"new id" : 4
}
{
"old id" : 4720977,
67
For Clauses
"new id" : 5
}
{
"old id" : 5454583,
"new id" : 6
}
{
"old id" : 6195094,
"new id" : 7
}
{
"old id" : 6210422,
"new id" : 8
}
JSONiq supports joins. For example, the counterpart of "SELECT q.title AS question,
q.question_id FROM faq q JOIN answers a ON q.question_id = a.question_id" is:
68
For Clauses
}
{
"question" : "Full-text search in NoSQL databases",
"answer score" : 6
}
{
"question" : "Find CouchDB docs missing an arbitrary field
",
"answer score" : 0
}
{
"question" : "Find CouchDB docs missing an arbitrary field
",
"answer score" : 1
}
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 answer for a question. The following query is the counterpart of "SELECT
q.title AS question, q.question_id FROM faq q LEFT JOIN answers a ON q.question_id =
a.question_id".
69
Where Clauses
"answer score" : 1
}
{
"question" : "The Next-gen Databases",
"answer score" : null
}
{
"question" : "Redis, CouchDB or Cassandra?",
"answer score" : 34
}
{
"question" : "Full-text search in NoSQL databases",
"answer score" : 6
}
{
"question" : "Find CouchDB docs missing an arbitrary field
",
"answer score" : 0
}
{
"question" : "Find CouchDB docs missing an arbitrary field
",
"answer score" : 1
}
Where Clauses
Where clauses are used for filtering.
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 q.title as question, q.question_id as id FROM
faq WHERE CONTAINS(question, 'NoSQL')".
70
Where Clauses
"question" : $question.title,
"id" : $question.question_id
}
Results:
{
"question" : "MySQL and NoSQL: Help me to choose the right
one",
"id" : 4419499
}
{
"question" : "Full-text search in NoSQL databases",
"id" : 5453872
}
JSONiq can do joins with where clauses, too:
Results:
{
"question" : "MySQL and NoSQL: Help me to choose the right
one",
"answer score" : 17
}
{
"question" : "MySQL and NoSQL: Help me to choose the right
one",
"answer score" : 1
}
71
Order Clauses
{
"question" : "Redis, CouchDB or Cassandra?",
"answer score" : 34
}
{
"question" : "Full-text search in NoSQL databases",
"answer score" : 6
}
{
"question" : "Find CouchDB docs missing an arbitrary field
",
"answer score" : 0
}
{
"question" : "Find CouchDB docs missing an arbitrary field
",
"answer score" : 1
}
Order Clauses
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 a.display_name, a.score FROM
answers a ORDER BY a.display_name".
72
Order Clauses
Results:
{
"owner" : "JasonSmith",
"score" : 34
}
{
"owner" : "JasonSmith",
"score" : 6
}
{
"owner" : "JasonSmith",
"score" : 0
}
{
"owner" : "JasonSmith",
"score" : 1
}
{
"owner" : "Rob Wells",
"score" : 4
}
{
"owner" : "Ubiguchi",
"score" : 7
}
{
"owner" : "Victor Nicollet",
"score" : 17
}
{
"owner" : "descent89",
"score" : 1
}
Multiple sorting criteria can be given - they are treated with the semantics of a lexicographic
order, that is, incoming tuples are first sorted according to the first criterion, and in case of
equality the second criterion is used, etc.
73
Order Clauses
Results:
{
"owner" : "JasonSmith",
"score" : 0
}
{
"owner" : "JasonSmith",
"score" : 1
}
{
"owner" : "JasonSmith",
"score" : 6
}
{
"owner" : "JasonSmith",
"score" : 34
}
{
"owner" : "Rob Wells",
"score" : 4
}
{
"owner" : "Ubiguchi",
"score" : 7
}
{
"owner" : "Victor Nicollet",
"score" : 17
}
{
"owner" : "descent89",
"score" : 1
74
Order Clauses
}
For each criterion, 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 (even before null) or
last (even after null).
Results:
{
"owner" : "descent89",
"score" : 1
}
{
"owner" : "Victor Nicollet",
"score" : 17
}
{
"owner" : "Ubiguchi",
"score" : 7
}
{
"owner" : "Rob Wells",
"score" : 4
}
{
"owner" : "JasonSmith",
"score" : 0
}
{
75
Group Clauses
"owner" : "JasonSmith",
"score" : 1
}
{
"owner" : "JasonSmith",
"score" : 6
}
{
"owner" : "JasonSmith",
"score" : 34
}
An error is raised if the expression does not evaluate to an atomic or to the empty sequence.
Group Clauses
Grouping is also supported, like in SQL.
For each incoming tuple, the expression in the group clause is evaluated to an atomic. The
value of this atomic is called a grouping key. The incoming tuples are then grouped according
to the grouping key -- one group for each value of the grouping key.
For each group, a tuple is output, in which:
Each grouping variable (appearing in the group clause) is bound to the group's key
corresponding to this variable.
Each other (non-grouping) variable is bound to the sequence obtained by concatenating all
original values of the variable within the group. Aggregations can then be done on these
variables in further clauses.
Here is an example:
The first for clause produces four tuples (this is again an ad-hoc syntax for illustrative
purposes):
76
Group Clauses
"$i"
"$i"
"$i"
"$i"
:
:
:
:
1,
1,
2,
2,
"$j"
"$j"
"$j"
"$j"
:
:
:
:
3
4
3
4
Then the group clause groups according the value of $j. There are two distinct values (3 and
4), so that this results in two groups.
Group 1 (key $j : 3)
$i : 1, $j : 3
$i : 2, $j : 3
Group 2 (key $j : 4)
$i : 1, $j : 4
$i : 2, $j : 4
In each output tuple, $j is the grouping variable and is bound to the key of the group. $i is
non-grouping and is bound to the sequence of all values in the group.
$i : (1, 2), $j : 3
$i : (1, 2), $j : 4
The following query is equivalent to "SELECT question_id FROM answers GROUP BY
question_id".
Results:
{
"question" : 5453872
}
{
"question" : 6183352
77
Group Clauses
}
{
"question" : 4720508
}
{
"question" : 4419499
}
{
"question" : 37823
}
The following query is equivalent to "SELECT question_id, COUNT(*) FROM answers
GROUP BY question_id".
78
Group Clauses
{
"question" : 37823,
"count" : 2
}
The following query is equivalent to "SELECT question_id, AVG(score) FROM answers
GROUP BY question_id".
79
Group Clauses
80
Let Clauses
Results:
{
"question" : 6183352,
"count" : 2
}
{
"question" : 4419499,
"count" : 2
}
{
"question" : 37823,
"count" : 2
}
Let Clauses
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.
81
Let Clauses
"count" : $count
}
Results:
{
"question" : 6183352,
"count" : 2
}
{
"question" : 4419499,
"count" : 2
}
{
"question" : 37823,
"count" : 2
}
Note that it is perfectly fine to reuse a variable name and hide a variable binding.
Results:
{
82
Count Clauses
Count Clauses
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.
83
Map Operator
{
"id" : 3,
"faq" : "Redis, CouchDB or Cassandra?"
}
{
"id" : 4,
"faq" : "Find CouchDB docs missing an arbitrary field"
}
{
"id" : 5,
"faq" : "Full-text search in NoSQL databases"
}
Map Operator
JSONiq provides a shortcut for a for-return construct, automatically binding each item in the
left-hand-side sequence to the context item.
84
Composing FLWOR
Expressions
Results:
2
4
6
8
10
12
14
16
18
20
85
Ordered and
Unordered Expressions
) ge 2
where not $answer.is_accepted
return $answer.owner.display_name
)
]
Results:
[ "JasonSmith" ]
Results:
{
"_id" : "511C7C5D9A277C22D13880C3",
"question_id" : 37823,
"answer_id" : 37841,
"creation_date" : "2008-09-01T12:14:38",
"last_activity_date" : "2008-09-01T12:14:38",
"score" : 7,
"is_accepted" : false,
"owner" : {
"user_id" : 2562,
86
Ordered and
Unordered Expressions
"display_name" : "Ubiguchi",
"reputation" : 1871,
"user_type" : "registered",
"profile_image" : "http://www.gravatar.com/avatar/00b87a
917ec763c0c051dc6b8c06f402?d=identicon&r=PG",
"link" : "http://stackoverflow.com/users/2562/ubiguchi"
}
}
{
"_id" : "511C7C5D9A277C22D13880C4",
"question_id" : 37823,
"answer_id" : 37844,
"creation_date" : "2008-09-01T12:16:40",
"last_activity_date" : "2008-09-01T12:16:40",
"score" : 4,
"is_accepted" : false,
"owner" : {
"user_id" : 2974,
"display_name" : "Rob Wells",
"reputation" : 17543,
"user_type" : "registered",
"profile_image" : "http://www.gravatar.com/avatar/876928
1d99f8fe9c208fd6a926c383d1?d=identicon&r=PG",
"link" : "http://stackoverflow.com/users/2974/rob-wells"
,
"accept_rate" : 94
}
}
An ordered expression can be used to reactivate ordering behaviour in a subscope.
87
Ordered and
Unordered Expressions
return $answer
}
)
return $question
}
Results:
{
"_id" : "511C7C5C9A277C22D138808A",
"question_id" : 4720508,
"creation_date" : "2011-01-18T04:32:30",
"last_activity_date" : "2011-01-19T06:46:34",
"score" : 13,
"accepted_answer_id" : 4720977,
"title" : "Redis, CouchDB or Cassandra?",
"tags" : [ "nosql", "couchdb", "cassandra", "redis" ],
"view_count" : 5620,
"owner" : {
"user_id" : 216728,
"display_name" : "nornagon",
"reputation" : 3114,
"user_type" : "registered",
"profile_image" : "http://www.gravatar.com/avatar/13f271
99f9bf9c9f1261dc8a49630a6b?d=identicon&r=PG",
"link" : "http://stackoverflow.com/users/216728/nornagon
",
"accept_rate" : 86
},
"link" : "http://stackoverflow.com/questions/4720508/redis
-couchdb-or-cassandra",
"is_answered" : true
}
88
Instance-of Expressions
A quick glimpse on this expression was already given. An instance expression can be used
to tell whether a sequence matches a given sequence type, like in Java.
Results:
true
false
true
true
true
true
true
Treat Expressions
A treat expression just forwards its operand value, but only after checking that a JSONiq
value matches a given sequence type. If it is not the case, an error is raised.
89
Castable Expressions
Results:
1
"foo"
{
"foo"
}
{
"foo"
}
{
"bar"
}
[ 1, 2,
: "bar"
: "bar"
: "foo"
3 ]
Error:
"xs:integer" cannot be treated as type xs:string
Castable Expressions
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.
90
Cast Expressions
Results:
true
false
true
false
false
true
Cast Expressions
A cast expression casts a (single) JSONiq value to a given atomic type. The resulting value
is annotated with this type.
Also here, the question mark allows for an empty sequence. An error is raised if the cast is
unsuccessful.
Results:
1
91
Typeswitch Expressions
"2013-04-02"
"2013-04-02"
Error:
sequence of more than one item can not be cast to type with
quantifier '1' or '?'
Typeswitch Expressions
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.
Results:
"string"
In each clause, it is possible to bind the value of the first operand to a variable.
92
Typeswitch Expressions
case $i
case $s
case $o
default
as
as
as
$d
integer return $i + 1
string return $s || "foo"
object return [ $o ]
return $d
Results:
"foofoo"
The vertical bar can be used to allow several types in the same case clause.
Results:
{
"integer or string" : "foo"
}
93
94
Setters.
Setters allow to specify a default behaviour for various aspects of the language.
97
Results:
98
{
}
{
"foo" : "bar"
}
Results:
"12 345,68"
Namespaces
Variables and functions live in namespaces that are URIs -- the semantics is similar to that
of C++ namespaces. For convenience, namespaces are associated with a much shorter alias,
and this alias can be used as a prefix to a variable or a function.
Until now, we only dealt with main queries. In main queries, the namespace alias local: is
predefined so that global variables and functions that are local to the main query can use
this alias, for example local:myvariable or local:myfunction(). This alias is associated with
a namespace, but which namespace it is not relevant for writing queries.
For variables, the alias is optional -- variables not prefixed with an alias live in no namespace.
For functions, the absence of alias is only allowed for builtin functions. Builtin functions live
in their own special namespace.
Other namespaces and aliases can be defined as well with imported library modules. This is
defined in Chapter 13, Modules.
99
Global Variables
Global Variables
Variables can be declared global. Global variables are declared in the prolog.
100
User-Defined Functions
variable. A default value for an external variable can also be supplied in case none is provided
from outside.
Results:
{
"foo" : "bar"
}
In these examples, global variables have no prefix. They can also be prefixed with the
predefined alias local:, but them they must be prefixed both in the declaration and when used.
Results:
{
"foo" : "bar"
}
Global variables that are imported from other modules are prefixed with the alias associated
with the imported module, as will be explained in Chapter 13, Modules.
User-Defined Functions
You can define your own functions in the prolog.
Unlike variables, user-defined functions must be prefixed, because unprefixed functions are
the builtin functions.
101
User-Defined Functions
In the prolog of a main query, these user-defined functions must be prefixed with the
predefined alias local:, both in the declaration and when called.
Remember that types are optional, and if you do not specify any, item* is assumed, both for
parameters and for the return type.
102
User-Defined Functions
Error:
xs:integer can not be promoted to parameter type xs:string
of function local:say-hello()
103
104
Once you have defined a library module, you can import it in any other module (library
or main). An alias must be 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 -- only the namespace
really matters.
105
Results:
1764
An engine may come with a number of builtin library modules. For example, there is the
standardized math module.
Results:
3.1415926535897931
1.073741824E9
2.7182818284590451
100
0
0.3010299956639812
2
1.2246467991473532E-16
106
107
Results:
2.3
-2
-3
-2
-2.514
3.14
"000'001'234'567"
"1,234,567.890"
108
encode-for-uri("1 + 1 is 2"),
iri-to-uri(
"http://www.example.com/chuchichschtli"),
escape-html-uri(
"http://www.example.com/chuchichschtli")
Results:
"NoSQL"
78
111
83
81
76
true
"NOSQL"
"nosql"
"nOSQL"
"http://www.jsoniq.org/types"
"1%20%2B%201%20is%202"
"http://www.example.com/chuchich%C3%A4schtli"
"http://www.example.com/chuchich%C3%A4schtli"
109
Results:
"foo1truebar"
"1-2-3-4-5-6-7-8-9-10"
9
true
true
true
"56789"
"No"
"SQL"
true
"YesSQL"
"Go"
"Boldly"
"Where"
"No"
"Man"
"Has"
"Gone"
"Before"
110
"bar"
[ "foobar", "foo", "bar" ]
"bar"
[ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 ]
[ 2, 3, 4, 5 ]
"foo"
"bar"
Results:
"foo"
"bar"
1
3
5
true
"foo"
"foo"
"bar"
"foo"
111
count(1 to 100),
avg(1 to 100),
max(1 to 100),
min(1 to 100),
sum(1 to 100)
Results:
100
50.5
100
1
5050
Results:
"2013-06-10T15:23:24.163953+02:00"
"2013-06-10+02:00"
"15:23:24.163953+02:00"
"PT2H"
Results:
112
"2013-06-21"
"17:00:00"
"2013-06-21T17:00:00Z"
"2013-06-21T17:00:00+01:00"
"P2DT1H30M15S"
"511C7C5C9A277C22D138802F"
113
114
Error:
Lazy evaluation and optimizations with regard to errors are allowed. Raising errors is not
always deterministic, as in some cases the processor might (but is not required to) stop
evaluating the operands of an expression if it determines that only one possible value can be
returned by that expression. The following expression may return true, or may raise an error.
Results:
true
117
118
Results:
true
Results:
false
Results:
false
119
Results:
true
Two objects or arrays can be tested for logical equality as well, using deep-equal(), which
performs a recursive comparison.
Results:
true
Results:
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.
120
121
Results:
2
This is different for arrays: a singleton array is distinct from its unique member, like in JSON.
Results:
[ 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.
Results:
1
Results:
4
122
Other than that, arrays and sequences can contain exactly the same members (atomics, arrays,
objects).
Results:
[ 1, "foo", [ 1, 2, 3, 4 ], { "foo" : "bar" } ]
Results:
1
"foo"
[ 1, 2, 3, 4 ]
{
"foo" : "bar"
}
Arrays can be converted to sequences, and vice-versa.
Results:
1
"foo"
[ 1, 2, 3, 4 ]
{
"foo" : "bar"
123
Results:
[ 1, "foo", [ 1, 2, 3, 4 ], { "foo" : "bar" } ]
124
125
null
2
If an empty sequence is found as an object value, it is automatically converted to null.
126
null + ()
Results:
Results:
Results:
false
Results:
true
Results:
127
Results:
128
129
130