Skip to content

SmartShape QL Language Reference

Basic concepts

Functional programing

The goal of the SmartShape Query Language (aka "SmartShapeQL") is to perform computations on large tree-like data sets, such as the scene tree of the 3D scene. As such, it aims at efficiency and seeks performance through massive lock-free parallelisation. This is why SmartShapeQL is a (pure) functional programing language.

As a functional programing languages, SmartShapeQL is different from other languages such as C or Javascript. Those differences include (but are not limited to):

  • there are no variables, only immutable constants
  • there are no for or while loops, only iterators

You can read more on functional programing on Wikipedia.

Use cases

SmartShapeQL evaluation is used to implement many features of the SmartShape platform. In many place, the API will accept and store SmartShapeQL programs instead of values in order to implement business logic in key areas of the platforms.

SmartshapeQL (Smartshape Query Language) is used throughout Smartshape and covers: - Nodes search - Annotations search - Behaviors - Layers - Rendering styles - Search forms - Attribute metadata - Attribute transformation

SmartshapeQL queries: - Can only access data the current user is allowed to access to. - Are case-insensitive.

Similarities and differences with other languages

Similarities

Function calls

Functions are called like in most programming languages, by writing their name followed by their parameters in parentheses. Functions can return only one value.

Types

SmartShapeQL is a dynamically typed language. Its memory model is loosely based on the one of Javascript.

Differences

Variables

Variables are more similar to macros in other languages. They are defined only once by the runtime and the query cannot declare nor modify any variable.

Expressions

A SmartShapeQL query is composed of only one expression, so complex behavior is achieved by composing function calls.

Notion of context

Depending on the API the query is called in, it may behave differently.

The query is applied to each node of the scene and is casted to a boolean value to know whether the node is kept in the search results or not. By default, when no operator nor qualifier is used, the query matches the node name.

Variables

  • $smartshape: See the reference on the $smartshape variable [TBD].

Axis operators

children, /

Executes a subquery on the direct children of the current node and returns every child that matches the subquery.

Note: This operator won’t match the currently evaluated node if used in conjunction with “parent”.

## Match the nodes that have children.
count(children:true) != 0

parent, ..

Executes a subquery on the parents of the current node and returns every parent that matches the subquery.

## Match the nodes whose siblings have the attribute "TypeRemarque" equal to "Reprise".
## Note that the / operator won't match the currently evaluated node.
..children:(@"TypeRemarque"="Reprise")
Python:
count(..(count(children:(@"TypeRemarque"="Reprise")) > 0)) > 0

ancestors, ...

Executes a subquery on all the ancestors of the current node and returns every ancestor that matches the subquery.

.

The . operator references the current element that the query is being evaluated on. Useful for calling functions on it.

Axis List

Axis Operator Axis Operator Shorthand
children /
parent ..
ancestors ...
current element .

Qualifiers

Node qualifiers allow to access some properties of the currently evaluated node.

Syntax

All qualifiers have a long notation.

For some qualifiers, a short notation is available.

Long notation

<qualifier name>:<value>

Short notation

<qualifier shorthand><value>

Implicit “and”

All operators can be combined with an implicit “and”. For instance, you could find all the walls with a wood material with a query like:

name:wall attribute:material="wood*"
which is equivalent to:
name:wall and attribute:material="wood*"

Qualifiers list

Qualifier Qualifier Shorthand
name N/A
attribute @
layer %
inherited-layer %%
modifier N/A
inherited-modifier N/A
id N/A
uuid N/A

name

Matches a node with a given name.

## Get the node named "Pillar 1"
name:"Pillar 1"
## Get all the nodes whose name begins with "Pillar"
name:"Pillar*"

attribute, @

Matches a node which contains a given attribute. This operator returns the value of the matched attribute, so it can be used to also match the value of the attribute.

## Get the nodes with a "Thickness" attribute.
attribute:"Thickness"
## Format the "Thickness" attribute value of the node to display it in a legend for instance.
concat(round(multiply(@"Thickness", 1000), 0), " mm")

layer, %

Matches a node belonging to a given layer.

Please note that this operator accepts only layer IDs.

