Update

Example Setup

Table Definition
{
  "TableName": "electro",
  "KeySchema": [
    {
      "AttributeName": "pk",
      "KeyType": "HASH"
    },
    {
      "AttributeName": "sk",
      "KeyType": "RANGE"
    }
  ],
  "AttributeDefinitions": [
    {
      "AttributeName": "pk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "sk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "gsi1pk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "gsi1sk",
      "AttributeType": "S"
    }
  ],
  "GlobalSecondaryIndexes": [
    {
      "IndexName": "gsi1pk-gsi1sk-index",
      "KeySchema": [
        {
          "AttributeName": "gsi1pk",
          "KeyType": "HASH"
        },
        {
          "AttributeName": "gsi1sk",
          "KeyType": "RANGE"
        }
      ],
      "Projection": {
        "ProjectionType": "ALL"
      }
    },
    {
      "IndexName": "gsi2pk-gsi2sk-index",
      "KeySchema": [
        {
          "AttributeName": "gsi2pk",
          "KeyType": "HASH"
        },
        {
          "AttributeName": "gsi2sk",
          "KeyType": "RANGE"
        }
      ],
      "Projection": {
        "ProjectionType": "ALL"
      }
    }
  ],
  "BillingMode": "PAY_PER_REQUEST"
}
Example Entity
import DynamoDB from "aws-sdk/clients/dynamodb";
import { Entity } from "electrodb";

const client = new DynamoDB.DocumentClient();

const table = "electro";

const StoreLocations = new Entity(
  {
    model: {
      service: "MallStoreDirectory",
      entity: "MallStore",
      version: "1",
    },
    attributes: {
      cityId: {
        type: "string",
        required: true,
      },
      mallId: {
        type: "string",
        required: true,
      },
      storeId: {
        type: "string",
        required: true,
      },
      buildingId: {
        type: "string",
        required: true,
      },
      unitId: {
        type: "string",
        required: true,
      },
      category: {
        type: [
          "spite store",
          "food/coffee",
          "food/meal",
          "clothing",
          "electronics",
          "department",
          "misc",
        ],
        required: true,
      },
      leaseEndDate: {
        type: "string",
        required: true,
      },
      rent: {
        type: "string",
        required: true,
        validate: /^(\d+\.\d{2})$/,
      },
      discount: {
        type: "string",
        required: false,
        default: "0.00",
        validate: /^(\d+\.\d{2})$/,
      },
      tenants: {
        type: "set",
        items: "string",
      },
      warnings: {
        type: "number",
        default: 0,
      },
      deposit: {
        type: "number",
      },
      contact: {
        type: "set",
        items: "string",
      },
      rentalAgreement: {
        type: "list",
        items: {
          type: "map",
          properties: {
            type: {
              type: "string",
            },
            detail: {
              type: "string",
            },
          },
        },
      },
      petFee: {
        type: "number",
      },
      fees: {
        type: "number",
      },
      tags: {
        type: "set",
        items: "string",
      },
    },
    indexes: {
      stores: {
        pk: {
          field: "pk",
          composite: ["cityId", "mallId"],
        },
        sk: {
          field: "sk",
          composite: ["buildingId", "storeId"],
        },
      },
      units: {
        index: "gsi1pk-gsi1sk-index",
        pk: {
          field: "gsi1pk",
          composite: ["mallId"],
        },
        sk: {
          field: "gsi1sk",
          composite: ["buildingId", "unitId"],
        },
      },
      leases: {
        index: "gsi2pk-gsi2sk-index",
        pk: {
          field: "gsi2pk",
          composite: ["storeId"],
        },
        sk: {
          field: "gsi2sk",
          composite: ["leaseEndDate"],
        },
      },
    },
  },
  { table, client },
);
(Example code on this page that references the entity StoreLocations uses the following Entity and Table Definition found below)

NOTE: Use caution when using the update method. DynamoDB will create non-existent items when using update which can result in partial items to be written to your table. When possible, use the patch or upsert methods. If these methods cannot be used it is strongly recommended to use conditions when using update to prevent accidental item creation

The update() method in ElectroDB allows you to update individual properties of an item. This method utilizes the DynamoDB operation update under the hood.

The DynamoDB method update() will create an item if one does not exist. Because updates have reduced attribute validations when compared to put, the practical ramifications of this is that an update can create a record without all the attributes you’d expect from a newly created item. Depending on your project’s unique needs, the methods patch or upsert may be a better fit.

Unlike the patch method, update has a return type of Partial<EntityItem>.

Example Tasks Entity
import { Entity } from "electrodb";

const table = "your_table_name";

const tasks = new Entity({
model: {
entity: "tasks",
version: "1",
service: "taskapp"
},
attributes: {
team: {
type: "string",
required: true
},
task: {
type: "string",
required: true
},
project: {
type: "string",
required: true
},
user: {
type: "string",
required: true
},
title: {
type: "string",
required: true,
},
description: {
type: "string"
},
status: {
// use an array to type an enum
type: ["open", "in-progress", "on-hold", "closed"] as const,
default: "open"
},
points: {
type: "number",
},
tags: {
type: "set",
items: "string"
},
comments: {
type: "list",
items: {
type: "map",
properties: {
user: {
type: "string"
},
body: {
type: "string"
}
}
}
},
closed: {
type: "string",
validate: /[0-9]{4}-[0-9]{2}-[0-9]{2}/,
},
createdAt: {
type: "number",
default: () => Date.now(),
// cannot be modified after created
readOnly: true
},
updatedAt: {
type: "number",
// watch for changes to any attribute
watch: "\*",
// set current timestamp when updated
set: () => Date.now(),
readOnly: true
}
},
indexes: {
projects: {
pk: {
field: "pk",
composite: ["team"]
},
sk: {
field: "sk",
// create composite keys for partial sort key queries
composite: ["project", "task"]
}
},
assigned: {
// collections allow for queries across multiple entities
collection: "assignments",
index: "gsi1pk-gsi1sk-index",
pk: {
// map to your GSI Hash/Partition key
field: "gsi1pk",
composite: ["user"]
},
sk: {
// map to your GSI Range/Sort key
field: "gsi1sk",
composite: ["status"]
}
},
backlog: {
// map to the GSI name on your DynamoDB table
index: "gsi2pk-gsi2sk-index",
pk: {
field: "gsi2pk",
composite: ["project"]
},
sk: {
field: "gsi2sk",
composite: ["team", "closed"],
}
}
}
}, { table });
tasks
  .update({
    team: "core",
    task: "45-662",
    project: "backend",
  })
  .set({ status: "open" })
  .add({ points: 5 })
  .append({
    comments: [
      {
        user: "janet",
        body: "This seems half-baked.",
      },
    ],
  })
  .where(({ status }, { eq }) => eq(status, "in-progress"))
  .go();

Default Response Format

Note: Use the Execution Option response to impact the response type

{
  data: EntityIdentifiers<typeof tasks>;
}

Equivalent Parameters

{
  "UpdateExpression": "SET #status = :status_u0, #points = #points + :points_u0, #comments = list_append(#comments, :comments_u0), #updatedAt = :updatedAt_u0, #gsi1sk = :gsi1sk_u0",
  "ExpressionAttributeNames": {
    "#status": "status",
    "#points": "points",
    "#comments": "comments",
    "#updatedAt": "updatedAt",
    "#gsi1sk": "gsi1sk"
  },
  "ExpressionAttributeValues": {
    ":status0": "in-progress",
    ":status_u0": "open",
    ":points_u0": 5,
    ":comments_u0": [
      {
        "user": "janet",
        "body": "This seems half-baked."
      }
    ],
    ":updatedAt_u0": 1630977029015,
    ":gsi1sk_u0": "$assignments#tasks_1#status_open"
  },
  "TableName": "your_table_name",
  "Key": {
    "pk": "$taskapp#team_core",
    "sk": "$tasks_1#project_backend#task_45-662"
  },
  "ConditionExpression": "attribute_exists(pk) AND attribute_exists(sk) AND #status = :status0"
}

