npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

dynamodb-graph-model

v1.0.4

Published

This is a library aimed to work with DynamoDB as if it was a Graph. The idea came from the "Advanced Design Patterns for Amazon DynamoDB (DAT403-R)" talk from Rick Houlihan on 2017 AWS re:Invent conference. Close to the end he describes a way to use a Dyn

Downloads

2

Readme

DynamoDB Graph Model

This is a library aimed to work with DynamoDB as if it was a Graph. The idea came from the "Advanced Design Patterns for Amazon DynamoDB (DAT403-R)" talk from Rick Houlihan on 2017 AWS re:Invent conference. Close to the end he describes a way to use a DynamoDB table to represent a graph. I found that idea very interesting, so I wanted to create a library that would let me interact with that kind of table structure. I added only two things to the pattern presented on this talk:

  1. The concept of a tenant.
  2. A way to handle the amount of GSI partitions to use.

So, each node can belong to a tenant by concatenating the random ID of the node (a cuid in this case), with the tenant id (also a CUID). The GSI Keys are also prepended with the tenant ID, so that you can check for the data only in the proper GSI partitions.

To control the number of GSI Keys you can use the maxGSIK option.

This library is constructed on top of anther library, which is in charge of handling the communication with DynamoDB. When instantiating a new Model, you can provide a DynamoDB DocumentClient driver, or, just let it instantiate one for you. This will work, provided you have configured the AWS SDK correctly. Having the ability to pass your own driver, simplifies the way the library can be tested.

DynamoDB Table

As mentioned before the dynamodb-graph library is used to interact with the DynamoDB table, which must have a schema similar to this.

## DynamoDB table.

The schema for the DynamoDB table, written as a CloudFormation template is the
following:

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  Graph:
    Type: "AWS::DynamoDB::Table"
    Properties:
      AttributeDefinitions:
        -
          AttributeName: "Node"
          AttributeType: "S"
        -
          AttributeName: "Type"
          AttributeType: "S"
        -
          AttributeName: "Data"
          AttributeType: "S"
        -
          AttributeName: "GSIK"
          AttributeType: "S"
      KeySchema:
        -
          AttributeName: "Node"
          KeyType: "HASH"
        -
          AttributeName: "Type"
          KeyType: "RANGE"
      ProvisionedThroughput:
        ReadCapacityUnits: "1"
        WriteCapacityUnits: "1"
      TableName: "GraphExample"
      GlobalSecondaryIndexes:
        -
          IndexName: "ByType"
          KeySchema:
            -
              AttributeName: "GSIK"
              KeyType: "HASH"
            -
              AttributeName: "Type"
              KeyType: "RANGE"
          Projection:
            NonKeyAttributes:
              - "Data"
              - "Target"
              - "MetaData"
            ProjectionType: "INCLUDE"
          ProvisionedThroughput:
            ReadCapacityUnits: "2"
            WriteCapacityUnits: "2"
        -
          IndexName: "ByData"
          KeySchema:
            -
              AttributeName: "GSIK"
              KeyType: "HASH"
            -
              AttributeName: "Data"
              KeyType: "RANGE"
          Projection:
            NonKeyAttributes:
              - "Type"
              - "Target"
              - "MetaData"
            ProjectionType: "INCLUDE"
          ProvisionedThroughput:
            ReadCapacityUnits: "2"
            WriteCapacityUnits: "2"

As you can see from the CloudFormation template, the table needs to use the Node as the hash key, and the Type as the sort key. The table also must include two GSI, one indexed by GSIK and Type, and the other by GSIK and Data. They should be named ByType, and ByData respectively.

The GSI keys are created by dynamodb-graph, combining the node id, tenant id, and the maxGSIK value. This way you can provision few GSIK partitions at the beginning and grow them further along. Take into account that the maxGSIK should never be decreased. Doing so will make some nodes unavailable.

Getting Started

Install the library on your project using npm or yarn.

npm install --save dynamodb-graph-model

yarn install dynamodb-graph-model

Then you can import it to your project using require. To interact with a model you must fist configure it, by passing some required and optional values. Here is the JSDoc that defines the types of all the options.

