Skip to main content

WQL Operators

WebdaQL supports comparison operators, logical operators, and special-form operators for set membership, pattern matching, and array inspection.

Comparison operators​

OperatorMeaningExample
=Equalstatus = 'published'
!=Not equalstatus != 'draft'
>Greater thanviewCount > 1000
>=Greater than or equalviewCount >= 100
<Less thanviewCount < 10
<=Less than or equalrating <= 5

All comparison operators work on strings, integers, and booleans.

String comparison​

username = 'alice'
username != 'admin'

Integer comparison​

viewCount >= 100
price < 50
rating > 3

Boolean comparison​

featured = TRUE
deleted = FALSE

Logical operators​

AND​

Both conditions must be true. Multiple ANDs at the same level are automatically flattened by the parser.

status = 'published' AND viewCount >= 100

OR​

At least one condition must be true.

status = 'draft' OR status = 'archived'

Grouping with parentheses​

Use parentheses to control evaluation order. AND binds more tightly than OR in WQL.

(status = 'draft' OR status = 'archived') AND author.active = TRUE

LIKE operator​

Pattern matching for strings. Two wildcard characters:

WildcardMeaning
_Exactly one character
%Zero or more characters
title LIKE "Intro%" -- starts with "Intro"
slug LIKE "%-tutorial" -- ends with "-tutorial"
username LIKE "a_ice" -- five chars, second position any char

In-memory evaluation (MemoryStore) converts the LIKE pattern to a JavaScript RegExp. Backend stores (MongoDB, PostgreSQL) use their native LIKE/regex support.

import * as WebdaQL from "@webda/ql";

const v = new WebdaQL.QueryValidator('title LIKE "Intro%"');
console.log(v.eval({ title: "Introduction to Webda" })); // true
console.log(v.eval({ title: "Advanced Webda" })); // false

IN operator​

Tests if a field's value is one of a set of values.

status IN ['published', 'archived']
status IN ['draft', 'published', 'archived']
id IN ['u-1', 'u-2', 'u-3']

The set can contain strings, integers, and booleans:

code IN [200, 201, 204]
flag IN [TRUE, FALSE]
const v = new WebdaQL.QueryValidator("status IN ['published', 'archived']");
console.log(v.eval({ status: "published" })); // true
console.log(v.eval({ status: "draft" })); // false

CONTAINS operator​

Tests if an array field contains a specific value. This is distinct from IN (which checks a scalar field against a list).

tags CONTAINS 'typescript'
categories CONTAINS 'webda'
const v = new WebdaQL.QueryValidator("tags CONTAINS 'typescript'");
console.log(v.eval({ tags: ["typescript", "webda"] })); // true
console.log(v.eval({ tags: ["javascript"] })); // false

In MongoDB, CONTAINS translates to the same syntax as = on an array field (MongoDB checks if the array contains the value).

IS NULL / IS NOT NULL operators​

IS NULL matches a field that is missing, undefined or null. IS NOT NULL matches any other value, including falsy ones such as 0, false or "".

deletedAt IS NULL
author.email IS NOT NULL
const v = new WebdaQL.QueryValidator("deletedAt IS NULL");
console.log(v.eval({})); // true
console.log(v.eval({ deletedAt: null })); // true
console.log(v.eval({ deletedAt: 0 })); // false

NULL is not a value: field = NULL is a syntax error, use field IS NULL.

ORDER BY clause​

Sort results by one or more fields. Default direction is ASC if omitted.

ORDER BY createdAt DESC
ORDER BY status ASC, title DESC
ORDER BY viewCount DESC, createdAt ASC
import * as WebdaQL from "@webda/ql";

const validator = new WebdaQL.QueryValidator(
"status = 'published' ORDER BY createdAt DESC, title ASC"
);
const query = validator.getQuery();
console.log(query.orderBy);
// [
// { field: "createdAt", direction: "DESC" },
// { field: "title", direction: "ASC" }
// ]

LIMIT clause​

Limit the maximum number of results returned.

status = 'published' LIMIT 20
const validator = new WebdaQL.QueryValidator("status = 'published' LIMIT 20");
const query = validator.getQuery();
console.log(query.limit); // 20

OFFSET clause​

Provide an opaque continuation token for keyset pagination. The token is a string (often base64-encoded) returned by the previous page of results.

status = 'published' ORDER BY createdAt DESC LIMIT 20 OFFSET "eyJsYXN0IjoiMjAyNC0wMS0wMSJ9"

The token is backend-specific. MongoDB uses a numeric offset; DynamoDB uses a LastEvaluatedKey encoded as a JSON string; the in-memory store uses a numeric index. You should treat the token as opaque and pass it back from the previous query's continuationToken response field.

Combining all clauses​

status = 'published' AND viewCount >= 100
ORDER BY createdAt DESC, title ASC
LIMIT 20
OFFSET "eyJsYXN0S2V5IjoiYWJjZCJ9"

Clauses must appear in this order: filter → ORDER BY → LIMIT → OFFSET.

Verify​

npx vitest run packages/ql/src/query.spec.ts

The spec file covers all operators including equality, LIKE, IN, CONTAINS, >=, <=, >, <, !=, AND, OR, and mixed expressions.

See also​