Skip to content

JQ Executor

The JQ executor allows you to process, transform, and query JSON data using the powerful jq command-line JSON processor.

Overview

The JQ executor enables you to:

  • Transform JSON data structures
  • Extract specific fields from JSON
  • Filter and query JSON data
  • Format and prettify JSON output
  • Perform complex JSON manipulations
  • Integrate JSON processing into workflows

Basic Usage

Simple JSON Query

yaml
steps:
  - name: extract-field
    executor: jq
    command: '.name'
    script: |
      {"name": "John Doe", "age": 30, "city": "New York"}

Output:

json
"John Doe"

Format JSON

yaml
steps:
  - name: pretty-print
    executor: jq
    script: |
      {"id": "user123", "data": {"name": "Alice", "scores": [95, 87, 92]}}

Output:

json
{
  "id": "user123",
  "data": {
    "name": "Alice",
    "scores": [
      95,
      87,
      92
    ]
  }
}

JSON Transformations

Object Transformation

yaml
steps:
  - name: transform-object
    executor: jq
    command: '{id: .user_id, fullName: (.first_name + " " + .last_name), email: .email}'
    script: |
      {
        "user_id": 12345,
        "first_name": "John",
        "last_name": "Smith",
        "email": "[email protected]",
        "created_at": "2023-01-15"
      }

Output:

json
{
  "id": 12345,
  "fullName": "John Smith",
  "email": "[email protected]"
}

Array Processing

yaml
steps:
  - name: process-array
    executor: jq
    command: 'map({name: .name, total: .price * .quantity})'
    script: |
      [
        {"name": "Widget A", "price": 10.99, "quantity": 5},
        {"name": "Widget B", "price": 24.99, "quantity": 2},
        {"name": "Widget C", "price": 7.50, "quantity": 10}
      ]

Output:

json
[
  {"name": "Widget A", "total": 54.95},
  {"name": "Widget B", "total": 49.98},
  {"name": "Widget C", "total": 75}
]

Nested Data Access

yaml
steps:
  - name: access-nested
    executor: jq
    command: '.users[] | select(.active == true) | {id: .id, email: .contact.email}'
    script: |
      {
        "users": [
          {
            "id": 1,
            "name": "Alice",
            "active": true,
            "contact": {"email": "[email protected]", "phone": "555-0001"}
          },
          {
            "id": 2,
            "name": "Bob",
            "active": false,
            "contact": {"email": "[email protected]", "phone": "555-0002"}
          },
          {
            "id": 3,
            "name": "Carol",
            "active": true,
            "contact": {"email": "[email protected]", "phone": "555-0003"}
          }
        ]
      }

Output:

json
{"id": 1, "email": "[email protected]"}
{"id": 3, "email": "[email protected]"}

Real-World Examples

API Response Processing

yaml
name: process-api-response
steps:
  - name: fetch-data
    executor:
      type: http
      config:
        silent: true
    command: GET https://api.example.com/products
    output: API_RESPONSE

  - name: extract-products
    executor: jq
    command: '.products | map({id: .id, name: .name, inStock: .inventory > 0})'
    script: ${API_RESPONSE}
    output: PRODUCTS
    depends: fetch-data

  - name: filter-in-stock
    executor: jq
    command: 'map(select(.inStock == true))'
    script: ${PRODUCTS}
    output: IN_STOCK_PRODUCTS
    depends: extract-products

Configuration File Processing

yaml
steps:
  - name: read-config
    command: cat /etc/myapp/config.json
    output: CONFIG

  - name: extract-database-settings
    executor: jq
    command: '.database | {connection_string: ("postgresql://\(.user):\(.password)@\(.host):\(.port)/\(.name)")}'
    script: ${CONFIG}
    output: DB_SETTINGS
    depends: read-config

  - name: use-connection
    command: |
      CONNECTION=$(echo '${DB_SETTINGS}' | jq -r '.connection_string')
      psql "$CONNECTION" -c "SELECT 1"
    depends: extract-database-settings

Log Analysis

yaml
steps:
  - name: parse-json-logs
    command: cat /var/log/app/app.log
    output: LOGS

  - name: analyze-errors
    executor: jq
    command: |
      split("\n") | 
      map(select(. != "") | fromjson) | 
      map(select(.level == "ERROR")) |
      group_by(.error_type) | 
      map({error: .[0].error_type, count: length}) |
      sort_by(.count) | reverse
    script: ${LOGS}
    output: ERROR_SUMMARY
    depends: parse-json-logs

  - name: report-top-errors
    command: |
      echo "Top Errors:"
      echo '${ERROR_SUMMARY}' | jq -r '.[] | "\(.error): \(.count) occurrences"'
    depends: analyze-errors

