Skip to content

GSI template-based keys not written/updated during upsert/patch/update operations #572

Description

@ryota8128

Description

When using template-based GSI keys (both PK and SK), upsert, patch, and update operations do not write the GSI key attributes (gsiN1pk, gsiN1sk) to the DynamoDB item. Only create and put (which use PutItem internally) correctly write these attributes.

Reproduction

Entity definition

export const ArticleLikeEntity = new Entity(
  {
    model: {
      entity: 'articleLike',
      version: '1',
      service: 'media',
    },
    attributes: {
      articleId: {
        type: 'string',
        required: true,
      },
      likeCount: {
        type: 'number',
        required: true,
        default: 0,
      },
    },
    indexes: {
      byArticleId: {
        pk: {
          field: 'pk',
          composite: ['articleId'],
        },
        sk: {
          field: 'sk',
          composite: [],
        },
      },
      likeRanking: {
        index: 'gsiN1',
        pk: {
          field: 'gsiN1pk',
          composite: [],
          template: 'ARTICLE_LIKE',
        },
        sk: {
          field: 'gsiN1sk',
          composite: ['likeCount'],
          template: '${likeCount}',
        },
      },
    },
  },
  { client: dynamoClient, table: tableName }
);

Steps to reproduce

1. Using upsert (GSI keys NOT written):

await ArticleLikeEntity.upsert({ articleId: 'test-article' })
  .add({ likeCount: 1 })
  .go();

Result: Item is created with likeCount: 1, but gsiN1pk and gsiN1sk are missing from the record.

2. Using create (GSI keys ARE written):

await ArticleLikeEntity.create({
  articleId: 'test-article',
  likeCount: 1,
}).go();

Result: Item is created with likeCount: 1, gsiN1pk: "article_like", and gsiN1sk: 1. GSI keys are correctly populated.

3. Using patch after create (GSI SK not updated):

// After create with likeCount: 1
await ArticleLikeEntity.patch({ articleId: 'test-article' })
  .add({ likeCount: 1 })
  .go();

Result: likeCount is incremented to 2, but gsiN1sk remains 1 (not updated to 2).

Expected behavior

  • upsert should write GSI template-based key attributes.
  • patch/update should re-compute and update GSI template-based SK when the underlying attribute changes.

Actual behavior

  • upsert does not include GSI template-based keys in the UpdateExpression.
  • patch/update updates the underlying attribute but does not re-compute the GSI SK from the template.

Root cause analysis

Looking at the source code:

  • create/put use PutItem, which includes updatedKeys (containing GSI attributes) directly in the Item — works correctly.
  • upsert/patch/update use UpdateItem with UpdateExpression. The GSI key re-computation from templates appears to be missing from the expression builder for these operations.

This seems related to previously fixed issues (#343, #366, #412) but specifically affects the template-based key pattern with Number-typed SK.

Environment

  • ElectroDB: v3.7.5
  • AWS SDK: v3
  • DynamoDB Local: latest
  • Node.js: v24.1.0

Workaround

Use put (PutItem) instead of patch/upsert for mutations that affect GSI template-based keys:

const existing = await ArticleLikeEntity.get({ articleId }).go();
const newCount = (existing.data?.likeCount ?? 0) + 1;
await ArticleLikeEntity.put({ articleId, likeCount: newCount }).go();

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions