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

relational-json

v3.3.3

Published

Relation data model for JSON objects

Downloads

25

Readme

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

| Name | Type | Description | | --- | --- | --- | | name | string | Table's property key in the relational-json database | | pk | string | name of the Table's primary field | | primary | string | alias of Table.meta.pk | | aliasMap | object | hashmap of the the table's rows' properties pointing to other tables in the relational-json database | | ownRequiredFields | Array.<string> | list of all own required fields, which must have a value at all times | | allRequiredFields | Array.<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

| Param | Type | Description | | --- | --- | --- | | d | object | data 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

| Param | Type | Default | Description | | --- | --- | --- | --- | | d | object | | field:value object of data to modify | | pkValue | * | d.primary | primary 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

| Param | Type | Description | | --- | --- | --- | | id | * | primary field value of row to delete |