Try it out!

Operations

Update Methods are available after the update() or patch() method is called, and allow you to perform alter an item stored dynamodb. Each Update Method corresponds to a DynamoDB UpdateExpression clause.

ElectroDB will validate an attribute’s type when performing an operation (e.g. that the subtract() method can only be performed on numbers), but will defer checking the logical validity your update operation to the DocumentClient. For example, If your query performs multiple mutations on a single attribute, or perform other illogical operations given nature of an item/attribute, ElectroDB will not validate these edge cases and instead will simply pass back any error(s) thrown by the Document Client.

Update/Patch MethodAttribute TypesParameter
setnumber string boolean enum map list set anyobject
removenumber string boolean enum map list set anyarray
addnumber any setobject
subtractnumberobject
appendany listobject
deleteany setobject
data*callback

The methods above can be used (and reused) in a chain to form update parameters, when finished with .params() or .go() terminal. If your application requires the update method to return values related to the update (e.g. via the ReturnValues DocumentClient parameters), you can use the Execution Option {response: "none" | "all_old" | "updated_old" | "all_new" | "updated_new"} with the value that matches your need. By default, the Update operation returns an empty object when using .go().

Set

The set() method will accept all attributes defined on the model. Provide a value to apply or replace onto the item.

Example

Example Setup

Table Definition
{
  "TableName": "electro",
  "KeySchema": [
    {
      "AttributeName": "pk",
      "KeyType": "HASH"
    },
    {
      "AttributeName": "sk",
      "KeyType": "RANGE"
    }
  ],
  "AttributeDefinitions": [
    {
      "AttributeName": "pk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "sk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "gsi1pk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "gsi1sk",
      "AttributeType": "S"
    }
  ],
  "GlobalSecondaryIndexes": [
    {
      "IndexName": "gsi1pk-gsi1sk-index",
      "KeySchema": [
        {
          "AttributeName": "gsi1pk",
          "KeyType": "HASH"
        },
        {
          "AttributeName": "gsi1sk",
          "KeyType": "RANGE"
        }
      ],
      "Projection": {
        "ProjectionType": "ALL"
      }
    },
    {
      "IndexName": "gsi2pk-gsi2sk-index",
      "KeySchema": [
        {
          "AttributeName": "gsi2pk",
          "KeyType": "HASH"
        },
        {
          "AttributeName": "gsi2sk",
          "KeyType": "RANGE"
        }
      ],
      "Projection": {
        "ProjectionType": "ALL"
      }
    }
  ],
  "BillingMode": "PAY_PER_REQUEST"
}
Example Entity
import DynamoDB from "aws-sdk/clients/dynamodb";
import { Entity } from "electrodb";

const client = new DynamoDB.DocumentClient();

const table = "electro";

const StoreLocations = new Entity(
  {
    model: {
      service: "MallStoreDirectory",
      entity: "MallStore",
      version: "1",
    },
    attributes: {
      cityId: {
        type: "string",
        required: true,
      },
      mallId: {
        type: "string",
        required: true,
      },
      storeId: {
        type: "string",
        required: true,
      },
      buildingId: {
        type: "string",
        required: true,
      },
      unitId: {
        type: "string",
        required: true,
      },
      category: {
        type: [
          "spite store",
          "food/coffee",
          "food/meal",
          "clothing",
          "electronics",
          "department",
          "misc",
        ],
        required: true,
      },
      leaseEndDate: {
        type: "string",
        required: true,
      },
      rent: {
        type: "string",
        required: true,
        validate: /^(\d+\.\d{2})$/,
      },
      discount: {
        type: "string",
        required: false,
        default: "0.00",
        validate: /^(\d+\.\d{2})$/,
      },
      tenants: {
        type: "set",
        items: "string",
      },
      warnings: {
        type: "number",
        default: 0,
      },
      deposit: {
        type: "number",
      },
      contact: {
        type: "set",
        items: "string",
      },
      rentalAgreement: {
        type: "list",
        items: {
          type: "map",
          properties: {
            type: {
              type: "string",
            },
            detail: {
              type: "string",
            },
          },
        },
      },
      petFee: {
        type: "number",
      },
      fees: {
        type: "number",
      },
      tags: {
        type: "set",
        items: "string",
      },
    },
    indexes: {
      stores: {
        pk: {
          field: "pk",
          composite: ["cityId", "mallId"],
        },
        sk: {
          field: "sk",
          composite: ["buildingId", "storeId"],
        },
      },
      units: {
        index: "gsi1pk-gsi1sk-index",
        pk: {
          field: "gsi1pk",
          composite: ["mallId"],
        },
        sk: {
          field: "gsi1sk",
          composite: ["buildingId", "unitId"],
        },
      },
      leases: {
        index: "gsi2pk-gsi2sk-index",
        pk: {
          field: "gsi2pk",
          composite: ["storeId"],
        },
        sk: {
          field: "gsi2sk",
          composite: ["leaseEndDate"],
        },
      },
    },
  },
  { table, client },
);
(Example code on this page that references the entity StoreLocations uses the following Entity and Table Definition found below)
await StoreLocations.update({ cityId, mallId, storeId, buildingId })
  .set({ category: "food/meal" })
  .where((attr, op) => op.eq(attr.category, "food/coffee"))
  .go();

Default Response Format

Note: Use the Execution Option response to impact the response type

{
  data: EntityIdentifiers<typeof StoreLocations>;
}

Equivalent Parameters

{
  "UpdateExpression": "SET #category = :category",
  "ExpressionAttributeNames": {
    "#category": "category"
  },
  "ExpressionAttributeValues": {
    ":category_w1": "food/coffee",
    ":category": "food/meal"
  },
  "TableName": "StoreDirectory",
  "Key": {
    "pk": "$mallstoredirectory#cityid_atlanta1#mallid_eastpointe",
    "sk": "$mallstore_1#buildingid_f34#storeid_lattelarrys"
  },
  "ConditionExpression": "#category = :category_w1"
}

Remove

The remove() method will accept all attributes defined on the model. Unlike most other update methods, the remove() method accepts an array with the names of the attributes that should be removed.

that the attribute property required functions as a sort of NOT NULL flag. Because of this, if a property exists as required:true it will not be possible to remove that property in particular. If the attribute is a property is on “map”, and the “map” is not required, then the “map” can be removed.

Example

Example Setup

Table Definition
{
  "TableName": "electro",
  "KeySchema": [
    {
      "AttributeName": "pk",
      "KeyType": "HASH"
    },
    {
      "AttributeName": "sk",
      "KeyType": "RANGE"
    }
  ],
  "AttributeDefinitions": [
    {
      "AttributeName": "pk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "sk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "gsi1pk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "gsi1sk",
      "AttributeType": "S"
    }
  ],
  "GlobalSecondaryIndexes": [
    {
      "IndexName": "gsi1pk-gsi1sk-index",
      "KeySchema": [
        {
          "AttributeName": "gsi1pk",
          "KeyType": "HASH"
        },
        {
          "AttributeName": "gsi1sk",
          "KeyType": "RANGE"
        }
      ],
      "Projection": {
        "ProjectionType": "ALL"
      }
    },
    {
      "IndexName": "gsi2pk-gsi2sk-index",
      "KeySchema": [
        {
          "AttributeName": "gsi2pk",
          "KeyType": "HASH"
        },
        {
          "AttributeName": "gsi2sk",
          "KeyType": "RANGE"
        }
      ],
      "Projection": {
        "ProjectionType": "ALL"
      }
    }
  ],
  "BillingMode": "PAY_PER_REQUEST"
}
Example Entity
import DynamoDB from "aws-sdk/clients/dynamodb";
import { Entity } from "electrodb";

