3.3.3 • Published 8 years ago

relational-json v3.3.3

Weekly downloads
7
License
MIT
Repository
github
Last release
8 years ago

relational-json makes working with complex data simpler than ever.

Getting started

Install relational-json

npm install relational-json --save

Require

Relational-json is quite straight-forward. You require relational-json into your source:

var rJSON = require("relational-json");

and you create a db based on a schema you provide:

var db = rJSON(schema);


How it works

Relational-json wraps your primitive data (strings, numbers, booleans) in an immutable & relational structure. The format is just like JSON, except your data can reference other parts of your data tree. This is done dynamically, thanks to ES5 object getters & setters. Each object created inside relational-json is "pure", in the sense that it has no native prototype. This allows us to leverage JavaScripts prototypical inheritance in very elegant ways.

relational-json is JSON with conscience.

Why use it

When building web applications you'll be dealing with a wide variety of data and multiple data sources. Managing application state updates can quickly become problematic, especially when certain data branches rely on other branches. When you update a value, you have to make sure the change propagates everywhere. Relational-json makes all of that simpler:

  1. It eliminates data duplication
  2. It simplifies data updates (you only ever need to modify data in 1 place, since there are no duplicates)
  3. It encapsulates your data, data manipulation logic and restricts certain operations (such as wrong datatype in a given field)
  4. It's extremely portable, since the data uses the well-known JSON syntax.
  5. It makes no environment assumptions (can be used in browser, on node, etc.)

Limitation

Because of it's referential nature, JSON.stringify() does not work as expected. This is due to infinite nesting, which makes you jump from prototype to child and back again, infinitely, if you don't control the data traversal. (we provide a simple utility, which entirely mitigates the issue)

Also, relational-json is IE9+ compatible, but cannot be shimmed or polyfilled for older browsers.


The schema

Relational-json expects a javascript-object schema representing the data tree you wish to use. Below is a full break down of the schema notation:

{
    tableName: {
        primary: "field to use as unique identifier. Field must be in fields object.",
        fields: {
            fieldName: {
                allowNull: "can the value be set to null (true / false)",
                dataType: "datatype of the field (string, integer, float, date, time, datetime, boolean)",
                defaultValue: "value to use if none provided at creation time"
            }
        },
        extends: {
            table: "tableName that is a parent of this table",
            localField: "own field that references parent table",
            foreignField: "parent table field that localField is matched with"
        },
        aggregates: [
            {
                table: "tableName that is aggregated",
                alias: "property alias to use for relation",
                localField: "local field used to build aggregate relations",
                foreignField: "foreign field used in aggregate relation",
                cardinality: "(single / many), a single relation provides a direct relation to another object. A many relation creates an array of objects"
            }
        ]
    }
}

Table fields can also be written in shorthand. This creates a field that has no defaultValue and cannot be null:

tableName: {
    fields: {
        id: "integer",
        name: "string"
    }
}

Schema relations

extends

Extends is an inheritance pattern. It signifies that a table's rows are the child of another table's rows. Concretely, every row object created in this table will use a row object from a parent table as prototype. The extends relationship is also reflected in the parent tables' rows, which will contain a property to the child row. This will allow you to traverse your data trees in both directions (up ancestors and down descendants).

extends example

var schema = {
    Vehicle: {
        primary: "id",
        fields: {
            id: "integer",
            year: "integer",
            maker: { allowNull: true, dataType: "string" }
        }
    },
    Car: {
        primary: "id",
        fields: {
            id: "integer",
            model: "string"
        },
        extends: { table: "Vehicle", localField: "id", foreignField: "id" }
    }
};

var db = rJSON(schema);

db.Car.post({
    id: 1,
    year: 2001,
    maker: "Toyota",
    model: "Camry"
});

db.Car.get(1);
/*  own props
    {
        id: 1,
        model: "Camry"
    }
*/

// prototype props
db.Car.get(1).year; // 2001
db.Car.get(1).maker; // "Toyota"

