Query Structure

ObjectQuel is inspired by QUEL, the query language from UC Berkeley's Ingres project. Unlike SQL's table-centric approach, ObjectQuel works directly with your domain entities using object-oriented syntax, and supports wildcards, regex, and full-text search alongside standard comparison operators.

explanation

The Shape of a Statement

Every range-based statement — retrieve, append, replace, delete, and upsert (append ... or replace ...) — shares the same skeleton, written in this order. DDL statements (create/alter/destroy/ index) are the exception: they target a table by name directly and don't declare ranges at all — see Creating, Altering & Destroying Tables & Indexes.

Query configuration diagram

Clauses at a Glance

  • Flags (optional, verb-dependent) — Written before all other clauses, flags adjust query execution behavior. For example, @InValuesAreFinal tells the optimizer that every value in an IN list will match exactly one row.
  • Range definitions (optional) — Declare which entities the statement operates on and assign each one a short alias. Every alias used afterward — in the verb clause, where, or sort by — must be declared here first. See Range Definitions.
  • Verb clause (required) — What the statement does: retrieve (...) reads data (see Retrieving Data); append/replace/delete write it (see Inserting Data, Updating Data, and Deleting Data).
  • Where (optional on retrieve, mandatory on replace/delete) — Filters or targets rows using comparisons, set membership, null checks, and function calls. See Filtering & Parameters.
  • Sort by (optional, retrieve only) — Orders results. Multiple fields are evaluated left-to-right as tie-breakers. See Sorting & Pagination.
  • Window (optional, retrieve only) — Paginates results by page number and page size. Always pair with sort by to keep page boundaries stable.

Query Optimization Flags

Flags are optional and their availability depends on the verb. @InValuesAreFinal applies to retrieve; @ignoreSoftDelete also applies to delete.

@InValuesAreFinal

By default, ObjectQuel scans all rows even when using an IN clause, because it cannot know whether a column is unique without being told. The @InValuesAreFinal flag asserts that each value in the IN list matches exactly one row, allowing the query planner to stop as soon as all values have been found:

$results = $entityManager->executeQuery("
    @InValuesAreFinal
    range of p is App\\Entity\\ProductEntity
    retrieve (p) where p.productId in (:ids)
", ['ids' => [1, 2, 3, 4, 5]]);
Only use @InValuesAreFinal when the IN clause targets a primary key or unique column. Applying it to a non-unique column will cause the query to return incomplete results.

@ignoreSoftDelete

By default, ObjectQuel automatically excludes soft-deleted entities from query results. When an entity property is annotated with @SoftDelete, ObjectQuel treats it as an implicit filter — rows where that column indicates deletion are never returned unless explicitly requested.

The @ignoreSoftDelete flag disables this filter for the duration of the query, returning all rows including soft-deleted ones:

$results = $entityManager->executeQuery("
    @ignoreSoftDelete
    range of p is App\\Entity\\ProductEntity
    retrieve (p) where p.productId in (1, 2, 3, 4, 5)
");
Common use cases include administrative interfaces that need to display or restore deleted records, and data audits that must account for all rows regardless of their deletion state.