const client = new DynamoDB.DocumentClient();

const table = "electro";

const StoreLocations = new Entity(
  {
    model: {
      service: "MallStoreDirectory",
      entity: "MallStore",
      version: "1",
    },
    attributes: {
      cityId: {
        type: "string",
        required: true,
      },
      mallId: {
        type: "string",
        required: true,
      },
      storeId: {
        type: "string",
        required: true,
      },
      buildingId: {
        type: "string",
        required: true,
      },
      unitId: {
        type: "string",
        required: true,
      },
      category: {
        type: [
          "spite store",
          "food/coffee",
          "food/meal",
          "clothing",
          "electronics",
          "department",
          "misc",
        ],
        required: true,
      },
      leaseEndDate: {
        type: "string",
        required: true,
      },
      rent: {
        type: "string",
        required: true,
        validate: /^(\d+\.\d{2})$/,
      },
      discount: {
        type: "string",
        required: false,
        default: "0.00",
        validate: /^(\d+\.\d{2})$/,
      },
      tenants: {
        type: "set",
        items: "string",
      },
      warnings: {
        type: "number",
        default: 0,
      },
      deposit: {
        type: "number",
      },
      contact: {
        type: "set",
        items: "string",
      },
      rentalAgreement: {
        type: "list",
        items: {
          type: "map",
          properties: {
            type: {
              type: "string",
            },
            detail: {
              type: "string",
            },
          },
        },
      },
      petFee: {
        type: "number",
      },
      fees: {
        type: "number",
      },
      tags: {
        type: "set",
        items: "string",
      },
    },
    indexes: {
      stores: {
        pk: {
          field: "pk",
          composite: ["cityId", "mallId"],
        },
        sk: {
          field: "sk",
          composite: ["buildingId", "storeId"],
        },
      },
      units: {
        index: "gsi1pk-gsi1sk-index",
        pk: {
          field: "gsi1pk",
          composite: ["mallId"],
        },
        sk: {
          field: "gsi1sk",
          composite: ["buildingId", "unitId"],
        },
      },
      leases: {
        index: "gsi2pk-gsi2sk-index",
        pk: {
          field: "gsi2pk",
          composite: ["storeId"],
        },
        sk: {
          field: "gsi2sk",
          composite: ["leaseEndDate"],
        },
      },
    },
  },
  { table, client },
);
(Example code on this page that references the entity StoreLocations uses the following Entity and Table Definition found below)
await StoreLocations.update({ cityId, mallId, storeId, buildingId })
  .remove(["discount"])
  .where((attr, op) => op.eq(attr.category, "food/coffee"))
  .go();

Default Response Format

Note: Use the Execution Option response to impact the response type

{
  data: EntityIdentifiers<typeof StoreLocations>;
}

Equivalent Parameters

{
  "UpdateExpression": "SET #cityId = :cityId_u0, #mallId = :mallId_u0, #buildingId = :buildingId_u0, #storeId = :storeId_u0, #__edb_e__ = :__edb_e___u0, #__edb_v__ = :__edb_v___u0 REMOVE #discount",
  "ExpressionAttributeNames": {
    "#category": "category",
    "#discount": "discount",
    "#cityId": "cityId",
    "#mallId": "mallId",
    "#buildingId": "buildingId",
    "#storeId": "storeId",
    "#__edb_e__": "__edb_e__",
    "#__edb_v__": "__edb_v__"
  },
  "ExpressionAttributeValues": {
    ":category0": "food/coffee",
    ":cityId_u0": "Portland",
    ":mallId_u0": "EastPointe",
    ":buildingId_u0": "A34",
    ":storeId_u0": "LatteLarrys",
    ":__edb_e___u0": "MallStore",
    ":__edb_v___u0": "1"
  },
  "TableName": "electro",
  "Key": {
    "pk": "$mallstoredirectory#cityid_portland#mallid_eastpointe",
    "sk": "$mallstore_1#buildingid_a34#storeid_lattelarrys"
  },
  "ConditionExpression": "#category = :category0"
}

Add

The add() method will accept attributes with type number, set, and any defined on the model. In the case of a number attribute, provide a number to add to the existing attribute’s value on the item.

If the attribute is defined as any, the syntax compatible with the attribute type set will be used. For this reason, do not use the attribute type any to represent a number.

Example

Example Setup

Table Definition
{
  "TableName": "electro",
  "KeySchema": [
    {
      "AttributeName": "pk",
      "KeyType": "HASH"
    },
    {
      "AttributeName": "sk",
      "KeyType": "RANGE"
    }
  ],
  "AttributeDefinitions": [
    {
      "AttributeName": "pk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "sk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "gsi1pk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "gsi1sk",
      "AttributeType": "S"
    }
  ],
  "GlobalSecondaryIndexes": [
    {
      "IndexName": "gsi1pk-gsi1sk-index",
      "KeySchema": [
        {
          "AttributeName": "gsi1pk",
          "KeyType": "HASH"
        },
        {
          "AttributeName": "gsi1sk",
          "KeyType": "RANGE"
        }
      ],
      "Projection": {
        "ProjectionType": "ALL"
      }
    },
    {
      "IndexName": "gsi2pk-gsi2sk-index",
      "KeySchema": [
        {
          "AttributeName": "gsi2pk",
          "KeyType": "HASH"
        },
        {
          "AttributeName": "gsi2sk",
          "KeyType": "RANGE"
        }
      ],
      "Projection": {
        "ProjectionType": "ALL"
      }
    }
  ],
  "BillingMode": "PAY_PER_REQUEST"
}
Example Entity
import DynamoDB from "aws-sdk/clients/dynamodb";
import { Entity } from "electrodb";

const client = new DynamoDB.DocumentClient();

const table = "electro";

const StoreLocations = new Entity(
  {
    model: {
      service: "MallStoreDirectory",
      entity: "MallStore",
      version: "1",
    },
    attributes: {
      cityId: {
        type: "string",
        required: true,
      },
      mallId: {
        type: "string",
        required: true,
      },
      storeId: {
        type: "string",
        required: true,
      },
      buildingId: {
        type: "string",
        required: true,
      },
      unitId: {
        type: "string",
        required: true,
      },
      category: {
        type: [
          "spite store",
          "food/coffee",
          "food/meal",
          "clothing",
          "electronics",
          "department",
          "misc",
        ],
        required: true,
      },
      leaseEndDate: {
        type: "string",
        required: true,
      },
      rent: {
        type: "string",
        required: true,
        validate: /^(\d+\.\d{2})$/,
      },
      discount: {
        type: "string",
        required: false,
        default: "0.00",
        validate: /^(\d+\.\d{2})$/,
      },
      tenants: {
        type: "set",
        items: "string",
      },
      warnings: {
        type: "number",
        default: 0,
      },
      deposit: {
        type: "number",
      },
      contact: {
        type: "set",
        items: "string",
      },
      rentalAgreement: {
        type: "list",
        items: {
          type: "map",
          properties: {
            type: {
              type: "string",
            },
            detail: {
              type: "string",
            },
          },
        },
      },
      petFee: {
        type: "number",
      },
      fees: {
        type: "number",
      },
      tags: {
        type: "set",
        items: "string",
      },
    },
    indexes: {
      stores: {
        pk: {
          field: "pk",
          composite: ["cityId", "mallId"],
        },
        sk: {
          field: "sk",
          composite: ["buildingId", "storeId"],
        },
      },
      units: {
        index: "gsi1pk-gsi1sk-index",
        pk: {
          field: "gsi1pk",
          composite: ["mallId"],
        },
        sk: {
          field: "gsi1sk",
          composite: ["buildingId", "unitId"],
        },
      },
      leases: {
        index: "gsi2pk-gsi2sk-index",
        pk: {
          field: "gsi2pk",
          composite: ["storeId"],
        },
        sk: {
          field: "gsi2sk",
          composite: ["leaseEndDate"],
        },
      },
    },
  },
  { table, client },
);
(Example code on this page that references the entity StoreLocations uses the following Entity and Table Definition found below)
await StoreLocations.update({ cityId, mallId, storeId, buildingId })
  .add({
    warnings: 1, // "number" attribute
    tenants: ["larry"], // "set" attribute
  })
  .where((attr, op) => op.eq(attr.category, "food/coffee"))
  .go();

