Parascope Docs

ParaQL Language Reference

Complete syntax reference for ParaQL including SELECT, FROM, WHERE, aggregation, traversal, temporal queries, and built-in functions

This page is the definitive syntax reference for ParaQL. Every clause, operator, function, and special statement is documented here.

Basic SELECT

SELECT <columns>
FROM <source>
[WHERE <conditions>]
[GROUP BY <columns>]
[HAVING <conditions>]
[ORDER BY <column> [ASC|DESC]]
[LIMIT <n>]
[OFFSET <n>]

SELECT Columns

SyntaxDescription
*All universal columns
nameUniversal field
config.phaseConfig-namespace field (type-specific)
metrics.cpu_percentMetrics-namespace field
raw.metadata.labelsRaw JSONB deep path access
COUNT(*) AS totalAggregate with alias
config.ram_mb / 1024 AS ram_gbArithmetic expression

DISTINCT

SELECT DISTINCT namespace
FROM kubernetes.pod

Removes duplicate rows from the result set.

FROM Clause

CI Types

Query a specific CI type using the source.type format:

FROM kubernetes.pod
FROM openstack.instance
FROM ceph.osd

Use SHOW TABLES to list all available CI types.

Wildcard (All Types)

FROM *

Queries across all CI types. Useful for cross-system searches. Note that this carries a higher complexity cost.

Special Tables

TableDescription
changesGlobal change history (ci_name, change_type, changed_at, field details)
relationshipsRelationship records (source_ci_id, target_ci_id, relationship_type)
SELECT ci_name, change_type, changed_at
FROM changes
WHERE changed_at > NOW() - INTERVAL '24 hours'
ORDER BY changed_at DESC

Table Aliases

SELECT p.name, p.config.phase
FROM kubernetes.pod p
WHERE p.config.phase = 'Running'

WHERE Clause

Comparison Operators

OperatorExample
=WHERE status = 'active'
!=WHERE config.phase != 'Running'
>WHERE config.vcpus > 4
>=WHERE config.ram_mb >= 1024
<WHERE criticality_score < 50
<=WHERE config.disk_gb <= 100

Pattern Matching

OperatorDescriptionExample
LIKECase-sensitive pattern (% = any chars, _ = single char)WHERE name LIKE 'web-%'
ILIKECase-insensitive patternWHERE name ILIKE '%nginx%'

Set Operators

WHERE config.phase IN ('Pending', 'Failed', 'Unknown')
WHERE source NOT IN ('kubernetes', 'ceph')

Range

WHERE changed_at BETWEEN NOW() - INTERVAL '48 hours' AND NOW()
WHERE config.vcpus BETWEEN 2 AND 8

NULL Checks

WHERE namespace IS NULL
WHERE criticality IS NOT NULL

Staleness

Check if a CI hasn't been seen recently:

WHERE last_seen IS STALE BY INTERVAL '2 hours'

Logical Operators

Combine conditions with AND, OR, and NOT. Use parentheses for grouping:

WHERE (config.phase = 'Running' OR config.phase = 'Succeeded')
  AND namespace != 'kube-system'
  AND NOT name LIKE 'test-%'

Automatic Active Filter

ParaQL automatically adds status = 'active' to CI queries unless you explicitly reference status in your WHERE clause. This means:

-- Implicitly filtered to active CIs only
SELECT name FROM kubernetes.pod

-- No auto-filter because status is referenced
SELECT name, status FROM kubernetes.pod WHERE status = 'inactive'

Column Namespaces

Every CI has three data namespaces:

NamespaceAccessDescription
Universalname, ci_type, status, source, created_at, last_seen, namespace, criticality, criticality_scoreCommon fields shared by all CI types
Configconfig.phase, config.vcpusType-specific configuration data (tracked in change history)
Metricsmetrics.cpu_percentType-specific runtime metrics (not tracked in changes)
Rawraw.metadata.labelsDeep JSONB access into the original collected data

Use DESCRIBE kubernetes.pod to see all available columns for a CI type, grouped by namespace.

Aggregation

GROUP BY

SELECT source, COUNT(*) AS count
FROM *
WHERE status = 'active'
GROUP BY source
ORDER BY count DESC

Aggregate Functions

FunctionDescription
COUNT(*)Count rows
COUNT(column)Count non-null values
SUM(column)Sum numeric values
AVG(column)Average
MIN(column)Minimum value
MAX(column)Maximum value

HAVING

Filter aggregated results (applied after GROUP BY):

SELECT namespace, COUNT(*) AS pod_count
FROM kubernetes.pod
GROUP BY namespace
HAVING COUNT(*) > 5
ORDER BY pod_count DESC

Ordering and Pagination

ORDER BY name ASC                    -- ascending (default)
ORDER BY criticality_score DESC      -- descending
ORDER BY namespace, name             -- multi-column sort
LIMIT 50                             -- max rows returned
OFFSET 20                            -- skip first N rows

When no LIMIT is specified, ParaQL applies a default limit of 1000 rows to prevent accidental full-table scans.

