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

Can't remove many elements using "begins" condition

Open NevRA opened this issue 2 years ago • 3 comments

Hey

Trying to understand is it possible to use remove + many + begins_with syntax

Please check my test bellow. It looks like tunnel: { begins: { 'sk': 'user' } } works for the find but not for the remove operation

Is it correct?

Thanks!


/*
    debug.ts - Just for debug

    Edit your test case here and invoke via: "jest debug"

    Or run VS Code in the top level directory and just run.
 */
import {AWS, Client, Entity, Match, Model, Table, print, dump, delay} from './utils/init'
import { OneSchema } from '../src/index.js'

jest.setTimeout(7200 * 1000)

//  Change with your schema
const schema: OneSchema = {
    version: '0.0.1',
    indexes: {
        primary: { hash: 'pk', sort: 'sk' },
        gs1: { hash: 'gs1pk', sort: 'gs1sk', project: 'all' },
    },
    models: {
        User: {
            pk:          { type: String, value: '${_type}#' },
            sk:          { type: String, value: '${name}#${id}' },

            gs1pk:       { type: String, value: '${_type}#' },
            gs1sk:       { type: String, value: '${_type}#${id}' },

            name:        { type: String },
            email:       { type: String },
            id:          { type: String, generate: 'ulid' },
        }
    }
}

//  Change your table params as required
const table = new Table({
    name: 'DebugTable',
    client: Client,
    schema,
    logger: true,
})

//  This will create a local table
test('Create Table', async() => {
    if (!(await table.exists())) {
        await table.createTable()
        expect(await table.exists()).toBe(true)
    }
})

test('Test', async() => {
    const User = table.getModel('User')
    await User.create({ name: 'user1' })
    await User.create({ name: 'user2' })
    const created = await User.find({})
    expect(created.length).toBe(2)
    const found = await User.find({}, { tunnel: { begins: { 'sk': 'user' } } })
    expect(found.length).toBe(2)

    await User.remove({}, { many: true, tunnel: { begins: { 'sk': 'user' } } }) // Can't be removed

    const exist = await User.find({})
    expect(exist.length).toBe(0)
})

test('Destroy Table', async() => {
    await table.deleteTable('DeleteTableForever')
    expect(await table.exists()).toBe(false)
})

 FAIL  test/debug.ts
  ✓ Create Table (321 ms)
  ✕ Test (142 ms)
  ✓ Destroy Table (12 ms)

  ● Test

    expect(received).toBe(expected) // Object.is equality

    Expected: 0
    Received: 2

      61 |
      62 |     const exist = await User.find({})
    > 63 |     expect(exist.length).toBe(0)
         |                          ^
      64 | })
      65 |
      66 | test('Destroy Table', async() => {

      at Object.<anonymous> (test/debug.ts:63:26)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 2 passed, 3 total

NevRA avatar Jun 30 '22 12:06 NevRA

Turn logging on and we can see the actual request issued.

ie.

await User.remove({}, { log: true, many: true, tunnel: { begins: { 'sk': 'user' } } })

Remove with many operates when you don't provide an SK and internally, OneTable does a find. It will only do this if you do not provide an SK.

In your case, you implicitly provide the PK by providing the type for the PK value template. Similarly, you are providing the type which allows a partial SK. Find can use this.

So the following should do what you want I believe:

   await User.remove({}, { log: true, many: true })

mobsense avatar Jul 05 '22 07:07 mobsense

Hey!

Logs:

    info OneTable result for "delete" "User" {
        "cmd": {
            "TableName": "DebugTable",
            "ReturnValues": "ALL_OLD",
            "Key": {
                "pk": {
                    "S": "User#"
                },
                "sk": {
                    "S": "[object Object]"
                }
            }
        },
        "items": [],
        "op": "delete",
        "properties": {
            "pk": "User#",
            "sk": "[object Object]"
        },
        "params": {
            "parse": true,
            "exists": null,
            "high": true,
            "log": true,
            "many": true,
            "tunnel": {
                "begins": {
                    "sk": "user"
                }
            },
            "checked": true
        }
    }

await User.remove({}, { log: true, many: true }) will always remove all tems, but I want to remove some subset based on a sk composite key e.g const found = await User.find({}, { tunnel: { begins: { 'sk': 'user1' } } })

From what I understand, it's not possible right now if Im using SK in begins/contains/...expressions with remove operation but works with find?

NevRA avatar Jul 05 '22 09:07 NevRA

Yes. Currently remove many only works if you don't provide an SK like you are doing.

I'll flag this as a bug to be addressed.

mobsense avatar Jul 05 '22 22:07 mobsense

Fixed this so that if you provide {many: true} to remove, it will always do a find and then remove those items.

dev-embedthis avatar Jan 12 '23 01:01 dev-embedthis