NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
Packagist · #5340 most downloaded on Packagist
A simple library providing a query abstraction for filtering, ordering, and pagination
Last release 2 days ago
07 Oct 2026
Ships fairly regularly
a new release about every 5 weeks
Nearly every release is documented
notes for 12 of 13 stable releases
Nothing withdrawn
no release was ever pulled
7 months old
13 releases · first in 2026
One column per month.
fix(query): convert both operands of an OR join condition from parsed SQL
join|leftJoin|rightJoin|fullOuterJoin(string $collection, string $alias, array $on)
and crossJoin|naturalJoin(string $collection, string $alias). The column
form (join($table, $left, $right, $operator, $alias)) and the alias-less
and alias-last overloads are gone. An empty alias throws
ValidationException.Method::isJoinCondition(), new): on(), the
comparisons, between, null checks, contains/containsAny/notContains,
the startsWith/endsWith family, and and/or of those. Anything else
(limit(), select(), ordering, cursors, aggregates, joins, search(),
regex(), spatial, JSON, raw(), JSON strings) throws ValidationException
(Unsupported join ON condition: <method>), at any depth.Query::getAlias() replaces getJoinAlias(), and
aggregates (count('*', 'total'), …) keep it there instead of in
values[0]. toArray() writes it as "alias", and new Query() takes it
as a fourth argument, so subclasses that override the constructor should
pass it on.cursorAfter()/cursorBefore() take array|object (the row a page starts
after or ends before), on Query and on Builder\Feature\Selects. The
builders bind the row's _cursor value and throw ValidationException
when it has none; a scalar cursor parsed from JSON still binds as-is.compileSearchExpr,
compileJsonContainsExpr, compileJsonOverlapsExpr, compileJsonPathExpr
and compileVectorOrderExpr are now compile*Expression. Subclasses that
override them must rename their overrides; an un-renamed override of an
abstract method fails to load, and one of compileVectorOrderExpression()
is silently no longer called.join($table, [$conditions], $alias) dropped the alias, and an ON list
accepted members such as limit() that only failed, or were ignored, when
the query was compiled (BUG-05). Both are rejected or impossible now.Query factories: a single
on() equality becomes the $lookup, under the join's alias. Filtered ON
lists throw UnsupportedException instead of producing a broken stage.| 0.6 | 0.7 |
|---|---|
Query::join('orders', 'users.id', 'orders.user_id') |
Query::join('orders', 'orders', [Query::on('users.id', 'orders.user_id')]) |
Query::leftJoin('orders', 'u.id', 'o.uid', '!=', 'o') |
Query::leftJoin('orders', 'o', [Query::on('u.id', 'o.uid', '!=')]) |
Query::join('orders', [$on], 'o') / Query::join('orders', 'o', [$on]) |
Query::join('orders', 'o', [$on]) |
Query::crossJoin('colors') |
Query::crossJoin('colors', 'colors') |
$join->getJoinAlias() |
$join->getAlias() |
$aggregate->getValue('') for the alias |
$aggregate->getAlias() |
Query::cursorAfter('id') |
Query::cursorAfter(['_cursor' => 'id']) for the bundled builders, or the row your own compiler pages on (a document, ['id' => …]) |
$builder->cursorAfter('value') |
$builder->cursorAfter(['_cursor' => 'value']) |
protected function compileSearchExpr(...) |
protected function compileSearchExpression(...) (likewise for the other four) |
Serialised 0.6 queries still parse: parseQuery() reads "alias" and falls
back to values[0] for aggregates, cross/natural joins and alias-first joins,
and to values[3] for column-form joins, which become a single on()
condition. A join that ends up with no alias throws ValidationException.
First release cut from packages/query in appwrite/appwrite.
First release cut from packages/query in appwrite/appwrite.
Query::equal('meta.a.b', …) with attribute type object) are
now written as escaped string literals. A key containing ' used to end the
literal early, so the rest of the key was read as SQL; it now names a single
key, whatever it contains. Keys containing a backslash use the E'' form, so
they read the same with standard_conforming_strings on or off. A key
containing a NUL byte throws ValidationException.setJsonPath() quotes path segments that are not plain
identifiers in the text[] path it binds, so a segment containing ,, {,
}, ", a backslash or whitespace addresses that one key instead of
splitting into several path elements.equal, comparisons, contains, isNull, exists, …,
including filters nested in and/or) reject an empty field name or one
starting with $ with ValidationException, as set()/push() already
did, so a filter's field can no longer become a query operator such as
$where or $expr.PostgreSQL::alterColumnNullable() — emits ALTER COLUMN … SET NOT NULL / DROP NOT NULL .
PostgreSQL::alterColumnNullable() — emits ALTER COLUMN … SET NOT NULL / DROP NOT NULL.Postgres carries NOT NULL through an ALTER COLUMN … TYPE, so a column that moves between required and optional keeps its old constraint. MySQL resets nullability as part of MODIFY COLUMN, so only Postgres needs the constraint altered on its own — there was previously no way to express that through the builder.
Full changelog: 0.6.0...0.6.1
(feat): add Schema\Order for index column directions by @abnegate in #21
Full Changelog: 0.5.0...0.6.0
feat: nest join ON conditions as Query objects by @abnegate in #20
Full Changelog: 0.4.0...0.5.0
docs(readme): sync with code, document AST layer and correct dialect claims by @abnegate in #15
Full Changelog: 0.3.3...0.4.0
fix(schema): emit LowCardinality(Nullable(T)) with LowCardinality outermost; the previous Nullable(LowCardinality(T)) nesting is rejected by ClickHous
meta.key) are not split into qualified references (#13)Full Changelog: 0.3.2...0.3.3
feat(clickhouse): INSERT FORMAT, DELETE forms+settings, MV, groupByTimeBucket, typed bindings by @lohanidamodar in #11
Full Changelog: 0.3.1...0.3.2
feat(clickhouse): support LowCardinality, FixedString, CODEC, and SAMPLE BY by @lohanidamodar in #8
Full Changelog: 0.3.0...0.3.1
Fluent Schema builder API by @abnegate in #7
Full Changelog: 0.2.0...0.3.0
feat: add Query::fingerprint() for shape-only query hashing by @lohanidamodar in #3
Full Changelog: 0.1.1...0.2.0
Nothing published for this version
Update ignore by @abnegate in #1
Your coding agent can read these notes before it upgrades. Set up the MCP server →