These guides are primarily for agent readers. They explain how agents use Sails. If you are a person, start at sails.app or Add Sails.

Filter JSON files

Job: you have a folder of .json (or markdown with YAML front matter) and need the matching rows as JSON, paths, a page, or a count/sum. Use filter. Do not reach for sql --db unless the data lives in a .db bundle.

filter reads a Postgres jsonb index after files are checkpointed. PATH must sit under a repo mount (/{org}/{repo}/…). Host /tmp is rejected.

Full flag reference: Filter. Page lists of the same files: Collections (filter:).

Done looks like

filter --where 'status = open' --order date:desc data/invoices

Stdout is a typed JSON array. Each record is the stored object plus _path. With --page / --limit, stdout is { "items": […], "page": N, "limit": N, "total": N, "pages": N } instead of a bare array.

Worked example: open invoices

mkdir -p data/invoices
cat > data/invoices/a.json <<'EOF'
{"title":"Invoice follow-up","status":"open","amount":120,"date":"2026-09-01"}
EOF
cat > data/invoices/b.json <<'EOF'
{"title":"Closed deal","status":"closed","amount":40,"date":"2026-08-15"}
EOF
cat > data/invoices/c.json <<'EOF'
{"title":"Big open","status":"open","amount":250,"date":"2026-09-10"}
EOF

Autosave indexes them. Then:

filter --where 'status = open' --order date:desc data/invoices

Expected shape (paths vary by cwd / repo):

[
  {"_path":"…/data/invoices/c.json","amount":250,"date":"2026-09-10","status":"open","title":"Big open"},
  {"_path":"…/data/invoices/a.json","amount":120,"date":"2026-09-01","status":"open","title":"Invoice follow-up"}
]

b.json is omitted because status is not open.

Pagination, aggregates, paths

# page 1 of open invoices (limit 1 → envelope, not a bare array)
filter --where 'status = open' --page 1 --limit 1 data/invoices

# count + sum (no record list; cannot combine with --page / --limit)
filter --where 'amount >= 100' --agg count --agg sum:amount data/invoices

# newline paths only
filter --where 'status = open' --paths data/invoices

Expected aggregate shape: { "count": 2, "amount": 370 }.

Semantic search

Same embedding index as search. Default --order is relevance (desc). Records include _score.

filter --search 'billing invoice' data/invoices
filter --search 'billing' --where 'status = open' --order date data/invoices

--search is not valid on a typed JSON array piped on stdin (that path filters in memory only).

Capture for a script

rows=$(filter --where 'status = open' data/invoices)          # typed JSON array
page=$(filter --where 'status = open' --limit 10 data/invoices) # envelope

Stdin paths or in-memory JSON

find data/invoices -name '*.json' | filter --where 'status = open' --order date

printf '%s\n' '[{"status":"open","amount":120},{"status":"closed","amount":40}]' \
  | filter --where 'status = open'

CLI (verified)

filter [--where EXPR] [--search QUERY] [--order FIELD[:asc|:desc]|relevance] \
  [--page N] [--limit N] [--agg count|sum:FIELD]... [--paths] [PATH...]
FlagEffect
--whereSame language as page collections (status = open and amount >= 100). Missing fields are null. Top-level fields only
--searchSemantic match over file contents. Default --order relevance. Records include _score. Ranks up to 100, then applies --where / --order / pagination
--orderdate asc; date:desc or -date desc. relevance / _score require --search
--page / --limit1-based page; limit 1–100. --page alone uses limit 10. Output becomes the envelope. --paths still emits a sliced newline list
--aggcount and/or sum:FIELD. No record list. Sum skips null/missing; non-numeric values error. Not with --page / --limit
--pathsNewline _path values instead of JSON records
PATHFiles, directories (recursive json/md), or globs. No PATH → cwd. Stdin newline paths (from find) are included

On a static site page

Same file set, request-time, via front-matter filter: (not glob:):

---
title: Invoices
invoices:
  filter: data/invoices/**
  where: status = open
  sort: date
  order: desc
  paginate: 10
---

A collection must set glob: or filter:, not both. See Collections.

Pick the right tool

JobTool
App rows (contacts, todos) in a .db bundlesql --db — Keep app data in a database
Other pages as a MiniJinja listglob: collection
JSON / front-matter filesfilter / page filter:

Failure modes

What you seeWhat it meansWhat to do
Empty arrayNo match, or files not checkpointed yetAutosave the JSON / front-matter. Confirm PATH from the cwd you meant
search path must be under a repo mountHost path like /tmpUse a path under /{org}/{repo}/…
You expected contacts / todosThose are table rowssql --db, not filter
--search with no hitsEmbedding index empty or query offWrite/checkpoint files first. Try --where alone
--agg plus --pageNot allowedDrop pagination when aggregating
Non-numeric sum:FIELDSum needs numbersFix the field or skip those files
Unreadable path omittedUnix mode blocked the fileFix mode, or sudo filter as a repo owner

More detail: Filter. Related: Collections, SQL.