Data Aggregation

yaml
steps:
  - name: aggregate-sales
    executor: jq
    command: |
      group_by(.category) |
      map({
        category: .[0].category,
        total_sales: map(.amount) | add,
        item_count: length,
        avg_sale: (map(.amount) | add / length)
      })
    script: |
      [
        {"id": 1, "category": "Electronics", "amount": 299.99},
        {"id": 2, "category": "Clothing", "amount": 49.99},
        {"id": 3, "category": "Electronics", "amount": 899.99},
        {"id": 4, "category": "Clothing", "amount": 79.99},
        {"id": 5, "category": "Electronics", "amount": 149.99},
        {"id": 6, "category": "Books", "amount": 29.99}
      ]

Output:

json
[
  {
    "category": "Books",
    "total_sales": 29.99,
    "item_count": 1,
    "avg_sale": 29.99
  },
  {
    "category": "Clothing",
    "total_sales": 129.98,
    "item_count": 2,
    "avg_sale": 64.99
  },
  {
    "category": "Electronics",
    "total_sales": 1349.97,
    "item_count": 3,
    "avg_sale": 449.99
  }
]

Advanced Queries

Complex Filtering

yaml
steps:
  - name: complex-filter
    executor: jq
    command: |
      .orders |
      map(select(.status == "completed" and .total > 100)) |
      map({
        order_id: .id,
        customer: .customer.name,
        total: .total,
        items: .items | map(.name) | join(", ")
      })
    script: |
      {
        "orders": [
          {
            "id": "ORD001",
            "status": "completed",
            "total": 150.00,
            "customer": {"id": 1, "name": "Alice Johnson"},
            "items": [
              {"name": "Laptop Stand", "price": 50.00},
              {"name": "Wireless Mouse", "price": 100.00}
            ]
          },
          {
            "id": "ORD002",
            "status": "pending",
            "total": 200.00,
            "customer": {"id": 2, "name": "Bob Smith"},
            "items": [{"name": "Monitor", "price": 200.00}]
          },
          {
            "id": "ORD003",
            "status": "completed",
            "total": 75.00,
            "customer": {"id": 3, "name": "Carol White"},
            "items": [{"name": "Keyboard", "price": 75.00}]
          }
        ]
      }

Recursive Descent

yaml
steps:
  - name: find-all-emails
    executor: jq
    command: '.. | objects | select(has("email")) | .email'
    script: |
      {
        "company": {
          "name": "Tech Corp",
          "departments": [
            {
              "name": "Engineering",
              "manager": {"name": "John", "email": "[email protected]"},
              "employees": [
                {"name": "Alice", "contact": {"email": "[email protected]"}},
                {"name": "Bob", "contact": {"email": "[email protected]"}}
              ]
            },
            {
              "name": "Sales",
              "manager": {"name": "Sarah", "email": "[email protected]"},
              "employees": [
                {"name": "Dave", "contact": {"email": "[email protected]"}}
              ]
            }
          ]
        }
      }

JSON to CSV Conversion

yaml
steps:
  - name: json-to-csv
    executor: jq
    command: |
      ["Name","Age","City"],
      (.[] | [.name, .age, .city]) |
      @csv
    script: |
      [
        {"name": "Alice", "age": 30, "city": "New York"},
        {"name": "Bob", "age": 25, "city": "Los Angeles"},
        {"name": "Carol", "age": 35, "city": "Chicago"}
      ]
    output: CSV_DATA

  - name: save-csv
    command: echo '${CSV_DATA}' > users.csv
    depends: json-to-csv

Integration with Other Steps

Combining with HTTP Requests

yaml
steps:
  - name: fetch-weather
    executor:
      type: http
      config:
        silent: true
    command: GET https://api.weather.com/current?city=NewYork
    output: WEATHER_DATA

  - name: extract-temperature
    executor: jq
    command: '{city: .location.city, temp_celsius: .current.temp_c, condition: .current.condition.text}'
    script: ${WEATHER_DATA}
    output: WEATHER_SUMMARY
    depends: fetch-weather

  - name: check-temperature
    command: |
      TEMP=$(echo '${WEATHER_SUMMARY}' | jq -r '.temp_celsius')
      if (( $(echo "$TEMP > 30" | bc -l) )); then
        echo "High temperature alert: ${TEMP}°C"
      fi
    depends: extract-temperature

Processing Command Output