db.Vehicle.get(1);
/*
{
    id: 1,
    year: 2001,
    maker: "Toyota",
    Car: {
        id: 1,
        model: "Camry"
    }
}
*/

aggregates

This is a composite relation between tables, not inheritance-based. Concretely, if table A "aggregates" table B, rows from table A will have a property pointing to one row (object) or many rows (array) from table B, based on the relations cardinality. Unlike extends, aggregate does not affect the prototype chain.

aggregates example (single)

var schema = {
    Vehicle: {
        primary: "id",
        fields: {
            id: "integer",
            year: "integer",
            maker: { allowNull: true, dataType: "string" }
        }
    },
    Client: {
        primary: "id",
        fields: {
            id: "integer",
            name: "string",
            vehicle_id: { allowNull: true, dataType: "integer" }
        },
        aggregates: [
            { table: "Vehicle", alias: "Vehicle", localField: "vehicle_id", foreignField: "id", cardinality: "single" }
        ]
    }
};

var db = rJSON(schema);

db.Vehicle.post({
    id: 1,
    year: 2001,
    maker: "hyundai"
});

db.Client.post({
    id: 2,
    name: "bob the builder",
    vehicle_id: 1
});

db.Client.get(2);
/*
{
    id: 2,
    name: "bob the builder",
    Vehicle: {
        id: 1,
        year: 2001,
        maker: "hyundai"
    }
}
*/

Table : object

Kind: global typedef
Summary: Once relational-json parsed your schema, it will return your relational database, which will be a collection of all the Tables in your data. Each Table uses an internal Dictionary to store, retrieve and manipulate it's data (rows). Tables are essentially the interface through which you manipulate data.

table.meta : object

Kind: instance property of Table
Summary: partial interface into the Table's inner details
Properties

NameTypeDescription
namestringTable's property key in the relational-json database
pkstringname of the Table's primary field
primarystringalias of Table.meta.pk
aliasMapobjecthashmap of the the table's rows' properties pointing to other tables in the relational-json database
ownRequiredFieldsArray.<string>list of all own required fields, which must have a value at all times
allRequiredFieldsArray.<string>list of all required fields, including own & ancestors' required fields

table.get() ⇒ object | Array.<object>

Kind: instance method of Table
Summary: returns row data matching the provided arguments on their primary field value
Returns: object | Array.<object> - if no argument is provided, it returns an array of all data within the table. if 1 argument is provided, it returns that row object if many arguments are provided, it returns an array containing those row objects

table.post(d) ⇒ object

Kind: instance method of Table
Summary: creates a new row of data
Returns: object - row instance created

ParamTypeDescription
dobjectdata bundle, must contain all required fields

table.put(d, pkValue) ⇒ object

Kind: instance method of Table
Summary: modifies a data row, by re-creating the row merged with the new data
Returns: object - newly created row

ParamTypeDefaultDescription
dobjectfield:value object of data to modify
pkValue*d.primaryprimary value to find row to modify

table.delete(id) ⇒ object

Kind: instance method of Table
Summary: recursively deletes the target row and it's children (if any)
Returns: object - deleted row

ParamTypeDescription
id*primary field value of row to delete
3.3.3

8 years ago

3.3.2

8 years ago

3.3.1

8 years ago

3.3.0

8 years ago

3.1.2

8 years ago

3.1.1

8 years ago

3.1.0

8 years ago

3.0.0

8 years ago

2.0.6

8 years ago

2.0.5

8 years ago

2.0.4

8 years ago

2.0.3

8 years ago

2.0.2

8 years ago

2.0.1

8 years ago

2.0.0

8 years ago

1.0.10

8 years ago

1.0.9

8 years ago

1.0.8

8 years ago

1.0.7

8 years ago

1.0.6

8 years ago

1.0.5

8 years ago

1.0.4

8 years ago

1.0.3

8 years ago

1.0.2

8 years ago

1.0.1

8 years ago

1.0.0

8 years ago