Faceted Search & Filter for JSON

Paste a JSON array of records and get a faceted result: the records that match your search and filters, sorted and paginated, plus a value → count breakdown for every facet field. Runs entirely in your browser — no upload, no sign-up.

Try:
Result

About this tool

Faceted Search & Filter for JSON turns a pasted array of records into the two things a faceted-navigation UI needs: the records that match the current search, and a value → count breakdown ("Brand: Northwind 2, Summit 1, Trailhead 1") for every facet field. It is the query-time half of a search sidebar without an index, a server, or an account — the whole search runs in your browser through WebAssembly, so the data you paste never leaves your machine.

Paste a JSON array (a single object, or NDJSON / JSON Lines with one record per line, also work). Optionally narrow the set with Search text (all whitespace-separated terms must appear somewhere in the record) and a Filter expression, sort and page the survivors, and list the Facet fields you want counted — or leave that blank to auto-detect. Facet counts are always computed over the whole match set, not just the page you are looking at.

A filter expression is made of path op value clauses. Operators are ==, !=, >, >=, <, <=, contains, startswith, endswith, ~ (regex, also spelled matches), and in (membership: brand in [Northwind, Summit]). Join clauses with and, or, not, and group them with parentheses; and binds tighter than or. A bare path on its own (in_stock) is true when that field exists and is truthy. Comparisons are numeric when both sides are numbers, otherwise string. Dotted paths (meta.color) and array indexes (items.0.id) reach into nested data, and a clause on an array field holds when any element satisfies it.

Worked example — count facet values across a small catalog:

Input (JSON dataset):

[
  {"name":"Aero Jacket","brand":"Northwind","price":180,"tags":["outdoor","sale"],"in_stock":true},
  {"name":"Basalt Boots","brand":"Northwind","price":120,"tags":["outdoor"],"in_stock":false},
  {"name":"Cirrus Cap","brand":"Trailhead","price":25,"tags":["sale"],"in_stock":true},
  {"name":"Delta Pack","brand":"Summit","price":95,"tags":["outdoor","travel"],"in_stock":true}
]

Facet fields: brand, tags — Output: summary

Output:

4 records matched — page 1 of 1 (4 shown, 10 per page)

brand (3 distinct values)
  Northwind  2
  Summit     1
  Trailhead  1

tags (3 distinct values)
  outdoor  3
  sale     2
  travel   1

tags counts to 6 across 4 records because each array element is counted on its own — that is what a collection facet means.

Worked example — filter, then read the recounted facet:

Filter expression: tags contains outdoor and in_stock — Facet fields: brand — Sort records: price:desc — Output: facets

Output:

[
  {
    "field": "brand",
    "distinct": 2,
    "values": [
      {
        "value": "Northwind",
        "count": 1
      },
      {
        "value": "Summit",
        "count": 1
      }
    ]
  }
]

Two of the four records survive, and the brand facet is recounted over just those two — Trailhead drops out entirely because nothing it contains matches.

Worked example — numeric facet stats:

Facet fields: price — Facet value order: Value A→Z / low→highAdd min / max / avg / sum for numeric facets on — Output: summary

Output:

4 records matched — page 1 of 1 (4 shown, 10 per page)

price (4 distinct values)
  25   1
  95   1
  120  1
  180  1
  min 25 · max 180 · avg 105 · sum 420

Limits and edge cases

FAQ

What is a facet, and how is this different from just filtering JSON?

A facet is a field broken down into its distinct values with a count for each — brand: Northwind (2), Summit (1) — computed from the records that currently match. A plain filter answers "which records match?"; faceting also answers "what else could I narrow by, and how many rows would each choice leave?" That is the data behind the checkbox sidebar on a shop or docs search page. This tool returns both halves together: items for the rows and facets for the counts, plus total / page / per_page / total_pages.

When should I use disjunctive instead of conjunctive counting?

Use multi-select friendly (disjunctive) when a facet's checkboxes should stay clickable after the first one is ticked. In conjunctive mode, filtering on brand in [Northwind] recounts the brand facet over only Northwind rows, so every other brand shows zero and the sidebar becomes a dead end. Disjunctive mode ignores that facet's own clauses when counting it, so Summit and Trailhead keep their real counts and a user can add a second brand. Other facets are still counted with the brand filter applied, and the returned records are identical in both modes.

How do I facet or filter on a nested or array field?

Use a dotted path. meta.color reaches the color key inside a meta object, and a numeric segment indexes an array, so items.0.id is the first element's id. A non-numeric segment applied to an array of objects maps over the elements, so items.sku reaches every sku. In a filter, a clause on an array field holds when any element satisfies it (tags contains sale), and when that path is used as a facet each element gets its own bucket.

Can I paste NDJSON / JSON Lines instead of an array?

Yes. If the input is not a valid JSON array it is retried as NDJSON — one JSON value per line, no enclosing brackets or commas — which is what log pipelines and database exports emit. Blank lines are skipped. A single JSON object is accepted as well and treated as a one-record dataset. If neither parse works you get the JSON error rather than an empty result.

How do I filter a numeric range, like a price slider?

Write two clauses joined with and: price >= 50 and price < 150. Comparisons are numeric whenever both sides are numbers, so no quoting or padding is needed, and the same works for ISO-8601 date strings as a string comparison (date >= 2026-01-01). To see the spread before choosing a range, facet the field and turn on Add min / max / avg / sum for numeric facets.

Is my data uploaded anywhere?

No. The search runs entirely in your browser through WebAssembly — the JSON you paste never leaves your machine, so it is safe for private catalogs, database exports, and API payloads. There is no server round-trip, no index to build, and no sign-up. The same tool is available offline through the command line.

Developer & Automation Access

Run it from the terminal

Same engine as this page, headless — via the gizza CLI:

gizza tool faceted-filter '[
  {"name":"Aero Jacket","brand":"Northwind","price":180,"tags":["outdoor","sale"],"in_stock":true},
  {"name":"Basalt Boots","brand":"Northwind","price":120,"tags":["outdoor"],"in_stock":false},
  {"name":"Cirrus Cap","brand":"Trailhead","price":25,"tags":["sale"],"in_stock":true}
]'

New to the CLI? Get gizza →

Open it by URL

Pre-fill and auto-run this tool with query parameters — the names match the API/CLI:

https://gizza.ai/tools/faceted-filter/?data=%5B%0A%20%20%7B%22name%22%3A%22Aero%20Jacket%22%2C%22brand%22%3A%22Northwind%22%2C%22price%22%3A180%2C%22tags%22%3A%5B%22outdoor%22%2C%22sale%22%5D%2C%22in_stock%22%3Atrue%7D%2C%0A%20%20%7B%22name%22%3A%22Basalt%20Boots%22%2C%22brand%22%3A%22Northwind%22%2C%22price%22%3A120%2C%22tags%22%3A%5B%22outdoor%22%5D%2C%22in_stock%22%3Afalse%7D%2C%0A%20%20%7B%22name%22%3A%22Cirrus%20Cap%22%2C%22brand%22%3A%22Trailhead%22%2C%22price%22%3A25%2C%22tags%22%3A%5B%22sale%22%5D%2C%22in_stock%22%3Atrue%7D%0A%5D&query=outdoor%20jacket&search_fields=name%2C%20tags&filters=price%20%3C%20150%20and%20tags%20contains%20outdoor&facets=brand%2C%20tags%2C%20in_stock&facet_limit=10&facet_sort=count_desc&facet_mode=conjunctive&facet_stats=true&sort=price%3Adesc%2C%20name&page=1&per_page=10&output=json

Machine-readable descriptor: tool.json — title + parameters JSON Schema for agents.