ConditionParser
Builds a Condition Expression that can be used to condition write operations, or filter the results of a Query or a Scan:
import { ConditionParser } from 'dynamodb-toolbox/entity/actions/parseCondition'
// 👇 To be used in DynamoDB commands
const {
ConditionExpression,
ExpressionAttributeNames,
ExpressionAttributeValues
} = PokemonEntity.build(ConditionParser)
.parse({
// Pokemons with levels ≥ 50
attr: 'level',
gte: 50
})
.toCommandOptions()
Methods
parse(...)
(condition: Condition<ENTITY>) => ConditionParser
Parses a condition. Throws an invalidCondition
error if the condition is invalid:
PokemonEntity.build(ConditionParser).parse({
attr: 'level',
gte: 50
})
Note that the parse
method should only be used once per instance (for now). See Building Conditions for more details on how to write conditions.
toCommandOptions()
() => CommandOptions
Collapses the ConditionParser
state to a set of options that can be used in a DynamoDB command:
const {
ConditionExpression,
ExpressionAttributeNames,
ExpressionAttributeValues
} = PokemonEntity.build(ConditionParser)
.parse({ attr: 'level', gte: 50 })
.toCommandOptions()
setId(...)
(id: string) => ConditionParser
Adds a prefix to expression attribute keys. Useful to avoid conflicts when using several expressions in a single command:
PokemonEntity.build(ConditionParser)
.parse({ attr: 'level', gte: 50 })
.toCommandOptions()
// => {
// ConditionExpression: '#c_1 >= :c_1',
// ExpressionAttributeNames: { '#c_1': 'sk' },
// ExpressionAttributeValues: { ':c_1': 50 }
// }
PokemonEntity.build(ConditionParser)
.setId('0')
.parse({ attr: 'level', gte: 50 })
.toCommandOptions()
// => {
// ConditionExpression: '#c0_1 >= :c0_1',
// ExpressionAttributeNames: { '#c0_1': 'sk' },
// ExpressionAttributeValues: { ':c0_1': 50 }
// }
Building Conditions
The condition syntax from DynamoDB-Toolbox follows the DynamoDB specifications, while making it type-safe and much simpler:
import type { Condition } from 'dynamodb-toolbox/entity/actions/parseCondition'
const condition: Condition<typeof PokemonEntity> = {
attr: 'level',
gte: 50
}
Each condition contains an attribute path and an operator.
You can only specify one operator per condition. To combine multiple conditions, use Logical Combinations.
Paths
attr
contains the path of the attribute value to check (potentially deep). You can also specify size
instead of attr
if you want to check the size of an attribute (in which case the attribute type becomes number
):
- Root
- Deep (Map)
- Deep (List)
- Special characters
- Size
const nameCheck: Condition<typeof PokemonEntity> = {
attr: 'name',
eq: 'Pikachu'
}
const nameCheck: Condition<typeof PokemonEntity> = {
attr: 'name.firstName',
eq: 'Pikachu'
}
const nameCheck: Condition<typeof PokemonEntity> = {
attr: 'names[0]',
eq: 'Pikachu'
}
const nameCheck: Condition<typeof PokemonEntity> = {
attr: "name['.first#Name!']",
eq: 'Pikachu'
}
const imgLte64KB: Condition<typeof PokemonEntity> = {
size: 'image',
lte: 64_000
}
Value conditions
Value conditions evaluate against the value of an attribute:
Key | Value | Attribute Type | Description |
---|---|---|---|
eq | scalar | scalar * | Checks that the attribute is equal to the specified value |
ne | scalar | scalar | Checks that the attribute is different than the specified value |
in | scalar[] | scalar | Checks that the attribute is in a finite range of values (100 values max) |
contains | scalar | string , sets or lists | Checks that the attribute is one of the following:
|
exists | boolean | - | Checks that the attribute is present in the item (or not) |
type | string | - | Checks that the attribute is of a particular data type:
|
boolean
, number
, string
or binary
- Equal
- Not equal
- In
- Contains
- Contains (string)
- Exists
- Type
const nameCheck: Condition<typeof PokemonEntity> = {
attr: 'name',
eq: 'Pikachu'
}
const nameCheck: Condition<typeof PokemonEntity> = {
attr: 'name',
ne: 'Pikachu'
}
const nameCheck: Condition<typeof PokemonEntity> = {
attr: 'name',
in: ['Pikachu', 'Charizard', 'MewTwo']
}
const pokeTypeCheck: Condition<typeof PokemonEntity> = {
// 👇 `pokeTypes` = list/set of strings
attr: 'pokeTypes',
contains: 'fire'
}
const nameCheck: Condition<typeof PokemonEntity> = {
// 👇 string
attr: 'name',
contains: 'Pika'
}
const customNameCheck: Condition<typeof PokemonEntity> = {
attr: 'customName',
exists: true
}
const pokeTypeCheck: Condition<typeof PokemonEntity> = {
// 👇 Checks that `pokeTypes` is a list
attr: 'pokeTypes',
type: 'L'
}
Range conditions
Range conditions evaluate whether an attribute of sortable type (i.e. number, string or binary) is within a certain range.
Apart from the eq
value condition, only range conditions are accepted in Query
ranges.
Key | Value | Attribute Type | Description |
---|---|---|---|
gte | sortable | sortable | Checks that the attribute is greater than or equal to the specified value |
gt | sortable * | sortable | Checks that the attribute is strictly greater than the specified value |
lte | sortable | sortable | Checks that the attribute is lower than or equal to the specified value |
lt | sortable | sortable | Checks that the attribute is strictly lower than the specified value |
between | [sortable, sortable] | sortable | Checks that the attribute is between two values (inclusive) |
beginsWith | string | string | Checks that the string attribute specified begins with a particular substring |
number
, string
or binary
- ≥
- >
- ≤
- <
- Between
- Begins with
const levelGte50: Condition<typeof PokemonEntity> = {
attr: 'level',
gte: 50
}
const levelAbove50: Condition<typeof PokemonEntity> = {
attr: 'level',
gt: 50
}
const levelLte50: Condition<typeof PokemonEntity> = {
attr: 'level',
lte: 50
}
const levelBelow50: Condition<typeof PokemonEntity> = {
attr: 'level',
lt: 50
}
const levelFrom50To70: Condition<typeof PokemonEntity> = {
attr: 'level',
between: [50, 70]
}
const capturedIn2024: Condition<typeof PokemonEntity> = {
attr: 'captureDate',
beginsWith: '2024'
}
Again, only one operator can be applied per condition: Using gte
and lte
simultaneously does not result in a between
.
Combining Conditions
You can combine conditions logically with the or
, and
and not
operators:
Name | Value | Attribute Type | Description |
---|---|---|---|
or | Condition<ENTITY>[] | - | Checks that one of the child conditions evaluate to true |
and | Condition<ENTITY>[] | - | Checks that all of the child conditions evaluate to true |
not | Condition<ENTITY> | - | Negates the evaluation of the condition |
- Or
- And
- Not
- Deep
const lvlGte50OrElec: Condition<typeof PokemonEntity> = {
or: [
{ attr: 'level', gte: 50 },
{ attr: 'pokeType', eq: 'electric' }
]
}
const lvlGte50AndElec: Condition<typeof PokemonEntity> = {
and: [
{ attr: 'level', gte: 50 },
{ attr: 'pokeType', eq: 'electric' }
]
}
const notElectric: Condition<typeof PokemonEntity> = {
not: {
attr: 'pokeType',
eq: 'electric'
}
}
const deepCondition: Condition<typeof PokemonEntity> = {
and: [
{
// Level ≥ 50 or ≤ 20...
or: [
{ attr: 'level', gte: 50 },
{ not: { attr: 'level', gt: 20 } }
]
},
// ...and pokeType not 'electric'
{ not: { attr: 'pokeType', eq: 'electric' } }
]
}
Comparing Attributes
Instead of directly providing values, you can compare attributes to other attributes by providing objects with an attr
key to the operators:
const atMaxLevel: Condition<typeof PokemonEntity> = {
attr: 'level',
eq: { attr: 'maxLevel' }
}
Note that the compared attribute path is type-checked and validated, but whether its type CAN be compared is not for the moment, so be extra-careful:
const invalidCondition: Condition<typeof PokemonEntity> = {
attr: 'level',
// ❌ Reaches DynamoDB and fail
gte: { attr: 'name' }
}