dynamodb-toolbox icon indicating copy to clipboard operation
dynamodb-toolbox copied to clipboard

Confusion about Table-wide batchWrite

Open martinval opened this issue 3 years ago • 3 comments

Thanks in advance for a clarification on how best to delete a set of linked records. I can't work out how it's done from the docs.

I have Post, Categories and PostCat Entities to store many-to-many relationship data between Posts and Categories. A Post has pk: POST#postname / sk: POST#postname When a category is added to a post, a PostCat record is created with pk: POST#postname and sk: CAT#catname. PostCat has a GSI with pk/sk reversed so I can query a Post with it's categories and a Category with its associated Posts.

When I delete a post, I want to delete all related PostCat records as well. I can get all the records I want with the following query:

var allLinkedRecords = await MyTable.query('POST#' + postname )

This throws an error (MyTable.deleteBatch is not a function):

const result = await MyTable.batchWrite( 
            [ 
            MyTable.deleteBatch({ allLinkedRecords.Items }) 
            ]  
)

The example in the docs looks something like this:

const result = await Default.batchWrite( 
            [ 
            MyTable.Post.deleteBatch({ slug: 'postname' }),
            MyTable.PostCat.deleteBatch({ slug: 'postname' })
            ]  
)

So here's what I don't understand:

  1. The docs have "Default.batchWrite". Where does the Default come from, does it have to be imported like "Table or Entity"

  2. How to get an Items array from a query into BatchWrite (docs seem to show specific record rather than variables)

  3. If records from different entities are in a query array, how can they be devided up into batchWrite

  4. If I don't know how many records there are, the docs say there is a next() method on the result. Does this mean I need to simply do:

const result = ....
result.next()

just in case there might be more than 25 results ?

Thanks in advance for any clarifications and if batchWrite is the way to go for this use case. Is the alternative to do this at the entity level by looping through an Items array to delete ?

martinval avatar Apr 09 '21 10:04 martinval

Hi @martinval. deleteBatch is an Entity level method, not a Table level method, so your MyTable.deleteBatch call will throw an error because you're calling on the Table. In the example, Default is the table, which is why Post and PostCat (the Entities) are specified as part of the chained call to deleteBatch. If you've exposed a variable for your Entity, you can just use that (e.g. MyEntityName.deleteBatch()), otherwise, the [Table].[Entity].deleteBatch() syntax is there for your convenience.

That hopefully answers 1 and 3 above. In terms of 2, that should just be a matter of transforming your array into the correct format (and the [Table].[Entity].deleteBatch() syntax should help you with any dynamic variables).

The .next() method is a convenience method on the query() operation that will auto paginate for you. You could run a while loop that checks for its existence and keep call it until all records have been returned.

jeremydaly avatar Apr 16 '21 13:04 jeremydaly

Thanks for the response @jeremydaly. I'll try some batchWrite cases to see how I get along.

with regards to the next() function, I'm still unclear how it works. Seeing actual code on how it's supposed to be used would help. If I understand correctly it would go something like this if you wanted all the records (this is just a broad guess..):

var postArray = []
let posts = await MyTable.Post.query('POSTS#en', {beginsWith: 'POST'},)
postArray.push(posts.Items)
while( posts.Count < posts.ScannedCount ) { 
let morePosts = posts.next()
postArray.push(morePosts.Items)
}

martinval avatar Apr 16 '21 14:04 martinval

@martinval

I could not seem to get .next to work and it was because I disabled it by setting parse to false.

vespertilian avatar Apr 27 '21 08:04 vespertilian

Hey @martinval, thanks for opening this issue, you're awesome 😎

here's an example of using the next function for anyone who's looking at this thread:

export const getUsersForOrganization = async (organizationId: string, options?: GetUsersForOrganizationOptions): Promise<EntityItem<typeof UserEntity>[]> => {
  let response = await User.query(`ORGANIZATION#${organizationId}`, {
    beginsWith: 'USER#',
    ...(options || {}),
  });

  let users = response.Items || []

  while (response.next) {
    response = await response.next();
	users = [...users, ...(response.Items || [])]
  }

  return users;
};

naorpeled avatar Nov 30 '22 21:11 naorpeled