Skip to content

Name the filter condition's target field - #956

Open
aaronccasanova wants to merge 1 commit into
tobi:mainfrom
aaronccasanova:feature/metadata-filter-field
Open

aaronccasanova wants to merge 1 commit into
tobi:mainfrom
aaronccasanova:feature/metadata-filter-field

Conversation

@aaronccasanova

Copy link
Copy Markdown
Contributor

Follow-up to #910, before it ships. This renames one property in the filter AST, key to field, so a condition reads as what it is: a predicate over a named field of whatever record is being evaluated.

qmd search "authentication" --filter '{"field":"status","operator":"eq","value":"published"}'

Nothing else in the grammar changes. operator, value, operands, and operand keep their names, and the parser rejects key the same way it rejects any unknown property, naming the JSON path.

Why

The AST in #910 is a small predicate grammar: logical groups, negation, and conditions of the form { <target>, operator, value }. The target was named key because, for a document, the fields a condition can test are its metadata keys. That is true, but it is a special case of what the property does, and the name leaks the special case into the grammar.

The general statement is: a condition tests one field of the record under evaluation. Name the slot field and the grammar describes itself without reference to what it happens to be applied to. That matters as soon as the same predicate is evaluated against something other than a document. #951 does exactly that, applying the filter grammar to individual metadata entries (whose fields are key and value) so discovery can narrow what it reports with the same language filtering uses. With the property named key, the entry case reads {"key":"key",...}. With field, it reads {"field":"key",...}, and the one-line explanation becomes: --filter evaluates documents, --match evaluates metadata entries, same predicate either way.

field is also the conventional name for this slot: Elasticsearch, Typesense, Vespa, and Solr all use it. Qdrant uses key. Weaviate uses path, which implies nesting #910 deliberately does not support. Meilisearch and Algolia say attribute, the same idea with a longer word.

Why now

#910 is merged but not released, so no published version of qmd has accepted key. After the next release this becomes a breaking change to a documented, agent-facing grammar. Before it, it is a rename.

What changed

  • src/metadata-filter.ts: the MetadataCondition types, the parser (allowed properties, the two validation messages that named the property), and the compiler's parameter binding. The module comment states the predicate framing.
  • README filter table and semantics, skills/qmd/SKILL.md, the MCP query tool description, the CLI help and --filter error example, and the CHANGELOG entry for filtering.
  • Every test that builds a condition, plus one new case asserting that key is rejected as an unknown property.

Typecheck, lint, and the metadata, MCP, SDK, store, and CLI suites pass under Node and Bun.

A filter condition is a predicate over one field of the record under
evaluation: `field` names it, `operator` says how to compare, `value`
is the operand. For a document, the fields are its metadata keys, and
that special case is what the property was named after.

Rename the property from `key` to `field` so the grammar describes
itself without reference to what it happens to be applied to. Nothing
else in the AST changes: `operator`, `value`, `operands`, and `operand`
keep their names, and the parser rejects the old property the same way
it rejects any unknown one, with the JSON path of the failing node.

The rename lands before metadata filtering (tobi#910) ships, so no released
version accepts `key`.

Updates the README filter table and semantics, the skill, the MCP
`query` tool description, the CLI help and error examples, the
changelog entry, and every test that builds a condition.

Assisted-by: Claude Fable 5.1 via Pi
This was referenced Sep 15, 2026
@aaronccasanova
aaronccasanova marked this pull request as ready for review September 15, 2026 02:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant