# Information Model DSL Queries
Developers of applications using the ABB Ability™ Platform need query capabilities in the Information Model to cover several use cases. These include retrieving the structure of IoT devices to be presented in the UI widget, enabling the end user to navigate the devices tree, etc. This capability is implemented as a separate Information Model query endpoint, which expects a query written in custom Domain Specific Language (DSL). The primary benefit of using custom DSL is that the developer does not have to know the actual implementation details of the graph database in the Information Model. A second benefit is that this query endpoint is integrated with the authorization server, enabling more fine-grained access control, including support for multitenancy. A third benefit is that a query returns results in a structured JSON form following a standard Information Model structure for Object Models and References as in other REST API endpoints.
If needing to return multiple models, objects, or references, using a DSL query will return results much faster than using individual calls provided elsewhere in the Information Model API. It is highly recommended to use DSL if possible when returning any result set containing more than one entity.
The DSL endpoint can be found in the Information Model API using the URL /query.
# Example
Before we move on to details of all DSL constructs, here's a quick example to give you an idea of what it's all about:
Let's say that you want to get all the information model objects (but only for abb.ability.device model definition) of type abb.motor@1. In order to do that, you'd use the following DSL query:
models('abb.ability.device').ofType('abb.motor@1')
This could return to you the collection of information model objects:
{
"data": [
{
"objectId": "6a3d0a20-06ce-425a-bb62-655b9eaec088",
"model": "abb.ability.device",
"type": "abb.motor@1",
"version": 1,
"lastModified": "2018-12-03T08:24:19.185Z",
"properties": {
"voltage": {
"value": 120
},
}
},
{
"objectId": "6a3d0a20-06ce-425a-bb62-655b9eaec088",
"model": "abb.ability.device",
"type": "abb.motor@1",
"version": 1,
"lastModified": "2018-12-03T08:24:19.185Z",
"properties": {
"voltage": {
"value": 150
},
}
},
...
]
}
As you will see later on, the DSL constructs might be much more complex. The language is very expressive and allows you to realize various different scenarios.
TIP
When using the backslashes ("\") in the object model properties and then
use DSL to query, the user cannot obtain it by a single escape character
("\\"), which is expected behavior.
The workaround is to use double escaping in the DSL query ("\\\\"").
For example, if you have the property:
{
browseName: {
"value": "some\\path"
}
}
Then use the DSL query:
models(...).hasProperty("browseName", "some\\\\\\\\path")
# Initial DSL Constructs
Each query can return models, references, or both. Models and references
encountered in this query traversal are obtained via selectModels
or
selectReferences
. By default, all properties are returned with each model, but
only specified properties can be returned using the withProperties
step.
In order to form the DSL query, the developer can use language constructs from the following groups:
# Traversals
Moves to a different element (model or reference)
# From a model:
- inReference(name...) - Move to the incoming references (parents) given the reference names.
- outReference(name...) - Move to the outgoing references (children) given the reference names.
- bothReferences(name...) - Move to both incoming and outgoing references given the names.
- otherModels(modelDefinitionId...) - Move to other models which belong to the same object given the model definition identifiers.
# From a reference
- fromModel - Move to the model that this reference is originating from.
- toModel - Move to the model that this reference is pointing to.
- bothModels - Move to both models that this reference is originating from and that it is pointing to.
# Filters
Returns a subset of the result based on some condition. For more information on predicates, please see the Predicates Section in this document.
# From a model
hasObjectId(value) - Return subset of the result, where object identifier matches the provided value.
hasObjectId(predicate) - Return subset of the result, where the predicate evaluates to
true
for the object's identifier.hasName(value) - Return subset of the result, where the object name matches the passed in value.
hasName(predicate) - Return subset of the result, where the predicate evaluates to
true
for the object's name.ofType(value) - Return subset of the result, where object type matches the passed in value.
ofType(predicate) - Return subset of the result, where the predicate evaluates to
true
for the object's type.ofTenant(tenantID) - Return subset of the result, where tenantId matches the passed in value.
ofTenant(predicate) - Return subset of the result, where the predicate evaluates to
true
for the object's tenantID.ownedBy() - Return subset of the result, where the object has an owner.
ownedBy(ownerId) - Return subset of the result, where owner matches the passed in value.
ownedBy(ownerId, path) - Return subset of the result, where the object’s ownerId and path match the passed in values.
ownedBy(predicate) - Return subset of the result, where the predicate evaluates to
true
for the object's owner.ownedBy(ownerId, predicate) - Return subset of the result, where the ownerId matches the given value AND the predicate evaluates to
true
for the path.ownedBy(predicate[ownerId], predicate[path]) - Return subset of the result, where both predicates (one evaluating against ownerId, the other evaluating against path) evaluate to
true
.hasTags(tag1[, tag2, tag3, ...]) - Return subset of the result, where tags match the passed in values.
hasTags(predicate) - Return subset of the result, where the predicate evaluates to
true
for the object's tags.hasProperty(name) - Return subset of the result where the object's property matches the passed in value. Note that you can filter only properties with a fully qualified name. For example, consider the property definition below:
properties: { foo: { bar: { dataType: string }, foobar: { dataType: number } } }
In this case, you can filter by
foo.bar
orfoo.foobar
, but not byfoo
. To filter models by a property with a namespace, usehasPropertyWith('foo')
instead.hasProperty(propertyName, value) - Return subset of the result, where the property exists and has a value that matches the passed in value. Note that
propertyName
must be a fully qualified name of a property.hasProperty(propertyName, predicate) - Return subset of the result, where the property exists and the predicate evaluates to
true
for the property's value. Note thatpropertyName
must be a fully qualified name of a property.hasPropertyWith(namespace) - Return subset of the result, where a property starts with the passed in value.
range(lowIndex, highIndex) - Return slice of the result, treating result as a 0-based array.
- Example 1: range(2,5) Will return objects 3 through 5 in the result.
- Example 2: range(2,-1) Will return all objects starting at 3. Note If highIndex is "-1", all objects are returned starting with the lowIndex.
limit(subresultSize) - Returns first subresultSize number of objects from result - same as range(0,subresultSize).
# From a reference
- isContainment(bool) - Return subset of the result, where the containment attribute matches the passed in value.
- isHierarchical(bool) - Return subset of the result, where the hierarchical attribute matches the passed in value.
- hasReferenceId(referenceId) - Return subset of the result, where the identifier matches the passed in value.
- hasReferenceId(predicate) - Return subset of the result, where the
predicate evaluates to
true
for the object's reference ID. - hasAttribute(name) - Return subset of the result, where a user defined attribute matches the passed in value.
- hasAttribute(name, value) - Return subset of the result, where the user defined attribute exists and has a value that matches the passed in value.
- hasAttribute(name, predicate) - Return subset of the result, where the
user defined attribute exists and the predicate evaluates to
true
for the attribute's value.
# Selectors
Select a part of a model/reference or a traversal.
# From a model
- withProperties(names...): include only model properties with given property names (note that these must be fully qualified names of properties). This acts as a final step in the query.
- withProperties(names...nested): include only nested or multi-level model properties with given property names ending in ".nested" (note that these must be fully qualified property names). This acts as a final step in the query.
# From any step
- selectModels() - returns a flat list of unique models encountered by this traverser. You can apply model filters to this list.
- selectReferences() - returns a flat list of unique references encountered by this traverser. Note that this is a final step - you cannot apply reference filters to this list.
# Branch
Split the traverser and send each to an arbitrary location in the traversal.
# From a model or a reference
- union(steps...) - combines the results of an arbitrary number of steps. Note that a union of differently typed steps is a final step (i.e. if results include both references and models).
# Order
Order result set based on some condition.
NESTED PROPERTIES It is not possible to structure a query to order
the returned objects by nested properties. The orderBy
constructs will only
query top level properties.
# From a model
- orderByProperty(name, asc) - Orders the models by a property with a given name, either in ascending (true) or descending (false) order.
- orderByName(asc) - Orders the models by a name, either in ascending (true) or descending (false) order.
- orderByModel(asc) - Orders the models by a model, either in ascending (true) or descending (false) order.
- orderByType(asc) - Orders the models by a type, either in ascending (true) or descending (false) order.
- orderByLastModified(asc) - Orders the models by a last modified date, either in ascending (true) or descending (false) order.
- orderBy(ascOnObjectId) - Orders the models by objectId, either in ascending (true) or descending (false) order.
- orderBy(ascOnObjectId, ascOnModel) - Orders the models by objectId and then by model, either in ascending (true) or descending (false) order.
# Built-in gremlin steps
Taken as-is, thus applicable to all (see Apache TinkerPop Documentation for more details).
- and(traversal...) - ensures that all provided traversals yield a result (filter).
- or(traversal...) - ensures that at least one of the provided traversals yields a result (filter).
- not(traversal) - removes objects from the result set when the traversal provided as an argument does not return any objects (filter).
- where(traversal) - filters the current object based on the object itself (filter).
- repeat(traversal) - used for looping over a traversal given some break predicate (branch).
- until(predicate/traversal) - modulator for repeat.
- emit(predicate/traversal) - modulator for repeat.
- times(number) – accounts for the number of times the traverser has gone through a loop iteration.
- count - counts a total number of objects in the result set.
# Examples
- models().union(hasName(gte('Pump')).and(hasName(lt('Pump~'))))
- models().union(hasName(gte('Pump')).and(hasName(lt('Pump~'))).withProperties())
- models().where(hasName(eq('edgerouter')))
# Predicates
A predicate is a function that, given some object, returns true or false. A
predicate can be used only as an argument in steps that accept predicates (i.e.
hasAttribute(name, predicate)
). Note that all predicates support only constant
values.
# Supported predicates
- eq(object) - Is the incoming object equal to the provided object?
- neq(object) - Is the incoming object not equal to the provided object?
- lt(string/number) - Is the incoming string/number less than the provided string/number?
- lte(string/number) - Is the incoming string/number less than or equal to the provided string/number?
- gt(string/number) - Is the incoming string/number greater than the provided string/number?
- gte(string/number) - Is the incoming string/number greater than or equal to the provided string/number?
- within(objects… ) - Is the incoming object in the array of provided objects?
- without(objects… ) - Is the incoming object not in the array of the provided objects?
- startingWith(string) - Does the incoming string start with the provided string?
- endingWith(string) - Does the incoming string end with the provided string?
- containing(string) - Does the incoming string contain the provided string?
# Examples
- models().otherModels(startingWith('abb.edge'))
- models().otherModels(endingWith('application'))
- models().union(hasName(gte('abb.edge')).and(hasName(lt('abb.edge~'))).withProperties())
- models().otherModels(containing('configuration'))
# Supported Queries
# models('{modelName}')
Filter object models by model name. Supports list of model names.
# Examples
- models()
- models('abb.ability.device')
- models('abb.ability.device', 'abb.ability.configuration')
# models('{modelsName}').ofType('{typeName}')
Filter object models by model name and type (in format typeId@majorVersion). Supports list of types.
# Examples
- models().ofType('type@1')
- models().ofType('type@1', 'type.second@2')
- models('abb.ability.device').ofType('type@1')
- models('abb.ability.device', 'abb.ability.configuration').ofType(‘type@1’)
# models('{modelName}').hasProperty('{propertyName}', '{propertyValue}').withProperties('{propertyName}')
Filter object models by model name and specific property value. Use full path for properties excluding 'properties' (i.e., 'category.propertyName').
# Example
models().hasProperty('deviceId', 'Device 1.1.0')
models('abb.ability.device').hasProperty('deviceId', 'Device 1.1.0')
models('abb.ability.device').hasProperty('deviceId', 'Device 1.1.0').withProperties('deviceId', 'location.latitude', 'location.longitude')
# models('{modelName}').hasObjectId('objectId', '{objectId}').withProperties('{propertyNameA.propertyNameB.nested}')
Filter object models by model name and object Ids. Use the full path for nested properties, excluding "properties" and adding the suffix "nested" (i.e., "category.propertyName.nested").
# Example
- models('abb.ability.device').hasObjectId(‘3b6de593-a2d0-4186-9402-6c3ed9d53b2e’).withProperties(‘settings.DigitalInput.Input2.nested’)
- models('abb.ability.device').hasObjectId(within(‘3b6de593-a2d0-4186-9402-6c3ed9d53b2e’,’ 184ba1a4-93f1-424a-9453-b5a38c9f244b’)).withProperties(‘settings.DigitalInput.Input2.nested’)
- models('abb.ability.device').hasObjectId(‘3b6de593-a2d0-4186-9402-6c3ed9d53b2e’).withProperties(‘settings.DigitalInput.Input2.nTransIn2.nested’)
- models('abb.ability.device', 'abb.ability.configuration').hasObjectId(within('33d7d569-64b8-4b61-8e90-9ab81895bab3', '3b6de593-a2d0-4186-9402-6c3ed9d53b2e', 'd3284c06-a479-4edc-97a0-2696a1d1d233')).withProperties(‘settings.DigitalInput.Input2.nested’)
- models('abb.ability.device').hasProperty(‘DeviceId’,’Device 1.0.1’).withProperties(‘settings.DigitalInput.Input2.nTransIn2.nested’)
# models('{modelName}').otherModels(predicate)
Filter object models by model name and return related object models.
# Examples
- models('abb.ability.device').otherModels()
- models('abb.ability.device').otherModels('abb.ability.configuration')
- models('abb.ability.device').otherModels(neq('abb.ability.configuration'))
# models('{modelName}').ofType('{type def@version}').outReference().toModel()
Filter object models that this reference is pointing to from a particular type defination
# Examples
- models('abb.ability.device').ofType('abb.ability.device.edge.sample@2').outReference().toModel()
- models('abb.ability.device').ofType('abb.ability.device.edge.sample@2').outReference().toModel().withProperties()
# models('{modelName}').hasObjectId('{objectId}')
Filter object models by model name and objectId.
# Examples
To return a single object model:
- models('abb.ability.device').hasObjectId('c6913808-c75b-41b9-8d7f-c858e99b60e7')
To return all related object models:
- models().hasObjectId('c6913808-c75b-41b9-8d7f-c858e99b60e7')
# models('{modelName}').hasObjectId(within('{objectId1}','{objectId2}'))
Batch load multiple instances at once.
- Input:
- List of modelIds
- List of GUIDs
- Result:
- List of all object instances with matching ID and model
# Example
- models('abb.ability.device', 'abb.ability.configuration').hasObjectId(within('33d7d569-64b8-4b61-8e90-9ab81895bab3', '3b6de593-a2d0-4186-9402-6c3ed9d53b2e', 'd3284c06-a479-4edc-97a0-2696a1d1d233'))
# models('{modelName}').hasObjectId('{objectId}').outReference('{referenceName}').toModel()
Filter object models by model name and objectId and return referenced object models of the first level with specified reference name.
# Example
- models('abb.ability.device').hasObjectId('184ba1a4-93f1-424a-9453-b5a38c9f244b').outReference('referenceName').toModel()
# models('{modelName}').hasObjectId('{objectId}').repeat(outReference().isHierarchical(true).toModel()).until(not(outReference())).union(selectModels(),selectReferences())**
# Retrieve tree structure in one read
- Input:
- ID of a root node or structure
- Output:
- Full tree of all children (including children of children.)
- With objectIds of the child nodes.
- With types of the child nodes.
- With names of the child nodes.
# Example
- models(‘abb.ability.device’).hasObjectId('184ba1a4-93f1-424a-9453-b5a38c9f244b').repeat(outReference().isHierarchical(true).toModel()).until(not(outReference())).union(selectModels(),selectReferences())
# Examples
models('abb.robotics.masterData').hasObjectId('6a3d0a20-06ce-425a-bb62-655b9eaec088')
This simple query will return only the
abb.robotics.masterData
model for a single object. BecausewithProperties
is not specified, all of the model's properties will be returned.
{
"data": [
{
"objectId": "6a3d0a20-06ce-425a-bb62-655b9eaec088",
"model": "abb.robotics.masterData",
"type": "abb.robotics.masterData.customer@1",
"name": "abb.robotics.masterData.customer@1.98238975",
"version": 1,
"lastModified": "2018-12-03T08:24:19.185Z",
"properties": {
"gis": {
"value": ""
},
"webConfigCustomerId": {
"value": "98238975"
},
"companyName": {
"value": "RemoteService BTP - RSE"
},
"localId": {
"value": ""
},
"industrySegment": {
"value": ""
}
}
}
]
}
models('abb.robotics.masterData').hasObjectId('feddafcd-54c0-1eb0-d4c4-73e90fabad4c').repeat(outReference().isHierarchical(true).toModel()).until(not(outReference())).union(selectModels(), selectReferences())
In this case the query is selecting the
abb.robotics.masterData
model for a given object ID, and then recursively iterating through the tree by using the references defined for each object. All referenced objects, as well as the reference definitions themselves, are returned.
{
"data": [
{
"objectId": "feddafcd-54c0-1eb0-d4c4-73e90fabad4c",
"model": "abb.robotics.masterData",
"type": "abb.robotics.masterData.site@1",
"name": "abb.robotics.masterData.site@1.98238977",
"version": 1,
"lastModified": "2018-12-03T08:24:20.107Z",
"properties": {
"webConfigAddressId": {
"value": "98238977"
},
"address": {
"streetName": {
"value": "Borukha TechPark, Mahadevpura"
}
}
}
},
{
"objectId": "0f323b14-2efb-1a21-4a44-9fe073982fd0",
"model": "abb.robotics.masterData",
"type": "abb.robotics.masterData.robotController@1",
"name": "abb.robotics.masterData.robotController@1.STK-0003",
"version": 1,
"lastModified": "2018-12-03T08:25:02.882Z",
"properties": {
"serialNumber": {
"value": "STK-0003"
},
"production": {
"pru": {
"value": "Remote Service (RIN)"
},
"pruWorkOrder": {
"value": "STK-0003"
},
"orderDate": {
"value": "2018-11-29"
}
}
}
},
{
"referenceId": "998a5bb0-41a6-4b06-8b91-a4f3afc43831",
"from": {
"objectId": "feddafcd-54c0-1eb0-d4c4-73e90fabad4c",
"model": "abb.robotics.masterData"
},
"to": {
"objectId": "0f323b14-2efb-1a21-4a44-9fe073982fd0",
"model": "abb.robotics.masterData"
},
"isHierarchical": true,
"isContainment": false,
"name": "hasRobotControllers"
}
]
}
models('abb.robotics.masterData').hasProperty('webConfigAddressId', '98238977').withProperties('internalReference', 'contractReference', 'validDate.to'))
The query returns all objects with a model of
abb.robotics.masterData
, awebConfigAddressId
property defined on the type definition, and a value for that property of98238977
. However, only the properties specified in the query are returned with the object data.
{
"data": [
{
"objectId": "feddafcd-54c0-1eb0-d4c4-73e90fabad4c",
"model": "abb.robotics.masterData",
"type": "abb.robotics.masterData.site@1",
"name": "abb.robotics.masterData.site@1.98238977",
"version": 1,
"lastModified": "2018-12-03T08:24:20.107Z"
},
{
"objectId": "dc6ebb8b-bf6a-0e2f-feed-b979c9ad124b",
"model": "abb.robotics.masterData",
"type": "abb.robotics.masterData.sla@1",
"name": "abb.robotics.masterData.sla@1.1000032247",
"version": 1,
"lastModified": "2018-12-03T08:34:36.244Z",
"properties": {
"internalReference": {
"value": "RW7"
},
"contractReference": {
"value": "RW7"
},
"validDate": {
"to": {
"value": "2019-06-04"
}
}
}
},
{
"objectId": "2c063c5c-f09d-7886-2047-8feef6288ce3",
"model": "abb.robotics.masterData",
"type": "abb.robotics.masterData.sla@1",
"name": "abb.robotics.masterData.sla@1.1000032385",
"version": 1,
"lastModified": "2018-12-03T09:44:28.804Z",
"properties": {
"internalReference": {
"value": "CSE-MyABB-A1"
},
"contractReference": {
"value": "CSE-MyABB-A1"
},
"validDate": {
"to": {
"value": "2019-11-01"
}
}
}
}
]
}
Multilevel (Nested) property
models('abb.ability.configuration').hasObjectId('e05859e5-fc30-41ae-b53d-d8b8d73e419d').withProperties('settings.AnalogInput.Input1.mod3T2Val1.nested')
This query will return only the abb.ability.configuration model for a single object, and the mentioned nested properties will be returned.
{
"data": [
{
"model": "abb.ability.configuration",
"type": "abb.ability.configuration.ClientCase11@1",
"name": "Smart sensor at client location11",
"version": 1,
"lastModified": "2023-01-19T04:49:13.1Z",
"properties": {
"settings": {
"AnalogInput": {
"Input1": {
"mod3T2Val1": {
"tagName": {
"value": ""
}
}
}
}
}
},
"objectId": "e05859e5-fc30-41ae-b53d-d8b8d73e419d",
"tenantId": "aa724244-8505-4b15-968e-80633f117cec"
}
]
}
models('abb.ability.configuration').or(hasProperty('nOperations',21)).withProperties('settings.AnalogInput.nested')
The query returns all objects with a model of abb.ability.configuration, an Operations property defined on the type definition, and a value for that property of 21. However, only the nested properties specified in the query are returned with the object data.
{
"data": [
{
"model": "abb.ability.configuration",
"type": "abb.ability.configuration.ClientCase11@1",
"name": "Smart sensor at client location",
"version": 1,
"lastModified": "2022-07-11T09:49:46.493Z",
"properties": {
"settings": {
"AnalogInput": {
"Input1": {
"mod3T1Val1": {
"tagName": {
"value": ""
}
},
"mod3T2Val1": {
"tagName": {
"value": ""
}
}
}
}
}
},
"objectId": "6789fba1-e044-4757-a4c9-cb0c8e478d40",
"tenantId": "c701f6f4-7ec1-4b83-94b4-442364bd2311"
},
{
"model": "abb.ability.configuration",
"type": "abb.ability.configuration.ClientCase11@1",
"name": "Smart sensor at client location",
"version": 1,
"lastModified": "2022-07-11T10:06:45.684Z",
"properties": {
"settings": {
"AnalogInput": {
"Input1": {
"mod3T1Val1": {
"tagName": {
"value": ""
}
},
"mod3T2Val1": {
"tagName": {
"value": ""
}
}
}
}
}
},
"objectId": "29695db3-7931-4755-b8f1-015b059483ca",
"tenantId": "c701f6f4-7ec1-4b83-94b4-442364bd2311"
}
- Multilayer Retrieval
# models('{edge - modelName}').ofType('{edge type def}').outReference().toModel()
This query travers from top to bottom and retrieves immediate out referenced details by using model name and type definition.
# Example:
models('abb.ability.device').ofType('abb.ability.device.edge.sample@2').outReference().toModel()
# models('{edge - modelName}').ofType('{edge type def}').repeat(outReference().toModel()).until(not(outReference()))
This query travers from top to bottom and retrieves all out referenced details with modelName and edge type definition@version. Below sample is retrieving all devices attached to an edge.
# Example:
models('abb.ability.device').ofType('abb.ability.device.edge.sample@2').repeat(outReference().toModel()).until(not(outReference()))
# models().hasObjectId('{ edge Id }').repeat(outReference().toModel()).until(not(outReference()))
This query travers from top to bottom and retrieves all out referenced details using Edge Id. Below sample is retrieving all devices attached to an edge.
# Example:
models().hasObjectId('7b946c0e-30c2-45b8-ac17-b07fb119742c').repeat(outReference().toModel()).until(not(outReference()))
# models('{edge - modelName}').ofType('{edge type def}').repeat(outReference().toModel()).until(not(outReference())).union(selectModels())
This query travers from top to bottom and collects all level data from an edge to device level with modelName and edge type definition@version.
# Example:
models('abb.ability.device').ofType('abb.ability.device.edge.sample@2').repeat(outReference().toModel()).until(not(outReference())).union(selectModels())
# models('{device - modelName}').ofType('{mid level type def}').hasName('referenceModule').union(outReference().toModel().withProperties(), outReference().fromModel().withProperties(), inReference().fromModel())
This query travers from bottom to top and collects all level data from devices to till top edge.
# Example:
models('abb.ability.device').ofType('abb.ability.device.edge.modules.sample@2').hasName('referenceModule').union(outReference().toModel().withProperties(), outReference().fromModel().withProperties(), inReference().fromModel())
# models('{device - modelName}').ofType('{mid level type def}').hasName('referenceModule').union(outReference().toModel().withProperties(),outReference().fromModel().withProperties(), inReference().fromModel(), bothReferences().selectReferences())
This query traverse from middle to both side (upper and lower) and collectes all level data including references eg. Edge <- ReferenceModule -> Devices
# Example:
models('abb.ability.device').ofType('abb.ability.device.edge.modules.sample@2').hasName('referenceModule').union(outReference().toModel().withProperties(),outReference().fromModel().withProperties(), inReference().fromModel(), bothReferences().selectReferences())