Rules#
rules defines data quality checks and access policies. For example, this rule allows each supplier to access only its own orders:
rules:
- id: supplier_orders
op: admit
expr: "row.supplier_id == ctx.tenant"
An order with supplier_id: globex passes this rule when the caller’s organization is also globex. An order belonging to initech does not.
Parts of a rule#
Field |
Meaning in this example |
|---|---|
|
|
|
|
|
The condition compares the order’s supplier with the caller’s organization. |
|
Optional text shown with the rule in |
Other rule types have additional fields, such as on_fail for the action taken when a quality check fails.
Namespaces#
A namespace identifies where a value used in a rule comes from. Parcel has three: row, ctx, and dataset.
Namespace |
Contains |
Example |
|---|---|---|
|
Values from one record in the data. |
|
|
Details about the user, agent, or service requesting access. |
|
|
Information about the dataset as a whole. |
|
For an order whose supplier_id is globex, this condition compares that value with the organization requesting access:
row.supplier_id == ctx.tenant
It is true when ctx.tenant is also globex. A quality check such as row.amount > 0 uses only the order’s values. A check such as dataset.row_count > 0 uses the total number of records.
The application supplies ctx values. For more details on the available fields, see Namespaces.
Rule types#
op selects one of six rule types. Each type can use specific namespaces:
Rule |
Purpose |
Values it can read |
|---|---|---|
|
Define who is allowed access. |
|
|
Define which rows they may access. |
|
|
Check the quality of individual rows. |
|
|
Replace a column’s value. |
|
|
Check the dataset as a whole. |
|
|
Limit small groups, sample rows, or add noise to numeric values. |
|
Caller access: decide#
decide defines whether a user, agent, or service is allowed access. For example, access to orders can be limited to fulfilment and audit work:
rules:
- id: approved_purpose
op: decide
expr: "ctx.purpose == 'fulfilment' || ctx.purpose == 'audit'"
ctx.purpose is the purpose supplied by the application. || means “or”. This rule allows either listed purpose and refuses any other purpose.
Which records: admit#
admit defines which rows are allowed. To limit each supplier to its own orders:
rules:
- id: own_orders
op: admit
expr: "row.supplier_id == ctx.tenant"
row.supplier_id is the supplier recorded on an order. ctx.tenant is the caller’s organization identifier, supplied by the application. An order marked globex passes when the caller’s organization is also globex.
When there are several admit rules, a row must pass all of them.
Record quality: assert#
assert checks each row against a data quality requirement. This rule requires a positive amount:
rules:
- id: positive_amount
op: assert
expr: "row.amount > 0"
on_fail: drop
An amount of 120 passes. An amount of 0, -5, or a missing amount fails. on_fail is required and specifies the response:
|
Behavior |
|---|---|
|
Exclude failing rows. |
|
Any failing row makes the dataset invalid for use. |
|
Failures are recorded; the check does not exclude rows. |
For a required order identifier, the check can instead be expr: "has(row.order_id)". With on_fail: deny, a missing identifier makes the dataset invalid. These actions do not delete the original data.
Returned values: transform#
transform replaces a column’s value. For example, an owner may want to share email addresses with internal staff and mask them for other callers:
expose:
- {name: email, type: utf8}
rules:
- id: mask_email
op: transform
column: email
expr: "ctx.tier == 'internal' ? row.email : redact(row.email)"
This expression keeps the email when ctx.tier is internal; otherwise, it replaces it with ***. Missing emails remain null. column names the column being changed. Its replacement value must match the type declared in expose—text (utf8) here.
Dataset quality: guarantee#
guarantee checks a requirement for the whole dataset. This rule requires at least one row:
rules:
- id: nonempty
op: guarantee
expr: "dataset.row_count > 0"
on_fail: deny
dataset.row_count is the number of rows. An empty dataset fails this rule. on_fail: deny marks it as invalid for use; on_fail: annotate records the failure without marking it invalid.
A requirement can also use column statistics. For example, dataset.amount.null_rate < 0.01 requires fewer than 1% of amounts to be missing.
Result shape: shape#
Shape rules control how results are returned. This rule removes groups calculated from fewer than five rows, such as a sales total based on only three orders:
rules:
- id: suppress_small_groups
op: shape
operator: suppress
params: {k: 5}
unless: "ctx.tier == 'internal'"
With k: 5, a total calculated from three orders is excluded; one calculated from six orders is kept. unless skips this rule for callers whose ctx.tier is internal.
Sampling#
A sample rule selects a proportion of rows using a column as the selection key:
rules:
- id: sample_orders
op: shape
operator: sample
params: {fraction: 0.1, key: order_id}
fraction: 0.1 requests a 10% sample. Selection is repeatable for the same order_id values; it does not guarantee an exact row count. The key must be listed in expose.
Numeric noise#
A noise rule adds random variation to numeric results:
rules:
- id: noise_revenue
op: shape
operator: noise
column: revenue
params: {sensitivity: 100.0, epsilon: 1.0, budget: partner_reports}
column names the numeric column. sensitivity and epsilon control the amount of noise; budget names the privacy budget the application must track. The example applies noise to calculations over revenue, such as totals. at: row inside params applies it to individual values instead. For parameter limits, see Shapes.
Rule expressions#
Expressions use CEL, a language for conditions and calculations. && means “and”, || means “or”, and has(row.field) checks for a value that is not null. For more details, see Expressions.