Default Response Format

Note: Use the Execution Option response to impact the response type

{
  data: EntityIdentifiers<typeof StoreLocations>;
}

Equivalent Parameters

{
  "UpdateExpression": "SET #cityId = :cityId_u0, #mallId = :mallId_u0, #buildingId = :buildingId_u0, #storeId = :storeId_u0, #__edb_e__ = :__edb_e___u0, #__edb_v__ = :__edb_v___u0 ADD #warnings :warnings_u0, #tenants :tenants_u0",
  "ExpressionAttributeNames": {
    "#category": "category",
    "#warnings": "warnings",
    "#tenants": "tenants",
    "#cityId": "cityId",
    "#mallId": "mallId",
    "#buildingId": "buildingId",
    "#storeId": "storeId",
    "#__edb_e__": "__edb_e__",
    "#__edb_v__": "__edb_v__"
  },
  "ExpressionAttributeValues": {
    ":category0": "food/coffee",
    ":warnings_u0": 1,
    ":tenants_u0": ["larry"],
    ":cityId_u0": "Portland",
    ":mallId_u0": "EastPointe",
    ":buildingId_u0": "A34",
    ":storeId_u0": "LatteLarrys",
    ":__edb_e___u0": "MallStore",
    ":__edb_v___u0": "1"
  },
  "TableName": "electro",
  "Key": {
    "pk": "$mallstoredirectory#cityid_portland#mallid_eastpointe",
    "sk": "$mallstore_1#buildingid_a34#storeid_lattelarrys"
  },
  "ConditionExpression": "#category = :category0"
}

Subtract

The subtract() method will accept attributes with type number. In the case of a number attribute, provide a number to subtract from the existing attribute’s value on the item.

Example

Example Setup

Table Definition
{
  "TableName": "electro",
  "KeySchema": [
    {
      "AttributeName": "pk",
      "KeyType": "HASH"
    },
    {
      "AttributeName": "sk",
      "KeyType": "RANGE"
    }
  ],
  "AttributeDefinitions": [
    {
      "AttributeName": "pk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "sk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "gsi1pk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "gsi1sk",
      "AttributeType": "S"
    }
  ],
  "GlobalSecondaryIndexes": [
    {
      "IndexName": "gsi1pk-gsi1sk-index",
      "KeySchema": [
        {
          "AttributeName": "gsi1pk",
          "KeyType": "HASH"
        },
        {
          "AttributeName": "gsi1sk",
          "KeyType": "RANGE"
        }
      ],
      "Projection": {
        "ProjectionType": "ALL"
      }
    },
    {
      "IndexName": "gsi2pk-gsi2sk-index",
      "KeySchema": [
        {
          "AttributeName": "gsi2pk",
          "KeyType": "HASH"
        },
        {
          "AttributeName": "gsi2sk",
          "KeyType": "RANGE"
        }
      ],
      "Projection": {
        "ProjectionType": "ALL"
      }
    }
  ],
  "BillingMode": "PAY_PER_REQUEST"
}
Example Entity
import DynamoDB from "aws-sdk/clients/dynamodb";
import { Entity } from "electrodb";

const client = new DynamoDB.DocumentClient();

const table = "electro";

const StoreLocations = new Entity(
  {
    model: {
      service: "MallStoreDirectory",
      entity: "MallStore",
      version: "1",
    },
    attributes: {
      cityId: {
        type: "string",
        required: true,
      },
      mallId: {
        type: "string",
        required: true,
      },
      storeId: {
        type: "string",
        required: true,
      },
      buildingId: {
        type: "string",
        required: true,
      },
      unitId: {
        type: "string",
        required: true,
      },
      category: {
        type: [
          "spite store",
          "food/coffee",
          "food/meal",
          "clothing",
          "electronics",
          "department",
          "misc",
        ],
        required: true,
      },
      leaseEndDate: {
        type: "string",
        required: true,
      },
      rent: {
        type: "string",
        required: true,
        validate: /^(\d+\.\d{2})$/,
      },
      discount: {
        type: "string",
        required: false,
        default: "0.00",
        validate: /^(\d+\.\d{2})$/,
      },
      tenants: {
        type: "set",
        items: "string",
      },
      warnings: {
        type: "number",
        default: 0,
      },
      deposit: {
        type: "number",
      },
      contact: {
        type: "set",
        items: "string",
      },
      rentalAgreement: {
        type: "list",
        items: {
          type: "map",
          properties: {
            type: {
              type: "string",
            },
            detail: {
              type: "string",
            },
          },
        },
      },
      petFee: {
        type: "number",
      },
      fees: {
        type: "number",
      },
      tags: {
        type: "set",
        items: "string",
      },
    },
    indexes: {
      stores: {
        pk: {
          field: "pk",
          composite: ["cityId", "mallId"],
        },
        sk: {
          field: "sk",
          composite: ["buildingId", "storeId"],
        },
      },
      units: {
        index: "gsi1pk-gsi1sk-index",
        pk: {
          field: "gsi1pk",
          composite: ["mallId"],
        },
        sk: {
          field: "gsi1sk",
          composite: ["buildingId", "unitId"],
        },
      },
      leases: {
        index: "gsi2pk-gsi2sk-index",
        pk: {
          field: "gsi2pk",
          composite: ["storeId"],
        },
        sk: {
          field: "gsi2sk",
          composite: ["leaseEndDate"],
        },
      },
    },
  },
  { table, client },
);
(Example code on this page that references the entity StoreLocations uses the following Entity and Table Definition found below)
await StoreLocations.update({ cityId, mallId, storeId, buildingId })
  .subtract({ deposit: 500 })
  .where((attr, op) => op.eq(attr.category, "food/coffee"))
  .go();

Default Response Format

Note: Use the Execution Option response to impact the response type

{
  data: EntityIdentifiers<typeof StoreLocations>;
}

Equivalent Parameters

{
  "UpdateExpression": "SET #deposit = #deposit - :deposit0",
  "ExpressionAttributeNames": {
    "#category": "category",
    "#deposit": "deposit"
  },
  "ExpressionAttributeValues": {
    ":category0": "food/coffee",
    ":deposit0": 500
  },
  "TableName": "StoreDirectory",
  "Key": {
    "pk": "$mallstoredirectory#cityid_atlanta#mallid_eastpointe",
    "sk": "$mallstore_1#buildingid_a34#storeid_lattelarrys"
  },
  "ConditionExpression": "#category = :category0"
}

Append