## Match the nodes that belong to a given layer.
layer:5bf7ce66ec8d606d53c2fb91
%5bf7ce66ec8d606d53c2fb91

inherited-layer, %%

Matches a node that has an ancestor (or itself) belonging to a given layer. Please note that this operator accepts only layer IDs.

## Match the nodes that belong to a given layer or that have a parent belonging to a given layer.
inherited-layer:5bf7ce66ec8d606d53c2fb91
%%5bf7ce66ec8d606d53c2fb91

modifier

Matches a node that has a given modifier applied to it.

Accepted modifier types:

  • hidden
  • color
  • transparent
  • lock
## Match the nodes that have a "hidden" modifier applied to them.
modifier:hidden

inherited-modifier

Matches a node that has a given modifier applied to itself or its ancestors. See “modifier” for the list of accepted modifier types.

## Match the nodes that have a "hidden" modifier applied to them or their ancestors.
modifier:hidden

id

Matches a node with a specific id.

uuid

Matches a node with a specific uuid.

Legacy

Because SmartShapeQL was originally just a fancy list of operators for SmartShape's scene search engine, some legacy behaviors still apply.

Node name matching

By default, String literals are evaluated as the regular expression match against the name of the context Node if any, or false otherwise.

For example, the program "groundfloor" will actually be evaluated to:

  • true if the current context Node has its name set to "groundfloor";
  • false otherwise.

In certain cases, it is also possible to omit the quotes. For example groundfloor is evaluated to true if and only if the name of the current Node contains "groundfloor". It is equivalent to "*groundfloor*" (see text matching).

Implicit logical and

Unless explicitly specified otherwise, literal values are casted to Boolean and combined with the and operators.

For example, the "*42" @status=controlled program is strictly equivalent to "*42" and @status=controlled. As such, it will return:

  • true if the current context Node name ends with "42" and has a status attribute set to "controlled";
  • false otherwise.

Types

Boolean

Boolean literal values are true and false.

SmartShapeQL supports implicit coercion of other types to "truthy" and "falsy" boolean values:

  • an empty String is evaluated as false
  • the 0 Number is evaluated as false, every other Number is evaluated as true

String

String literal values are made of any string of characters that do not match any reserved keyword. Ambiguity can be resolved by using quotes:

