Collections

Collections allow you to query across Entities. They can be used on Service instance.

const DynamoDB = require("aws-sdk/clients/dynamodb");
const table = "projectmanagement";
const client = new DynamoDB.DocumentClient();

const employees = new Entity(
  {
    model: {
      entity: "employees",
      version: "1",
      service: "taskapp",
    },
    attributes: {
      employeeId: {
        type: "string",
      },
      organizationId: {
        type: "string",
      },
      name: {
        type: "string",
      },
      team: {
        type: ["jupiter", "mercury", "saturn"],
      },
    },
    indexes: {
      staff: {
        pk: {
          field: "pk",
          composite: ["organizationId"],
        },
        sk: {
          field: "sk",
          composite: ["employeeId"],
        },
      },
      employee: {
        collection: "assignments",
        index: "gsi2",
        pk: {
          field: "gsi2pk",
          composite: ["employeeId"],
        },
        sk: {
          field: "gsi2sk",
          composite: [],
        },
      },
    },
  },
  { client, table },
);

const tasks = new Entity(
  {
    model: {
      entity: "tasks",
      version: "1",
      service: "taskapp",
    },
    attributes: {
      taskId: {
        type: "string",
      },
      employeeId: {
        type: "string",
      },
      projectId: {
        type: "string",
      },
      title: {
        type: "string",
      },
      body: {
        type: "string",
      },
    },
    indexes: {
      project: {
        pk: {
          field: "pk",
          composite: ["projectId"],
        },
        sk: {
          field: "sk",
          composite: ["taskId"],
        },
      },
      assigned: {
        collection: "assignments",
        index: "gsi2",
        pk: {
          field: "gsi2pk",
          composite: ["employeeId"],
        },
        sk: {
          field: "gsi2sk",
          composite: [],
        },
      },
    },
  },
  { client, table },
);

const TaskApp = new Service({ employees, tasks });

Available on your Service are two objects: entites and collections. Entities available on entities have the same capabilities as they would if created individually. When a Model added to a Service with join however, its Collections are automatically added and validated with the other Models joined to that Service. These Collections are available on collections.

Example

const results = await TaskApp.collections
  .assignments({ employeeId: "JExotic" })
  .go();

Equivalent Parameters

{
  TableName: 'projectmanagement',
  ExpressionAttributeNames: { '#pk': 'gsi2pk', '#sk1': 'gsi2sk' },
  ExpressionAttributeValues: { ':pk': '$taskapp_1#employeeid_joeexotic', ':sk1': '$assignments' },
  KeyConditionExpression: '#pk = :pk and begins_with(#sk1, :sk1)',
  IndexName: 'gsi3'
}

Example

Collections do not have the same query functionality and as an Entity, though it does allow for inline filters like an Entity. The attributes available on the filter object include all attributes across entities.

const results - await TaskApp.collections
  .assignments({employee: "CBaskin"})
  .where(({ project }, { notExists, contains }) => `
    ${notExists(project)} OR ${contains(project, "murder")}
  `)
  .go();

Equivalent Parameters

{
  TableName: 'projectmanagement',
  ExpressionAttributeNames: { '#project': 'project', '#pk': 'gsi2pk', '#sk1': 'gsi2sk' },
  ExpressionAttributeValues: {
    ':project1': 'murder',
    ':pk': '$taskapp_1#employeeid_carolbaskin',
    ':sk1': '$assignments'
  },
  KeyConditionExpression: '#pk = :pk and begins_with(#sk1, :sk1)',
  IndexName: 'gsi2',
  FilterExpression: 'attribute_not_exists(#project) OR contains(#project, :project1)'
}

Execution Options

Query options can be added the .params() and .go() 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';
  pager?: 'raw' | 'cursor';
  originalErr?: boolean;
  response?: "default" | "none" | "all_old" | "updated_old" | "all_new" | "updated_new";
  ignoreOwnership?: boolean;
  limit?: number;
  count?: number;
  pages?: number | 'all';
  logger?: (event) => void;
  listeners Array<(event) => void>;
  attributes?: string[];
  order?: 'asc' | 'desc';
};

Query Execution Options

params

Default Value: {} 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. Consider this an escape hatch for options not yet supported by ElectroDB.

table

Default Value: (from constructor) Use a different table than the one defined in Service Options and/or Entity Options.

attributes

Default Value: (all attributes) The attributes query option allows you to specify ProjectionExpression Attributes for your get or query operation.

data

Default Value: "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.

originalErr

Default Value: false By 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.

response

Default Value: "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.

ignoreOwnership

Default Value: false By 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.

limit

Default Value: none Used a convenience wrapper for the DynamoDB parameter Limit [read more]. When limit is paired option pages:"all", ElectroDB will paginate DynamoDB until the limit is at least reached or all items for that query have been returned; this means you may receive more items than your specified limit but never less.

Note: If your use case requires returning a specific number of items at a time, the count option is the better option.

count

Default Value: none A target for the number of items to return from DynamoDB. If this option is passed, Queries on entities will paginate DynamoDB until the items found match the count is reached or all items for that query have been returned. It is important to understand that this option may result in ElectroDB making multiple calls to DynamoDB to satisfy the count. For this reason, you should also consider using the pages option to limit the number of requests (or “pages”) made to DynamoDB.

pages

Default Value: 1 How many DynamoDB pages should a query iterate through before stopping. To have ElectroDB automatically paginate through all results, pass the string value 'all'.

order

Default Value: ‘asc’ Convenience option for ScanIndexForward, to the change the order of queries based on your index’s Sort Key — valid options include ‘asc’ and ‘desc’. [read more]

listeners

Default Value: [] An array of callbacks that are invoked when internal ElectroDB events occur.

logger

Default Value: none A convenience option for a single event listener that semantically can be used for logging.