The append() method will accept attributes with type any. This is a convenience method for working with DynamoDB lists, and is notably different that set because it will add an element to an existing array, rather than overwrite the existing value.

Example

Example Setup

Table Definition
{
  "TableName": "electro",
  "KeySchema": [
    {
      "AttributeName": "pk",
      "KeyType": "HASH"
    },
    {
      "AttributeName": "sk",
      "KeyType": "RANGE"
    }
  ],
  "AttributeDefinitions": [
    {
      "AttributeName": "pk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "sk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "gsi1pk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "gsi1sk",
      "AttributeType": "S"
    }
  ],
  "GlobalSecondaryIndexes": [
    {
      "IndexName": "gsi1pk-gsi1sk-index",
      "KeySchema": [
        {
          "AttributeName": "gsi1pk",
          "KeyType": "HASH"
        },
        {
          "AttributeName": "gsi1sk",
          "KeyType": "RANGE"
        }
      ],
      "Projection": {
        "ProjectionType": "ALL"
      }
    },
    {
      "IndexName": "gsi2pk-gsi2sk-index",
      "KeySchema": [
        {
          "AttributeName": "gsi2pk",
          "KeyType": "HASH"
        },
        {
          "AttributeName": "gsi2sk",
          "KeyType": "RANGE"
        }
      ],
      "Projection": {
        "ProjectionType": "ALL"
      }
    }
  ],
  "BillingMode": "PAY_PER_REQUEST"
}
Example Entity
import DynamoDB from "aws-sdk/clients/dynamodb";
import { Entity } from "electrodb";

const client = new DynamoDB.DocumentClient();

const table = "electro";

const StoreLocations = new Entity(
  {
    model: {
      service: "MallStoreDirectory",
      entity: "MallStore",
      version: "1",
    },
    attributes: {
      cityId: {
        type: "string",
        required: true,
      },
      mallId: {
        type: "string",
        required: true,
      },
      storeId: {
        type: "string",
        required: true,
      },
      buildingId: {
        type: "string",
        required: true,
      },
      unitId: {
        type: "string",
        required: true,
      },
      category: {
        type: [
          "spite store",
          "food/coffee",
          "food/meal",
          "clothing",
          "electronics",
          "department",
          "misc",
        ],
        required: true,
      },
      leaseEndDate: {
        type: "string",
        required: true,
      },
      rent: {
        type: "string",
        required: true,
        validate: /^(\d+\.\d{2})$/,
      },
      discount: {
        type: "string",
        required: false,
        default: "0.00",
        validate: /^(\d+\.\d{2})$/,
      },
      tenants: {
        type: "set",
        items: "string",
      },
      warnings: {
        type: "number",
        default: 0,
      },
      deposit: {
        type: "number",
      },
      contact: {
        type: "set",
        items: "string",
      },
      rentalAgreement: {
        type: "list",
        items: {
          type: "map",
          properties: {
            type: {
              type: "string",
            },
            detail: {
              type: "string",
            },
          },
        },
      },
      petFee: {
        type: "number",
      },
      fees: {
        type: "number",
      },
      tags: {
        type: "set",
        items: "string",
      },
    },
    indexes: {
      stores: {
        pk: {
          field: "pk",
          composite: ["cityId", "mallId"],
        },
        sk: {
          field: "sk",
          composite: ["buildingId", "storeId"],
        },
      },
      units: {
        index: "gsi1pk-gsi1sk-index",
        pk: {
          field: "gsi1pk",
          composite: ["mallId"],
        },
        sk: {
          field: "gsi1sk",
          composite: ["buildingId", "unitId"],
        },
      },
      leases: {
        index: "gsi2pk-gsi2sk-index",
        pk: {
          field: "gsi2pk",
          composite: ["storeId"],
        },
        sk: {
          field: "gsi2sk",
          composite: ["leaseEndDate"],
        },
      },
    },
  },
  { table, client },
);
(Example code on this page that references the entity StoreLocations uses the following Entity and Table Definition found below)
await StoreLocations.update({ cityId, mallId, storeId, buildingId })
  .append({
    rentalAgreement: [
      {
        type: "amendment",
        detail: "no soup for you",
      },
    ],
  })
  .where((attr, op) => op.eq(attr.category, "food/coffee"))
  .go();

Default Response Format

Note: Use the Execution Option response to impact the response type

{
  data: EntityIdentifiers<typeof StoreLocations>;
}

Equivalent Parameters

{
  "UpdateExpression": "SET #rentalAgreement = list_append(#rentalAgreement, :rentalAgreement0)",
  "ExpressionAttributeNames": {
    "#category": "category",
    "#rentalAgreement": "rentalAgreement"
  },
  "ExpressionAttributeValues": {
    ":category0": "food/coffee",
    ":rentalAgreement0": [
      {
        "type": "amendment",
        "detail": "no soup for you"
      }
    ]
  },
  "TableName": "StoreDirectory",
  "Key": {
    "pk": "$mallstoredirectory#cityid_atlanta#mallid_eastpointe",
    "sk": "$mallstore_1#buildingid_a34#storeid_lattelarrys"
  },
  "ConditionExpression": "#category = :category0"
}

Delete

The delete() method will accept attributes with type any or set . This operation removes items from a the contract attribute, defined as a set attribute.

Example

Example Setup

Table Definition
{
  "TableName": "electro",
  "KeySchema": [
    {
      "AttributeName": "pk",
      "KeyType": "HASH"
    },
    {
      "AttributeName": "sk",
      "KeyType": "RANGE"
    }
  ],
  "AttributeDefinitions": [
    {
      "AttributeName": "pk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "sk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "gsi1pk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "gsi1sk",
      "AttributeType": "S"
    }
  ],
  "GlobalSecondaryIndexes": [
    {
      "IndexName": "gsi1pk-gsi1sk-index",
      "KeySchema": [
        {
          "AttributeName": "gsi1pk",
          "KeyType": "HASH"
        },
        {
          "AttributeName": "gsi1sk",
          "KeyType": "RANGE"
        }
      ],
      "Projection": {
        "ProjectionType": "ALL"
      }
    },
    {
      "IndexName": "gsi2pk-gsi2sk-index",
      "KeySchema": [
        {
          "AttributeName": "gsi2pk",
          "KeyType": "HASH"
        },
        {
          "AttributeName": "gsi2sk",
          "KeyType": "RANGE"
        }
      ],
      "Projection": {
        "ProjectionType": "ALL"
      }
    }
  ],
  "BillingMode": "PAY_PER_REQUEST"
}
Example Entity
import DynamoDB from "aws-sdk/clients/dynamodb";
import { Entity } from "electrodb";

const client = new DynamoDB.DocumentClient();

const table = "electro";

