Hash writing-hashql-jexpr
HashQL J-Expr syntax for writing queries. Use when writing J-Expr code, using #literal/#struct/#list constructs, understanding function call syntax, or working with HashQL query files (.jsonc).
install
source · Clone the upstream repo
git clone https://github.com/hashintel/hash
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/hashintel/hash "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.claude/skills/writing-hashql-jexpr" ~/.claude/skills/hashintel-hash-writing-hashql-jexpr && rm -rf "$T"
manifest:
.claude/skills/writing-hashql-jexpr/SKILL.mdsource content
Writing HashQL J-Expr
J-Expr is a JSON-based expression syntax for HashQL. It represents typed expressions using JSON primitives.
Expression Types
J-Expr has three expression types:
| JSON Type | J-Expr Meaning |
|---|---|
| String | Path/identifier/symbol |
| Array | Function call |
| Object | Data constructor (with keys) |
Paths (Strings)
Strings are parsed as paths or identifiers:
"x" // Simple variable "vertex.id.entity_id" // Dotted path access "::core::types::String" // Namespaced/rooted path "::graph::head::entities" // Graph function path
Function Calls (Arrays)
Arrays represent function calls:
[function, arg1, arg2, ...]
// Basic function call ["add", {"#literal": 1}, {"#literal": 2}] // Namespaced function ["::graph::head::entities", ["::graph::tmp::decision_time_now"]] // Labeled argument with :prefix in object ["greet", {":name": {"#literal": "Alice"}}] // Shorthand labeled argument (string with :prefix) ["func", ":name"]
Data Constructors (Objects with # Keys)
Objects with special
# keys construct data:
| Key | Purpose | Example |
|---|---|---|
| Primitive values | |
| Named fields | |
| Variable-size ordered | |
| Fixed-size ordered | |
| Key-value map | |
| Type annotation | Used with other keys |
Literals
{"#literal": 42} {"#literal": "hello"} {"#literal": true} {"#literal": null} {"#literal": 3.14, "#type": "Float"}
Struct
{"#struct": {"name": {"#literal": "Alice"}, "age": {"#literal": 30}}} {"#struct": {"x": {"#literal": 1}}, "#type": "Point"}
List and Tuple
{"#list": [{"#literal": 1}, {"#literal": 2}]} {"#tuple": [{"#literal": 1}, {"#literal": "text"}]}
Dict
{"#dict": {"key": {"#literal": "value"}}}
Common Patterns
Let Binding
["let", "varName", {"#literal": 10}, ["add", "varName", {"#literal": 5}]]
Function Definition
["fn", {"#tuple": []}, {"#struct": {"vertex": "_"}}, "_", body_expr]
Conditionals
["if", condition_expr, then_expr, else_expr]
Comparison
["==", "left", "right"] [">", {"#literal": 5}, {"#literal": 3}]
Do
- Use
for all primitive values (numbers, strings, booleans, null)#literal - Use
prefix for namespaced paths:: - Use
prefix for labeled arguments: - Combine
with other constructors for type annotations#type
Don't
- Don't use bare JSON numbers/booleans - wrap in
{"#literal": ...} - Don't confuse
(variable-size) with#list
(fixed-size)#tuple - Don't use
prefix for labeled arguments (use#
): - Don't nest
keys incorrectly - each object should have one primary#
key#
Examples
Entity query:
["::graph::head::entities", ["::graph::tmp::decision_time_now"]]
Filtering with comparison:
["filter", "entities", ["fn", {"#tuple": []}, {"#struct": {"entity": "_"}}, "_", ["==", "entity.draft_id", {"#literal": null}]]]
Struct with type:
{"#struct": {"value": {"#literal": 100}}, "#type": "Amount"}
References
- Syntax Reference - Paths, function calls, operators
- Special Forms - Language constructs (
,if
,let
,fn
,type
, etc.)use - Data Constructors - Typed data (
,#literal
,#struct
,#tuple
,#list
,#dict
)#type - Type DSL - Embedded type annotation syntax
- Parser:
libs/@local/hashql/syntax-jexpr/src/parser/ - Object forms:
libs/@local/hashql/syntax-jexpr/src/parser/object/ - Type DSL:
libs/@local/hashql/syntax-jexpr/src/parser/string/type.rs