yaml
steps:
  - name: list-processes
    command: ps aux --no-headers | awk '{print "{\"user\": \"" $1 "\", \"pid\": " $2 ", \"cpu\": " $3 ", \"mem\": " $4 ", \"command\": \"" $11 "\"}"}' | jq -s '.'
    output: PROCESSES

  - name: find-high-cpu
    executor: jq
    command: 'map(select(.cpu > 50)) | sort_by(.cpu) | reverse'
    script: ${PROCESSES}
    output: HIGH_CPU_PROCESSES
    depends: list-processes

  - name: alert-high-cpu
    command: |
      COUNT=$(echo '${HIGH_CPU_PROCESSES}' | jq 'length')
      if [ $COUNT -gt 0 ]; then
        echo "Warning: $COUNT processes using >50% CPU"
        echo '${HIGH_CPU_PROCESSES}' | jq -r '.[] | "PID \(.pid): \(.command) (\(.cpu)% CPU)"'
      fi
    depends: find-high-cpu

Error Handling

Validate JSON

yaml
steps:
  - name: validate-json
    executor: jq
    command: '.'
    script: |
      {"valid": "json", "test": true}
    continueOn:
      failure: true
    output: VALIDATION_RESULT

  - name: handle-invalid
    command: |
      if [ -z "${VALIDATION_RESULT}" ]; then
        echo "Invalid JSON detected"
        exit 1
      fi
    depends: validate-json

Safe Property Access

yaml
steps:
  - name: safe-access
    executor: jq
    command: '.data.user // {name: "Unknown", id: 0}'
    script: |
      {"data": {}}
    output: USER_DATA

Type Checking

yaml
steps:
  - name: type-check
    executor: jq
    command: |
      if type == "array" then 
        map(select(type == "object"))
      elif type == "object" then 
        [.]
      else 
        error("Expected array or object")
      end
    script: |
      [{"id": 1}, {"id": 2}, "invalid", {"id": 3}]

Performance Tips

1. Use Streaming for Large Files

yaml
steps:
  - name: process-large-file
    command: |
      # Process line by line instead of loading entire file
      cat large_file.jsonl | jq -c 'select(.active == true)'

2. Combine Operations

yaml
steps:
  - name: efficient-processing
    executor: jq
    command: |
      # Combine multiple operations in one pass
      .items |
      map(select(.price > 10 and .in_stock == true)) |
      map({id: .id, discounted_price: .price * 0.9}) |
      sort_by(.discounted_price)
    script: ${PRODUCTS}

3. Use Built-in Functions

yaml
steps:
  - name: use-builtins
    executor: jq
    command: |
      # Use built-in functions for better performance
      {
        sum: .values | add,
        avg: .values | add / length,
        min: .values | min,
        max: .values | max
      }
    script: |
      {"values": [10, 20, 30, 40, 50]}

Common Patterns

Merging JSON Objects

yaml
steps:
  - name: merge-configs
    executor: jq
    command: 'reduce .[] as $item ({}; . + $item)'
    script: |
      [
        {"database": {"host": "localhost"}},
        {"database": {"port": 5432}},
        {"api": {"key": "secret"}}
      ]

Creating Lookup Tables

yaml
steps:
  - name: create-lookup
    executor: jq
    command: 'map({(.id|tostring): .}) | add'
    script: |
      [
        {"id": 1, "name": "Alice", "role": "admin"},
        {"id": 2, "name": "Bob", "role": "user"},
        {"id": 3, "name": "Carol", "role": "user"}
      ]

Flattening Nested Structures

yaml
steps:
  - name: flatten-data
    executor: jq
    command: |
      .departments[] |
      .employees[] as $emp |
      {
        department: .name,
        employee_name: $emp.name,
        employee_email: $emp.email
      }
    script: |
      {
        "departments": [
          {
            "name": "Engineering",
            "employees": [
              {"name": "Alice", "email": "[email protected]"},
              {"name": "Bob", "email": "[email protected]"}
            ]
          },
          {
            "name": "Sales",
            "employees": [
              {"name": "Carol", "email": "[email protected]"}
            ]
          }
        ]
      }

Debugging

Inspect Data Types

yaml
steps:
  - name: debug-types
    executor: jq
    command: 'map({value: ., type: type})'
    script: |
      [1, "string", true, null, {"key": "value"}, [1, 2, 3]]

Debug Complex Queries

yaml
steps:
  - name: debug-step-by-step
    executor: jq
    command: |
      # Add debug output at each step
      .users |
      debug("After users:") |
      map(select(.active == true)) |
      debug("After filter:") |
      map(.email) |
      debug("Final emails:")
    script: |
      {
        "users": [
          {"name": "Alice", "email": "[email protected]", "active": true},
          {"name": "Bob", "email": "[email protected]", "active": false}
        ]
      }

See Also

Released under the MIT License.