const StoreLocations = new Entity(
  {
    model: {
      service: "MallStoreDirectory",
      entity: "MallStore",
      version: "1",
    },
    attributes: {
      cityId: {
        type: "string",
        required: true,
      },
      mallId: {
        type: "string",
        required: true,
      },
      storeId: {
        type: "string",
        required: true,
      },
      buildingId: {
        type: "string",
        required: true,
      },
      unitId: {
        type: "string",
        required: true,
      },
      category: {
        type: [
          "spite store",
          "food/coffee",
          "food/meal",
          "clothing",
          "electronics",
          "department",
          "misc",
        ],
        required: true,
      },
      leaseEndDate: {
        type: "string",
        required: true,
      },
      rent: {
        type: "string",
        required: true,
        validate: /^(\d+\.\d{2})$/,
      },
      discount: {
        type: "string",
        required: false,
        default: "0.00",
        validate: /^(\d+\.\d{2})$/,
      },
      tenants: {
        type: "set",
        items: "string",
      },
      warnings: {
        type: "number",
        default: 0,
      },
      deposit: {
        type: "number",
      },
      contact: {
        type: "set",
        items: "string",
      },
      rentalAgreement: {
        type: "list",
        items: {
          type: "map",
          properties: {
            type: {
              type: "string",
            },
            detail: {
              type: "string",
            },
          },
        },
      },
      petFee: {
        type: "number",
      },
      fees: {
        type: "number",
      },
      tags: {
        type: "set",
        items: "string",
      },
    },
    indexes: {
      stores: {
        pk: {
          field: "pk",
          composite: ["cityId", "mallId"],
        },
        sk: {
          field: "sk",
          composite: ["buildingId", "storeId"],
        },
      },
      units: {
        index: "gsi1pk-gsi1sk-index",
        pk: {
          field: "gsi1pk",
          composite: ["mallId"],
        },
        sk: {
          field: "gsi1sk",
          composite: ["buildingId", "unitId"],
        },
      },
      leases: {
        index: "gsi2pk-gsi2sk-index",
        pk: {
          field: "gsi2pk",
          composite: ["storeId"],
        },
        sk: {
          field: "gsi2sk",
          composite: ["leaseEndDate"],
        },
      },
    },
  },
  { table, client },
);
(Example code on this page that references the entity StoreLocations uses the following Entity and Table Definition found below)
await StoreLocations.update({ cityId, mallId, storeId, buildingId })
  .delete({ contact: ["555-345-2222"] })
  .where((attr, op) => op.eq(attr.category, "food/coffee"))
  .go();

Default Response Format

Note: Use the Execution Option response to impact the response type

{
  data: EntityIdentifiers<typeof StoreLocations>;
}

Equivalent Parameters

{
  "UpdateExpression": "DELETE #contact :contact0",
  "ExpressionAttributeNames": {
    "#category": "category",
    "#contact": "contact"
  },
  "ExpressionAttributeValues": {
    ":category0": "food/coffee",
    ":contact0": "555-345-2222"
  },
  "TableName": "StoreDirectory",
  "Key": {
    "pk": "$mallstoredirectory#cityid_atlanta#mallid_eastpointe",
    "sk": "$mallstore_1#buildingid_a34#storeid_lattelarrys"
  },
  "ConditionExpression": "#category = :category0"
}

Data

The data() allows for different approach to updating your item, by accepting a callback with a similar argument signature to the where clause.

The callback provided to the data method is injected with an attributes object as the first parameter, and an operations object as the second parameter. All operations accept an attribute from the attributes object as a first parameter, and optionally accept a second value parameter.

As mentioned above, this method is functionally similar to the where clause with one exception: The callback provided to data() is not expected to return a value. When you invoke an injected operation method, the side effects are applied directly to update expression you are building.

operationexampleresultdescription
setset(category, value)#category = :category0Add or overwrite existing value
addadd(tenant, name)#tenant :tenant1Add value to existing set attribute (used when provided attribute is of type any or set)
addadd(rent, amount)#rent = #rent + :rent0Mathematically add given number to existing number on record
subtractsubtract(deposit, amount)#deposit = #deposit - :deposit0Mathematically subtract given number from existing number on record
removeremove(petFee)#petFeeRemove attribute/property from item
appendappend(rentalAgreement, amendment)#rentalAgreement = list_append(#rentalAgreement, :rentalAgreement0)Add element to existing list attribute
deletedelete(tenant, name)#tenant :tenant1Remove item from existing set attribute
deldel(tenant, name)#tenant :tenant1Alias for delete operation
namename(rent)#rentReference another attribute’s name, can be passed to other operation that allows leveraging existing attribute values in calculating new values
valuevalue(rent, amount):rent1Create a reference to a particular value, can be passed to other operation that allows leveraging existing attribute values in calculating new values
ifNotExistsifNotExists(rent, amount)#rent = if_not_exists(#rent, :rent0)Update a property’s value only if that property doesn’t yet exist on the record

Usage of name and value operations allow for some escape hatching in the case that a custom operation needs to be expressed. When used however, ElectroDB loses the context necessary to validate the expression created by the user. In practical terms, this means the validation function/regex on the impacted attribute will not be called.

Example

Example Setup

Table Definition
{
  "TableName": "electro",
  "KeySchema": [
    {
      "AttributeName": "pk",
      "KeyType": "HASH"
    },
    {
      "AttributeName": "sk",
      "KeyType": "RANGE"
    }
  ],
  "AttributeDefinitions": [
    {
      "AttributeName": "pk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "sk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "gsi1pk",
      "AttributeType": "S"
    },
    {
      "AttributeName": "gsi1sk",
      "AttributeType": "S"
    }
  ],
  "GlobalSecondaryIndexes": [
    {
      "IndexName": "gsi1pk-gsi1sk-index",
      "KeySchema": [
        {
          "AttributeName": "gsi1pk",
          "KeyType": "HASH"
        },
        {
          "AttributeName": "gsi1sk",
          "KeyType": "RANGE"
        }
      ],
      "Projection": {
        "ProjectionType": "ALL"
      }
    },
    {
      "IndexName": "gsi2pk-gsi2sk-index",
      "KeySchema": [
        {
          "AttributeName": "gsi2pk",
          "KeyType": "HASH"
        },
        {
          "AttributeName": "gsi2sk",
          "KeyType": "RANGE"
        }
      ],
      "Projection": {
        "ProjectionType": "ALL"
      }
    }
  ],
  "BillingMode": "PAY_PER_REQUEST"
}
Example Entity
import DynamoDB from "aws-sdk/clients/dynamodb";
import { Entity } from "electrodb";

const client = new DynamoDB.DocumentClient();

const table = "electro";

const StoreLocations = new Entity(
  {
    model: {
      service: "MallStoreDirectory",
      entity: "MallStore",
      version: "1",
    },
    attributes: {
      cityId: {
        type: "string",
        required: true,
      },
      mallId: {
        type: "string",
        required: true,
      },
      storeId: {
        type: "string",
        required: true,
      },
      buildingId: {
        type: "string",
        required: true,
      },
      unitId: {
        type: "string",
        required: true,
      },
      category: {
        type: [
          "spite store",
          "food/coffee",
          "food/meal",
          "clothing",
          "electronics",
          "department",
          "misc",
        ],
        required: true,
      },
      leaseEndDate: {
        type: "string",
        required: true,
      },
      rent: {
        type: "string",
        required: true,
        validate: /^(\d+\.\d{2})$/,
      },
      discount: {
        type: "string",
        required: false,
        default: "0.00",
        validate: /^(\d+\.\d{2})$/,
      },
      tenants: {
        type: "set",
        items: "string",
      },
      warnings: {
        type: "number",
        default: 0,
      },
      deposit: {
        type: "number",
      },
      contact: {
        type: "set",
        items: "string",
      },
      rentalAgreement: {
        type: "list",
        items: {
          type: "map",
          properties: {
            type: {
              type: "string",
            },
            detail: {
              type: "string",
            },
          },
        },
      },
      petFee: {
        type: "number",
      },
      fees: {
        type: "number",
      },
      tags: {
        type: "set",
        items: "string",
      },
    },
    indexes: {
      stores: {
        pk: {
          field: "pk",
          composite: ["cityId", "mallId"],
        },
        sk: {
          field: "sk",
          composite: ["buildingId", "storeId"],
        },
      },
      units: {
        index: "gsi1pk-gsi1sk-index",
        pk: {
          field: "gsi1pk",
          composite: ["mallId"],
        },
        sk: {
          field: "gsi1sk",
          composite: ["buildingId", "unitId"],
        },
      },
      leases: {
        index: "gsi2pk-gsi2sk-index",
        pk: {
          field: "gsi2pk",
          composite: ["storeId"],
        },
        sk: {
          field: "gsi2sk",
          composite: ["leaseEndDate"],
        },
      },
    },
  },
  { table, client },
);
(Example code on this page that references the entity StoreLocations uses the following Entity and Table Definition found below)
await StoreLocations.update({ cityId, mallId, storeId, buildingId })
  .data((a, o) => {
    const newTenant = o.value(a.tenants, ["larry"]);
    o.set(a.category, "food/meal"); // electrodb "enum"   -> dynamodb "string"
    o.add(a.tenants, newTenant); // electrodb "set"    -> dynamodb "set"
    o.add(a.warnings, 1); // electrodb "number" -> dynamodb "number"
    o.subtract(a.deposit, 200); // electrodb "number" -> dynamodb "number"
    o.remove(a.discount); // electrodb "number" -> dynamodb "number"
    o.append(a.rentalAgreement, [
      {
        // electrodb "list"   -> dynamodb "list"
        type: "amendment", // electrodb "map"    -> dynamodb "map"
        detail: "no soup for you",
      },
    ]);
    o.delete(a.tags, ["coffee"]);
    o.del(a.contact, ["555-345-2222"]); // electrodb "set"    -> dynamodb "set"
    o.add(a.fees, o.name(a.petFee)); // electrodb "number" -> dynamodb "number"
  })
  .where((attr, op) => op.eq(attr.category, "food/coffee"))
  .go();