Scalar Functions

FunctionDescriptionExample
COALESCE(a, b)First non-null valueCOALESCE(namespace, 'none')
NULLIF(a, b)Returns null if a = bNULLIF(status, 'unknown')
UPPER(col)Uppercase stringUPPER(name)
LOWER(col)Lowercase stringLOWER(ci_type)
TRIM(col)Strip whitespaceTRIM(name)
CONCAT(a, b, ...)Concatenate stringsCONCAT(source, '.', ci_type)
LENGTH(col)String lengthLENGTH(name)
CAST(col AS type)Type castCAST(config.vcpus AS float)
DATE_TRUNC(unit, col)Truncate timestampDATE_TRUNC('day', created_at)
NOW()Current timestampWHERE created_at > NOW() - INTERVAL '7 days'

Computed Virtual Columns

These are ParaQL-specific functions that compute derived values from CI metadata:

FunctionReturnsDescription
age()DurationTime since CI was first created
stale_for()DurationTime since CI was last seen by a collector
last_updated()TimestampMost recent change timestamp
change_count()IntegerTotal number of changes for the CI
change_count(INTERVAL '7 days')IntegerChanges within the specified window
relationship_count()IntegerTotal relationships (inbound + outbound)
inbound_count()IntegerRelationships pointing to this CI
outbound_count()IntegerRelationships from this CI to others
SELECT name, ci_type,
       age() AS ci_age,
       stale_for() AS staleness,
       change_count(INTERVAL '7 days') AS weekly_changes,
       relationship_count() AS rels
FROM *
WHERE status = 'active'
ORDER BY weekly_changes DESC
LIMIT 20

Tip: Always alias computed columns (e.g., age() AS ci_age) for readable column headers in results.

Arithmetic

Standard arithmetic operators work on numeric columns:

SELECT name,
       config.vcpus,
       config.ram_mb / 1024 AS ram_gb,
       config.vcpus * 2 AS double_vcpus
FROM openstack.instance
WHERE status = 'active'
ORDER BY config.vcpus DESC

Supported operators: +, -, *, /, % (modulo).

Intervals

Time intervals are used with temporal functions and comparisons:

INTERVAL '7 days'
INTERVAL '24 hours'
INTERVAL '30 minutes'
INTERVAL '2 hours'
NOW() - INTERVAL '7 days'

Relationship Traversal

Walk the infrastructure graph using arrow syntax:

SELECT dep.name, dep.ci_type,
       pod.name, pod.ci_type
FROM kubernetes.deployment dep
  -[manages]-> kubernetes.pod pod

Arrow Syntax

SyntaxDirectionMeaning
-[type]->ForwardSource runs/contains/manages target
<-[type]-ReverseTarget runs/contains/manages source
-[*]->Wildcard typeAny relationship type
-[*1..3]->Variable depth1 to 3 hops of any type

Multi-Hop Traversal

Chain multiple hops to walk deep into the infrastructure stack:

SELECT node.name, node.ci_type,
       vm.name, vm.ci_type,
       os.name, os.ci_type
FROM proxmox.node node
  <-[runs_on]- proxmox.vm vm
  <-[runs_on]- os.linux os

Wildcard Targets

Match any CI type at the other end:

SELECT src.name, src.ci_type,
       tgt.name, tgt.ci_type
FROM kubernetes.deployment src
  -[*]-> * tgt
LIMIT 30

Variable-Depth Traversal

Explore relationships up to N hops deep:

SELECT src.name, src.ci_type,
       tgt.name, tgt.ci_type
FROM proxmox.node src
  -[*1..3]-> * tgt
LIMIT 50

Maximum depth is 10 hops. Cycle detection prevents infinite loops.

Filtering Traversal Results

Apply WHERE conditions to any aliased table in the traversal:

SELECT dep.name, dep.ci_type,
       pod.name, pod.ci_type
FROM kubernetes.deployment dep
  -[manages]-> kubernetes.pod pod
WHERE pod.config.phase = 'Running'

LINEAGE (Stack Traversal)

Discover the full infrastructure stack above and below a CI, from physical hardware up through virtualization to application containers:

SELECT name, ci_type, tier, depth, direction
FROM LINEAGE(kubernetes.pod, name LIKE '%api%')
ColumnDescription
nameCI name
ci_typeCI type
tierThe CI's stack layer (for example physical, virtual, os, application)
depthHops from the seed CI
direction"ancestor" (toward hardware), "descendant" (toward applications), or "seed"
relationship_typeType of relationship connecting this hop

LINEAGE traverses by each relationship's direction classification. Every relationship type is classified as upward, downward, lateral, or non-lineage (graph-only). The ancestors and descendants walks follow the upward and downward edges; lateral edges (equivalence links such as correlations) are followed in both directions. Tier ordering does not gate traversal; it only labels each returned node with its stack layer. (IMPACT has always worked this way; it never enforced tier ordering either.)

