iotagent-node-lib 2.18.0 → 2.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +1 -2
- package/.nyc_output/76bc24ff-5fac-4b5a-997d-de2799342eb0.json +1 -0
- package/.nyc_output/processinfo/76bc24ff-5fac-4b5a-997d-de2799342eb0.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -0
- package/.readthedocs.yml +3 -1
- package/CHANGES_NEXT_RELEASE +1 -0
- package/README.md +5 -56
- package/doc/advanced-topics.md +121 -85
- package/doc/api.md +63 -54
- package/doc/development.md +8 -9
- package/doc/expressionLanguage.md +517 -316
- package/doc/installationguide.md +66 -64
- package/doc/northboundinteractions.md +40 -33
- package/doc/requirements.txt +4 -0
- package/{docs → doc}/roadmap.md +21 -6
- package/doc/usermanual.md +50 -18
- package/docker/Mosquitto/Dockerfile +28 -11
- package/docker/Mosquitto/README.md +8 -6
- package/docker/Mosquitto/startMosquitto.sh +14 -4
- package/lib/fiware-iotagent-lib.js +4 -2
- package/lib/jexlTranformsMap.js +11 -1
- package/lib/model/Device.js +4 -1
- package/lib/model/Group.js +19 -1
- package/lib/plugins/expressionParser.js +6 -4
- package/lib/plugins/expressionPlugin.js +63 -22
- package/lib/plugins/jexlParser.js +3 -1
- package/lib/plugins/multiEntity.js +2 -21
- package/lib/request-shim.js +111 -0
- package/lib/services/common/domain.js +6 -2
- package/lib/services/common/genericMiddleware.js +6 -2
- package/lib/services/common/iotManagerService.js +1 -1
- package/lib/services/common/securityServiceKeystone.js +1 -1
- package/lib/services/common/securityServiceOAuth2.js +3 -2
- package/lib/services/devices/deviceRegistryMemory.js +13 -2
- package/lib/services/devices/deviceRegistryMongoDB.js +16 -7
- package/lib/services/devices/deviceService.js +26 -2
- package/lib/services/devices/devices-NGSI-LD.js +1 -1
- package/lib/services/devices/devices-NGSI-v2.js +2 -6
- package/lib/services/devices/registrationUtils.js +0 -2
- package/lib/services/ngsi/entities-NGSI-LD.js +97 -11
- package/lib/services/ngsi/entities-NGSI-v2.js +95 -8
- package/lib/services/ngsi/ngsiService.js +5 -4
- package/lib/services/northBound/contextServer-NGSI-LD.js +3 -2
- package/lib/services/northBound/contextServer-NGSI-v2.js +32 -27
- package/lib/services/northBound/contextServerUtils.js +1 -1
- package/lib/services/northBound/deviceProvisioningServer.js +31 -6
- package/lib/services/northBound/northboundServer.js +2 -0
- package/lib/services/northBound/restUtils.js +1 -1
- package/lib/templates/createDevice.json +12 -0
- package/lib/templates/updateDevice.json +12 -0
- package/package.json +9 -15
- package/test/tools/utils.js +2 -0
- package/test/unit/expressions/jexlExpression-test.js +5 -5
- package/test/unit/general/contextBrokerKeystoneSecurityAccess-test.js +1 -1
- package/test/unit/general/deviceService-test.js +2 -5
- package/test/unit/general/loglevel-api_test.js +6 -11
- package/test/unit/general/startup-test.js +1 -0
- package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +1 -0
- package/test/unit/mongodb/mongodb-group-registry-test.js +1 -1
- package/test/unit/mongodb/mongodb-registry-test.js +2 -1
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin12a.json +7 -0
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin13.json +13 -13
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin1a.json +18 -0
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin29.json +18 -0
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin31.json +15 -0
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin32.json +17 -0
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin33.json +18 -0
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin34.json +17 -0
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin4a.json +36 -0
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin7.json +16 -16
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin8a.json +18 -0
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin15.json +25 -0
- package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +1018 -0
- package/test/unit/ngsi-ld/general/contextBrokerOAuthSecurityAccess-test.js +2 -2
- package/test/unit/ngsi-ld/general/deviceService-test.js +1 -1
- package/test/unit/ngsi-ld/general/https-support-test.js +2 -1
- package/test/unit/ngsi-ld/general/iotam-autoregistration-test.js +2 -1
- package/test/unit/ngsi-ld/general/startup-test.js +1 -0
- package/test/unit/ngsi-ld/lazyAndCommands/active-devices-attribute-update-test.js +3 -1
- package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +2 -1
- package/test/unit/ngsi-ld/lazyAndCommands/lazy-devices-test.js +2 -6
- package/test/unit/ngsi-ld/lazyAndCommands/polling-commands-test.js +2 -1
- package/test/unit/ngsi-ld/ngsiService/active-devices-test.js +1 -0
- package/test/unit/ngsi-ld/ngsiService/autocast-test.js +1 -0
- package/test/unit/ngsi-ld/ngsiService/geoproperties-test.js +1 -0
- package/test/unit/ngsi-ld/ngsiService/subscriptions-test.js +4 -3
- package/test/unit/ngsi-ld/plugins/alias-plugin_test.js +1 -0
- package/test/unit/ngsi-ld/plugins/bidirectional-plugin_test.js +3 -2
- package/test/unit/ngsi-ld/plugins/multientity-plugin_test.js +61 -0
- package/test/unit/ngsi-ld/provisioning/device-provisioning-api_test.js +2 -1
- package/test/unit/ngsi-ld/provisioning/device-registration_test.js +3 -2
- package/test/unit/ngsi-ld/provisioning/device-update-registration_test.js +1 -0
- package/test/unit/ngsi-ld/provisioning/listProvisionedDevices-test.js +42 -54
- package/test/unit/ngsi-ld/provisioning/provisionDeviceMultientity-test.js +2 -1
- package/test/unit/ngsi-ld/provisioning/removeProvisionedDevice-test.js +4 -4
- package/test/unit/ngsi-ld/provisioning/singleConfigurationMode-test.js +3 -5
- package/test/unit/ngsi-ld/provisioning/updateProvisionedDevices-test.js +12 -18
- package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +3 -1
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin17.json +1 -1
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin32.json +16 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin33.json +22 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin34.json +12 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin35.json +2 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin36.json +1 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin15.json +25 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin16.json +25 -0
- package/test/unit/ngsiv2/expressions/expressionBasedTransformations-test.js +4 -4
- package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +561 -0
- package/test/unit/ngsiv2/general/contextBrokerOAuthSecurityAccess-test.js +3 -2
- package/test/unit/ngsiv2/general/deviceService-test.js +9 -8
- package/test/unit/ngsiv2/general/https-support-test.js +2 -1
- package/test/unit/ngsiv2/general/iotam-autoregistration-test.js +2 -1
- package/test/unit/ngsiv2/general/startup-test.js +1 -0
- package/test/unit/ngsiv2/lazyAndCommands/active-devices-attribute-update-test.js +3 -1
- package/test/unit/ngsiv2/lazyAndCommands/command-test.js +2 -1
- package/test/unit/ngsiv2/lazyAndCommands/lazy-devices-test.js +14 -18
- package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +3 -1
- package/test/unit/ngsiv2/ngsiService/active-devices-test.js +1 -0
- package/test/unit/ngsiv2/ngsiService/queryDeviceInformationInCb-test.js +0 -1
- package/test/unit/ngsiv2/ngsiService/subscriptions-test.js +4 -3
- package/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js +3 -2
- package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +210 -0
- package/test/unit/ngsiv2/plugins/translation-inPlugins_test.js +1 -1
- package/test/unit/ngsiv2/provisioning/device-group-api-test.js +3 -2
- package/test/unit/ngsiv2/provisioning/device-group-utils-test.js +2 -1
- package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +2 -1
- package/test/unit/ngsiv2/provisioning/device-registration_test.js +3 -2
- package/test/unit/ngsiv2/provisioning/device-update-registration_test.js +4 -3
- package/test/unit/ngsiv2/provisioning/listProvisionedDevices-test.js +42 -53
- package/test/unit/ngsiv2/provisioning/provisionDeviceMultientity-test.js +2 -1
- package/test/unit/ngsiv2/provisioning/removeProvisionedDevice-test.js +4 -4
- package/test/unit/ngsiv2/provisioning/singleConfigurationMode-test.js +3 -4
- package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +13 -19
- package/test/unit/plugins/capture-configuration-inPlugins_test.js +3 -1
- package/test/unit/plugins/capture-provision-inPlugins_test.js +2 -1
- package/bin/agentConsole.js +0 -257
- package/bin/iotAgentTester.js +0 -44
- package/lib/command/commandLine.js +0 -918
- package/lib/command/migration.js +0 -176
- package/test/unit/general/migration-test.js +0 -256
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin20.json +0 -25
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
# Measurement Transformation Expression Language
|
|
2
2
|
|
|
3
3
|
- [Overview](#overview)
|
|
4
|
+
- [Comparison between expression languages](#comparison-between-expression-languages)
|
|
5
|
+
- [Configuring expression language used](#configuring-expression-language-used)
|
|
4
6
|
- [Measurement transformation](#measurement-transformation)
|
|
5
7
|
- [Expression definition](#expression-definition)
|
|
6
|
-
- [Variable values](#variable-values)
|
|
7
8
|
- [Expression execution](#expression-execution)
|
|
8
|
-
- [
|
|
9
|
+
- [Multientity plugin support (`object_id`)](#multientity-plugin-support-object_id)
|
|
10
|
+
- [JEXL Based Transformations](#jexl-based-transformations)
|
|
11
|
+
- [Examples of JEXL expressions](#examples-of-jexl-expressions)
|
|
12
|
+
- [Available functions](#available-functions)
|
|
13
|
+
- [Legacy Expression Language Transformations](#legacy-expression-language-transformations)
|
|
14
|
+
- [Expressions](#expressions)
|
|
9
15
|
- [Types](#types)
|
|
10
16
|
- [Values](#values)
|
|
11
17
|
- [Variables](#variables)
|
|
@@ -14,26 +20,99 @@
|
|
|
14
20
|
- [Number operations](#number-operations)
|
|
15
21
|
- [String operations](#string-operations)
|
|
16
22
|
- [Other available operators](#other-available-operators)
|
|
17
|
-
- [Examples of expressions](#examples-of-expressions)
|
|
18
|
-
- [NGSI v2 support](#ngsi-v2-support)
|
|
19
|
-
- [JEXL Based Transformations](#jexl-based-transformations)
|
|
20
|
-
- [Quick comparison to default language](#quick-comparison-to-default-language)
|
|
21
|
-
- [Examples of expressions](#examples-of-expressions-1)
|
|
22
|
-
- [Available functions](#available-functions)
|
|
23
|
+
- [Examples of expressions](#examples-of-expressions)
|
|
24
|
+
- [NGSI v2 support](#ngsi-v2-support)
|
|
23
25
|
|
|
24
26
|
## Overview
|
|
25
27
|
|
|
26
28
|
The IoTAgent Library provides an expression language for measurement transformation, that can be used to adapt the
|
|
27
|
-
information coming from the South Bound APIs to the information reported to the Context Broker.
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
information coming from the South Bound APIs to the information reported to the Context Broker. This is really useful
|
|
30
|
+
when you need to adapt measure.
|
|
31
|
+
|
|
32
|
+
There are available two differen expression languages `jexl` and `legacy`. The recommended language to use is `jexl`,
|
|
33
|
+
which is newer and most powerful.
|
|
34
|
+
|
|
35
|
+
## Comparison between expression languages
|
|
36
|
+
|
|
37
|
+
JEXL overpasses the legacy language in several aspects:
|
|
38
|
+
|
|
39
|
+
- JEXL supports multiples types: Boolean, String, Number, Object, Array, NULL.
|
|
40
|
+
- JEXL also supports the management and creation of JSON structures (including GeoJSON Objects)
|
|
41
|
+
- JEXL allows to navigate and [filter](https://github.com/TomFrost/jexl#collections) objects and arrays.
|
|
42
|
+
- JEXL supports if..then...else... via [ternary operator](https://github.com/TomFrost/jexl#ternary-operator).
|
|
43
|
+
- JEXL additionally supports the following operations: Divide and floor `//`, Modulus `%`, Logical AND `&&` and
|
|
44
|
+
Logical OR `||`. Negation operator is `!`
|
|
45
|
+
- JEXL supports [comparisons](https://github.com/TomFrost/jexl#comparisons).
|
|
46
|
+
- JEXL supports defining custom transformations (JEXL functions are not currently supported in Perseo).
|
|
47
|
+
|
|
48
|
+
For more details, check JEXL language details [here](https://github.com/TomFrost/jexl#all-the-details).
|
|
49
|
+
|
|
50
|
+
## Configuring expression language used
|
|
51
|
+
|
|
52
|
+
By default, in order to maintain backward compatibility, `legacy` language is applied. There are different levels to
|
|
53
|
+
configure the expression language used:
|
|
54
|
+
|
|
55
|
+
- At global level.
|
|
56
|
+
- At service group level.
|
|
57
|
+
- At device level.
|
|
58
|
+
|
|
59
|
+
**Setting the expression language at global level**
|
|
60
|
+
|
|
61
|
+
It is possible to set the default language at global level by setting the
|
|
62
|
+
[global configuration parameter](installationguide.md#global-configuration) `defaultExpressionLanguage` or the
|
|
63
|
+
[environment variable](installationguide.md#configuration-using-environment-variables)
|
|
64
|
+
`IOTA_DEFAULT_EXPRESSION_LANGUAGE`. This option configures the default expression language used to compute expressions,
|
|
65
|
+
possible values are: `legacy` or `jexl`. When not set or wrongly set, `legacy` is used as default value.
|
|
66
|
+
|
|
67
|
+
**Setting the expression language at service group level**
|
|
68
|
+
|
|
69
|
+
It is possible to define the expression language at service group by adding the `expressionLanguage` parameter. It is
|
|
70
|
+
optional and the possible values are: `legacy` or `jexl`. When not set or wrongly set, `legacy` is used as default. You
|
|
71
|
+
can check the following example:
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"services": [
|
|
76
|
+
{
|
|
77
|
+
"apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732",
|
|
78
|
+
"cbroker": "http://orion:1026",
|
|
79
|
+
"entity_type": "Thing",
|
|
80
|
+
"resource": "/iot/d"
|
|
81
|
+
"expressionLanguage": "jexl",
|
|
82
|
+
"attributes": [...]
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Setting the expression language at device level**
|
|
89
|
+
|
|
90
|
+
Expression language can be configured at device level by adding the `expressionLanguage` parameter to the device
|
|
91
|
+
provisioning payload. It is optional and the possible values are: `legacy` or `jexl`. When not set or wrongly set,
|
|
92
|
+
`legacy` is used as default. The following example shows how to provision a device using `jexl` language.
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"devices":[
|
|
97
|
+
{
|
|
98
|
+
"device_id":"45",
|
|
99
|
+
"protocol":"GENERIC_PROTO",
|
|
100
|
+
"entity_name":"WasteContainer:WC45",
|
|
101
|
+
"entity_type":"WasteContainer",
|
|
102
|
+
"expressionLanguage": "jexl",
|
|
103
|
+
"attributes":[...]
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
```
|
|
30
108
|
|
|
31
109
|
## Measurement transformation
|
|
32
110
|
|
|
33
111
|
### Expression definition
|
|
34
112
|
|
|
35
113
|
Expressions can be defined for Active attributes, either in the Device provisioning or in the Configuration
|
|
36
|
-
provisioning. The following example shows a device provisioning payload with defined expressions
|
|
114
|
+
provisioning. The following example shows a device provisioning payload with defined expressions (using the
|
|
115
|
+
[JEXL Expression language](#jexl-based-transformations)):
|
|
37
116
|
|
|
38
117
|
```json
|
|
39
118
|
{
|
|
@@ -43,16 +122,17 @@ provisioning. The following example shows a device provisioning payload with def
|
|
|
43
122
|
"protocol": "GENERIC_PROTO",
|
|
44
123
|
"entity_name": "WasteContainer:WC45",
|
|
45
124
|
"entity_type": "WasteContainer",
|
|
125
|
+
"expressionLanguage": "jexl",
|
|
46
126
|
"attributes": [
|
|
47
127
|
{
|
|
48
128
|
"name": "location",
|
|
49
|
-
"type": "geo:
|
|
50
|
-
"expression": "
|
|
129
|
+
"type": "geo:json",
|
|
130
|
+
"expression": "{coordinates: [longitude,latitude], type: 'Point'}"
|
|
51
131
|
},
|
|
52
132
|
{
|
|
53
133
|
"name": "fillingLevel",
|
|
54
134
|
"type": "Number",
|
|
55
|
-
"expression": "
|
|
135
|
+
"expression": "level / 100"
|
|
56
136
|
},
|
|
57
137
|
{
|
|
58
138
|
"name": "level",
|
|
@@ -72,11 +152,13 @@ provisioning. The following example shows a device provisioning payload with def
|
|
|
72
152
|
}
|
|
73
153
|
```
|
|
74
154
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
155
|
+
[Interactive expression `{coordinates: [longitude,latitude], type: 'Point'}`][1]
|
|
156
|
+
|
|
157
|
+
[Interactive expression `level / 100`][2]
|
|
158
|
+
|
|
159
|
+
The value of the `expression` attribute is a string that can contain any number of expression patterns. In order to
|
|
160
|
+
complete expression to be evaluated, all the expression patterns must be evaluable (there must be a value in the
|
|
161
|
+
measurement for all the variables of all the expression patterns).
|
|
80
162
|
|
|
81
163
|
Note that you need to include in the provision operation all the attributes required as inputs for the expressions. In
|
|
82
164
|
this example, they are `level`, `latitude` and `longitude`. Otherwise the device sending the measures will get
|
|
@@ -85,62 +167,52 @@ expression will not be calculated.
|
|
|
85
167
|
|
|
86
168
|
The exact same syntax works for Configuration and Device provisioning.
|
|
87
169
|
|
|
88
|
-
### Variable values
|
|
89
|
-
|
|
90
|
-
Attribute expressions can contain values taken from the value of other attributes. Those values have, by default, the
|
|
91
|
-
String type. For most arithmetic operations (`*`, `/`, etc...) if a variable is involved, its value will be cast to
|
|
92
|
-
Number, regardless of the original type. For, example, if a variable @humidity has the value `'50'` (a String value),
|
|
93
|
-
the following expression:
|
|
94
|
-
|
|
95
|
-
```
|
|
96
|
-
${@humidity * 10}
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
will give `500` as the result (i.e.: the value `'50'` is cast to number, to get `50`, that is then multiplied by 10). If
|
|
100
|
-
this cast fails (because the value of the variable is not a number, e.g.: `'Fifty'`), the overall result will be `NaN`.
|
|
101
|
-
|
|
102
170
|
### Expression execution
|
|
103
171
|
|
|
104
|
-
Whenever a new measurement arrives to the
|
|
105
|
-
the device will be checked for execution: for all the defined active attributes containing expressions, the
|
|
172
|
+
Whenever a new measurement arrives to the IoT Agent for a device with declared expressions, all of the expressions for
|
|
173
|
+
the device will be checked for execution: for all the defined active attributes containing expressions, the IoT Agent
|
|
106
174
|
will check which ones contain expressions whose variables are present in the received measurement. For all of those
|
|
107
175
|
whose variables are covered, their expressions will be executed with the received values, and their values updated in
|
|
108
176
|
the Context Broker.
|
|
109
177
|
|
|
110
|
-
E.g.: if a device with the following provisioning information is provisioned in the
|
|
178
|
+
E.g.: if a device with the following provisioning information is provisioned in the IoT Agent:
|
|
111
179
|
|
|
112
180
|
```json
|
|
113
181
|
{
|
|
114
182
|
"name":"location",
|
|
115
183
|
"type":"geo:point",
|
|
116
|
-
"expression": "
|
|
184
|
+
"expression": "longitude+', '+latitude"
|
|
117
185
|
},
|
|
118
186
|
{
|
|
119
187
|
"name":"fillingLevel",
|
|
120
188
|
"type":"Number",
|
|
121
|
-
"expression": "
|
|
189
|
+
"expression": "level / 100",
|
|
122
190
|
},
|
|
123
191
|
```
|
|
124
192
|
|
|
125
|
-
|
|
193
|
+
[Interactive expression `longitude+', '+latitude`][3]
|
|
194
|
+
|
|
195
|
+
[Interactive expression `level / 100`][4]
|
|
196
|
+
|
|
197
|
+
and a measurement with the following values arrive to the IoT Agent:
|
|
126
198
|
|
|
127
199
|
```text
|
|
128
200
|
latitude: 1.9
|
|
129
201
|
level: 85.3
|
|
130
202
|
```
|
|
131
203
|
|
|
132
|
-
The only expression rule that will be executed will be that of the
|
|
133
|
-
|
|
204
|
+
The only expression rule that will be executed will be that of the `fillingLevel` attribute. It will produce the value
|
|
205
|
+
`0.853` that will be sent to the Context Broker.
|
|
134
206
|
|
|
135
207
|
Note that expressions are only applied if the attribute name (as received by the IoT Agent in the southbound interface)
|
|
136
208
|
matches the expression variable. Otherwise, the southbound value is used directly. Let's illustrate with the following
|
|
137
209
|
example:
|
|
138
210
|
|
|
139
211
|
```json
|
|
140
|
-
|
|
212
|
+
"consumption": {
|
|
141
213
|
"type": "String",
|
|
142
|
-
"value": "
|
|
143
|
-
|
|
214
|
+
"value": "spaces | trim"
|
|
215
|
+
}
|
|
144
216
|
```
|
|
145
217
|
|
|
146
218
|
- Case 1: the following measure is received at the southbound interface:
|
|
@@ -154,8 +226,8 @@ directly used, so the following is sent to CB:
|
|
|
154
226
|
|
|
155
227
|
```json
|
|
156
228
|
"consumption": {
|
|
157
|
-
|
|
158
|
-
|
|
229
|
+
"type": "String",
|
|
230
|
+
"value": "0.44"
|
|
159
231
|
}
|
|
160
232
|
```
|
|
161
233
|
|
|
@@ -171,27 +243,308 @@ following to CB:
|
|
|
171
243
|
|
|
172
244
|
```json
|
|
173
245
|
"consumption": {
|
|
174
|
-
|
|
175
|
-
|
|
246
|
+
"type": "String",
|
|
247
|
+
"value": "foobar"
|
|
176
248
|
}
|
|
177
249
|
```
|
|
178
250
|
|
|
179
|
-
|
|
251
|
+
[Interactive expression `spaces | trim`][5]
|
|
252
|
+
|
|
253
|
+
### Multientity plugin support (`object_id`)
|
|
254
|
+
|
|
255
|
+
To allow support for expressions in combination with multi entity plugin, where the same attribute is generated for
|
|
256
|
+
different entities out of different incoming attribute values (i.e. `object_id`), we introduced support for `object_id`
|
|
257
|
+
in the expression context.
|
|
258
|
+
|
|
259
|
+
For example, the following device:
|
|
260
|
+
|
|
261
|
+
```json
|
|
262
|
+
"WeatherStation": {
|
|
263
|
+
"commands": [],
|
|
264
|
+
"type": "WeatherStation",
|
|
265
|
+
"lazy": [],
|
|
266
|
+
"active": [
|
|
267
|
+
{
|
|
268
|
+
"object_id": "v1",
|
|
269
|
+
"name": "vol",
|
|
270
|
+
"expression" : "v1*100",
|
|
271
|
+
"type": "Number",
|
|
272
|
+
"entity_name": "WeatherStation1"
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
"object_id": "v2",
|
|
276
|
+
"name": "vol",
|
|
277
|
+
"expression" : "v2*100",
|
|
278
|
+
"type": "Number",
|
|
279
|
+
"entity_name": "WeatherStation2"
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
"object_id": "v",
|
|
283
|
+
"name": "vol",
|
|
284
|
+
"expression" : "v*100",
|
|
285
|
+
"type": "Number"
|
|
286
|
+
}
|
|
287
|
+
]
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
When receiving the attributes `v`, `v1` and `v2` in a payload from a message received in the southbound:
|
|
292
|
+
|
|
293
|
+
```json
|
|
294
|
+
({
|
|
295
|
+
"name": "v",
|
|
296
|
+
"type": "Number",
|
|
297
|
+
"value": 0
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
"name": "v1",
|
|
301
|
+
"type": "Number",
|
|
302
|
+
"value": 1
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
"name": "v2",
|
|
306
|
+
"type": "Number",
|
|
307
|
+
"value": 2
|
|
308
|
+
})
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Will now generate the following NGSI v2 payload:
|
|
312
|
+
|
|
313
|
+
```json
|
|
314
|
+
{
|
|
315
|
+
"actionType": "append",
|
|
316
|
+
"entities": [
|
|
317
|
+
{
|
|
318
|
+
"id": "ws9",
|
|
319
|
+
"type": "WeatherStation",
|
|
320
|
+
"vol": {
|
|
321
|
+
"type": "Number",
|
|
322
|
+
"value": 0
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
"vol": {
|
|
327
|
+
"type": "Number",
|
|
328
|
+
"value": 100
|
|
329
|
+
},
|
|
330
|
+
"type": "WeatherStation",
|
|
331
|
+
"id": "WeatherStation1"
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
"vol": {
|
|
335
|
+
"type": "Number",
|
|
336
|
+
"value": 200
|
|
337
|
+
},
|
|
338
|
+
"type": "WeatherStation",
|
|
339
|
+
"id": "WeatherStation2"
|
|
340
|
+
}
|
|
341
|
+
]
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## JEXL Based Transformations
|
|
346
|
+
|
|
347
|
+
The recommended expression language for the IoTAgent Library is [JEXL](https://github.com/TomFrost/jexl). To use JEXL,
|
|
348
|
+
you will need to either configure it as default language using the `defaultExpressionLanguage` field to `jexl` (see
|
|
349
|
+
[configuration documentation](installationguide.md)) or configuring the usage of JEXL as expression language for a given
|
|
350
|
+
group or device (as shown above).
|
|
351
|
+
|
|
352
|
+
```json
|
|
353
|
+
{
|
|
354
|
+
"devices": [
|
|
355
|
+
{
|
|
356
|
+
"device_id": "45",
|
|
357
|
+
"protocol": "GENERIC_PROTO",
|
|
358
|
+
"entity_name": "WasteContainer:WC45",
|
|
359
|
+
"entity_type": "WasteContainer",
|
|
360
|
+
"expressionLanguage": "jexl",
|
|
361
|
+
"attributes": [
|
|
362
|
+
{
|
|
363
|
+
"name": "location",
|
|
364
|
+
"type": "geo:json",
|
|
365
|
+
"expression": "{coordinates: [longitude,latitude], type: 'Point'}"
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
"name": "fillingLevel",
|
|
369
|
+
"type": "Number",
|
|
370
|
+
"expression": "level / 100"
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
"name": "level",
|
|
374
|
+
"type": "Number"
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
"name": "latitude",
|
|
378
|
+
"type": "Number"
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
"name": "longitude",
|
|
382
|
+
"type": "Number"
|
|
383
|
+
}
|
|
384
|
+
]
|
|
385
|
+
}
|
|
386
|
+
]
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
In the following we provide examples of using JEXL to apply transformations.
|
|
391
|
+
|
|
392
|
+
### Examples of JEXL expressions
|
|
393
|
+
|
|
394
|
+
The following table shows expressions and their expected outcomes taking into account the following measures at
|
|
395
|
+
southbound interface:
|
|
396
|
+
|
|
397
|
+
- `value` with value 6 (number)
|
|
398
|
+
- `ts` with value 1637245214901 (unix timestamp)
|
|
399
|
+
- `name` with value `"DevId629"` (string)
|
|
400
|
+
- `object` with value `{name: "John", surname: "Doe"}` (JSON object)
|
|
401
|
+
- `array` with value `[1, 3]` (JSON Array)
|
|
402
|
+
|
|
403
|
+
| Expression | Expected outcome | Format | Playground |
|
|
404
|
+
| :-------------------------------------------- | :---------------------------------------- | ------------------- | ------------- |
|
|
405
|
+
| `5 * value` | `30` | Integer | [Example][6] |
|
|
406
|
+
| `(6 + value) * 3` | `36` | Integer | [Example][7] |
|
|
407
|
+
| `value / 12 + 1` | `1.5` | Float | [Example][8] |
|
|
408
|
+
| `(5 + 2) * (value + 7)` | `91` | Integer | [Example][9] |
|
|
409
|
+
| `value * 5.2` | `31.2` | Float | [Example][10] |
|
|
410
|
+
| `"Pruebas " + "De Strings"` | `"Pruebas De Strings"` | String | [Example][11] |
|
|
411
|
+
| `name + "value is " +value` | `"DevId629 value is 6"` | String | [Example][12] |
|
|
412
|
+
| `{coordinates: [value,value], type: 'Point'}` | `{"coordinates": [6,6], "type": "Point"}` | GeoJSON `Object` | [Example][13] |
|
|
413
|
+
| <code>ts|toisodate</code> | `2021-11-18T14:20:14.901Z` | ISO 8601 `DateTime` | [Example][14] |
|
|
414
|
+
|
|
415
|
+
Support for `trim`, `length`, `substr` and `indexOf` transformations was added.
|
|
416
|
+
|
|
417
|
+
| Expression | Expected outcome | Playground |
|
|
418
|
+
| :-------------------------------------------------------- | :--------------- | ------------- |
|
|
419
|
+
| <code>" a "| trim</code> | `a` | [Example][15] |
|
|
420
|
+
| <code>name|length</code> | `8` | [Example][16] |
|
|
421
|
+
| <code>name|indexOf("e")</code> | `1` | [Example][17] |
|
|
422
|
+
| <code>name|substr(0,name|indexOf("e")+1)</code> | `"De"` | [Example][18] |
|
|
423
|
+
|
|
424
|
+
The following are some examples of **JEXL** expressions not supported by the **legacy** expression language:
|
|
425
|
+
|
|
426
|
+
| Expression | Expected outcome | Format | Playground |
|
|
427
|
+
| :-------------------------------------------------- | :---------------------------------- | ----------- | ------------- |
|
|
428
|
+
| `value == 6? true : false` | `true` | Boolean | [Example][19] |
|
|
429
|
+
| <code>value == 6 && name|indexOf("e")>0</code> | `true` | Boolean | [Example][20] |
|
|
430
|
+
| `array[1]+1` | `3` | Number | [Example][21] |
|
|
431
|
+
| `object.name` | `"John"` | String | [Example][22] |
|
|
432
|
+
| `{type:"Point",coordinates: [value,value]}` | `{type:"Point",coordinates: [6,6]}` | JSON Object | [Example][23] |
|
|
433
|
+
|
|
434
|
+
### Available functions
|
|
435
|
+
|
|
436
|
+
There are several predefined JEXL transformations available to be used at any JEXL expression. The definition of those
|
|
437
|
+
transformations and their JavaScript implementation can be found at jexlTransformsMap.js.
|
|
438
|
+
|
|
439
|
+
The library module also exports a method `iotAgentLib.dataPlugins.expressionTransformation.setJEXLTransforms(Map)` to be
|
|
440
|
+
used by specific IoT Agent implementations in order to incorporate extra transformations to this set. It is important to
|
|
441
|
+
remark that the lib `jexlTransformsMap` cannot be overwritten by the API additions. The idea behind this is to be able
|
|
442
|
+
to incorporate new transformations from the IoT Agent configuration file in a fast and tactical way.
|
|
443
|
+
|
|
444
|
+
Current common transformation set:
|
|
445
|
+
|
|
446
|
+
| JEXL Transformation | Equivalent JavaScript Function |
|
|
447
|
+
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
448
|
+
| jsonparse: (str) | `JSON.parse(str);` |
|
|
449
|
+
| jsonstringify: (obj) | `JSON.stringify(obj);` |
|
|
450
|
+
| indexOf: (val, char) | `String(val).indexOf(char);` |
|
|
451
|
+
| length: (val) | `String(val).length;` |
|
|
452
|
+
| trim: (val) | `String(val).trim();` |
|
|
453
|
+
| substr: (val, int1, int2) | `String(val).substr(int1, int2);` |
|
|
454
|
+
| addreduce: (arr) | <code>arr.reduce((i, v) | i + v));</code> |
|
|
455
|
+
| lengtharray: (arr) | `arr.length;` |
|
|
456
|
+
| typeof: (val) | `typeof val;` |
|
|
457
|
+
| isarray: (arr) | `Array.isArray(arr);` |
|
|
458
|
+
| isnan: (val) | `isNaN(val);` |
|
|
459
|
+
| parseint: (val) | `parseInt(val);` |
|
|
460
|
+
| parsefloat: (val) | `parseFloat(val);` |
|
|
461
|
+
| toisodate: (val) | `new Date(val).toISOString();` |
|
|
462
|
+
| timeoffset:(isostr) | `new Date(isostr).getTimezoneOffset();` |
|
|
463
|
+
| tostring: (val) | `val.toString();` |
|
|
464
|
+
| urlencode: (val) | `encodeURI(val);` |
|
|
465
|
+
| urldecode: (val) | `decodeURI(val);` |
|
|
466
|
+
| replacestr: (str, from, to) | `str.replace(from, to);` |
|
|
467
|
+
| replaceregexp: (str, reg, to) | `str.replace(new RegExp(reg), to);` |
|
|
468
|
+
| replaceallstr: (str, from, to) | `str.replaceAll(from, to);` |
|
|
469
|
+
| replaceallregexp: (str, reg, to) | `str.replaceAll(new RegExp(reg,"g"), to);` |
|
|
470
|
+
| split: (str, ch) | `str.split(ch);` |
|
|
471
|
+
| mapper: (val, values, choices) | <code>choices[values.findIndex((target) | target == val)]);</code> |
|
|
472
|
+
| thmapper: (val, values, choices) | <code>choices[values.reduce((acc,curr,i,arr) | (acc==0)||acc?acc:val<=curr?acc=i:acc=null,null)];</code> |
|
|
473
|
+
| bitwisemask: (i,mask,op,shf) | <code>(op==="&"?parseInt(i)&mask: op==="|"?parseInt(i)|mask: op==="^"?parseInt(i)^mask:i)>>shf;</code> |
|
|
474
|
+
| slice: (arr, init, end) | `arr.slice(init,end);` |
|
|
475
|
+
| addset: (arr, x) | <code>{ return Array.from((new Set(arr)).add(x)) }</code> |
|
|
476
|
+
| removeset: (arr, x) | <code>{ let s = new Set(arr); s.delete(x); return Array.from(s) }</code> |
|
|
477
|
+
| touppercase: (val) | `String(val).toUpperCase()` |
|
|
478
|
+
| tolowercase: (val) | `String(val).toLowerCase()` |
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
You have available this [JEXL interactive playground][99] with all the transformations already loaded, in which you can
|
|
482
|
+
test all the functions described above.
|
|
483
|
+
|
|
484
|
+
## Legacy Expression Language Transformations
|
|
485
|
+
|
|
486
|
+
As described in previous sections, this is the default language just for backward compatibility reasons, but **we
|
|
487
|
+
strongly encourage you not to use this language** for defining expression in order to transform measures, since it is
|
|
488
|
+
not capable to handle JSON native types, it is not extensible and many other reasons as described on the section
|
|
489
|
+
[Comparison between expression languages](#comparison-between-expression-languages). The following example shows a
|
|
490
|
+
device provisioning payload with defined expressions using the Legacy Expression Language:
|
|
491
|
+
|
|
492
|
+
```json
|
|
493
|
+
{
|
|
494
|
+
"devices": [
|
|
495
|
+
{
|
|
496
|
+
"device_id": "45",
|
|
497
|
+
"protocol": "GENERIC_PROTO",
|
|
498
|
+
"entity_name": "WasteContainer:WC45",
|
|
499
|
+
"entity_type": "WasteContainer",
|
|
500
|
+
"attributes": [
|
|
501
|
+
{
|
|
502
|
+
"name": "location",
|
|
503
|
+
"type": "geo:point",
|
|
504
|
+
"expression": "${@latitude}, ${@longitude}"
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
"name": "fillingLevel",
|
|
508
|
+
"type": "Number",
|
|
509
|
+
"expression": "${@level / 100}"
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
"name": "level",
|
|
513
|
+
"type": "Number"
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
"name": "latitude",
|
|
517
|
+
"type": "Number"
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
"name": "longitude",
|
|
521
|
+
"type": "Number"
|
|
522
|
+
}
|
|
523
|
+
]
|
|
524
|
+
}
|
|
525
|
+
]
|
|
526
|
+
}
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### Expressions
|
|
530
|
+
|
|
531
|
+
Each expression pattern is marked with the following secuence: `${<expression>}` where `<expression>` is a construction
|
|
532
|
+
with a valid syntax.
|
|
180
533
|
|
|
181
534
|
### Types
|
|
182
535
|
|
|
183
|
-
The way the parse() function works (at expressionParser.js) is as follows:
|
|
536
|
+
The way the `parse()` function works (at `expressionParser.js`) is as follows:
|
|
184
537
|
|
|
185
|
-
- Expressions can have two return types: String or Number
|
|
186
|
-
is going to be converted. Default value type is String
|
|
538
|
+
- Expressions can have two return types: `String` or `Number`. This return type must be configured for each attribute
|
|
539
|
+
that is going to be converted. Default value type is `String`.
|
|
187
540
|
- Whenever an expression is executed without error, its result will be cast to the configured type. If the conversion
|
|
188
541
|
fails (e.g.: if the expression is null or a String and is cast to Number), the measurement update will fail, and an
|
|
189
542
|
error will be reported to the device.
|
|
190
543
|
|
|
191
|
-
However, the usage that the Expression Translation plugin does of that function is using always String type. That
|
|
192
|
-
that at the end, the result of the expression will be always cast to String
|
|
193
|
-
could be re-cast to the right type (i.e. the one defined for the attribute in the provision operation). Have a
|
|
194
|
-
the [NGSI v2 support](#ngsiv2) for more information on this.
|
|
544
|
+
However, the usage that the Expression Translation plugin does of that function is using always `String` type. That
|
|
545
|
+
means that at the end, the result of the expression will be always cast to `String`. However, in NGSI v2 that `String`
|
|
546
|
+
result could be re-cast to the right type (i.e. the one defined for the attribute in the provision operation). Have a
|
|
547
|
+
look at the [NGSI v2 support](#ngsiv2) for more information on this.
|
|
195
548
|
|
|
196
549
|
### Values
|
|
197
550
|
|
|
@@ -201,11 +554,23 @@ All the information reported in the measurement received by the IoT Agent is ava
|
|
|
201
554
|
every attribute coming from the South Bound, a variable with the syntax `@<object_id>` will be created for its use in
|
|
202
555
|
the expression language.
|
|
203
556
|
|
|
557
|
+
Attribute expressions can contain values taken from the value of other attributes. Those values have, by default, the
|
|
558
|
+
String type. For most arithmetic operations (`*`, `/`, etc...) if a variable is involved, its value will be cast to
|
|
559
|
+
Number, regardless of the original type. For, example, if a variable `@humidity` has the value `'50'` (a String value),
|
|
560
|
+
the following expression:
|
|
561
|
+
|
|
562
|
+
```
|
|
563
|
+
${@humidity * 10}
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
will give `500` as the result (i.e.: the value `'50'` is cast to number, to get `50`, that is then multiplied by 10). If
|
|
567
|
+
this cast fails (because the value of the variable is not a number, e.g.: `'Fifty'`), the overall result will be `NaN`.
|
|
568
|
+
|
|
204
569
|
#### Constants
|
|
205
570
|
|
|
206
571
|
The expression language allows for two kinds of constants:
|
|
207
572
|
|
|
208
|
-
- Numbers (integer or
|
|
573
|
+
- Numbers (integer or float)
|
|
209
574
|
- Strings (marked with double quotes)
|
|
210
575
|
|
|
211
576
|
Current allowed characters are:
|
|
@@ -239,46 +604,49 @@ The following operations are currently available, divided by attribute type
|
|
|
239
604
|
|
|
240
605
|
Parenthesis can be used to define precedence in the operations. Whitespaces between tokens are generally ignored.
|
|
241
606
|
|
|
242
|
-
|
|
607
|
+
### Examples of expressions
|
|
243
608
|
|
|
244
|
-
The following table shows expressions and their expected outcomes for a measure with two attributes:
|
|
245
|
-
6 and
|
|
609
|
+
The following table shows expressions and their expected outcomes for a measure with two attributes: `@value` with value
|
|
610
|
+
`6` and `@name` with value `DevId629`.
|
|
246
611
|
|
|
247
|
-
| Expression | Expected outcome |
|
|
248
|
-
| :-------------------------- | :-------------------- |
|
|
249
|
-
|
|
|
250
|
-
|
|
|
251
|
-
|
|
|
252
|
-
|
|
|
253
|
-
|
|
|
254
|
-
|
|
|
255
|
-
|
|
|
612
|
+
| Expression | Expected outcome | Format |
|
|
613
|
+
| :-------------------------- | :-------------------- | ------ |
|
|
614
|
+
| `5 \* @value` | `30` | Number |
|
|
615
|
+
| `(6 + @value) \* 3` | `36` | Number |
|
|
616
|
+
| `@value / 12 + 1` | `1.5` | Number |
|
|
617
|
+
| `(5 + 2) \* (@value + 7)` | `91` | Number |
|
|
618
|
+
| `@value \* 5.2` | `31.2` | Number |
|
|
619
|
+
| `"Pruebas " + "De Strings"` | `Pruebas De Strings` | String |
|
|
620
|
+
| `@name value is @value` | `DevId629 value is 6` | String |
|
|
256
621
|
|
|
257
|
-
|
|
622
|
+
### NGSI v2 support
|
|
258
623
|
|
|
259
|
-
As it is explained in previous sections, expressions can have two return types: String or Number
|
|
260
|
-
the default. Whenever an expression is executed without error, its result will be cast to the configured type.
|
|
624
|
+
As it is explained in previous sections, expressions can have two return types: `String` or `Number`, being the former
|
|
625
|
+
one the default. Whenever an expression is executed without error, its result will be cast to the configured type.
|
|
261
626
|
|
|
262
|
-
NGSI v2 and NGSI-LD fully supports all the types described in the JSON specification (string
|
|
263
|
-
array and null). Therefore, the result of an expression must be cast to the appropriate type (the type
|
|
264
|
-
the attribute) in order to avoid inconsistencies between the type field for an attribute and the type of
|
|
265
|
-
is being sent.
|
|
627
|
+
NGSI v2 and NGSI-LD fully supports all the types described in the JSON specification (`string`, `number`, `boolean`,
|
|
628
|
+
`object`, `array` and `null`). Therefore, the result of an expression must be cast to the appropriate type (the type
|
|
629
|
+
used to define the attribute) in order to avoid inconsistencies between the type field for an attribute and the type of
|
|
630
|
+
the value that is being sent.
|
|
266
631
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
following:
|
|
632
|
+
The expression parser for legacy does not support JSON Arrays and JSON document. For the rest of types the workflow will
|
|
633
|
+
be the following:
|
|
270
634
|
|
|
271
635
|
1. Variables will be cast to String no matter the expression type (see [comments above](#types) regarding this)
|
|
272
636
|
2. The expression will be applied
|
|
273
637
|
3. The output type will be cast again to the original attribute type.
|
|
274
638
|
|
|
275
|
-
- If attribute type is
|
|
276
|
-
- If attribute type is
|
|
277
|
-
- If attribute type is
|
|
639
|
+
- If attribute type is `Number` and the value is an `Integer`, then the value is casted to integer (JSON number)
|
|
640
|
+
- If attribute type is `Number` and the value is a `Float`, then the value is casted to float (JSON number)
|
|
641
|
+
- If attribute type is `Boolean` then the value is cast to boolean (JSON boolean). In order to do this conversion,
|
|
278
642
|
only `true` or `1` are cast to true.
|
|
279
|
-
- If attribute type is
|
|
643
|
+
- If attribute type is `None` then the value is cast to `null` (JSON null)
|
|
280
644
|
|
|
281
|
-
|
|
645
|
+
> **Note**. All the operations and castings described above are not performed using JELX, because the user has full
|
|
646
|
+
> control on the final value of the attributes, so there is no need of adding a layer of autocast that would interfere
|
|
647
|
+
> and makes things more complicated.
|
|
648
|
+
|
|
649
|
+
E.g.: if a device with the following provisioning information is provisioned in the IoT Agent:
|
|
282
650
|
|
|
283
651
|
```json
|
|
284
652
|
{
|
|
@@ -288,7 +656,7 @@ E.g.: if a device with the following provisioning information is provisioned in
|
|
|
288
656
|
}
|
|
289
657
|
```
|
|
290
658
|
|
|
291
|
-
and a measurement with the following values arrive to the
|
|
659
|
+
and a measurement with the following values arrive to the IoT Agent:
|
|
292
660
|
|
|
293
661
|
```
|
|
294
662
|
status: true
|
|
@@ -304,233 +672,66 @@ status: true
|
|
|
304
672
|
More examples of this workflow are presented below for the different types of attributes supported in NGSI v2 and the
|
|
305
673
|
two possible types of expressions: Integer (arithmetic operations) or Strings.
|
|
306
674
|
|
|
307
|
-
- pressure
|
|
308
|
-
|
|
309
|
-
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
{
|
|
371
|
-
name: 'v2',
|
|
372
|
-
type: 'Number',
|
|
373
|
-
value: 2
|
|
374
|
-
}
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
Will now generate the following NGSI v2 payload:
|
|
378
|
-
|
|
379
|
-
```
|
|
380
|
-
{
|
|
381
|
-
"actionType": "append",
|
|
382
|
-
"entities": [
|
|
383
|
-
{
|
|
384
|
-
"id": "ws9",
|
|
385
|
-
"type": "WeatherStation",
|
|
386
|
-
"vol": {
|
|
387
|
-
"type": "Number",
|
|
388
|
-
"value": 0
|
|
389
|
-
}
|
|
390
|
-
},
|
|
391
|
-
{
|
|
392
|
-
"vol": {
|
|
393
|
-
"type": "Number",
|
|
394
|
-
"value": 100
|
|
395
|
-
},
|
|
396
|
-
"type": "WeatherStation",
|
|
397
|
-
"id": "WeatherStation1"
|
|
398
|
-
},
|
|
399
|
-
{
|
|
400
|
-
"vol": {
|
|
401
|
-
"type": "Number",
|
|
402
|
-
"value": 200
|
|
403
|
-
},
|
|
404
|
-
"type": "WeatherStation",
|
|
405
|
-
"id": "WeatherStation2"
|
|
406
|
-
}
|
|
407
|
-
]
|
|
408
|
-
}
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
## JEXL Based Transformations
|
|
412
|
-
|
|
413
|
-
As an alternative, the IoTAgent Library supports as well [JEXL](https://github.com/TomFrost/jexl). To use JEXL, you will
|
|
414
|
-
need to either configure it as default language using the `defaultExpressionLanguage` field to `jexl` (see
|
|
415
|
-
[configuration documentation](installationguide.md)) or configuring the usage of JEXL as expression language for a given
|
|
416
|
-
device:
|
|
417
|
-
|
|
418
|
-
```
|
|
419
|
-
{
|
|
420
|
-
"devices":[
|
|
421
|
-
{
|
|
422
|
-
"device_id":"45",
|
|
423
|
-
"protocol":"GENERIC_PROTO",
|
|
424
|
-
"entity_name":"WasteContainer:WC45",
|
|
425
|
-
"entity_type":"WasteContainer",
|
|
426
|
-
"expressionLanguage": "jexl",
|
|
427
|
-
"attributes":[
|
|
428
|
-
{
|
|
429
|
-
"name":"location",
|
|
430
|
-
"type":"geo:point",
|
|
431
|
-
"expression": "..."
|
|
432
|
-
},
|
|
433
|
-
{
|
|
434
|
-
"name":"fillingLevel",
|
|
435
|
-
"type":"Number",
|
|
436
|
-
"expression": "..."
|
|
437
|
-
}
|
|
438
|
-
]
|
|
439
|
-
}
|
|
440
|
-
]
|
|
441
|
-
}
|
|
442
|
-
```
|
|
443
|
-
|
|
444
|
-
In the following we provide examples of using JEXL to apply transformations.
|
|
445
|
-
|
|
446
|
-
### Quick comparison to default language
|
|
447
|
-
|
|
448
|
-
- JEXL supports the following types: Boolean, String, Number, Object, Array.
|
|
449
|
-
- JEXL allows to navigate and [filter](https://github.com/TomFrost/jexl#collections) objects and arrays.
|
|
450
|
-
- JEXL supports if..then...else... via [ternary operator](https://github.com/TomFrost/jexl#ternary-operator).
|
|
451
|
-
- JEXL additionally supports the following operations: Divide and floor `//`, Modulus `%`, Logical AND `&&` and
|
|
452
|
-
Logical OR `||`. Negation operator is `!`
|
|
453
|
-
- JEXL supports [comparisons](https://github.com/TomFrost/jexl#comparisons).
|
|
454
|
-
|
|
455
|
-
For more details, check JEXL language details [here](https://github.com/TomFrost/jexl#all-the-details).
|
|
456
|
-
|
|
457
|
-
### Examples of expressions
|
|
458
|
-
|
|
459
|
-
The following table shows expressions and their expected outcomes taking into account the following measures at
|
|
460
|
-
southbound interface:
|
|
461
|
-
|
|
462
|
-
- `value` with value 6 (number)
|
|
463
|
-
- `name` with value `"DevId629"` (string)
|
|
464
|
-
- `object` with value `{name: "John", surname: "Doe"}` (JSON object)
|
|
465
|
-
- `array` with value `[1, 3]` (JSON Array)
|
|
466
|
-
|
|
467
|
-
| Expression | Expected outcome |
|
|
468
|
-
| :-------------------------- | :---------------------- |
|
|
469
|
-
| `5 * value` | `30` |
|
|
470
|
-
| `(6 + value) * 3` | `36` |
|
|
471
|
-
| `value / 12 + 1` | `1.5` |
|
|
472
|
-
| `(5 + 2) * (value + 7)` | `91` |
|
|
473
|
-
| `value * 5.2` | `31.2` |
|
|
474
|
-
| `"Pruebas " + "De Strings"` | `"Pruebas De Strings"` |
|
|
475
|
-
| `name + "value is " +value` | `"DevId629 value is 6"` |
|
|
476
|
-
|
|
477
|
-
Support for `trim`, `length`, `substr` and `indexOf` transformations was added.
|
|
478
|
-
|
|
479
|
-
| Expression | Expected outcome |
|
|
480
|
-
| :----------------------------------------------------------- | :--------------- |
|
|
481
|
-
| <code>" a "|trim</code> | `a` |
|
|
482
|
-
| <code>name|length</code> | `8` |
|
|
483
|
-
| <code>name|indexOf("e")</code> | `1` |
|
|
484
|
-
| <code>name|substring(0,name|indexOf("e")+1)</code> | `"De"` |
|
|
485
|
-
|
|
486
|
-
The following are some expressions not supported by the legacy expression language:
|
|
487
|
-
|
|
488
|
-
| Expression | Expected outcome |
|
|
489
|
-
| :-------------------------------------------------- | :---------------------------------- |
|
|
490
|
-
| `value == 6? true : false` | `true` |
|
|
491
|
-
| <code>value == 6 && name|indexOf("e")>0</code> | `true` |
|
|
492
|
-
| `array[1]+1` | `3` |
|
|
493
|
-
| `object.name` | `"John"` |
|
|
494
|
-
| `{type:"Point",coordinates: [value,value]}` | `{type:"Point",coordinates: [6,6]}` |
|
|
495
|
-
|
|
496
|
-
### Available functions
|
|
497
|
-
|
|
498
|
-
There are several predefined JEXL transformations available to be used at any JEXL expression. The definition of those
|
|
499
|
-
transformations and their JavaScript inmplementation can be found at jexlTransformsMap.js.
|
|
500
|
-
|
|
501
|
-
The library module also exports a method `iotAgentLib.dataPlugins.expressionTransformation.setJEXLTransforms(Map)` to be
|
|
502
|
-
used by specific IoTAgent implementations in order to incorporate extra transformations to this set. It is important to
|
|
503
|
-
remark that the lib jexlTransformsMap cannot be overwritten by the API additions. The idea behind this is to be able to
|
|
504
|
-
incorporate new trasformations from the IoTAgent configuration file in a fast and tactic way.
|
|
505
|
-
|
|
506
|
-
Current common transformation set:
|
|
507
|
-
|
|
508
|
-
- 'jsonparse': (str) => JSON.parse(str));
|
|
509
|
-
- 'jsonstringify': (obj) => JSON.stringify(obj));
|
|
510
|
-
- 'indexOf': (val, char) => String(val).indexOf(char));
|
|
511
|
-
- 'length': (val) => String(val).length);
|
|
512
|
-
- 'trim': (val) => String(val).trim());
|
|
513
|
-
- 'substr': (val, int1, int2) => String(val).substr(int1, int2));
|
|
514
|
-
- 'addreduce': (arr) => arr.reduce((i, v) => i + v));
|
|
515
|
-
- 'lengtharray': (arr) => arr.length);
|
|
516
|
-
- 'typeof': (val) => typeof val);
|
|
517
|
-
- 'isarray': (arr) => Array.isArray(arr));
|
|
518
|
-
- 'isnan': (val) => isNaN(val));
|
|
519
|
-
- 'parseint': (val) => parseInt(val));
|
|
520
|
-
- 'parsefloat': (val) => parseFloat(val));
|
|
521
|
-
- 'toisodate': (val) => new Date(val).toISOString());
|
|
522
|
-
- 'timeoffset':(isostr)=>new Date(isostr).getTimezoneOffset();
|
|
523
|
-
- 'tostring': (val) => val.toString());
|
|
524
|
-
- 'urlencode': (val) => encodeURI(val));
|
|
525
|
-
- 'urldecode': (val) => decodeURI(val));
|
|
526
|
-
- 'replacestr': (str, from, to) => str.replace(from, to));
|
|
527
|
-
- 'replaceregexp': (str, reg, to) => str.replace(new RegExp(reg), to));
|
|
528
|
-
- 'replaceallstr': (str, from, to) => str.replaceAll(from, to));
|
|
529
|
-
- 'replaceallregexp': (str, reg, to) => str.replaceAll(new RegExp(reg,"g"), to));
|
|
530
|
-
- 'split': (str, ch) => str.split(ch));
|
|
531
|
-
- 'mapper': (val, values, choices) => choices[values.findIndex((target) => target == val)]);
|
|
532
|
-
- 'thmapper': (val, values, choices) => choices[values.reduce((acc,curr,i,arr) =>
|
|
533
|
-
(acc==0)||acc?acc:val<=curr?acc=i:acc=null,null)]);
|
|
534
|
-
- 'bitwisemask': (i,mask,op,shf) => (op==="&"?parseInt(i)&mask: op==="|"?parseInt(i)|mask:
|
|
535
|
-
op==="^"?parseInt(i)^mask:i))>>shf;
|
|
536
|
-
- 'slice': (arr, init, end)=>arr.slice(init,end);
|
|
675
|
+
- `pressure` with value 52 (integer)
|
|
676
|
+
- `consumption` with value 0.44 (float)
|
|
677
|
+
- `active` with value `null` (None type)
|
|
678
|
+
|
|
679
|
+
| Expression | Expected outcome | Format |
|
|
680
|
+
| :---------------------- | :--------------- | ------- |
|
|
681
|
+
| `${@pressure * 20}` | `1040` | Integer |
|
|
682
|
+
| `${trim(@pressure)}` | `52` | Integer |
|
|
683
|
+
| `${@consumption * 20}` | `8.8` | Float |
|
|
684
|
+
| `${trim(@consumption)}` | `0.44` | Float |
|
|
685
|
+
| `${@pressure * 20}` | `1040` | Integer |
|
|
686
|
+
| `${@active * 20}` | `null` | None |
|
|
687
|
+
| `${trim(@active)` | `null` | None |
|
|
688
|
+
| `${trim(@consumption)}` | `0.44` | Float |
|
|
689
|
+
|
|
690
|
+
[1]:
|
|
691
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22longitude%22%3A%205%2C%0A%20%20%22latitude%22%3A%2037%2C%0A%20%20%22level%22%3A223%0A%7D&input=%7Bcoordinates%3A%20%5Blongitude%2Clatitude%5D%2C%20type%3A%20'Point'%7D&transforms=%7B%0A%7D
|
|
692
|
+
[2]:
|
|
693
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22longitude%22%3A%205%2C%0A%20%20%22latitude%22%3A%2037%2C%0A%20%20%22level%22%3A223%0A%7D&input=level%2F100&transforms=%7B%0A%7D
|
|
694
|
+
[3]:
|
|
695
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%20%0A%20%20%22latitude%22%3A%201.9%2C%0A%20%20%22level%22%3A85.3%0A%7D&input=longitude%2B%22%2C%20%22%2Blatitude&transforms=%7B%0A%7D
|
|
696
|
+
[4]:
|
|
697
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%20%0A%20%20%22latitude%22%3A%201.9%2C%0A%20%20%22level%22%3A85.3%0A%7D&input=level%2F100&transforms=%7B%0A%7D
|
|
698
|
+
[5]:
|
|
699
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22spaces%22%20%3A%20%22%20%20foobar%20%20%22%0A%7D&input=spaces%20%7C%20trim&transforms=%7B%0A%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%0A%7D
|
|
700
|
+
[6]:
|
|
701
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=5%20*%20value&transforms=%7B%0A%7D
|
|
702
|
+
[7]:
|
|
703
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=(6%20%2B%20value)%20*%203&transforms=%7B%0A%7D
|
|
704
|
+
[8]:
|
|
705
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=value%20%2F%2012%20%2B%201&transforms=%7B%0A%7D
|
|
706
|
+
[9]:
|
|
707
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=(5%20%2B%202)%20*%20(value%20%2B%207)&transforms=%7B%0A%7D
|
|
708
|
+
[10]:
|
|
709
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=value%20*%205.2&transforms=%7B%0A%7D
|
|
710
|
+
[11]:
|
|
711
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=%22Pruebas%20%22%20%2B%20%22De%20Strings%22&transforms=%7B%0A%7D
|
|
712
|
+
[12]:
|
|
713
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=name%20%2B%20%22value%20is%20%22%20%2Bvalue&transforms=%7B%0A%7D
|
|
714
|
+
[13]:
|
|
715
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=%7Bcoordinates%3A%20%5Bvalue%2Cvalue%5D%2C%20type%3A%20'Point'%7D&transforms=%7B%0A%7D
|
|
716
|
+
[14]:
|
|
717
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=ts%7Ctoisodate&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
|
|
718
|
+
[15]:
|
|
719
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=%22%20a%20%22%7Ctrim&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
|
|
720
|
+
[16]:
|
|
721
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=name%7Clength&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
|
|
722
|
+
[17]:
|
|
723
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=name%7CindexOf(%22e%22)&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
|
|
724
|
+
[18]:
|
|
725
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=name%7Csubstr(0%2Cname%7CindexOf(%22e%22)%2B1)&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
|
|
726
|
+
[19]:
|
|
727
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=value%20%3D%3D%206%3F%20true%20%3A%20false&transforms=%7B%7D
|
|
728
|
+
[20]:
|
|
729
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=value%20%3D%3D%206%20%26%26%20name%7CindexOf(%22e%22)%3E0&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
|
|
730
|
+
[21]:
|
|
731
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=array%5B1%5D%2B1&transforms=%7B%7D
|
|
732
|
+
[22]:
|
|
733
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=object.name&transforms=%7B%7D
|
|
734
|
+
[23]:
|
|
735
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22value%22%20%3A%206%2C%0A%20%20%22ts%22%3A%201637245214901%2C%0A%20%22name%22%3A%20%22DevId629%22%2C%0A%20%22object%22%3A%7Bname%3A%20%22John%22%2C%20surname%3A%20%22Doe%22%7D%2C%0A%20%20%22array%22%3A%5B1%2C3%5D%0A%7D&input=%7Btype%3A%22Point%22%2Ccoordinates%3A%20%5Bvalue%2Cvalue%5D%7D&transforms=%7B%7D
|
|
736
|
+
[99]:
|
|
737
|
+
https://czosel.github.io/jexl-playground/#/?context=%7B%0A%20%20%22text%22%20%3A%20%22%20%20foobar%7B%7D%20%20%22%0A%7D&input=text%20%7C%20replacestr(%22foo%22%2C%22FOO%22)%7Ctrim%7Curlencode&transforms=%7B%0A%20%20%20%20jsonparse%3A%20(str)%20%3D%3E%20JSON.parse(str)%2C%0A%20%20%20%20jsonstringify%3A%20(obj)%20%3D%3E%20JSON.stringify(obj)%2C%0A%20%20%20%20indexOf%3A%20(val%2C%20char)%20%3D%3E%20String(val).indexOf(char)%2C%0A%20%20%20%20length%3A%20(val)%20%3D%3E%20String(val).length%2C%0A%20%20%20%20trim%3A%20(val)%20%3D%3E%20String(val).trim()%2C%0A%20%20%20%20substr%3A%20(val%2C%20int1%2C%20int2)%20%3D%3E%20String(val).substr(int1%2C%20int2)%2C%0A%20%20%20%20addreduce%3A%20(arr)%20%3D%3E%20arr.reduce((i%2C%20v)%20%3D%3E%20i%20%2B%20v)%2C%0A%20%20%20%20lengtharray%3A%20(arr)%20%3D%3E%20arr.length%2C%0A%20%20%20%20typeof%3A%20(val)%20%3D%3E%20typeof%20val%2C%0A%20%20%20%20isarray%3A%20(arr)%20%3D%3E%20Array.isArray(arr)%2C%0A%20%20%20%20isnan%3A%20(val)%20%3D%3E%20isNaN(val)%2C%0A%20%20%20%20parseint%3A%20(val)%20%3D%3E%20parseInt(val)%2C%0A%20%20%20%20parsefloat%3A%20(val)%20%3D%3E%20parseFloat(val)%2C%0A%20%20%20%20toisodate%3A%20(val)%20%3D%3E%20new%20Date(val).toISOString()%2C%0A%20%20%20%20timeoffset%3A%20(isostr)%20%3D%3E%20new%20Date(isostr).getTimezoneOffset()%2C%0A%20%20%20%20tostring%3A%20(val)%20%3D%3E%20val.toString()%2C%0A%20%20%20%20urlencode%3A%20(val)%20%3D%3E%20encodeURI(val)%2C%0A%20%20%20%20urldecode%3A%20(val)%20%3D%3E%20decodeURI(val)%2C%0A%20%20%20%20replacestr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replace(from%2C%20to)%2C%0A%20%20%20%20replaceregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replace(new%20RegExp(reg)%2C%20to)%2C%0A%20%20%20%20replaceallstr%3A%20(str%2C%20from%2C%20to)%20%3D%3E%20str.replaceAll(from%2C%20to)%2C%0A%20%20%20%20replaceallregexp%3A%20(str%2C%20reg%2C%20to)%20%3D%3E%20str.replaceAll(new%20RegExp(reg%2C%20'g')%2C%20to)%2C%0A%20%20%20%20split%3A%20(str%2C%20ch)%20%3D%3E%20str.split(ch)%2C%0A%20%20%20%20mapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%20choices%5Bvalues.findIndex((target)%20%3D%3E%20target%20%3D%3D%3D%20val)%5D%2C%0A%20%20%20%20thmapper%3A%20(val%2C%20values%2C%20choices)%20%3D%3E%0A%20%20%20%20%20%20%20%20choices%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20values.reduce((acc%2C%20curr%2C%20i)%20%3D%3E%20(acc%20%3D%3D%3D%200%20%7C%7C%20acc%20%3F%20acc%20%3A%20val%20%3C%3D%20curr%20%3F%20(acc%20%3D%20i)%20%3A%20(acc%20%3D%20null))%2C%20null)%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20bitwisemask%3A%20(i%2C%20mask%2C%20op%2C%20shf)%20%3D%3E%0A%20%20%20%20%20%20%20%20(op%20%3D%3D%3D%20'%26'%20%3F%20parseInt(i)%20%26%20mask%20%3A%20op%20%3D%3D%3D%20'%7C'%20%3F%20parseInt(i)%20%7C%20mask%20%3A%20op%20%3D%3D%3D%20'%5E'%20%3F%20parseInt(i)%20%5E%20mask%20%3A%20i)%20%3E%3E%0A%20%20%20%20%20%20%20%20shf%2C%0A%20%20%20%20slice%3A%20(arr%2C%20init%2C%20end)%20%3D%3E%20arr.slice(init%2C%20end)%0A%7D
|