Default Response Format

Note: Use the Execution Option response to impact the response type

{
  data: EntityIdentifiers<typeof StoreLocations>;
}

Equivalent Parameters

{
  "UpdateExpression": "SET #category = :category_u0, #deposit = #deposit - :deposit_u0, #rentalAgreement = list_append(#rentalAgreement, :rentalAgreement_u0), #fees = #fees + #petFee, #cityId = :cityId_u0, #mallId = :mallId_u0, #buildingId = :buildingId_u0, #storeId = :storeId_u0, #__edb_e__ = :__edb_e___u0, #__edb_v__ = :__edb_v___u0 REMOVE #discount ADD #tenants :tenants_u0, #warnings :warnings_u0 DELETE #tags :tags_u0, #contact :contact_u0",
  "ExpressionAttributeNames": {
    "#category": "category",
    "#tenants": "tenants",
    "#warnings": "warnings",
    "#deposit": "deposit",
    "#discount": "discount",
    "#rentalAgreement": "rentalAgreement",
    "#tags": "tags",
    "#contact": "contact",
    "#petFee": "petFee",
    "#fees": "fees",
    "#cityId": "cityId",
    "#mallId": "mallId",
    "#buildingId": "buildingId",
    "#storeId": "storeId",
    "#__edb_e__": "__edb_e__",
    "#__edb_v__": "__edb_v__"
  },
  "ExpressionAttributeValues": {
    ":category0": "food/coffee",
    ":tenants_u0": ["larry"],
    ":category_u0": "food/meal",
    ":warnings_u0": 1,
    ":deposit_u0": 200,
    ":rentalAgreement_u0": [
      {
        "type": "amendment",
        "detail": "no soup for you"
      }
    ],
    ":tags_u0": ["coffee"],
    ":contact_u0": ["555-345-2222"],
    ":cityId_u0": "Portland",
    ":mallId_u0": "EastPointe",
    ":buildingId_u0": "A34",
    ":storeId_u0": "LatteLarrys",
    ":__edb_e___u0": "MallStore",
    ":__edb_v___u0": "1"
  },
  "TableName": "electro",
  "Key": {
    "pk": "$mallstoredirectory#cityid_portland#mallid_eastpointe",
    "sk": "$mallstore_1#buildingid_a34#storeid_lattelarrys"
  },
  "ConditionExpression": "#category = :category0"
}

Complex Data Types

ElectroDB supports updating DynamoDB’s complex types (list, map, set) with all of its Update Methods.

When using the chain methods set, add, subtract, remove, append, and delete, you can access map properties, list elements, and set items by supplying the json path of the property as the name of the attribute.

The data() method also allows for working with complex types. Unlike using the update chain methods, the data() method ensures type safety when using TypeScript. When using the injected attributes object, simply drill into the attribute itself to apply your update directly to the required object.

The following are examples on how update complex attributes, using both with chain methods and the data() method.

Example 1: Set property on a map attribute

Specifying a property on a map attribute is expressed with dot notation.

// via Chain Method
await StoreLocations.update({ cityId, mallId, storeId, buildingId })
  .set({ "mapAttribute.mapProperty": "value" })
  .go();

// via Data Method
await StoreLocations.update({ cityId, mallId, storeId, buildingId })
  .data(({ mapAttribute }, { set }) => set(mapAttribute.mapProperty, "value"))
  .go();

Example 2: Removing an element from a list attribute

Specifying an index on a list attribute is expressed with square brackets containing the element’s index number.

// via Chain Method
await StoreLocations.update({ cityId, mallId, storeId, buildingId })
  .remove(["listAttribute[0]"])
  .go();

// via Data Method
await StoreLocations.update({ cityId, mallId, storeId, buildingId })
  .data(({ listAttribute }, { remove }) => remove(listAttribute[0]))
  .go();

Example 3: Adding an item to a set attribute, on a map attribute, that is an element of a list attribute

All other complex structures are simply variations on the above two examples.

// Set values must use the DocumentClient to create a `set`
const newSetValue = StoreLocations.client.createSet("setItemValue");

// via Data Method
await StoreLocations.update({ cityId, mallId, storeId, buildingId })
  .add({ "listAttribute[1].setAttribute": newSetValue })
  .go();

await StoreLocations.update({ cityId, mallId, storeId, buildingId })
  .data(({ listAttribute }, { add }) => {
    add(listAttribute[1].setAttribute, newSetValue);
  })
  .go();

Composite

The composite chain method helps build keys during an update using attributes that are not being modified. For example, some cases where this is helpful are the following:

  • Migrating existing entities in place to use a new secondary index (best used with patch).
  • Updating attributes that appear in an index composite, when one ore more of the other composite attributes are readOnly.

When updating attributes that appear in an index composite, there is an opportunity for data consistency issues to appear. If a secondary index key is a composite of two or more attributes, it is critical those values stay in sync with their attribute values. ElectroDB prevents data inconsistencies between attributes and keys by adding a constraint to updates to prevent partial key updates, and requires you provide all attributes associated with an index key.

Read more about “Missing Composite Attribute” errors

One edge case arises when requiring all composite attributes for a key are provided: if one of your attributes is flagged as readOnly, how can you provide it to an update? This edge case is handled with the .composite() chain method. This method allows you to provide key composite members without providing those values to a mutation method like set. The value provided will only be used in key construction and a ConditionExpression to assert the value provided will automatically be added.

Example

Example Entity
import { Entity } from "electrodb";

const table = "electro";

const Organization = new Entity(
  {
    model: {
      entity: "organization",
      service: "app",
      version: "1",
    },
    attributes: {
      id: {
        type: "string",
      },
      name: {
        type: "string",
        required: true,
      },
      description: {
        type: "string",
      },
      deleted: {
        type: "boolean",
        required: true,
        default: false,
      },
      createdAt: {
        type: "string",
        readOnly: true,
        required: true,
        set: () => new Date().toISOString(),
        default: () => new Date().toISOString(),
      },
    },
    indexes: {
      record: {
        pk: {
          field: "pk",
          composite: ["id"],
        },
        sk: {
          field: "sk",
          composite: [],
        },
      },
      all: {
        index: "gsi1pk-gsi1sk-index",
        pk: {
          field: "gsi1pk",
          composite: [],
        },
        sk: {
          field: "gsi1sk",
          composite: ["deleted", "createdAt"], // SK has both readonly and mutable attributes
        },
      },
    },
  },
  { table },
);
const id = "00001";
const existing = await Organization.get({ id }).go();

