awslabs / dynamodb-data-mapper-js

A schema-based data mapper for Amazon DynamoDB.
https://awslabs.github.io/dynamodb-data-mapper-js/
Apache License 2.0
817 stars 106 forks source link

Collection or Custom, needing advice #80

Open LucasBadico opened 6 years ago

LucasBadico commented 6 years ago

Hey guys, needing some advice, @jeskew, maybe you know the answer...

I have Accounts, that have a phones attribute, that is a phone map.

{
 phones: {
  'phone1-with-uuid': {
       number: '99999999',
       areaCode: '00'
  }
 }
}

I was looking ate the collection and I can't use a memberType for it, the mapper will do a auto-marshaller, rigth?

There is a way to make a custom use the scheme? Or we extend the mapper to understand this use case?

I want to do this to have a unique identifier on phone.

jeskew commented 6 years ago

There's a 'Map' type that will be unmarshalled into an ES6 Map that gives you strongly typed member types. There's no equivalent that uses an object literal instead of a map, but one could be added.

LucasBadico commented 6 years ago

Let's see.

My wanted schema


const phone = {
    number: {
       type: 'String'
    },
   areaCode: {
      type: 'String'
   }
 }
const schema = {
   phones: {
      type: 'Collection',
      memberType: embed(phone)
   }
}

my entity

const entity = {
 phones: {
  'sjahdjasdh-kjsahdjksahd-klsjadj': { // unique id
       number: '99999999',
       areaCode: '00'
  },
 'sjahdjasdh-asdasdfsds-klsjadj': { // unique id
       number: '99999999',
       areaCode: '01'
  }
 }
}

This work with map?

jeskew commented 6 years ago

’Collection’ wouldn’t be the right choice here — that’s used for arrays with untyped elements. There is a ’Hash’ type for objects with string keys and untyped values, but the values in phones are strongly typed.

When unmarshalling the entity, you would get the follow if you used a ’Map’ type for phones:

const entity = {
    phones: new Map([
        [‘<uuid1>’, {number: ‘...’, areaCode: ‘00’}],
        [‘<uuid2>’, {number: ‘...’, areaCode: ‘01’}],
    ]),
};

I think what you’re describing would require a different scheme type (let’s call it ObjectMap for now) that allows arbitrary keys that map to strongly typed values. I wouldn’t be opposed to adding that — the primary motivation for using ES6 Map objects was to aid in type inference in a case like:

@table(‘foos’)
class Foo {
    @attribute({memberType: embed(Widget)})
    public widgets: Map<string, Widget> = new Map();
}

It’s more difficult to infer if you meant to use a map, an untyped hash, or a structured document if the map type is Object, but that wouldn’t be an issue if you’re explicitly defining the schema.

LucasBadico commented 6 years ago

Could we do this? I think that mappedCollection is a better name by the way.

I can help with this. I have been looking into the codebase.

Em qui, 12 de jul de 2018 às 12:47, Jonathan Eskew notifications@github.com escreveu:

When unmarshalling the entity, you would get the follow if you used a map:

const entity = { phones: new Map([ [‘’, {number: ‘...’, areaCode: ‘00’}], [‘’, {number: ‘...’, areaCode: ‘01’}], ]), };

I think what you’re describing would require a different scheme type (let’s call it ObjectMap for now) that allows arbitrary keys that map to strongly typed values. I wouldn’t be opposed to adding that — the primary motivation for using ES6 Map objects was to aid in type inference in a case like:

@table(‘foos’)class Foo { @attribute({memberType: embed(Widget)}) public widgets: Map<string, Widget> = new Map(); }

It’s more difficult to infer if you meant to use a map, an untyped hash, or a structured document if the map type is Object, but that wouldn’t be an issue if you’re explicitly defining the schema.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/awslabs/dynamodb-data-mapper-js/issues/80#issuecomment-404558613, or mute the thread https://github.com/notifications/unsubscribe-auth/APwHGbCjYuSW9nhh7oQ3MzbARfiMcIaDks5uF299gaJpZM4VL6uU .

LucasBadico commented 6 years ago

I was thinking, we can't call it just a Collection with a memberType defined?

jeskew commented 6 years ago

I would rather not use the term "collection" in the name, as it is typically used for containers of items without external keys (e.g., lists, sets, queues, stacks, etc., but not maps).

Other alternatives that commonly denote collections of key => value pairs include 'AssociativeArray', 'Table', or 'Dictionary'... of those, I like 'Dictionary' most.

LucasBadico commented 6 years ago

Agreed on Dictionary.

Already make a fork of the repo on my non-profit organization, then I we start make some tests, and rigth on start I open the pr and point to this issue.

[Edited] Working on branch dictionary-feature. https://github.com/semeietech/dynamodb-data-mapper-js/tree/dictionary-feature

LucasBadico commented 6 years ago

[Editing the proposal for the annotation] It will do, right?

class MyDomainClass {
    @attribute({ memberType: embed(Phone) })
    phones?: Dictionary<Phone>;
}

Or this Dictionary key should be a typescript type? Is this a problem?

Also, have not beeing able to use annotations here on my project, so I build my domain with the Schema being injected on the class prototype.

So, in without annotations it will be something like this:

Object.defineProperties(MyDomainModel.prototype, {
    [DynamoDbSchema]: {
        value: {
            phone: {
                type: 'Dictionary',
                memberType: embed(Phone)
            },
        },
    },
}); 
jeskew commented 6 years ago

I think you would want to declare the property as:

@attribute({ memberType: embed(Phone) })
phones?: {[key: string]: Phone;

TypeScript's metadata decorator will just tell you that phones has a type of Object, so you would need to rely on the presence of a memberType property in the object passed to the annotation (as is done to distinguish Lists from Collections).

LucasBadico commented 6 years ago

Get it. And in the class.prototype way, we could go for this:

Object.defineProperties(MyDomainModel.prototype, {
    [DynamoDbSchema]: {
        value: {
            phone: {
                type: 'Dictionary',
                memberType: embed(Phone)
            },
        },
    },
}); 
akleiber commented 5 years ago

Am I right that this would be the only way to add an existing TypeScript type definition to a DynamoDB column of Map<string,any> (JSON)?