/**
 * Factory functions that returns a model, than can talk to a DynamoDB table
 * that is used to represent a directed graph.
 * @param {object} options
 * @property {any} [data] - Node main data.
 * @property {DynamoDBGraph} [db] - DynamoDB Graph object. Useful for testing.
 * @property {DocumentClientDriver} [documentClient] - DynamoDB DocumentClient
 *                                                     driver.
 * @property {EdgesMap} [edges]=[] - Map of node edges.
 * @property {object[]} [history]=[] - History of the model.
 * @property {number} [maxGSIK] - Maximum number of GSIK.
 * @property {string} [node] - Node of the current model.
 * @property {boolean} [log] - If set, all updates will include a CreatedAt or
 *                             UpdatedAt property generated along them.
 * @property {PropertyMap} [properties]=[] - Map of node properties.
 * @property {string} [table] - Table name. If not provided, it will try to pull
 *                              it from an environment variable called
 *                              TABLE_NAME.
 * @property {string} [tenant=''] - Tenant identifier.
 * @property {string} type - Node type.
 */

The table name can also be taken from an environment table called TABLE_NAME.

Here is an example of how to instantiate a new Model.

var AWS = require('aws-sdk');
var Model = require('dynamodb-graph-model');

// Configure the AWS SDK however you like.
AWS.config.update({ region: 'us-east-1' });

// On this example, we will provide our own DynamoDB Document Client driver.
var documentClient = new AWS.DynamoDB.DocumentClient();

// The table can be passed in as a parameter or stored inside the TABLE_NAME
// environment variable.
var table = process.env.TABLE_NAME;

// If we provide a GSIK value smaller than 2, then only 1 GSIK partition will
// be created. Since this value is important, it is asked explicitly for it.
var Book = Model({ type: 'Book', documentClient, maxGSIK: 0 });
var Author = Model({ type: 'Author', documentClient, maxGSIK: 0 });

Promise.all([
  Book.create({
    data: 'Elantris',
    properties: [
      {
        Type: 'Published',
        Data: '21/04/2005'
      },
      {
        Type: 'PublishedBy',
        Data: 'Tor Books'
      }
    ]
  }),
  Author.create({
    data: 'Brandon Sanderson',
    properties: [
      {
        Type: 'Gender',
        Data: 'Male'
      },
      {
        Type: 'Born',
        Data: '19/12/1975'
      }
    ]
  })
])
  .then(([book, author]) => {
    return book.connect({ type: 'Author', target: author });
  })
  .then(book => {
    console.log(book.Edges);
    // [{Node: ..., Data: 'Brandon Sanderson', Target: ..., Type: 'Author'}]
  })
  .catch(error => {
    console.log(error);
  });

// To get a node we use the `get` method, providing the Node.
Book.get('cjbfbo53x0000v3vm5egmtkr7')
  .then(book => {
    console.log(book.type);
    // Book
    console.log(book.data);
    // Mistborn
    console.log(book.properties);
    // [{Type: 'Published', Data: '21/04/2005'}, ...]
    console.log(book.edges);
    // [{Type: 'Author', Data: '21/04/2005', Target: 'cjbfbo53x00v3vm5egmtkr7']
  })
  .catch(error => {
    console.log(error);
  });

// To get a list of models we use the `collection` method.
Book.collection()
  .then(books => {
    books.forEach(book => {
      console.log(book.data);
    });
    // Elantris
    // Mistborn
  })
  .catch(error => {
    console.log(error);
  });

All the functions return a promise, with a new model in the result, or a list of models. I was thinking if it would be useful to mutate the initial model, but ended up opting not to. Might change in the future, or I'll might add that feature under a new flag.

Documentation

TODO

I tried to include information on each function as a JSDoc comment. I plan in the future to transform it into a proper documentation page. I wish there was something like Sphix for JavaScript.

Test

I am using jest to test the library. So, just clone the repo, install the dependencies, and run yarn test or npm run test to run them.

git clone [email protected]:guzmonne/dynamodb-graph.git
yarn install
yarn test

Licence

MIT