{"id":19754731,"url":"https://github.com/northmccormick/a-d-d","last_synced_at":"2026-04-16T05:04:31.798Z","repository":{"id":99391141,"uuid":"83156301","full_name":"NorthMcCormick/A-D-D","owner":"NorthMcCormick","description":"Automatic Data Denormalizing for NoSQL Databases in NodeJS","archived":false,"fork":false,"pushed_at":"2017-02-27T03:23:01.000Z","size":47,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-10T22:22:18.269Z","etag":null,"topics":["database","denormalize","firebase","ionic","nodejs","nosql"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NorthMcCormick.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-02-25T19:22:24.000Z","updated_at":"2017-02-27T01:00:04.000Z","dependencies_parsed_at":"2023-04-03T15:09:27.636Z","dependency_job_id":null,"html_url":"https://github.com/NorthMcCormick/A-D-D","commit_stats":{"total_commits":20,"total_committers":1,"mean_commits":20.0,"dds":0.0,"last_synced_commit":"0aebaf06d6215807cc543830cb1fca7847752885"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NorthMcCormick%2FA-D-D","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NorthMcCormick%2FA-D-D/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NorthMcCormick%2FA-D-D/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NorthMcCormick%2FA-D-D/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NorthMcCormick","download_url":"https://codeload.github.com/NorthMcCormick/A-D-D/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241085971,"owners_count":19907258,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database","denormalize","firebase","ionic","nodejs","nosql"],"created_at":"2024-11-12T03:01:41.054Z","updated_at":"2026-04-16T05:04:31.770Z","avatar_url":"https://github.com/NorthMcCormick.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/NorthMcCormick/A-D-D.svg?branch=master)](https://travis-ci.org/NorthMcCormick/A-D-D) [![Coverage Status](https://coveralls.io/repos/github/NorthMcCormick/A-D-D/badge.svg?branch=master)](https://coveralls.io/github/NorthMcCormick/A-D-D?branch=master) \u003cimg src=\"http://www.northmccormick.com/content/images/2017/02/build-with-love@2x.png\" height=\"20\"\u003e\n\n# Automatic Data Denormalizer\n\n*Note: This is still in beta and is subject to change. PRs, suggestions, and issues are welcome.*\n\nA-D-D for short, the automatic data denormalizer takes a developer defined schema and uses an input to duplicate the data.\n\n## Changelog\n\n#### 0.1.0\n\nFirst release, yay! Basically this readme is everything in the first release.\n\n## Feature Goals\n\n### Handle update and remove\n\nThe initial version of the lib is built on the idea of adding data, updating and removing will come along and will be able to utilize the same schema that the add does.\n\n### Complex objects\n\nThe first version of the lib will be able to handle the top most level properties on an object. Eventually better path parsing will be added so that you can reach into an object and select a nested property to use in the duplicated object.\n\n### Functions for data\n\nThe ability to define a function for a property and perform a calculation on it. Maybe this function grabs other data first, does some validation, or alters the format of the data.\n\n## Installation\n\nNPM: `npm install a-d-d --save`\n\n## Database Handlers\n\nA-D-D allows you to write your own handlers for any database. You can even use the idea of the handler as a custom hook or extend them to do \n\nThere is a template handler in the repo under `databaseHandlers`. There is also one for IonicDB and Firebase to get you started.\n\n## Quick Start\n\nRequire the library into your node project:\n\n```javascript\nvar ADDConfig\t\t\t \t= require('a-d-d').Config;\nvar ADDDenormalizer \t\t= require('a-d-d').Denormalizer;\n```\n\nLoad your databse handling object(s):\n\n```javascript\nvar ADDDatabase_Firebase \t= require('./add.firebase.db.js');\nvar ADDDatabase_IonicDB \t= require('./add.ionicdb.db.js');\n```\n\nCreate your databases and assign them to the config:\n\n```javascript\nvar firebaseAdminDB \t\t= admin.database(); // From the Firebase Admin SDK\nvar ionicAdminDb \t\t\t= new IonicDB(db_settings); // From the Ionic SDK\n\nvar myFirebaseDatabase \t\t= new ADDDatabase_Firebase(firebaseAdminDB);\nvar myIonicDatabase \t\t= new ADDDatabase_IonicDB(ionicAdminDb);\n\nADDConfig.database.ionic \t= myIonicDatabase;\nADDConfig.database.default \t= myFirebaseDatabase;\n```\n\nCreate a denormalizer object: \n\n```javascript\nvar tweetDenormalizer = new ADDDenormalizer({\n\tschema: {\n\t\texpectingType: 'object',\n\t\texpectingProperties: ['handle', 'tweet'],\t\n\t\tplaces: [{\n\t\t\toperation: 'push',\n\t\t\ttype: 'object',\n\t\t\tpath: '/userTweets/{{userHandle}}',\n\t\t\tvariables: \n\t\t\t\tuserHandle: 'handle'\n\t\t\t},\n\t\t\tproperties: ['tweet']\n\t\t},\n\t\t{\n\t\t\toperation: 'push',\n\t\t\ttype: 'string',\n\t\t\tpath: '/usersWhoTweeted',\n\t\t\tvariables: {},\n\t\t\tproperty: 'handle'\n\t\t},\n\t\t{\n\t\t\toperation: 'set',\n\t\t\ttype: 'string',\n\t\t\tpath: '/lastUser',\n\t\t\tvariables: {},\n\t\t\tproperty: 'handle',\n\t\t\toptions: {\n\t\t\t\tignore: {\n\t\t\t\t\tdelete: true // Ignore the request for delete\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\toperation: 'set',\n\t\t\ttype: 'object',\n\t\t\tpath: 'allTweets/{{key}}',\n\t\t\tvariables: {\n\t\t\t\tuserHandle: 'handle',\n\t\t\t\tkey: '$key'\n\t\t\t},\t\n\t\t\tproperties: ['tweet'],\n\t\t\toptions: {\n\t\t\t\tdatabase: 'ionic' // Note, we set the database here. This will perform the operations on that db.\n\t\t\t}\n\t\t}]\n\t}\n});\n```\n\nAnd ask it to denormalize some data for you:\n\n```javascript\ntweetDenormalizer.denormalize(newTweet).then(function(result) {\n\tconsole.log('Denormalized', result);\n\n}, function(error) {\n\tconsole.error('Could not denormalize', error);\n\n}).catch(function(e) {\n\tconsole.log('Exception');\n\tconsole.log(e);\n});\n```\n\n## Denormalizer Object\n\nThe denormalizer is where the magic happens. You want to create one of these for each set of data you want to denormalize. In the Quick Start example I use tweets. When I send a tweet to the server that looks like this:\n\n```json\n{\n  \"handle\": \"Sammy_Yundt36\",\n  \"tweet\": \"Wow hello this is my tweet, how cool is this\"\n}\n```\n\nIt takes the data, validates that the schema matches, and then updates 3 places:\n\n1. It pushes the tweet onto the user handle using a variable from the data\n2. It adds their handle to a list of handles but just as a string, not as an object containing a string\n3. It overwrites the node `lastUser` to their handle so we know who tweeted last\n\n### Options\n\n#### schema: Object *required*\n\n`schema` holds the information for the input and output of your data. \n\n#### schema.expectingType: String *required*\n\n`expectingType` is the type we expect to see. This keeps malformed data from clogging your denormalization process.\n\nThe available types are: `object`\n\n#### schema.expectingProperties: Array\u003cString\u003e *required when `expectingType` is `object`*\n\nAn array of properties that the object *must* have to be denormalized. If the object is missing a property, it will not duplicate.\n\n#### schema.places: Array\u003cObject\u003e *required*\n\nAn array of objects, each object describing where and how you want the data to be duplicated\n\n#### schema.places.operation: String *required*\n\nThe type of operation to perform. The available operations are `set` and `push`.\n\n#### schema.places.type: String *required*\n\nWhat type you want to duplicate the data as. The avaiable types are `object` and `string`.\n\n#### schema.places.properties: Array\u003cString\u003e *required if `type` is `object`*\n\nAn array of properties that you want to pull from the original data input. You must have at least one property to pull. The library does not support selecting nested properties at this time. If your object looks like this:\n\n```json\n{\n\t\"a\": {\n\t\t\"aa\": true,\n\t\t\"bb\": true\n\t},\n\t\"b\": {\n\t\t\"cc\": true\n\t}\n}\n```\n\nAnd your properties looks like this:\n\n```javascript\n{\n\tproperties: ['a']\n}\n```\n\nThe value it will duplicate is:\n\n```javascript\n{\n\ta: {\n\t\taa: true,\n\t\tbb: true\n\t}\n}\n```\n\n#### schema.places.property: String *required if `type` is `string`, `number`, or `boolean` and the input is an object*\n\nThe property from the object you want to use. This propety must be one of the above 3 types. It will fail if it is an object or an array.\n\nIf we have this as our original data:\n\n```json\n{\n\t\"a\": {\n\t\t\"aa\": true,\n\t\t\"bb\": true\n\t},\n\t\"b\": \"I am a beautiful butterfly\"\n}\n```\n\nAnd our property looks like this:\n\n```javascript\n{\n\tproperty: 'b'\n}\n```\n\nThe value it will duplicate is:\n\n```javascript\n{\n\tb: 'I am a beautiful butterfly'\n}\n```\n\n#### schema.places.path: String *required*\n\nWhere you want to save the new data. The path string can contain variable names (more on those below) and must be the absolute path.\n\nExample: `/userTweets/{{userHandle}}` or `/users`\n\n#### schema.places.variables: Object *required if a variable is defined in the path* \n\n`variables` is an object where the key is the name of the variable in the path and the value is the key of the original input.\n\nExample:\n\n```javascript\nvariables: {\n\tuserHandle: 'handle'\n}\n```\n\n`userHandle` is what it will search for in the path (like our previous path example). The parser will pull the value for `handle` (from the data we gave at the beginning of the denormalizer object) and use it there. \n\nOur data:\n\n```json\n{\n  \"handle\": \"Sammy_Yundt36\",\n  \"tweet\": \"Wow hello this is my tweet, how cool is this\"\n}\n```\n\nWhat our final path will be:\n\n`/userTweets/Sammy_Yundt36`\n\n#### schema.places.options: Object\n\nPlaces can have options to help handle their functions. If no options are provided, the default values will be used.\n\n#### schema.places.options.ignore: Object\n\nIf you want to ignore a specific operation on a place, you can set that operation to `true` in the ignore object. \n\nExample: If we want to show what user last did something we want to update it but we do not want to delete the node even if the main object in the database was deleted. \n\n```javascript\n{\n\toperation: 'set',\n\ttype: 'string',\n\tpath: '/lastUser',\n\tvariables: {},\n\tproperty: 'handle',\n\toptions: {\n\t\tignore: {\n\t\t\tdelete: true // We will update the node, but we will never delete it\n\t\t}\n\t}\n}\n```\n\n#### schema.places.options.database: String\n\nThis is the string name of a previously configured database you would like to use. If nothing is defined it will use 'default'.\n\n## Required Libraries\n\n- Q\n- Colors","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnorthmccormick%2Fa-d-d","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnorthmccormick%2Fa-d-d","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnorthmccormick%2Fa-d-d/lists"}