if (existing.data?.deleted) {
  await Organization.update({ id: "00001" })
    .set({ deleted: false })
    .composite({ createdAt: existing.data.createdAt })
    .go();
}

Equivalent Parameters

{
  "UpdateExpression": "SET #deleted = :deleted_u0, #gsi1sk = :gsi1sk_u0, #id = :id_u0, #__edb_e__ = :__edb_e___u0, #__edb_v__ = :__edb_v___u0",
  "ExpressionAttributeNames": {
    "#createdAt": "createdAt",
    "#deleted": "deleted",
    "#gsi1sk": "gsi1sk",
    "#id": "id",
    "#__edb_e__": "__edb_e__",
    "#__edb_v__": "__edb_v__"
  },
  "ExpressionAttributeValues": {
    ":createdAt0": "2023-08-22T16:37:45.700Z",
    ":deleted_u0": false,
    ":gsi1sk_u0": "$organization_1#deleted_false#createdat_2023-08-22t16:37:45.700z",
    ":id_u0": "00001",
    ":__edb_e___u0": "organization",
    ":__edb_v___u0": "1"
  },
  "TableName": "electro",
  "Key": {
    "pk": "$app#id_00001",
    "sk": "$organization_1"
  },
  "ConditionExpression": "#createdAt = :createdAt0"
}

Updating Composite Attributes

ElectroDB adds some constraints to update calls to prevent the accidental loss of data. If an access pattern is defined with multiple composite attributes, then ElectroDB ensure the attributes cannot be updated individually. If an attribute involved in an index composite is updated, then the index key also must be updated, and if the whole key cannot be formed by the attributes supplied to the update, then it cannot create a composite key without overwriting the old data.

This example shows why a partial update to a composite key is prevented by ElectroDB:

{
  "index": "my-gsi",
  "pk": {
    "field": "gsi1pk",
    "composite": ["attr1"]
  },
  "sk": {
    "field": "gsi1sk",
    "composite": ["attr2", "attr3"]
  }
}

The above secondary index definition would generate the following index keys:

{
  "gsi1pk": "$service#attr1_value1",
  "gsi1sk": "$entity_version#attr2_value2#attr3_value6"
}

If a user attempts to update the attribute attr2, then ElectroDB has no way of knowing value of the attribute attr3 or if forming the composite key without it would overwrite its value. The same problem exists if a user were to update attr3, ElectroDB cannot update the key without knowing each composite attribute’s value.

In the event that a secondary index includes composite values from the table’s primary index, ElectroDB will draw from the values supplied for the update key to address index gaps in the secondary index. For example:

For the defined indexes:

{
  "accessPattern1": {
    "pk": {
      "field": "pk",
      "composite": ["attr1"]
    },
    "sk": {
      "field": "sk",
      "composite": ["attr2"]
    }
  },
  "accessPattern2": {
    "index": "my-gsi",
    "pk": {
      "field": "gsi1pk",
      "composite": ["attr3"]
    },
    "sk": {
      "field": "gsi1sk",
      "composite": ["attr2", "attr4"]
    }
  }
}

A user could update attr4 alone because ElectroDB is able to leverage the value for attr2 from values supplied to the update() method:

Example

entity
  .update({ attr1: "value1", attr2: "value2" })
  .set({ attr4: "value4" })
  .go();

Default Response Format

Note: Use the Execution Option response to impact the response type

{
  data: EntityIdentifiers<typeof StoreLocations>;
}

Equivalent Parameters

{
  "UpdateExpression": "SET #attr4 = :attr4_u0, #gsi1sk = :gsi1sk_u0, #attr1 = :attr1_u0, #attr2 = :attr2_u0",
  "ExpressionAttributeNames": {
    "#attr4": "attr4",
    "#gsi1sk": "gsi1sk",
    "#attr1": "attr1",
    "#attr2": "attr2"
  },
  "ExpressionAttributeValues": {
    ":attr4_u0": "value6",
    // This index was successfully built
    ":gsi1sk_u0": "$update-edgecases_1#attr2_value2#attr4_value6",
    ":attr1_u0": "value1",
    ":attr2_u0": "value2"
  },
  "TableName": "YOUR_TABLE_NAME",
  "Key": {
    "pk": "$service#attr1_value1",
    "sk": "$entity_version#attr2_value2"
  }
}

Included in the update are all attributes from the table’s primary index. These values are automatically included on all updates in the event an update results in an insert.

Execution Options

Execution options can be provided to the .params() and .go() terminal functions to change query behavior or add customer parameters to a query.

By default, ElectroDB enables you to work with records as the names and properties defined in the model. Additionally, it removes the need to deal directly with the docClient parameters which can be complex for a team without as much experience with DynamoDB. The Query Options object can be passed to both the .params() and .go() methods when building you query. Below are the options available:

{
  params?: object;
  table?: string;
  data?: 'raw' | 'includeKeys' | 'attributes';
  originalErr?: boolean;
  concurrent?: number;
  unprocessed?: "raw" | "item";
  response?: "default" | "none" | "all_old" | "updated_old" | "all_new" | "updated_new";
  ignoreOwnership?: boolean;
  logger?: (event) => void;
  listeners Array<(event) => void>;
  attributes?: string[];
}
OptionDefaultDescription
params{}Properties added to this object will be merged onto the params sent to the document client. Any conflicts with ElectroDB will favor the params specified here.
table(from constructor)Use a different table than the one defined when creating your Entity or Service.
attributes(all attributes)The attributes query option allows you to specify ProjectionExpression Attributes for your get or query operation. As of 1.11.0 only root attributes are allowed to be specified.
data"attributes"Accepts the values 'raw', 'includeKeys', 'attributes' or undefined. Use 'raw' to return query results as they were returned by the docClient. Use 'includeKeys' to include item partition and sort key values in your return object. By default, ElectroDB does not return partition, sort, or global keys in its response.
originalErrfalseBy default, ElectroDB alters the stacktrace of any exceptions thrown by the DynamoDB client to give better visibility to the developer. Set this value equal to true to turn off this functionality and return the error unchanged.
concurrent1When performing batch operations, how many requests (1 batch operation == 1 request) to DynamoDB should ElectroDB make at one time. Be mindful of your DynamoDB throughput configurations.
unprocessed"item"Used in batch processing to override ElectroDBs default behaviour to break apart DynamoDBs Unprocessed records into composite attributes. See more detail about this in the sections for BatchGet, BatchDelete, and BatchPut.
response"default"Used as a convenience for applying the DynamoDB parameter ReturnValues. The options here are the same as the parameter values for the DocumentClient except lowercase. The "none" option will cause the method to return null and will bypass ElectroDB’s response formatting — useful if formatting performance is a concern.
ignoreOwnershipfalseBy default, ElectroDB interrogates items returned from a query for the presence of matching entity “identifiers”. This helps to ensure other entities, or other versions of an entity, are filtered from your results. If you are using ElectroDB with an existing table/dataset you can turn off this feature by setting this property to true.
listeners[]An array of callbacks that are invoked when internal ElectroDB events occur.
loggernoneA convenience option for a single event listener that semantically can be used for logging.