-- Lineage for a specific node
SELECT name, ci_type, tier, depth, direction, relationship_type
FROM LINEAGE(proxmox.node)

Lineage Metadata

When a LINEAGE query is returned whole, the response carries a lineage_metadata object alongside the result rows. The query console uses it to render the lineage as a hierarchy tree instead of a flat table; the rows themselves are unchanged.

FieldDescription
edgesThe active lineage relationships among the returned nodes, each with source_ci_id, target_ci_id, and relationship_type
ancestors_truncatedtrue if the ancestors walk was cut short: it hit either the per-direction node cap of 500 nodes or the server-side cap on the path rows one traversal materializes
descendants_truncatedtrue if the descendants walk was cut short: it hit either the per-direction node cap of 500 nodes or the server-side cap on the path rows one traversal materializes
edges_truncatedtrue if the relationships among the returned nodes exceeded the edge cap, so some links are omitted
max_depthThe traversal depth used to build the result

lineage_metadata is attached only when the LINEAGE query is unmodified enough to reassemble into a tree. It is omitted (and the console falls back to the plain results table) when the query:

  • adds an outer WHERE, GROUP BY, LIMIT, or OFFSET (an ORDER BY only reorders the rows, so it is allowed)
  • projects away any of the columns the tree needs to render (every lineage column except tier_index and source must survive the projection)
  • returns more than 1000 flattened rows

When a walk is truncated, the tree shows a banner for that direction so the result is not mistaken for a complete stack.

IMPACT (Blast Radius)

Determine what would be affected if a CI goes down:

SELECT name, ci_type, depth, path, criticality, criticality_score
FROM IMPACT(proxmox.node, name LIKE '%pve%')
ORDER BY depth, criticality_score DESC
ColumnDescription
nameAffected CI name
ci_typeAffected CI type
depthHops from the failed CI
pathRelationship path from source to this CI
criticalityCriticality tier (Critical, High, Medium, Low)
criticality_scoreNumeric criticality score (0-100)

Impact analysis follows all relationships outward from the seed CI, building a dependency tree of everything that would break.

-- Storage failure: what depends on this pool?
SELECT name, ci_type, depth, path, criticality, criticality_score
FROM IMPACT(ceph.pool)
ORDER BY criticality_score DESC
LIMIT 50

ORPHANS

Find CIs with no relationships (potential configuration drift or missing discovery):

SELECT name, ci_type, source, age() AS ci_age, last_seen
FROM ORPHANS()
WHERE status = 'active'
ORDER BY ci_age DESC
LIMIT 30

You can optionally filter orphans to a specific CI type:

SELECT COUNT(*) AS orphaned_pods
FROM ORPHANS(kubernetes.pod)

DIFF (Temporal Comparison)

Compare infrastructure state between two points in time:

DIFF kubernetes.pod
BETWEEN NOW() - INTERVAL '7 days' AND NOW()

Results include three categories:

Diff TypeMeaning
AddedCIs that were created in the time window
RemovedCIs that were deleted in the time window
ModifiedCIs with field-level changes

DIFF supports WHERE, ORDER BY, and LIMIT:

DIFF openstack.instance
BETWEEN NOW() - INTERVAL '30 days' AND NOW()
ORDER BY name
LIMIT 100

AS OF (Point-in-Time Snapshot)

View infrastructure state as it existed at a specific point in the past:

SELECT name, config.phase, config.node_name
FROM kubernetes.pod
AS OF NOW() - INTERVAL '7 days'
WHERE config.phase = 'Running'

AS OF reconstructs CI state by walking the change history backwards from the specified timestamp. This works with any standard SELECT query.

Schema Introspection

StatementDescription
SHOW TABLESList all CI types
SHOW TABLES FROM kubernetesCI types from a specific source
DESCRIBE kubernetes.podColumn names, types, and namespaces
SHOW RELATIONSHIPSAll relationship types with source/target
SHOW RELATIONSHIPS FOR proxmox.nodeRelationships involving a specific type
SHOW SOURCESAll configured data sources
SHOW TIERSInfrastructure tier hierarchy

EXPLAIN

Preview the execution plan without running the query:

EXPLAIN SELECT name, config.phase
FROM kubernetes.pod
WHERE config.phase = 'Running'

The explain view shows the query execution plan, complexity score, and complexity breakdown. Useful for understanding query cost and optimizing performance.

Complexity System

Every query is scored for complexity before execution. This protects the database from accidentally expensive queries.

TierScore RangeBehavior
Immediate0-15Executed immediately
Warning16-25Executed with a performance warning
Elevated26-40Requires elevated API key privileges
Reject41+Rejected; simplify the query

Factors that increase complexity:

  • FROM * (wildcard across all types)
  • Relationship traversal (per hop)
  • Variable-depth traversal
  • IMPACT, LINEAGE, ORPHANS
  • DIFF, AS OF
  • Missing LIMIT clause
  • Leading-wildcard ILIKE (ILIKE '%term%')

Use EXPLAIN to inspect the complexity breakdown of any query.