Comparing version 0.0.8 to 0.0.9
{ | ||
"name": "treeize", | ||
"version": "0.0.8", | ||
"version": "0.0.9", | ||
"description": "Converts tabular row data (as from SQL joins, flat JSON, etc) to deep tree graphs based on simple column naming conventions.", | ||
@@ -5,0 +5,0 @@ "main": "./lib/treeize.js", |
251
README.md
@@ -6,2 +6,22 @@ treeize | ||
##Why? | ||
Most of us still have our hands in traditional relational databases (e.g. MySQL). | ||
While the normalized tables do a fine job of representing the parent/child | ||
relationships, the joined SQL results do not. In fact, they look more like an Excel | ||
spreadsheet than anything. This presents us with a | ||
problem when trying to supply a nice deep object graph for applications. | ||
Using a traditional ORM is slow (either many fragmented SQL | ||
calls, slow object hydration of models, or both). Beyond that, for a lightweight API, | ||
you don't want to have to first pick an ORM and then model out all your relationships. For complex queries, especially where results are | ||
filtered by multiple columns across multiple tables, it becomes even more troublesome, | ||
or borderline impossible to use these model helpers. | ||
The problem is, you can write the | ||
complex deeply joined SQL call that has all the results you wanted - but you can't get it back into | ||
an object graph so it looks/behaves like something other than data vomit. | ||
Now you can. | ||
## Installation | ||
@@ -19,3 +39,3 @@ | ||
### Notes | ||
#### Notes | ||
@@ -27,3 +47,3 @@ - The column/attribute order is not important. All attributes are sorted by depth before mapping. This ensures parent nodes exist before children nodes are created within. | ||
### Assumptions | ||
#### Assumptions | ||
@@ -35,66 +55,199 @@ This library has several assumptions that make it possible. | ||
### Example | ||
### Examples | ||
``` | ||
In this short series of examples, we'll take a standard "join dump", originally keyed | ||
(via attribute names) to organize by movie - and demonstrate how other organizations can | ||
be easily derived from the same original feed... by simply modifying the column/attribute | ||
names in the output. | ||
#### Example 1 | ||
In this example, we'll take our dump (as if from a CSV or SQL result) - and name the keys to | ||
group by movies (as if for an `/api/movies`). | ||
```js | ||
var treeize = require('treeize'); | ||
var flatData = [ | ||
var movieDump = [ | ||
{ | ||
"name": "Mittens", | ||
"age": 12, | ||
"toys:name": "mouse", | ||
"toys:owner:name": "Mittens" | ||
"title": "The Prestige", | ||
"director": "Christopher Nolan", | ||
"actors:name": "Christian Bale", | ||
"actors:as": "Alfred Borden" | ||
}, | ||
{ | ||
"name": "Mittens", | ||
"age": 12, | ||
"toys:name": "yarn", | ||
"toys:owner:name": "Ms. Threadz" | ||
"title": "The Prestige", | ||
"director": "Christopher Nolan", | ||
"actors:name": "Hugh Jackman", | ||
"actors:as": "Robert Angier" | ||
}, | ||
{ | ||
"name": "Tiger", | ||
"age": 7, | ||
"toys:name": "a stick", | ||
"toys:owner:name": "Mother Nature" | ||
"title": "The Dark Knight Rises", | ||
"director": "Christopher Nolan", | ||
"actors:name": "Christian Bale", | ||
"actors:as": "Bruce Wayne" | ||
}, | ||
{ | ||
"title": "The Departed", | ||
"director": "Martin Scorsese", | ||
"actors:name": "Leonardo DiCaprio", | ||
"actors:as": "Billy" | ||
}, | ||
{ | ||
"title": "The Departed", | ||
"director": "Martin Scorsese", | ||
"actors:name": "Matt Damon", | ||
"actors:as": "Colin Sullivan" | ||
} | ||
]; | ||
var converted = treeize.grow(flatData); | ||
var movies = treeize.grow(movieDump); | ||
/* | ||
'movies' now contains the following: | ||
[ | ||
{ | ||
"director": "Christopher Nolan", | ||
"title": "The Prestige", | ||
"actors": [ | ||
{ | ||
"as": "Alfred Borden", | ||
"name": "Christian Bale" | ||
}, | ||
{ | ||
"as": "Robert Angier", | ||
"name": "Hugh Jackman" | ||
} | ||
] | ||
}, | ||
{ | ||
"director": "Christopher Nolan", | ||
"title": "The Dark Knight Rises", | ||
"actors": [ | ||
{ | ||
"as": "Bruce Wayne", | ||
"name": "Christian Bale" | ||
} | ||
] | ||
}, | ||
{ | ||
"director": "Martin Scorsese", | ||
"title": "The Departed", | ||
"actors": [ | ||
{ | ||
"as": "Billy", | ||
"name": "Leonardo DiCaprio" | ||
}, | ||
{ | ||
"as": "Colin Sullivan", | ||
"name": "Matt Damon" | ||
} | ||
] | ||
} | ||
] | ||
*/ | ||
``` | ||
### Output | ||
#### Example 2 | ||
``` | ||
[ | ||
{ | ||
"name": "Mittens", | ||
"age": 12, | ||
"toys": [ | ||
{ | ||
"name": "mouse", | ||
"owner": { | ||
"name": "Mittens" | ||
Taking the same feed, but modifying the target paths through the attribute/column | ||
names we can completely transform the data (as you would for another API endpoint, | ||
for example). This time we'll organize the data by directors, as you would for | ||
and endpoint like `/api/directors`. | ||
Notice the feed is left unchanged - only the attribute names have been modified to | ||
define their new target path. In this case, by changing the base node to the directors | ||
name (instead of the movie name), we group everything by director at a high level. | ||
```js | ||
var treeize = require('treeize'); | ||
var moviesDump = [ | ||
{ | ||
"movies:title": "The Prestige", | ||
"name": "Christopher Nolan", | ||
"workedWith:name": "Christian Bale", | ||
"workedWith:as": "Alfred Borden" | ||
}, | ||
{ | ||
"movies:title": "The Prestige", | ||
"name": "Christopher Nolan", | ||
"workedWith:name": "Hugh Jackman", | ||
"workedWith:as": "Robert Angier" | ||
}, | ||
{ | ||
"movies:title": "The Dark Knight Rises", | ||
"name": "Christopher Nolan", | ||
"workedWith:name": "Christian Bale", | ||
"workedWith:as": "Bruce Wayne" | ||
}, | ||
{ | ||
"movies:title": "The Departed", | ||
"name": "Martin Scorsese", | ||
"workedWith:name": "Leonardo DiCaprio", | ||
"workedWith:as": "Billy" | ||
}, | ||
{ | ||
"movies:title": "The Departed", | ||
"name": "Martin Scorsese", | ||
"workedWith:name": "Matt Damon", | ||
"workedWith:as": "Colin Sullivan" | ||
} | ||
]; | ||
var directors = treeize.grow(movieDump); | ||
/* | ||
'directors' now contains the following: | ||
[ | ||
{ | ||
"name": "Christopher Nolan", | ||
"workedWith": [ | ||
{ | ||
"as": "Alfred Borden", | ||
"name": "Christian Bale" | ||
}, | ||
{ | ||
"as": "Robert Angier", | ||
"name": "Hugh Jackman" | ||
}, | ||
{ | ||
"as": "Bruce Wayne", | ||
"name": "Christian Bale" | ||
} | ||
}, | ||
{ | ||
"name": "yarn", | ||
"owner": { | ||
"name": "Ms. Threadz" | ||
], | ||
"movies": [ | ||
{ | ||
"title": "The Prestige" | ||
}, | ||
{ | ||
"title": "The Dark Knight Rises" | ||
} | ||
} | ||
] | ||
}, | ||
{ | ||
"name": "Tiger", | ||
"age": 7, | ||
"toys": [ | ||
{ | ||
"name": "a stick", | ||
"owner": { | ||
"name": "Mother Nature" | ||
] | ||
}, | ||
{ | ||
"name": "Martin Scorsese", | ||
"workedWith": [ | ||
{ | ||
"as": "Billy", | ||
"name": "Leonardo DiCaprio" | ||
}, | ||
{ | ||
"as": "Colin Sullivan", | ||
"name": "Matt Damon" | ||
} | ||
} | ||
] | ||
} | ||
] | ||
], | ||
"movies": [ | ||
{ | ||
"title": "The Departed" | ||
} | ||
] | ||
} | ||
] | ||
*/ | ||
``` |
16018
250