Composable expression objects for Amazon DynamoDB
npm install @nova-odm/expressions
This library provides a number of abstractions designed to make dealing with
Amazon DynamoDB expressions easier and more natural for JavaScript developers.
The AttributePath class provides a simple way to write DynamoDB document
paths.
If the constructor receives a string, it will parse the path by scanning for
dots (.), which designate map property dereferencing and left brackets ([),
which designate list attribute dereferencing. For example,'ProductReviews.FiveStar[0].reviewer.username' would be understood as
referring to the username property of the reviewer property of the first
element of the list stored at the FiveStar property of the top-levelProductReviews document attribute.
If a property name contains a left bracket or dot, it may be escaped with a
backslash \. For example, Product\.Reviews would be interpreted as a single
top-level document attribute rather than as a map property access.
This library will marshall values encountered using runtime type detection. If
you have a value that is already in the format expected by DynamoDB, you may
pass it to the AttributeValue constructor to direct other expression helpers
not to marshall the value further.
DynamoDB condition expressions may come in the form of a function call or as the
combination of values and infix operators. This library therefore defines aConditionExpression as the union of FunctionExpression
and a tagged union of the expression operator types. Expressions may be compound
or simple.
These expressions envelope one or more simple expressions and are true or false
based on the value of the subexpressions they contain. The recognized compound
expressions are:
#### And expressions
Asserts that all of the subexpressions' conditions are satisfied.
``typescript
import {ConditionExpression} from '@nova-odm/expressions';
const andExpression: ConditionExpression = {
type: 'And',
conditions: [
// one or more subexpressions
]
};
`
#### Or expressions
Asserts that at least one of the subexpressions' conditions are satisfied.
`typescript
import {ConditionExpression} from '@nova-odm/expressions';
const orExpression: ConditionExpression = {
type: 'Or',
conditions: [
// one or more subexpressions
]
};
`
#### Not expressions
Asserts that the subexpression's condition is not satisfied.
`typescript
import {ConditionExpression} from '@nova-odm/expressions';
const notExpression: ConditionExpression = {
type: 'Not',
condition: {
type: 'LessThan',
subject: 'foo',
object: 100
}
};
`
These expressions make an assertion about a property in a DynamoDB object known
as the expression's subject. The subject must be a string or an attribute
path.
The particular assertion used is referred to in this library as a
ConditionExpressionPredicate. A predicate may be declared separately from itssubject but only becomes a valid expression when paired with a subject. The
supported condition expression predicates are:
#### Equals expression predicate
Creates a condition which is true if the defined subject is equal to theobject
defined . For example, the following predicate object asserts that the'bar'
subject has a value of :
`typescript
import {
ConditionExpression,
ConditionExpressionPredicate,
} from '@nova-odm/expressions';
let equalsExpressionPredicate: ConditionExpressionPredicate = {
type: 'Equals',
object: 'bar'
};
// you can also define an equality predicate with the equals helper method
import {equals} from '@nova-odm/expressions';
equalsExpressionPredicate = equals('bar');
// combine with a subject to create a valid condition expression
const equalsExpression: ConditionExpression = {
...equalsExpressionPredicate,
subject: 'foo'
};
`
object may be an attribute path, an attribute@nova-odm/auto-marshaller
value, or another type. If the lattermost type is received,
it will be serialized using the package.
#### NotEquals expression predicate
Creates a condition which is true if the defined subject is NOT equal to theobject
defined . For example, the following predicate object asserts that the'bar'
subject does not have a value of :
`typescript
import {
ConditionExpression,
ConditionExpressionPredicate,
} from '@nova-odm/expressions';
let equalsExpressionPredicate: ConditionExpressionPredicate = {
type: 'NotEquals',
object: 'bar'
};
// you can also define an equality predicate with the equals helper method
import {notEquals} from '@nova-odm/expressions';
equalsExpressionPredicate = notEquals('bar');
// combine with a subject to create a valid condition expression
const equalsExpression: ConditionExpression = {
...equalsExpressionPredicate,
subject: 'foo'
};
`
object may be an attribute path, an attribute@nova-odm/auto-marshaller
value, or another type. If the lattermost type is received,
it will be serialized using the package.
#### LessThan expression predicate
Creates a condition which is true if the defined subject is less than theobject
defined . For example, the following predicate object asserts that the
subject is less than 10:
`typescript
import {
ConditionExpression,
ConditionExpressionPredicate,
} from '@nova-odm/expressions';
let equalsExpressionPredicate: ConditionExpressionPredicate = {
type: 'LessThan',
object: 10
};
// you can also define an equality predicate with the equals helper method
import {lessThan} from '@nova-odm/expressions';
equalsExpressionPredicate = lessThan(10);
// combine with a subject to create a valid condition expression
const equalsExpression: ConditionExpression = {
...equalsExpressionPredicate,
subject: 'foo'
};
`
object may be an attribute path, an attribute@nova-odm/auto-marshaller
value, or another type. If the lattermost type is received,
it will be serialized using the package.
#### LessThanOrEqualTo expression predicate
Creates a condition which is true if the defined subject is less than or equalobject
to the defined . For example, the following predicate object asserts
that the subject is less than or equal to 10:
`typescript
import {
ConditionExpression,
ConditionExpressionPredicate,
} from '@nova-odm/expressions';
let equalsExpressionPredicate: ConditionExpressionPredicate = {
type: 'LessThanOrEqualTo',
object: 10
};
// you can also define an equality predicate with the equals helper method
import {lessThanOrEqualTo} from '@nova-odm/expressions';
equalsExpressionPredicate = lessThanOrEqualTo(10);
// combine with a subject to create a valid condition expression
const equalsExpression: ConditionExpression = {
...equalsExpressionPredicate,
subject: 'foo'
};
`
object may be an attribute path, an attribute@nova-odm/auto-marshaller
value, or another type. If the lattermost type is received,
it will be serialized using the package.
#### GreaterThan expression predicate
Creates a condition which is true if the defined subject is greater than theobject
defined . For example, the following predicate object asserts that the
subject is greater than 10:
`typescript
import {
ConditionExpression,
ConditionExpressionPredicate,
} from '@nova-odm/expressions';
let equalsExpressionPredicate: ConditionExpressionPredicate = {
type: 'GreaterThan',
object: 10
};
// you can also define an equality predicate with the equals helper method
import {greaterThan} from '@nova-odm/expressions';
equalsExpressionPredicate = greaterThan(10);
// combine with a subject to create a valid condition expression
const equalsExpression: ConditionExpression = {
...equalsExpressionPredicate,
subject: 'foo'
};
`
object may be an attribute path, an attribute@nova-odm/auto-marshaller
value, or another type. If the lattermost type is received,
it will be serialized using the package.
#### GreaterThanOrEqualTo expression predicate
Creates a condition which is true if the defined subject is greater than orobject
equal to the defined . For example, the following predicate object
asserts that the subject is greater than or equal to 10:
`typescript
import {
ConditionExpression,
ConditionExpressionPredicate,
} from '@nova-odm/expressions';
let equalsExpressionPredicate: ConditionExpressionPredicate = {
type: 'GreaterThanOrEqualTo',
object: 10
};
// you can also define an equality predicate with the equals helper method
import {greaterThanOrEqualTo} from '@nova-odm/expressions';
equalsExpressionPredicate = greaterThanOrEqualTo(10);
// combine with a subject to create a valid condition expression
const equalsExpression: ConditionExpression = {
...equalsExpressionPredicate,
subject: 'foo'
};
`
object may be an attribute path, an attribute@nova-odm/auto-marshaller
value, or another type. If the lattermost type is received,
it will be serialized using the package.
#### Between expression predicate
Creates a condition which is true if the defined subject is between a definedlowerBound and upperBound. For example, the following predicate object
asserts that the subject is greater than or equal to 10 and less than or equal
to 99:
`typescript
import {
ConditionExpression,
ConditionExpressionPredicate,
} from '@nova-odm/expressions';
let equalsExpressionPredicate: ConditionExpressionPredicate = {
type: 'Between',
lowerBound: 10,
upperBound: 99
};
// you can also define an equality predicate with the equals helper method
import {between} from '@nova-odm/expressions';
equalsExpressionPredicate = between(10, 99);
// combine with a subject to create a valid condition expression
const equalsExpression: ConditionExpression = {
...equalsExpressionPredicate,
subject: 'foo'
};
`
lowerBound and upperBound may both be an attribute path,@nova-odm/auto-marshaller
an attribute value, or another type. If the lattermost type
is received, it will be serialized using the
package.
#### Membership expression predicate
Creates a condition which is true if the defined subject is equal to a member'fizz'
of a list of defined values. For example, the following predicate object asserts
that the subject is one of , 'buzz', or 'fizzbuzz':
`typescript
import {
ConditionExpression,
ConditionExpressionPredicate,
} from '@nova-odm/expressions';
let equalsExpressionPredicate: ConditionExpressionPredicate = {
type: 'Membership',
values: ['fizz', 'buzz', 'fizzbuzz']
};
// you can also define an equality predicate with the equals helper method
import {inList} from '@nova-odm/expressions';
equalsExpressionPredicate = inList('fizz', 'buzz', 'fizzbuzz');
// combine with a subject to create a valid condition expression
const equalsExpression: ConditionExpression = {
...equalsExpressionPredicate,
subject: 'foo'
};
`
Each value in the values array may be an attribute path,@nova-odm/auto-marshaller
an attribute value, or another type. If the lattermost type
is received, it will be serialized using the
package.
To serialize a condition expression, pass a ConditionExpression object and anExpressionAttributes
instance of .
Amazon DynamoDB expressions are serialized as strings with semantically
important control characters and reserved words. The ExpressionAttributesExpressionAttributeNames
object will escape both attribute names and attribute values for safe use in
any expression. When a full DynamoDB request input is ready to be sent, you can
retrieve a the and ExpressionAttributeValues shapes
to send alongside the input:
`typescript
import {
AttributePath,
AttributeValue,
ExpressionAttributes,
} from '@nova-odm/expressions';
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
const attributes = new ExpressionAttributes();
// you can add a string attribute name
const escapedFoo = attributes.addName('foo');
// or a complex path
const escapedPath = attributes.addName('bar.baz[3].snap.crackle.pop');
// or an already parsed attribute path
attributes.addName(new AttributePath('path.to.nested.field'));
// raw JavaScript values added will be converted to AttributeValue objects
const escapedRaw = attributes.addValue(42);
// already marshalled values must be wrapped in an AttributeValue object
const escapedMarshalled = attributes.addValue(new AttributeValue({N: "42"}));
const client = new DynamoDBClient({ region: 'eu-west-1' });
client.query({
TableName: 'my_table',
KeyConditionExpression: ${escapedFoo} = ${escapedRaw} AND ${escapedPath} = ${escapedMarshalled},`
ExpressionAttributeNames: attributes.names,
ExpressionAttributeValues: attributes.values,
})
Function expressions represent named functions that DynamoDB will execute on
your behalf. The first parameter passed to the FunctionExpression representsAttributePath
the function name and must be a string; all subsequent parameters represent
arguments to pass to the function. These parameters may be instances of (to have the function evaluate part of the DynamoDB document toAttributeValue
which the function applies), (for already-marshalled@nova-odm/marshaller
AttributeValue objects), or arbitrary JavaScript values (these will be converted
by the package's Marshaller):
`typescript
import {
AttributePath,
ExpressionAttributes,
FunctionExpression,
} from '@nova-odm/expressions';
const expr = new FunctionExpression(
'list_append',
new AttributePath('path.to.list'),
'foo'
);
const attributes = new ExpressionAttributes();
// serializes as 'list_append(#attr0.#attr1.#attr2, :val3)'
const serialized = expr.serialize(attributes);
console.log(attributes.names); // {'#attr0': 'path', '#attr1': 'to', '#attr2': 'list'}
console.log(attributes.values); // {':val3': {S: 'foo'}}
`
Mathematical expressions are used in the SET clause of update expressions to
add or subtract numbers from attribute properties containing number values:
`typescript
import {MathematicalExpression} from '@nova-odm/expressions';
const expr = new MathematicalExpression('version', '+', 1);
`
Projection expressions tell DynamoDB which attributes to include in fetched
records returned by GetItem, Query, or Scan operations. This library usesProjectionExpression as a type alias for an array of strings andAttributePath objects.
Update expressions allow the partial, in place update of a record in DynamoDB.
The expression may have up to four clauses, one containing directives to set
values in the record, one containing directives to remove attributes from the
record, one containing directives to add values to a set, and the last
containing directives to delete values from a set.
`typescript
import {
AttributePath,
FunctionExpression,
UpdateExpression,
} from '@nova-odm/expressions';
const expr = new UpdateExpression();
// set a value by providing its key and the desired value
expr.set('foo', 'bar');
// you may also set properties in nested maps and lists
expr.set(
'path.to.my.desired[2].property',
new FunctionExpression(
'list_append',
new AttributePath('path.to.my.desired[2].property'),
'baz'
)
);
// remove a value by providing its key or path
expr.remove('fizz.buzz.pop[0]');
// add a value to a set
expr.add('string_set', 'foo');
// delete a value from the same set
expr.delete('string_set', 'bar');
``