For example:

  • false is not a String literal (it's a Boolean literal), but "false" is
  • 42f is a String literal
  • 42 is not a String literal (it's a Number literal), but "42" is

  • String operators

  • String functions

Number

SmartShapeQL Number values are 64bit floating precision numerical values.

Integers are also represented using the Number type.

Array

SmartShapeQL support compound arrays: each Array can store values of mixed types.

Array values have no literal notation. Instead,Array values are created using the array() function.

Variables

Operators

Logical operators

Boolean operators

The &&, and ||, or, ! and not operators are available and work as expected.

Comparison operators

The =, !=, >, <, >= and <= operators are available and work as expected.

Note that there are no arithmetic operators.

This query is not valid

@"Strength" = 4 + 5
Use add instead
@"Strength" = add(4, 5)

Math operators

Math operators are not available. Use math functions instead.

Array operators

[<

Works down the array and matches the first element found with a given text.

## Match the nodes whose "progress" attribute contains "done" as its first value.
@progress[<"done"

>]

Works down the array backwards and matches the first element found with a given text.

## Match the nodes whose "progress" array attribute contains "done" as its last value.
@progress>]"done"

[*]

Matches any element of an array with a given text.

## Match the nodes whose "progress" array attribute contains the value "done" at any index.
@progress[*]"done"

Text matching

The simplest form of a search query is a node name. Entering any text will match the nodes that have this character sequence in their name.

Find nodes whose name contain "groundfloor_entrance":

groundfloor_entrance

The characters authorized for simple text search are: * Alphanumeric characters and letters with accents. * ., -, _, @

If you need to search nodes by name with other characters, you can use quoting.

Globbing and quoting

You can use globbing (*) and quoting (" ") to fine-tune your query.

* characters match any character sequence:

## Match nodes whose name contains "basement_" then anything then "_ext"
## like "basement_room_a_ext_building_1" for instance.
basement_*_ext

" " characters match exactly the character sequence:

## Match all nodes whose name is exactly "groundfloor_entrance"
## unlike "groundfloor_entrance_door" for instance.
"groundfloor_entrance"

Functions

add(a: Number, b: Number) : Number

Returns a + b.

all(values: Array) : Boolean

Returns true if all elements of values are truthy, false otherwise.

apply(element: Any, query: Query) : Any

Runs query on element. Returns the return value of query.

array(value1: Any, value2: Any, …) : Array

Creates an array containing each value passed in argument in the same order they are passed in.

concat(leftString: String, rightString: String) : String

Concatenates two strings together.

Concatenate the “Status” attribute of the node with another String

concat("Status : ", @"Status")

count(values: Array) : Number

Counts the number of elements of an array.

Counts the number of children of the current node

count(/*)

divide(dividend: Number, divisor: Number) : Number

Returns dividend / divisor.

dot(vec1: Array, vec2: Array) : Number

Returns the dot product between vec1 and vec2.

vec1 and vec2 must be arrays of numbers of length 3.

flatten(values: Array) : Array

Flattens values a single level deep.

indexOf(values: Array, value: Any) : Number

Returns the index at which the first occurrence of value is stored in the values array.

indexOf(array(2, 14, 3), 3)

fill(values: Array, item: Any, count: Number) : Array

Add the value item to the values array, count times.

filter(values: Array, query: Query) : Array

Creates a new array with all elements that pass the test implemented by the query.

Example:

filter(array(1, 2, 3, 4, 5), . > 2) = array(3, 4, 5)

floor(num: Number) : Number

Computes number rounded down to 0.

get(object: Object, field: String) : Any

Returns the field field of the object object.

getRangeIndex(value: Number, min: Number, max: Number, numRanges: Number) : Number

Returns the index of the range in which value is found.

Example:

getRangeIndex(55, 0, 60, 4) = 3

map(values: Array, query: Query) : Array

Creates an array of values by running query over each element of values and returns the resulting array.

max(values: Array) : Number

Returns the maximum value of an array.

min(values: Array) : Number

Returns the minimum value of an array.

multiply(a: Number, b: Number) : Number

Returns a * b.

nth(values: Array, index: Number) : Any

Returns the element of values at index index.

range(min: Number, max: Number, step: Number) : Array

Returns the range [min, max]. If only one argument is given this method will assume it is for the range max.

Examples:

range(5) = array(0, 1, 2, 3, 4, 5)
range(3, 5) = array(3, 4, 5)
range(0, 10, 2) = array(0, 2, 4, 6, 8, 10)

replace(string: String, pattern: String, replacement: String) : String

Replaces matches for pattern in string with replacement.

round(num: Number, precision: Number) : Number

Computes num rounded to precision.

select(query: Query): Array<Node>

Executes a query on every node of the scene. Returns the nodes matched by the query.

Returns every node of the scene

select(*)

sort(values: Array) : Array

Returns the values array sorted in ascending order. This method performs a stable sort, that is, it preserves the original sort order of equal elements.

string(value: Any) : String

Convert value to a String.

timestampToIsoDate(timestamp: Number, offset: Number) : String

Converts a UNIX timestamp (in milliseconds, like the dates stored in annotations) to an ISO 8601 date. The timezone can be chosen with the offset parameter which corresponds to the offset from UTC.

converts a timestamp to Eastern Standard Time

timestampToIsoDate(1645201497000, -5) = "2022-02-18T11:24:57-05:00"

unique(values: Array) : Array

Returns a duplicate-free version of the values array. Only the first occurrence of each element is kept. The order of result values is determined by the order they occur in the array.

Example:

flatten(array(1, array(2, array(3, array(4)), 5))) = array(1, 2, array(3, array(4)), 5)

zip(leftArray: Array, rightArray: Array) : Array

Creates an array of grouped elements, the first of which contains the first elements of the given arrays, the second of which contains the second elements of the given arrays, and so on.

Example:

zip(array("a", "b"), array(1, 2)) = array(array("a", 1), array("b", 2))


November 24, 2022 June 11, 2019