dynamodb-toolbox
dynamodb-toolbox copied to clipboard
v0.2 feedback - alias vs. map , and function to map data out on return
Great job on the toolbox. I appreciate it.
It was a little unclear from the documentation what the difference between alias and map is. I figured it out by looking into the source, but a little clarification would probably help others.
I was trying to use the map property as a way to translate a string in the DB table to an object output. org:{id:1234} into orgId:1234 in the db table and then back to org:{id:1234} when read. On the input using the default function solves it, but there seems to be no way to translate the output back when read. Is that correct?
Hi @rbethel, thanks for the feedback! I actually was thinking about removing alias since the map functionality makes it redundant. It was a feature in v0.1 (map didn't exist), so I wanted to give people the ability to either specify TABLE attributes and alias them to ENTITY attributes, or the other way around. I'll add a section to the docs about them.
The object conversion you mention is an interesting idea. Is there are reason to store it as a map versus just a string? I guess we could add a way to parse the data into separate attributes, but I'm just trying to figure out the use case.
Hey @jeremydaly, the map vs. alias makes more sense knowing you want to eventually drop alias in favor of map. Part of my confusion was that it did seem to do almost the same thing in a slightly different way.
As far as the mapping into and out of a nested object property here is a simplified example to illustrate the use case.
//Application data
let appObj = {
name: "John Smith",
id: "usr-123",
org: {id:"org-567", name: "My Organization"}
}
//Option 1 - most efficient on db table, least duplication of attributes.
//But not possible to translate back into desired output on a read. Or to filter based on nested property.
let schema1: {
pk: { partitionKey: true, hidden: true, default: (data)=>`${data.org.id}#${data.id}` },
name: 'string',
id: {save:false},
org: {save:false},
orgName: {hidden: true, default: (data) => `${data.org.name}`}
},
//data in DB table
let dbOption1 = {
pk: "org-567#usr-123"
name: "John Smith"
orgName: "My Organization"
}
//Option 2 - Redundant data in table but allows for mapping in and out with arbitrary shape
let schema2: {
pk: { partitionKey: true, hidden: true, default: (data)=>`${data.org.id}#${data.id}` },
name: 'string',
id: 'string',
org: 'map',
},
let dbOption2 = {
pk: "org-567#usr-123"
name: "John Smith"
id: "usr-123"
org: {id:"org-567", name: "My Organization"}
}
//Option 3 - Allows for filtering based on orgName which would not be possible nested in an object/map. But has more duplication of data to table.
let schema3: {
pk: { partitionKey: true, hidden: true, default: (data)=>`${data.org.id}#${data.id}` }, //? is data.id available here only if id is defined in the attributes or is it available if it is
name: 'string',
id: 'string',
org: 'map',
orgName: {hidden:true, default: (data)=>`${data.org.name}`}
},
let dbOption3 = {
pk: "org-567#usr-123"
name: "John Smith"
id: "usr-123"
org: {id:"org-567"},
orgName: "My Organization"
}
Option 1 would be my first choice because it allows mapping into most efficient table representation, but I don't think it is possible with the existing API. In order for this to work I would need map the table data to output as a function (i.e. org: (dbData) => {id:(dbData.pk.split('#')[0],name:(dbData.orgName)} and id: (dbData) => (dbData.pk.split('#')[1]) ). Option 2 and 3 are both possible with the API but they require duplicating data in the table in order to map into a key or index from a nested property and also to filter output based on a nested property.
Also, for clarification, in option 1 I assume that the {save:false} is available outside an array based key mapping like id: [pk, 1, {save:false}]. If that is not correct than the id would have to be duplicated in the db table. For this I am assuming that to use id in a function based composite key mapping (i.e. pk: {default: (data) => data.id+data.somethingElse) that id also needs to be one of the schema attributes. Is that correct?
Sorry for the long explanation, but hope that is more clear on the use case.
Ryan
Hi Ryan,
There are a few different ways to handle what you're looking for. You can certainly use the custom default functions to construct keys, and it is possible to use the { save: false } setting to avoid attributes getting saved to the table. The alias and map features are strictly for attribute naming, so the point is simple to have a "local" attribute name versus what gets saved in the table. The use cases for this could be to simply to minimize attribute names in the DB (e.g. customerId maps to cid, or cid aliases to customerId), or to reuse attribute names across multiple entities (e.g. customerNumberon entity A andcustomerIdon entity B both get saved tocid` on the table).
However, what you're looking to do seems like composite keys. This is supported by using an array to define the attribute (see here). This allows you to do all kinds of attribute composition and parsing.
Let me know if this is not what you are trying to accomplish.
Jeremy
Is there any way to use the array composite key mapping to map one attribute to the primary key and also to an index? In other words I want to have two access patterns that use some of the same attributes in different orders. Like I want to put the userID#Date in the sk and then date#userID in the gsi1sk. As far as I can see this can't be done with the array mapping. Is that correct?
Hi @rbethel,
Right now the composite key array mapping only works for one field. You can use the convert setting to generate the value that can include other items. Interesting idea, though. Let me looking into building multiple composites.
Thanks,
Jeremy
Hey @ryanbethel,
we've recently added the format property that allows transforming the DDB output, this should resolve the issue mentioned in the beginning of the thread.
Regarding composite key arrays, this syntax is deprecated and will be deleted in one of the future releases so I don't recommend using it.
I'll be closing this issue but feel free to ping me if you need anything :)