@dx-do/cli 5.2.49 → 6.0.1
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/README.md +24 -6
- package/dist-node/01-discover-vertices.tas-pwngv2fz.md +31 -0
- package/dist-node/01-discover-vertices.tas.data-store-svjfrm1f.json5 +29 -0
- package/dist-node/01-discover-vertices.tas.data-store-tmd-w650nfzt.json +4 -0
- package/dist-node/02-discover-services.tas-867m0m88.md +30 -0
- package/dist-node/02-discover-services.tas.data-store-jz0gx5vn.json5 +40 -0
- package/dist-node/02-discover-services.tas.data-store-tmd-eq264m6y.json +4 -0
- package/dist-node/03-discover-sources.nassql-4tgp9jvv.md +34 -0
- package/dist-node/03-discover-sources.nassql.data-store-by6sqk23.json5 +63 -0
- package/dist-node/03-discover-sources.nassql.data-store-tmd-n3gy57wm.json +4 -0
- package/dist-node/04-discover-metadata-columns.nassql-vhzb0mrq.md +26 -0
- package/dist-node/04-discover-metadata-columns.nassql.data-store-c9zr7p0q.json5 +35 -0
- package/dist-node/04-discover-metadata-columns.nassql.data-store-tmd-4ygrjvty.json +4 -0
- package/dist-node/10-filter-attribute-matches.tas-tafqmtw1.md +33 -0
- package/dist-node/10-filter-attribute-matches.tas.data-store-tmd-m2sendv0.json +4 -0
- package/dist-node/10-filter-attribute-matches.tas.data-store-whdc6vbc.json5 +35 -0
- package/dist-node/11-filter-and-compose.tas-m8856738.md +29 -0
- package/dist-node/11-filter-and-compose.tas.data-store-dh5meyk8.json5 +56 -0
- package/dist-node/11-filter-and-compose.tas.data-store-tmd-mfn8a16f.json +4 -0
- package/dist-node/12-filter-or-not.tas-21zab96s.md +35 -0
- package/dist-node/12-filter-or-not.tas.data-store-7vjr4fnd.json5 +83 -0
- package/dist-node/12-filter-or-not.tas.data-store-tmd-am9smwe5.json +4 -0
- package/dist-node/13-filter-layer.tas-r1ff5anv.md +29 -0
- package/dist-node/13-filter-layer.tas.data-store-5mneyz77.json5 +30 -0
- package/dist-node/13-filter-layer.tas.data-store-tmd-9qmhyfzr.json +4 -0
- package/dist-node/14-filter-traverse.tas-da9jene0.md +38 -0
- package/dist-node/14-filter-traverse.tas.data-store-p0vxtfvj.json5 +63 -0
- package/dist-node/14-filter-traverse.tas.data-store-tmd-5hepg5wf.json +4 -0
- package/dist-node/15-filter-take-vertices-edges.tas-m160qc7z.md +35 -0
- package/dist-node/15-filter-take-vertices-edges.tas.data-store-drmcme43.json5 +46 -0
- package/dist-node/15-filter-take-vertices-edges.tas.data-store-tmd-8fewsp5s.json +4 -0
- package/dist-node/16-filter-projection.tas-dh39mcx8.md +31 -0
- package/dist-node/16-filter-projection.tas.data-store-tmd-3r8anggx.json +4 -0
- package/dist-node/16-filter-projection.tas.data-store-xjbdry1x.json5 +36 -0
- package/dist-node/17-filter-lucene.tas-gyvtzwaa.md +29 -0
- package/dist-node/17-filter-lucene.tas.data-store-1knw6srt.json5 +39 -0
- package/dist-node/17-filter-lucene.tas.data-store-tmd-5cf3tygg.json +5 -0
- package/dist-node/18-filter-variable-reuse.tas-89fq0y6x.md +46 -0
- package/dist-node/18-filter-variable-reuse.tas.data-store-by35113t.json5 +55 -0
- package/dist-node/18-filter-variable-reuse.tas.data-store-tmd-ak7aprgk.json +4 -0
- package/dist-node/19-filter-order-statefilter.tas-hm3z71qj.md +36 -0
- package/dist-node/19-filter-order-statefilter.tas.data-store-ra9hj1rz.json5 +51 -0
- package/dist-node/19-filter-order-statefilter.tas.data-store-tmd-wqer9xy2.json +4 -0
- package/dist-node/20-nassql-from-metadata-basic.nassql-szr2xax1.md +28 -0
- package/dist-node/20-nassql-from-metadata-basic.nassql.data-store-tmd-c7drxs1m.json +4 -0
- package/dist-node/20-nassql-from-metadata-basic.nassql.data-store-zdf1gp1v.json5 +42 -0
- package/dist-node/21-nassql-from-metadata-regex.nassql-78jnsn3e.md +30 -0
- package/dist-node/21-nassql-from-metadata-regex.nassql.data-store-ckzsv7h1.json5 +53 -0
- package/dist-node/21-nassql-from-metadata-regex.nassql.data-store-tmd-zgr6r9my.json +4 -0
- package/dist-node/22-nassql-from-topology.nassql-a71qw9r0.md +42 -0
- package/dist-node/22-nassql-from-topology.nassql.data-store-81m23nge.json5 +58 -0
- package/dist-node/22-nassql-from-topology.nassql.data-store-tmd-vhpjy6c7.json +4 -0
- package/dist-node/23-nassql-join-topology-metadata.nassql-hywxhcg2.md +35 -0
- package/dist-node/23-nassql-join-topology-metadata.nassql.data-store-da7q90n2.json5 +76 -0
- package/dist-node/23-nassql-join-topology-metadata.nassql.data-store-tmd-rr8wt9qa.json +4 -0
- package/dist-node/24-nassql-from-data-window-mean.nassql-q6qsgdxw.md +33 -0
- package/dist-node/24-nassql-from-data-window-mean.nassql.data-store-j7xmg7fc.json5 +81 -0
- package/dist-node/24-nassql-from-data-window-mean.nassql.data-store-tmd-qgzz2f7v.json +4 -0
- package/dist-node/25-nassql-group-order-top.nassql-awgnwn3r.md +30 -0
- package/dist-node/25-nassql-group-order-top.nassql.data-store-cmrn300b.json5 +48 -0
- package/dist-node/25-nassql-group-order-top.nassql.data-store-tmd-7xpqeh7c.json +4 -0
- package/dist-node/26-nassql-filter-predicate.nassql-2t27h5ev.md +41 -0
- package/dist-node/26-nassql-filter-predicate.nassql.data-store-k2rgp609.json5 +59 -0
- package/dist-node/26-nassql-filter-predicate.nassql.data-store-tmd-m4dddgwm.json +4 -0
- package/dist-node/27-nassql-distinct-keep.nassql-6z55dvk3.md +24 -0
- package/dist-node/27-nassql-distinct-keep.nassql.data-store-mrx00ys5.json5 +52 -0
- package/dist-node/27-nassql-distinct-keep.nassql.data-store-tmd-0p9hy42g.json +4 -0
- package/dist-node/28-nassql-format-time.nassql-6wraqgdk.md +30 -0
- package/dist-node/28-nassql-format-time.nassql.data-store-tmd-bbbqhz1x.json +4 -0
- package/dist-node/28-nassql-format-time.nassql.data-store-tvy8y2cs.json5 +59 -0
- package/dist-node/29-nassql-describe-log.nassql-t9vnxeb0.md +31 -0
- package/dist-node/29-nassql-describe-log.nassql.data-store-tmd-q4mtczy8.json +4 -0
- package/dist-node/29-nassql-describe-log.nassql.data-store-x16y4crx.json5 +51 -0
- package/dist-node/30-nassql-map-string.nassql-f2tdknzs.md +30 -0
- package/dist-node/30-nassql-map-string.nassql.data-store-t8ahcabn.json5 +53 -0
- package/dist-node/30-nassql-map-string.nassql.data-store-tmd-a6xq0bdx.json +4 -0
- package/dist-node/31-nassql-join-data-sum.nassql-p16y3xk6.md +26 -0
- package/dist-node/31-nassql-join-data-sum.nassql.data-store-dje7wm6v.json5 +64 -0
- package/dist-node/31-nassql-join-data-sum.nassql.data-store-tmd-c1pyx1qw.json +4 -0
- package/dist-node/32-nassql-bottom-aggregation.nassql-hpgfn77p.md +26 -0
- package/dist-node/32-nassql-bottom-aggregation.nassql.data-store-tmd-p0ssj1vc.json +4 -0
- package/dist-node/32-nassql-bottom-aggregation.nassql.data-store-v9580caa.json5 +43 -0
- package/dist-node/33-nassql-cross-domain-pipeline.nassql-fm0ynphf.md +45 -0
- package/dist-node/33-nassql-cross-domain-pipeline.nassql.data-store-tmd-18881drs.json +4 -0
- package/dist-node/33-nassql-cross-domain-pipeline.nassql.data-store-vqs9hkx4.json5 +79 -0
- package/dist-node/3rdpartylicenses-hx59bakt.txt +885 -0
- package/dist-node/50-discover-custom-layers.tas-2hvvpkzw.md +66 -0
- package/dist-node/50-discover-custom-layers.tas.data-store-h85zgna9.json5 +36 -0
- package/dist-node/50-discover-custom-layers.tas.data-store-tmd-hagn9eak.json +4 -0
- package/dist-node/51-collect-counts-everything.tas-nz0ksgdc.md +46 -0
- package/dist-node/51-collect-counts-everything.tas.data-store-eypcjah8.json5 +48 -0
- package/dist-node/51-collect-counts-everything.tas.data-store-tmd-4pcj94s9.json +4 -0
- package/dist-node/52-collect-counts-bulk.tas-eerw4z8s.md +54 -0
- package/dist-node/52-collect-counts-bulk.tas.data-store-scedtw1m.json5 +65 -0
- package/dist-node/52-collect-counts-bulk.tas.data-store-tmd-csyzj189.json +4 -0
- package/dist-node/53-collect-attributes-by-type.tas-cw0285hx.md +71 -0
- package/dist-node/53-collect-attributes-by-type.tas.data-store-fvjge4yr.json5 +65 -0
- package/dist-node/53-collect-attributes-by-type.tas.data-store-tmd-274qrd8f.json +4 -0
- package/dist-node/README-ghxecaz0.md +84 -0
- package/dist-node/SKILL-1xn7r9nt.md +104 -0
- package/dist-node/agent-25q752kd.md +55 -0
- package/dist-node/agent_connection_and_status-0dq7zkpc.md +62 -0
- package/dist-node/agent_source_collector-6s06n3rs.md +40 -0
- package/dist-node/agentic-mcp-rycd2gh8.md +140 -0
- package/dist-node/application-dfva8tz0.md +48 -0
- package/dist-node/application-m0q2vaxj.md +74 -0
- package/dist-node/attribute_resource_metric_name-pxrceab5.md +56 -0
- package/dist-node/browseragent-snippet.template-9megjp8a.html +12 -0
- package/dist-node/bulkvertexpatch-1a4qy5vb.md +78 -0
- package/dist-node/bundle.pbd-38r15kyd.template +13 -0
- package/dist-node/bundle.profile-1wpzpt3d.template +2 -0
- package/dist-node/business_transaction-mbqz5ex9.md +61 -0
- package/dist-node/chunk-4I3HBO6U-2ebgf7kh.js +127 -0
- package/dist-node/chunk-4PMCLJMS-0mqvr4m4.js +1 -0
- package/dist-node/chunk-5VSFINOX-ewzpx7wh.js +5 -0
- package/dist-node/chunk-72HYG3XZ-kf7hy4vs.js +3625 -0
- package/dist-node/chunk-JRM4BLOM-rg32z8w4.js +1 -0
- package/dist-node/chunk-Q2JA73UH-akkb8bh3.js +14 -0
- package/dist-node/chunk-RNMHSXZF-pdwasrg7.js +1358 -0
- package/dist-node/chunk-VV2FJEMA-3rvtkmga.js +321 -0
- package/dist-node/chunk-YVD3UK5I-9pxr1jka.js +695 -0
- package/dist-node/configuration-1vczsdex.md +104 -0
- package/dist-node/dashboards-x0xddksy.md +17 -0
- package/dist-node/database_or_inferred-8vqf5gyr.md +75 -0
- package/dist-node/default-licensing-config-0p879qpb.template +122 -0
- package/dist-node/dependency-3b0neg5x.md +40 -0
- package/dist-node/description.md-qwc2bj9r.template +30 -0
- package/dist-node/discovery-flow-fw79kbx4.md +116 -0
- package/dist-node/dxi_service-13prnpd5.md +59 -0
- package/dist-node/entity-relationships-cevz61kj.md +142 -0
- package/dist-node/gotchas-8ab64kcd.md +389 -0
- package/dist-node/host-es6fxtgx.md +46 -0
- package/dist-node/host-j3qqrm5f.md +55 -0
- package/dist-node/index-104hyb1m.html +13 -0
- package/dist-node/index-7fp2dfas.json +178 -0
- package/dist-node/index-g3hh5wez.json +403 -0
- package/dist-node/index-mbzg9rhc.json +270 -0
- package/dist-node/index-qffdhwgm.json +2479 -0
- package/dist-node/inferred-w998vfq1.md +41 -0
- package/dist-node/installInstructions.md-k9ghf3dr.template +21 -0
- package/dist-node/inventorize-xc9h9bjr.md +34 -0
- package/dist-node/investigation-planning-6kcm01h9.md +149 -0
- package/dist-node/investigator-flow-jc2s0n46.md +186 -0
- package/dist-node/k8s_deployment_and_namespace-69c29152.md +88 -0
- package/dist-node/k8s_pod_and_container-9h4v6cmj.md +64 -0
- package/dist-node/main-SGLYO5YX-ht69eb0y.js +13 -0
- package/dist-node/main.js +397415 -0
- package/dist-node/marketplace-srdmzxkj.json +15 -0
- package/dist-node/metric-source-names-6cbczyks.md +75 -0
- package/dist-node/metrics-grounding-2h4kkbe3.md +130 -0
- package/dist-node/mm-cookbook-23jpw721.md +231 -0
- package/dist-node/mm-quickstart-x2adfc16.md +106 -0
- package/dist-node/nassql-cookbook-n8kc0mff.md +812 -0
- package/dist-node/nassql-quickstart-090e0yex.md +149 -0
- package/dist-node/plugin-c3bavxvf.json +18 -0
- package/dist-node/polyfills-A7ZF72EO-mp884a0b.js +2 -0
- package/dist-node/prerendered-routes-523d8gat.json +3 -0
- package/dist-node/primeicons-4GST5W3O-jac3wxrf.woff2 +0 -0
- package/dist-node/primeicons-DHQU4SEP-760n99pp.svg +345 -0
- package/dist-node/primeicons-GEFHGEHP-rc4kaa3b.ttf +0 -0
- package/dist-node/primeicons-P53SE5CV-4saz3d5j.woff +0 -0
- package/dist-node/primeicons-RSSEDYLY-4d4vbd67.eot +0 -0
- package/dist-node/query-vs-analysis-separation-sag1ezcq.md +97 -0
- package/dist-node/run-query-vs-run-partial-6138pc94.md +80 -0
- package/dist-node/service-5pz5nhzf.md +133 -0
- package/dist-node/service-hierarchies-87a4ynpj.md +178 -0
- package/dist-node/service-k4f5mkbq.md +51 -0
- package/dist-node/servlet_or_frontend-1kjcb7ar.md +76 -0
- package/dist-node/src-apm-mfnsq6vw.svg +4 -0
- package/dist-node/src-axa-nn28yqmj.svg +4 -0
- package/dist-node/src-dxim-fv7ne4qa.svg +4 -0
- package/dist-node/styles-23VUPSCU-9ehggc1f.css +1 -0
- package/dist-node/tas-cookbook-0y4826rp.md +693 -0
- package/dist-node/tas-quickstart-wgcvwffc.md +138 -0
- package/dist-node/time-format-0595g01j.md +41 -0
- package/dist-node/toggles.pbd-9wscbmng.template +2 -0
- package/dist-node/type-host-agbhmn6v.svg +6 -0
- package/dist-node/type-metric-p9b90bpx.svg +4 -0
- package/dist-node/type-service-k7f1x71k.svg +4 -0
- package/dist-node/ui-0b5grqrg.md +113 -0
- package/dist-node/universe-b9nhf325.md +47 -0
- package/dist-node/universe-fzpwzvxr.md +91 -0
- package/dist-node/universes-and-scopes-1cb9pfk7.md +105 -0
- package/dist-node/vertex_entity_node-mm3yp9d0.md +31 -0
- package/package.json +1 -1
|
@@ -0,0 +1,812 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: nassql-cookbook
|
|
3
|
+
title: NASSQL cookbook — operations reference and recipe catalog
|
|
4
|
+
applies_to: nassql
|
|
5
|
+
tags: [reference, recipes, authoring]
|
|
6
|
+
related: [nassql-quickstart, mm-cookbook, gotchas, run-query-vs-run-partial]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# NASSQL cookbook
|
|
10
|
+
|
|
11
|
+
Full operation reference for NASSQL (`/metrics/nassQuery`) authoring, plus a
|
|
12
|
+
recipe catalog. Use after `nassql-quickstart.md` when you need the breadth
|
|
13
|
+
of ops, the column-availability table, or a concrete pattern to start
|
|
14
|
+
from. Embeds Metrics Metadata specifiers in `FROM_METADATA` and
|
|
15
|
+
`JOIN_METADATA` — `mm-cookbook.md` documents that vocabulary.
|
|
16
|
+
|
|
17
|
+
## NASSQL at a glance
|
|
18
|
+
|
|
19
|
+
A NASSQL query is a **pipeline of operations** (`query: [op1, op2, ...]`)
|
|
20
|
+
executed in order. Each op transforms the working set. The first op is
|
|
21
|
+
always a source; the last op is almost always `KEEP` (or `DESCRIBE` while
|
|
22
|
+
iterating).
|
|
23
|
+
|
|
24
|
+
```mermaid
|
|
25
|
+
flowchart LR
|
|
26
|
+
src["source: FROM_TOPOLOGY / FROM_METADATA / FROM / FROM_DATA"] --> joins["joins (optional): JOIN_TOPOLOGY / JOIN_METADATA / JOIN_DATA"]
|
|
27
|
+
joins --> transforms["transforms: GROUP / FILTER / WINDOW / MAP_STRING / FORMAT_TIME / DISTINCT / ORDER"]
|
|
28
|
+
transforms --> aggs["aggregations: COUNT / SUM / MEAN / TOP / BOTTOM / FIRST / LAST / QUANTILE / AGG"]
|
|
29
|
+
aggs --> shape["shape: KEEP (always last)"]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Mental model: a **dataframe pipeline**. Source ops produce rows with named
|
|
33
|
+
columns; transforms add/rename/filter columns; aggregations collapse rows;
|
|
34
|
+
`KEEP` projects the final column set.
|
|
35
|
+
|
|
36
|
+
## Top-level envelope
|
|
37
|
+
|
|
38
|
+
| Field | Type | Purpose |
|
|
39
|
+
|-------|------|---------|
|
|
40
|
+
| `query` | `QueryFunctionSpec[]` | Pipeline ops (required) |
|
|
41
|
+
| `limit` | number | Row cap on the final result |
|
|
42
|
+
| `authorizationView` | string | Auth-view restriction |
|
|
43
|
+
|
|
44
|
+
## Source ops
|
|
45
|
+
|
|
46
|
+
Each source op produces an initial row set with a known column shape.
|
|
47
|
+
|
|
48
|
+
**`alias` is a reference scope, NOT a row-key prefix.** Setting
|
|
49
|
+
`alias: "health"` on a `FROM` does **not** rename its output columns —
|
|
50
|
+
`rows[0]` keys stay flat (`data.value`, `metric.source`, …). Aliases
|
|
51
|
+
are how the engine disambiguates expression-context references (AGG
|
|
52
|
+
inner `column`, FILTER predicates, MAP `fn`, SCRIPT references) when
|
|
53
|
+
multiple FROMs / JOINs share column names. So:
|
|
54
|
+
|
|
55
|
+
- `AGG { spec: [{ op: "LAST", column: "health.data.value", as: "Service Health" }] }` — works (expression context).
|
|
56
|
+
- `KEEP { columns: ["health.data.value"] }` — returns no rows; the bare
|
|
57
|
+
`data.value` is in `rows[0]`, not the prefixed form.
|
|
58
|
+
- See `gotchas` for the full rule and disambiguation pattern when two
|
|
59
|
+
FROMs emit the same column name.
|
|
60
|
+
|
|
61
|
+
### `FROM_TOPOLOGY` — vertices/edges as rows
|
|
62
|
+
|
|
63
|
+
`querySpecifier` types as a full TAS query, but **only the `filter` field
|
|
64
|
+
is honored by the server**. `limit`, `projection`, `order`, etc. inside
|
|
65
|
+
`querySpecifier` are silently ignored or cause a 400. Use the top-level
|
|
66
|
+
`limit` for row capping.
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"op": "FROM_TOPOLOGY",
|
|
71
|
+
"querySpecifier": {
|
|
72
|
+
"filter": { "op": "ATTRIBUTE", "expressions": [
|
|
73
|
+
{ "name": "type", "values": ["HOST"], "operator": "IN" } ] }
|
|
74
|
+
},
|
|
75
|
+
"alias": "hosts"
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Produces columns: `vertex.id`, `vertex.externalId`, `vertex.startTime`,
|
|
80
|
+
`vertex.endTime`, and `vertex.attr.<attribute_name>` for each vertex
|
|
81
|
+
attribute.
|
|
82
|
+
|
|
83
|
+
### `FROM_METADATA` — metric metadata as rows
|
|
84
|
+
|
|
85
|
+
`querySpecifier` is a **Metrics Metadata `QuerySpecifier`** (NOT a TAS
|
|
86
|
+
query). See `mm-cookbook.md` for the specifier vocabulary.
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"op": "FROM_METADATA",
|
|
91
|
+
"querySpecifier": { "op": "ALL" },
|
|
92
|
+
"alias": "all_metadata"
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"op": "FROM_METADATA",
|
|
99
|
+
"querySpecifier": {
|
|
100
|
+
"op": "SPEC",
|
|
101
|
+
"sourceNameSpecifier": { "op": "REGEX", "pattern": ".*Infrastructure.*" },
|
|
102
|
+
"attributeNameSpecifier": { "op": "REGEX", "pattern": ".*CPU.*" }
|
|
103
|
+
},
|
|
104
|
+
"alias": "cpu_metrics"
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Produces columns: `metric.source`, `metric.path` (and `metric.id` /
|
|
109
|
+
`metric.firstSeen` / `metric.lastSeen` if joined later).
|
|
110
|
+
|
|
111
|
+
### `FROM` — raw time-series data
|
|
112
|
+
|
|
113
|
+
Same `querySpecifier` shape as `FROM_METADATA` (Metrics Metadata
|
|
114
|
+
specifier), but pulls actual data points.
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"op": "FROM",
|
|
119
|
+
"querySpecifier": {
|
|
120
|
+
"op": "SPEC",
|
|
121
|
+
"sourceNameSpecifier": { "op": "REGEX", "pattern": ".*Infrastructure.*" },
|
|
122
|
+
"attributeNameSpecifier": { "op": "REGEX", "pattern": ".*CPU.*Utilization.*" }
|
|
123
|
+
},
|
|
124
|
+
"queryRange": { "endTime": 0, "rangeSize": 3600000, "frequency": 60000 },
|
|
125
|
+
"alias": "cpu_data"
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Produces columns: `data.time`, `data.value`, `metric.source`, `metric.path`,
|
|
130
|
+
`metric.id`. (`metric.id` is available without any `JOIN_METADATA` —
|
|
131
|
+
verified by `[FROM, KEEP({columns:["metric.id"]})]`.)
|
|
132
|
+
`queryRange` is optional but recommended for windowed aggregations.
|
|
133
|
+
|
|
134
|
+
**`metric.attr.<name>` is also accessible in expression contexts** (AGG
|
|
135
|
+
inner `column`, FILTER predicates, MAP `fn`, SCRIPT references) even
|
|
136
|
+
though those keys don't appear in `rows[0]`. The engine resolves them
|
|
137
|
+
at execution time against the matched metric's metadata. Example:
|
|
138
|
+
`AGG { spec: [{ op: "LAST", column: "metric.attr.service_name", as: "Service Name" }] }`
|
|
139
|
+
works.
|
|
140
|
+
|
|
141
|
+
**Diagnostic — verify a FROM is matching anything**: run the FROM in
|
|
142
|
+
isolation with `KEEP(metric.id)` and inspect the rowcount. Empty rows
|
|
143
|
+
means the specifier matches no metric in this tenant — common when an
|
|
144
|
+
ATTRIBUTE predicate (e.g. `is_Custom_health IN ["true"]`) doesn't match
|
|
145
|
+
the values actually stored. The data-store editor exposes a "Run JUST
|
|
146
|
+
this step" control on FROM / FROM_METADATA for exactly this purpose.
|
|
147
|
+
|
|
148
|
+
### `FROM_DATA` — raw time-series by metric id list
|
|
149
|
+
|
|
150
|
+
When you already have specific metric IDs (e.g. from a prior `FROM_METADATA`).
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"op": "FROM_DATA",
|
|
155
|
+
"querySpecifier": { "metrics": ["m-id-1", "m-id-2"] },
|
|
156
|
+
"queryRange": { "endTime": 0, "rangeSize": 3600000, "frequency": 60000 }
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Produces columns: `data.time`, `data.interval`, `data.min`, `data.max`,
|
|
161
|
+
`data.value`, `data.count`, `metric.source`, `metric.path`, `metric.id`.
|
|
162
|
+
Same `metric.attr.<name>` lazy-resolution rule as `FROM` for expression
|
|
163
|
+
contexts.
|
|
164
|
+
|
|
165
|
+
### `FROM_TABLE` / `SHOW_TABLES`
|
|
166
|
+
|
|
167
|
+
Internal NASSQL table access; rarely used in normal queries.
|
|
168
|
+
|
|
169
|
+
## Join ops
|
|
170
|
+
|
|
171
|
+
### `JOIN_METADATA`
|
|
172
|
+
|
|
173
|
+
After a `FROM_TOPOLOGY` source, join in metric metadata for those vertices.
|
|
174
|
+
|
|
175
|
+
```json
|
|
176
|
+
{
|
|
177
|
+
"op": "JOIN_METADATA",
|
|
178
|
+
"querySpecifier": { "op": "ALL" },
|
|
179
|
+
"alias": "vertex_metrics"
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
`querySpecifier` is a Metrics Metadata specifier. Adds `metric.source`,
|
|
184
|
+
`metric.path`, `metric.id`, `metric.firstSeen`, `metric.lastSeen` columns.
|
|
185
|
+
|
|
186
|
+
`joinType`: `INNER` (default) | `LEFT`. Use `LEFT` when you want to keep
|
|
187
|
+
vertices that have no metrics (rows with null metric columns are retained).
|
|
188
|
+
|
|
189
|
+
### `JOIN_TOPOLOGY`
|
|
190
|
+
|
|
191
|
+
Inverse: after a metadata source, join the topology for the metrics' source
|
|
192
|
+
vertices. Configured purely via `externalIdColumn` / `joinType` /
|
|
193
|
+
`rowsClampSize` — **no `querySpecifier`**, the rows already point at
|
|
194
|
+
vertices.
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{ "op": "JOIN_TOPOLOGY", "externalIdColumn": "metric.source", "joinType": "INNER" }
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### `JOIN_DATA`
|
|
201
|
+
|
|
202
|
+
After a metadata or topology source, join time-series data for the matching
|
|
203
|
+
metrics.
|
|
204
|
+
|
|
205
|
+
```json
|
|
206
|
+
{
|
|
207
|
+
"op": "JOIN_DATA",
|
|
208
|
+
"metricIdColumn": "metric.id",
|
|
209
|
+
"queryRange": { "endTime": 0, "rangeSize": 3600000, "frequency": 60000 }
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Transform ops
|
|
214
|
+
|
|
215
|
+
### `GROUP`
|
|
216
|
+
|
|
217
|
+
Group rows by the listed columns. Required before windowed/aggregated ops
|
|
218
|
+
if you want per-group results (e.g. per source).
|
|
219
|
+
|
|
220
|
+
```json
|
|
221
|
+
{ "op": "GROUP", "columns": ["metric.source"] }
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**Context-dependent column-drop.** `GROUP` itself doesn't immediately
|
|
225
|
+
prune columns — it just registers the grouping key. The drop happens at
|
|
226
|
+
the **next aggregator** (`AGG` / `COUNT` / `SUM` / `MEAN` / `MIN` /
|
|
227
|
+
`MAX` / `FIRST` / `LAST` / `QUANTILE` / `NASS_AGG`):
|
|
228
|
+
|
|
229
|
+
- `GROUP → AGG` (or any aggregator) → output collapses to *grouping
|
|
230
|
+
key columns* + each aggregator's `as` outputs. Non-grouped,
|
|
231
|
+
non-aggregated columns are gone after this point.
|
|
232
|
+
- `GROUP → BOTTOM` / `→ TOP` / `→ ORDER` / `→ KEEP` (no aggregator
|
|
233
|
+
between) → upstream columns are preserved; `GROUP` is effectively
|
|
234
|
+
a no-op for column shape.
|
|
235
|
+
|
|
236
|
+
**Repeated `GROUP` overrides** the previous user-grouping key. If you
|
|
237
|
+
need a multi-pass pipeline, GROUP-then-aggregate-then-GROUP-again works
|
|
238
|
+
because the second GROUP starts a fresh key.
|
|
239
|
+
|
|
240
|
+
### `FILTER`
|
|
241
|
+
|
|
242
|
+
Filter rows by a `QueryFilterPredicateSpec`. Predicate ops:
|
|
243
|
+
|
|
244
|
+
| Op | Use |
|
|
245
|
+
|----|-----|
|
|
246
|
+
| `NUMERIC` | `{ op:"NUMERIC", column, operator: EQ\|LT\|LE\|GT\|GE\|NE, value }` |
|
|
247
|
+
| `IN` | `{ op:"IN", column, values: [...] }` |
|
|
248
|
+
| `REGEX` | `{ op:"REGEX", column, pattern, ignoreCase }` |
|
|
249
|
+
| `EXPR` | `{ op:"EXPR", spec: "string expression" }` |
|
|
250
|
+
| `AND` | `{ op:"AND", spec: [predA, predB, ...] }` |
|
|
251
|
+
| `OR` | `{ op:"OR", spec: [predA, predB, ...] }` |
|
|
252
|
+
| `NOT` | `{ op:"NOT", spec: predA }` |
|
|
253
|
+
|
|
254
|
+
```json
|
|
255
|
+
{ "op": "FILTER", "spec": {
|
|
256
|
+
"op": "NUMERIC", "column": "metric_count", "operator": "GT", "value": 2000 } }
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### `FILTER_EXPR`
|
|
260
|
+
|
|
261
|
+
String-based filter expression for cases the predicate spec cannot express.
|
|
262
|
+
|
|
263
|
+
```json
|
|
264
|
+
{ "op": "FILTER_EXPR", "spec": "metric_count > 100 && metric.source matches '.*prod.*'" }
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### `ORDER`
|
|
268
|
+
|
|
269
|
+
Sort by one or more columns.
|
|
270
|
+
|
|
271
|
+
```json
|
|
272
|
+
{ "op": "ORDER",
|
|
273
|
+
"columns": [{ "column": "metric_count", "sortDescending": true }],
|
|
274
|
+
"topN": 10 }
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
`topN` is an optional cap — equivalent to `TOP` without a separate op.
|
|
278
|
+
|
|
279
|
+
### `KEEP` — must be last
|
|
280
|
+
|
|
281
|
+
Project the final column set. Optional `as[]` renames columns positionally.
|
|
282
|
+
|
|
283
|
+
```json
|
|
284
|
+
{ "op": "KEEP", "columns": ["metric.source", "metric_count"] }
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
```json
|
|
288
|
+
{ "op": "KEEP", "columns": ["vertex.attr.name", "metric_count"],
|
|
289
|
+
"as": ["host", "metrics"] }
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
The API rejects `"KEEP function has to be the last one"` if anything
|
|
293
|
+
follows.
|
|
294
|
+
|
|
295
|
+
### `DISTINCT`
|
|
296
|
+
|
|
297
|
+
Collapse duplicate rows.
|
|
298
|
+
|
|
299
|
+
```json
|
|
300
|
+
{ "op": "DISTINCT" }
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### `MAP_STRING`
|
|
304
|
+
|
|
305
|
+
Apply a regex to a column, capturing groups into new named columns.
|
|
306
|
+
|
|
307
|
+
```json
|
|
308
|
+
{ "op": "MAP_STRING",
|
|
309
|
+
"column": "vertex.attr.name", "pattern": "(.*)/(.*)",
|
|
310
|
+
"as": ["dir", "leaf"] }
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
`as[]` length should match the number of regex capture groups. Use
|
|
314
|
+
`filterNotMatching: true` to drop rows that do not match.
|
|
315
|
+
|
|
316
|
+
### `MAP`
|
|
317
|
+
|
|
318
|
+
Per-row arithmetic expression. `fn` supports column references, arithmetic
|
|
319
|
+
(+ - * / ^), comparisons, logical operators, sqrt, and parentheses.
|
|
320
|
+
|
|
321
|
+
```json
|
|
322
|
+
{ "op": "MAP", "fn": "abs(data.value)", "as": "abs_value" }
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### `FORMAT_TIME`
|
|
326
|
+
|
|
327
|
+
Format an epoch-millisecond column as a string.
|
|
328
|
+
|
|
329
|
+
```json
|
|
330
|
+
{ "op": "FORMAT_TIME", "column": "vertex.endTime",
|
|
331
|
+
"as": "last_seen", "pattern": "yyyy-MM-dd HH:mm:ss",
|
|
332
|
+
"timezone": "UTC" }
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
`duration: true` formats the value as a duration instead of a timestamp.
|
|
336
|
+
|
|
337
|
+
### `FORMAT`
|
|
338
|
+
|
|
339
|
+
General-purpose string formatter using a Java-style format spec.
|
|
340
|
+
|
|
341
|
+
```json
|
|
342
|
+
{ "op": "FORMAT", "format": "%s (%d)", "columns": ["name", "count"], "as": "label" }
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## Window ops
|
|
346
|
+
|
|
347
|
+
### `WINDOW`
|
|
348
|
+
|
|
349
|
+
Time-bucket rows into windows of `every` milliseconds. Use **before** an
|
|
350
|
+
aggregation to get per-window aggregates.
|
|
351
|
+
|
|
352
|
+
```json
|
|
353
|
+
{ "op": "WINDOW", "every": 3600000 }
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
`align`: `ABSOLUTE | LEFT | RIGHT`. `incomplete: false` (default) drops
|
|
357
|
+
partial windows.
|
|
358
|
+
|
|
359
|
+
### `WINDOW_CALENDAR`
|
|
360
|
+
|
|
361
|
+
Calendar-aligned windows.
|
|
362
|
+
|
|
363
|
+
```json
|
|
364
|
+
{ "op": "WINDOW_CALENDAR", "calendarInterval": "HOUR", "timeZone": "UTC" }
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
`calendarInterval`: `MINUTE | HOUR | DAY | WEEK | MONTH | QUARTER | YEAR`.
|
|
368
|
+
|
|
369
|
+
## Aggregation ops
|
|
370
|
+
|
|
371
|
+
All take optional `as` (output column name) and `column` (input).
|
|
372
|
+
Aggregations **collapse** non-grouped, non-aggregated columns — only
|
|
373
|
+
`GROUP` columns and the produced `as` column survive.
|
|
374
|
+
|
|
375
|
+
| Op | Field shape | Purpose |
|
|
376
|
+
|----|-------------|---------|
|
|
377
|
+
| `COUNT` | `{ as }` (no column required — counts rows) | Row count |
|
|
378
|
+
| `SUM` | `{ column, as }` | Sum |
|
|
379
|
+
| `MEAN` | `{ column, as, weightColumn? }` | Mean (optionally weighted) |
|
|
380
|
+
| `MIN` / `MAX` | `{ column, as }` | Extremes |
|
|
381
|
+
| `FIRST` / `LAST` | `{ column, as, orderSrc? }` | First/last by time or `orderSrc` |
|
|
382
|
+
| `QUANTILE` | `{ column, as, index, scale?, method? }` | Quantile / percentile |
|
|
383
|
+
| `DERIVATIVE` | `{ column, as, unit?, negative?, timeSrc? }` | Per-time-unit slope |
|
|
384
|
+
| `DIFFERENCE` | `{ column, as, negative?, timeSrc? }` | Successive differences |
|
|
385
|
+
| `AGG` | `{ spec: [{ op, column, as, ... }, ...] }` | Multi-aggregation in one pass |
|
|
386
|
+
| `NASS_AGG` | `{ fromAlias?, as }` | Aggregation specific to nass values |
|
|
387
|
+
| `TOP` | `{ column, n, sortAscending? }` | Top-N by column |
|
|
388
|
+
| `BOTTOM` | `{ column, n, sortAscending? }` | Bottom-N by column |
|
|
389
|
+
|
|
390
|
+
`TOP`/`BOTTOM` use `n`, not `count`. `topN` exists only on `ORDER`.
|
|
391
|
+
|
|
392
|
+
**Default sort direction differs between TOP and BOTTOM** — when
|
|
393
|
+
`sortAscending` is omitted:
|
|
394
|
+
|
|
395
|
+
- `TOP` → `sortAscending = false` (descending; **largest** first).
|
|
396
|
+
- `BOTTOM` → `sortAscending = true` (ascending; **smallest** first).
|
|
397
|
+
|
|
398
|
+
Setting `sortAscending: true` on `TOP` is legal but produces the same
|
|
399
|
+
ordering as `BOTTOM` — at that point use `BOTTOM` for clarity.
|
|
400
|
+
|
|
401
|
+
## Debug ops
|
|
402
|
+
|
|
403
|
+
### `DESCRIBE`
|
|
404
|
+
|
|
405
|
+
Returns the column schema of the working set instead of the data. Insert
|
|
406
|
+
anywhere while authoring to learn what columns are available.
|
|
407
|
+
|
|
408
|
+
```json
|
|
409
|
+
{ "op": "DESCRIBE" }
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
Use it after every source/join op while iterating; remove for the final
|
|
413
|
+
query.
|
|
414
|
+
|
|
415
|
+
### `LOG`
|
|
416
|
+
|
|
417
|
+
Emits an intermediate-result log without modifying the rows.
|
|
418
|
+
`countRecords=true` logs only the row count. `excludeFinalResult=true`
|
|
419
|
+
prevents the LOG output from being included in the response. **Remove
|
|
420
|
+
before saving the final query.**
|
|
421
|
+
|
|
422
|
+
```json
|
|
423
|
+
{ "op": "LOG", "name": "after_join", "limit": 5, "excludeFinalResult": true }
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### `SCRIPT`
|
|
427
|
+
|
|
428
|
+
Execute a script body that emits computed columns. Significantly slower
|
|
429
|
+
than native ops (disables query-plan optimizations and runs server-side
|
|
430
|
+
per row) — use sparingly; prefer `MAP` / `FORMAT` / `DERIVATIVE` /
|
|
431
|
+
`DIFFERENCE` for arithmetic / formatting.
|
|
432
|
+
|
|
433
|
+
**Canonical signature** (the function shape the engine invokes; copy
|
|
434
|
+
this skeleton verbatim and fill in the body):
|
|
435
|
+
|
|
436
|
+
```js
|
|
437
|
+
(function nassqlfn(rows) {
|
|
438
|
+
// rows is an array, and each row is an array of the input column values
|
|
439
|
+
// — each row's positions map to inputColumns[] in the same order.
|
|
440
|
+
|
|
441
|
+
// return an array of rows; each row should be an array of length =
|
|
442
|
+
// outputColumns.length, in outputColumns[] order.
|
|
443
|
+
})
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Worked example — pick the first non-null/non-blank column and emit it
|
|
447
|
+
as `Health`:
|
|
448
|
+
|
|
449
|
+
```json
|
|
450
|
+
{
|
|
451
|
+
"op": "SCRIPT",
|
|
452
|
+
"inputColumns": ["Custom Health", "Service Health"],
|
|
453
|
+
"outputColumns": ["Health"],
|
|
454
|
+
"script": "(function nassqlfn(rows) { return rows.map(function(row) { return [row[0] != null && row[0] !== ' ' ? row[0] : row[1]]; }); })"
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
`inputColumns[]` declares the upstream columns the script can read
|
|
459
|
+
(positional), and `outputColumns[]` declares the columns it emits
|
|
460
|
+
(also positional). The visual editor pre-populates new SCRIPT steps
|
|
461
|
+
with the skeleton above.
|
|
462
|
+
|
|
463
|
+
## Column conventions
|
|
464
|
+
|
|
465
|
+
| After op | Columns added |
|
|
466
|
+
|----------|---------------|
|
|
467
|
+
| `FROM_METADATA` | `metric.id`, `metric.source`, `metric.path`, `metric.name`, `metric.description`, `metric.firstSeen`, `metric.lastSeen` (plus `metric.attr.<X>` resolvable in expression contexts) |
|
|
468
|
+
| `FROM` (data via metadata spec) | `data.time`, `data.value`, `metric.source`, `metric.path`, `metric.id` (plus `metric.attr.<X>` resolvable in expression contexts) |
|
|
469
|
+
| `FROM_DATA` (data via metric ids) | `data.time`, `data.interval`, `data.min`, `data.max`, `data.value`, `data.count`, `metric.source`, `metric.path`, `metric.id` (plus `metric.attr.<X>` resolvable in expression contexts) |
|
|
470
|
+
| `FROM_TOPOLOGY` | `vertex.id`, `vertex.externalId`, `vertex.startTime`, `vertex.endTime`, `vertex.attr.<X>` per attribute |
|
|
471
|
+
| `JOIN_METADATA` (after `FROM_TOPOLOGY`) | + `metric.source`, `metric.path`, `metric.id`, `metric.firstSeen`, `metric.lastSeen` |
|
|
472
|
+
| `JOIN_TOPOLOGY` (after metadata) | + the vertex columns above |
|
|
473
|
+
| `JOIN_DATA` | + `data.time`, `data.interval`, `data.min`, `data.max`, `data.value`, `data.count` |
|
|
474
|
+
| `MAP` / `MAP_STRING` / `FORMAT` / `FORMAT_TIME` / `DERIVATIVE` / `DIFFERENCE` | + the op's `as` column (preserves all upstream columns) |
|
|
475
|
+
| `SCRIPT` | + every column named in `outputColumns[]` (preserves all upstream columns) |
|
|
476
|
+
| `AGG` / `COUNT` / `SUM` / `MEAN` / `MIN` / `MAX` / `FIRST` / `LAST` / `QUANTILE` | output is *grouping key* + each aggregator's `as` (defaults to source column for most; `count` for COUNT). Non-grouped non-aggregated columns are dropped. |
|
|
477
|
+
| `NASS_AGG` | output is *grouping key* + 7-column burst `<as>.{time, interval, min, max, value, count}` (`<as>` defaults to `fromAlias`) |
|
|
478
|
+
| `KEEP` | terminal — projects to `columns[]` (renamed via positional `as[]`) |
|
|
479
|
+
| `WINDOW` / `WINDOW_CALENDAR` | + `window.timestart`, `window.timeend` (only materialize after the next aggregator; before that they're a marker) |
|
|
480
|
+
|
|
481
|
+
**Key naming surprises:**
|
|
482
|
+
- `FROM` (data) gives `metric.source` (NOT `metric.sourceName`) and `data.value` (NOT `metric.value`).
|
|
483
|
+
- All FROM/FROM_DATA/FROM_METADATA emit `metric.id` — no `JOIN_METADATA` needed to access it.
|
|
484
|
+
- `WINDOW` + aggregation collapses non-grouped columns — always `GROUP` before `WINDOW`.
|
|
485
|
+
- `metric.attr.<name>` and `vertex.attr.<name>` are resolvable in expression
|
|
486
|
+
contexts (AGG inner `column`, FILTER predicates, MAP `fn`, SCRIPT references)
|
|
487
|
+
even though they don't appear in the row-level column list. The engine
|
|
488
|
+
resolves them lazily at execution time.
|
|
489
|
+
|
|
490
|
+
## Pipeline ordering rules
|
|
491
|
+
|
|
492
|
+
Distilled from live validation:
|
|
493
|
+
|
|
494
|
+
1. **Source first** — `FROM_TOPOLOGY` / `FROM_METADATA` / `FROM` /
|
|
495
|
+
`FROM_DATA` must be the first op.
|
|
496
|
+
2. **`KEEP` last** — anything after `KEEP` is rejected with
|
|
497
|
+
`"KEEP function has to be the last one"`.
|
|
498
|
+
3. **`GROUP` before `WINDOW`** — for time-series per-entity aggregates,
|
|
499
|
+
group first or the entity column collapses away.
|
|
500
|
+
4. **`GROUP` before aggregations** — to get per-group results; otherwise
|
|
501
|
+
the aggregation runs across all rows.
|
|
502
|
+
5. **REGEX inside SPEC** — never use `{ op: "REGEX", ... }` directly as a
|
|
503
|
+
`querySpecifier`. Wrap in `SPEC` with `sourceNameSpecifier` and/or
|
|
504
|
+
`attributeNameSpecifier` (see `mm-cookbook.md`).
|
|
505
|
+
6. **`JOIN_*` after compatible source** — `JOIN_TOPOLOGY` requires a
|
|
506
|
+
metadata source above; `JOIN_METADATA` typically follows
|
|
507
|
+
`FROM_TOPOLOGY`.
|
|
508
|
+
|
|
509
|
+
See `gotchas.md` for more pitfalls.
|
|
510
|
+
|
|
511
|
+
## Recipe catalog
|
|
512
|
+
|
|
513
|
+
Each recipe links to a worked example shipped in the corpus —
|
|
514
|
+
`corpus_get("queries", "<id>")` returns the full payload + per-op
|
|
515
|
+
descriptions.
|
|
516
|
+
|
|
517
|
+
### "Count all metrics on the tenant"
|
|
518
|
+
`corpus_get("queries", "20-nassql-from-metadata-basic")`
|
|
519
|
+
|
|
520
|
+
```json
|
|
521
|
+
{
|
|
522
|
+
"query": [
|
|
523
|
+
{ "op": "FROM_METADATA", "querySpecifier": { "op": "ALL" }, "alias": "all_metrics" },
|
|
524
|
+
{ "op": "COUNT", "as": "total" },
|
|
525
|
+
{ "op": "KEEP", "columns": ["total"] }
|
|
526
|
+
],
|
|
527
|
+
"limit": 10
|
|
528
|
+
}
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### "List metric paths matching source/attribute regex"
|
|
532
|
+
`corpus_get("queries", "21-nassql-from-metadata-regex")`
|
|
533
|
+
|
|
534
|
+
```json
|
|
535
|
+
{
|
|
536
|
+
"query": [
|
|
537
|
+
{ "op": "FROM_METADATA",
|
|
538
|
+
"querySpecifier": {
|
|
539
|
+
"op": "SPEC",
|
|
540
|
+
"sourceNameSpecifier": { "op": "REGEX", "pattern": ".*Infrastructure.*" },
|
|
541
|
+
"attributeNameSpecifier": { "op": "REGEX", "pattern": ".*CPU.*" }
|
|
542
|
+
},
|
|
543
|
+
"alias": "cpu_metrics" },
|
|
544
|
+
{ "op": "KEEP", "columns": ["metric.source", "metric.path"] }
|
|
545
|
+
],
|
|
546
|
+
"limit": 50
|
|
547
|
+
}
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### "Top N most-emitting metric sources"
|
|
551
|
+
`corpus_get("queries", "25-nassql-group-order-top")` (also see
|
|
552
|
+
`corpus_get("queries", "03-discover-sources")` for the same shape without
|
|
553
|
+
TOP for a full sorted list)
|
|
554
|
+
|
|
555
|
+
```json
|
|
556
|
+
{
|
|
557
|
+
"query": [
|
|
558
|
+
{ "op": "FROM_METADATA", "querySpecifier": { "op": "ALL" }, "alias": "all_metadata" },
|
|
559
|
+
{ "op": "GROUP", "columns": ["metric.source"] },
|
|
560
|
+
{ "op": "COUNT", "as": "metric_count" },
|
|
561
|
+
{ "op": "TOP", "column": "metric_count", "n": 10 },
|
|
562
|
+
{ "op": "KEEP", "columns": ["metric.source", "metric_count"] }
|
|
563
|
+
],
|
|
564
|
+
"limit": 10
|
|
565
|
+
}
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
### "Bottom N (least-active) sources"
|
|
569
|
+
`corpus_get("queries", "32-nassql-bottom-aggregation")` — same as TOP but
|
|
570
|
+
`BOTTOM`.
|
|
571
|
+
|
|
572
|
+
### "List entities of a type, projected"
|
|
573
|
+
`corpus_get("queries", "22-nassql-from-topology")`
|
|
574
|
+
|
|
575
|
+
```json
|
|
576
|
+
{
|
|
577
|
+
"query": [
|
|
578
|
+
{ "op": "FROM_TOPOLOGY",
|
|
579
|
+
"querySpecifier": { "filter": { "op": "ATTRIBUTE", "expressions": [
|
|
580
|
+
{ "name": "type", "values": ["HOST"], "operator": "IN" } ] } },
|
|
581
|
+
"alias": "hosts" },
|
|
582
|
+
{ "op": "KEEP", "columns": ["vertex.attr.name", "vertex.attr.type", "vertex.externalId"] }
|
|
583
|
+
],
|
|
584
|
+
"limit": 20
|
|
585
|
+
}
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
### "Cross-domain count: entities -> metric counts per entity"
|
|
589
|
+
`corpus_get("queries", "23-nassql-join-topology-metadata")`
|
|
590
|
+
|
|
591
|
+
```json
|
|
592
|
+
{
|
|
593
|
+
"query": [
|
|
594
|
+
{ "op": "FROM_TOPOLOGY",
|
|
595
|
+
"querySpecifier": { "filter": { "op": "ATTRIBUTE", "expressions": [
|
|
596
|
+
{ "name": "type", "values": ["AGENT"], "operator": "IN" } ] } },
|
|
597
|
+
"alias": "agents" },
|
|
598
|
+
{ "op": "JOIN_METADATA", "querySpecifier": { "op": "ALL" }, "alias": "agent_metrics" },
|
|
599
|
+
{ "op": "GROUP", "columns": ["vertex.attr.name", "vertex.attr.type"] },
|
|
600
|
+
{ "op": "COUNT", "as": "metric_count" },
|
|
601
|
+
{ "op": "ORDER", "columns": [{ "column": "metric_count", "sortDescending": true }] },
|
|
602
|
+
{ "op": "KEEP", "columns": ["vertex.attr.name", "vertex.attr.type", "metric_count"] }
|
|
603
|
+
],
|
|
604
|
+
"limit": 20
|
|
605
|
+
}
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
### "Per-source mean over a window"
|
|
609
|
+
`corpus_get("queries", "24-nassql-from-data-window-mean")` — note `GROUP`
|
|
610
|
+
is **before** `WINDOW` so `metric.source` survives.
|
|
611
|
+
|
|
612
|
+
```json
|
|
613
|
+
{
|
|
614
|
+
"query": [
|
|
615
|
+
{ "op": "FROM",
|
|
616
|
+
"querySpecifier": {
|
|
617
|
+
"op": "SPEC",
|
|
618
|
+
"sourceNameSpecifier": { "op": "REGEX", "pattern": ".*Infrastructure.*" },
|
|
619
|
+
"attributeNameSpecifier": { "op": "REGEX", "pattern": ".*CPU.*Utilization.*" }
|
|
620
|
+
},
|
|
621
|
+
"alias": "cpu_data" },
|
|
622
|
+
{ "op": "GROUP", "columns": ["metric.source"] },
|
|
623
|
+
{ "op": "WINDOW", "every": 3600000 },
|
|
624
|
+
{ "op": "MEAN", "column": "data.value", "as": "avg_cpu" },
|
|
625
|
+
{ "op": "KEEP", "columns": ["metric.source", "avg_cpu"] }
|
|
626
|
+
],
|
|
627
|
+
"limit": 50
|
|
628
|
+
}
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
### "Filter rows by numeric threshold"
|
|
632
|
+
`corpus_get("queries", "26-nassql-filter-predicate")`
|
|
633
|
+
|
|
634
|
+
```json
|
|
635
|
+
{
|
|
636
|
+
"query": [
|
|
637
|
+
{ "op": "FROM_METADATA", "querySpecifier": { "op": "ALL" }, "alias": "all_metrics" },
|
|
638
|
+
{ "op": "GROUP", "columns": ["metric.source"] },
|
|
639
|
+
{ "op": "COUNT", "as": "metric_count" },
|
|
640
|
+
{ "op": "FILTER", "spec": { "op": "NUMERIC", "column": "metric_count", "operator": "GT", "value": 2000 } },
|
|
641
|
+
{ "op": "ORDER", "columns": [{ "column": "metric_count", "sortDescending": true }] },
|
|
642
|
+
{ "op": "KEEP", "columns": ["metric.source", "metric_count"] }
|
|
643
|
+
],
|
|
644
|
+
"limit": 50
|
|
645
|
+
}
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
### "Distinct rows / projection only"
|
|
649
|
+
`corpus_get("queries", "27-nassql-distinct-keep")`
|
|
650
|
+
|
|
651
|
+
```json
|
|
652
|
+
{
|
|
653
|
+
"query": [
|
|
654
|
+
{ "op": "FROM_TOPOLOGY",
|
|
655
|
+
"querySpecifier": { "filter": { "op": "ATTRIBUTE", "expressions": [
|
|
656
|
+
{ "name": "type", "values": ["k8s_NAMESPACE"], "operator": "IN" } ] } },
|
|
657
|
+
"alias": "namespaces" },
|
|
658
|
+
{ "op": "KEEP", "columns": ["vertex.attr.name", "vertex.attr.type"] }
|
|
659
|
+
],
|
|
660
|
+
"limit": 50
|
|
661
|
+
}
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
### "Format an epoch column as a date string"
|
|
665
|
+
`corpus_get("queries", "28-nassql-format-time")`
|
|
666
|
+
|
|
667
|
+
```json
|
|
668
|
+
{
|
|
669
|
+
"query": [
|
|
670
|
+
{ "op": "FROM_TOPOLOGY",
|
|
671
|
+
"querySpecifier": { "filter": { "op": "ATTRIBUTE", "expressions": [
|
|
672
|
+
{ "name": "type", "values": ["AGENT"], "operator": "IN" } ] } },
|
|
673
|
+
"alias": "agents" },
|
|
674
|
+
{ "op": "FORMAT_TIME", "column": "vertex.endTime",
|
|
675
|
+
"as": "last_seen", "pattern": "yyyy-MM-dd HH:mm:ss" },
|
|
676
|
+
{ "op": "KEEP", "columns": ["vertex.attr.name", "vertex.attr.type", "last_seen"] }
|
|
677
|
+
],
|
|
678
|
+
"limit": 20
|
|
679
|
+
}
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
### "Discover columns at any pipeline step"
|
|
683
|
+
`corpus_get("queries", "29-nassql-describe-log")` (also
|
|
684
|
+
`corpus_get("queries", "04-discover-metadata-columns")`)
|
|
685
|
+
|
|
686
|
+
```json
|
|
687
|
+
{
|
|
688
|
+
"query": [
|
|
689
|
+
{ "op": "FROM_TOPOLOGY", "querySpecifier": { "filter": { } }, "alias": "x" },
|
|
690
|
+
{ "op": "JOIN_METADATA", "querySpecifier": { "op": "ALL" }, "alias": "y" },
|
|
691
|
+
{ "op": "DESCRIBE" }
|
|
692
|
+
],
|
|
693
|
+
"limit": 100
|
|
694
|
+
}
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
**MCP shortcut for the source-step case.** When the question is specifically "what `metric.attr.*` / `vertex.attr.*` columns will my source op expose?", `nassql_step_columns` answers it in one call. Pass your in-progress payload + the 0-based index of the source / join op and it returns `{attrs: [...], kind, durationMs}`. The handler picks the right probe automatically (MM `queryMetric` for `FROM_METADATA`, a `KEEP(metric.id)` slice + MM lookup for `FROM`, `COLLECT_ATTRIBUTE_NAMES` for `FROM_TOPOLOGY`, an external-id harvest + COLLECT for `JOIN_TOPOLOGY`). Use the MCP probe when authoring; use the `DESCRIBE` recipe above when you need to see the entire row shape (including non-attribute columns) or the join surface across two sources at once.
|
|
698
|
+
|
|
699
|
+
### "Verify a FROM is matching any metrics"
|
|
700
|
+
|
|
701
|
+
When a downstream aggregation produces nulls or zero rows, isolate
|
|
702
|
+
each source op and confirm it actually matches something.
|
|
703
|
+
|
|
704
|
+
```json
|
|
705
|
+
{
|
|
706
|
+
"query": [
|
|
707
|
+
{ "op": "FROM",
|
|
708
|
+
"querySpecifier": { "op": "SPEC",
|
|
709
|
+
"sourceNameSpecifier": { "op": "EXACT", "names": ["<your-source>"] },
|
|
710
|
+
"attributeNameSpecifier": { "op": "REGEX", "pattern": "<your-pattern>" } },
|
|
711
|
+
"alias": "probe" },
|
|
712
|
+
{ "op": "KEEP", "columns": ["metric.id"] }
|
|
713
|
+
],
|
|
714
|
+
"limit": 50
|
|
715
|
+
}
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
Result rows are `[["metric.id"], <id1>, <id2>, …]`. Zero data rows ⇒
|
|
719
|
+
the specifier doesn't match in this tenant. From here, sample one of
|
|
720
|
+
the IDs via Metrics-Metadata to inspect the metric's attributes and
|
|
721
|
+
refine the filter against actual stored values. The data-store editor
|
|
722
|
+
exposes a "Run JUST this step" control on `FROM` / `FROM_METADATA`
|
|
723
|
+
that runs this exact pipeline and shows the result in the Debug pane.
|
|
724
|
+
|
|
725
|
+
### "Extract substrings into new columns via regex"
|
|
726
|
+
`corpus_get("queries", "30-nassql-map-string")`
|
|
727
|
+
|
|
728
|
+
```json
|
|
729
|
+
{
|
|
730
|
+
"query": [
|
|
731
|
+
{ "op": "FROM_TOPOLOGY",
|
|
732
|
+
"querySpecifier": { "filter": { "op": "ATTRIBUTE", "expressions": [
|
|
733
|
+
{ "name": "type", "values": ["k8s_POD"], "operator": "IN" } ] } },
|
|
734
|
+
"alias": "pods" },
|
|
735
|
+
{ "op": "MAP_STRING", "column": "vertex.attr.name",
|
|
736
|
+
"pattern": "(.*)", "as": ["pod_label"] },
|
|
737
|
+
{ "op": "KEEP", "columns": ["pod_label", "vertex.attr.type"] }
|
|
738
|
+
],
|
|
739
|
+
"limit": 20
|
|
740
|
+
}
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
### "Cross-domain: entities + filtered metric counts per entity"
|
|
744
|
+
`corpus_get("queries", "31-nassql-join-data-sum")` — shows `JOIN_METADATA`
|
|
745
|
+
with a `SPEC` specifier to filter the joined metrics.
|
|
746
|
+
|
|
747
|
+
```json
|
|
748
|
+
{
|
|
749
|
+
"query": [
|
|
750
|
+
{ "op": "FROM_TOPOLOGY",
|
|
751
|
+
"querySpecifier": { "filter": { "op": "ATTRIBUTE", "expressions": [
|
|
752
|
+
{ "name": "type", "values": ["AGENT"], "operator": "IN" } ] } },
|
|
753
|
+
"alias": "agents" },
|
|
754
|
+
{ "op": "JOIN_METADATA",
|
|
755
|
+
"querySpecifier": { "op": "SPEC",
|
|
756
|
+
"attributeNameSpecifier": { "op": "REGEX", "pattern": ".*Responses Per Interval.*" } },
|
|
757
|
+
"alias": "response_metrics" },
|
|
758
|
+
{ "op": "GROUP", "columns": ["vertex.attr.name"] },
|
|
759
|
+
{ "op": "COUNT", "as": "response_metric_count" },
|
|
760
|
+
{ "op": "ORDER", "columns": [{ "column": "response_metric_count", "sortDescending": true }] },
|
|
761
|
+
{ "op": "KEEP", "columns": ["vertex.attr.name", "response_metric_count"] }
|
|
762
|
+
],
|
|
763
|
+
"limit": 20
|
|
764
|
+
}
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
### "Topology traversal -> aggregation pipeline"
|
|
768
|
+
`corpus_get("queries", "33-nassql-cross-domain-pipeline")` — shows TAS
|
|
769
|
+
`TRAVERSE` inside `FROM_TOPOLOGY.querySpecifier`.
|
|
770
|
+
|
|
771
|
+
```json
|
|
772
|
+
{
|
|
773
|
+
"query": [
|
|
774
|
+
{ "op": "FROM_TOPOLOGY",
|
|
775
|
+
"querySpecifier": { "filter": {
|
|
776
|
+
"op": "TRAVERSE",
|
|
777
|
+
"input": { "op": "ATTRIBUTE", "expressions": [
|
|
778
|
+
{ "name": "type", "values": ["k8s_CLUSTER"], "operator": "IN" } ] },
|
|
779
|
+
"traverse": [{ "direction": "ANY", "repeat": 3 }],
|
|
780
|
+
"includeInput": true } },
|
|
781
|
+
"alias": "k8s_entities" },
|
|
782
|
+
{ "op": "GROUP", "columns": ["vertex.attr.type"] },
|
|
783
|
+
{ "op": "COUNT", "as": "entity_count" },
|
|
784
|
+
{ "op": "ORDER", "columns": [{ "column": "entity_count", "sortDescending": true }] },
|
|
785
|
+
{ "op": "KEEP", "columns": ["vertex.attr.type", "entity_count"] }
|
|
786
|
+
],
|
|
787
|
+
"limit": 20
|
|
788
|
+
}
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
### "All metric sources sorted by count" (no TOP cap)
|
|
792
|
+
`corpus_get("queries", "03-discover-sources")` — same shape as the TOP
|
|
793
|
+
recipe without the TOP op.
|
|
794
|
+
|
|
795
|
+
## Authoring tips
|
|
796
|
+
|
|
797
|
+
1. **Sketch the pipeline first.** Source → joins → transforms →
|
|
798
|
+
aggregation → KEEP. If you cannot name the source op, you have not
|
|
799
|
+
decided whether you want entities, metric metadata, or raw data.
|
|
800
|
+
2. **Insert `DESCRIBE` early and often.** After every source/join op
|
|
801
|
+
while iterating. Remove for the final query.
|
|
802
|
+
3. **Group before windowed aggregations** so the entity column survives
|
|
803
|
+
into `KEEP`.
|
|
804
|
+
4. **`KEEP` last, always.** And only list columns that exist after the
|
|
805
|
+
aggregations.
|
|
806
|
+
5. **Use the recipes verbatim** when starting from a familiar pattern —
|
|
807
|
+
substitute attribute names, regex patterns, and limits, but keep the
|
|
808
|
+
op shapes intact.
|
|
809
|
+
6. **For `FROM` / `FROM_METADATA` regex matching**, remember the REGEX op
|
|
810
|
+
must be wrapped in a `SPEC` specifier. See `mm-cookbook.md`.
|
|
811
|
+
7. **Verify intermediate stages** with `run_partial_query` (`upToStep:
|
|
812
|
+
N`) — see `run-query-vs-run-partial.md`.
|