Skip to content

ValeTheVioletMote/relvar

Repository files navigation

relvar.js

An amateur database inspired by CJ Date and Project M36.

Documentation will be sparse as I'm focused more on the development, but here's a demo that should give you the idea of what I'm gunning to do:

var {relvar, union, _sel, _un, selection
    , rvts, logrv, S, P, SP, _j, join, inv_selection, _but
    , where, _where, minus, _minus, rename, _ren, matching, _mat, not_matching, _nmat
    , db, save_db, assign_rv, update, _up, extend, _ext, count, _cnt, sum, _sum, assign_js_constraint} = require("../main.js");

const _arv = (...rvs) => (db) => assign_rv(db, ...rvs).db;

var GameDB = db(require("path").join(__dirname, "test.yaml"), {name: "GameDB"});
var {db: GameDB} = save_db(GameDB);

var C = relvar({
    attrs: {
        "C#": "number",
        "Cname": "string",
        "HP": "number"
    },
    tuples: [
        {"C#": 1, "Cname": "Reese of Wellington", "HP": 4}
        ,{"C#": 2, "Cname": "Sir Nic of the Weils", "HP": 18}
    ]
});

var I = relvar({
    attrs: {
        "I#": "number",
        "Iname": "string",
        "Durability": "number",
        "Damage": "number"
    },
    tuples: [
        {"I#": 1, "Iname": "Bronze Poniard", "Durability": 10, "Damage": 10}
        ,{"I#": 2, "Iname": "Glazed Donut", "Durability": 1, "Damage": 1}
        ,{"I#": 3, "Iname": "Venom-Soaked Blade", "Durability": 5, "Damage": 25}
        ,{"I#": 4, "Iname": "Ancient Spoon", "Durability": 1, "Damage": 80}
        ,{"I#": 5, "Iname": "Holy Hand Grenade of Antioch", "Durability": 1, "Damage": 999}
        ,{"I#": 6, "Iname": "Common Shortsword", "Durability": 30, "Damage": 15}
        ,{"I#": 7, "Iname": "Rot-Hilted Axe", "Durability": 8, "Damage": 15}
    ]
});

var CI = relvar({
    attrs: {
        "C#": "number",
        "I#": "number",
        "Slot": "number"
    },
    tuples: [
        {"C#": 1, "I#": 1, "Slot": 3},
        {"C#": 1, "I#": 3, "Slot": 4},
        {"C#": 2, "I#": 2, "Slot": 4},
        {"C#": 2, "I#": 4, "Slot": 3},
        {"C#": 2, "I#": 1, "Slot": 10},
    ]
});

var {db: GameDB} = GameDB.tap(_arv(["C", C], ["I", I], ["CI", CI])).tap(save_db);

var {run} = require("../db_lang.js");

console.log("C:")
logrv(C);

console.log("C join CI:")
join(C, CI).tap(logrv);

console.log("C{C#, Cname} join CI")
selection(C, "C#", "Cname").tap(_j(CI)).tap(logrv);

console.log("C join CI join I");
join(C, CI).tap(_j(I)).tap(logrv);

console.log("(C join CI join I){ALL BUT C#, HP, I#}");
join(C, CI).tap(_j(I)).tap(_but("C#", "HP", "I#")).tap(logrv);

console.log("Variant to test/display commutative properties of join:")
console.log("(C join I join CI){ALL BUT C#, HP, I#}");
join(C, I, CI).tap(_but("C#", "HP", "I#")).tap(logrv);

console.log("Give me all items that are owned by no character:")
console.log("I NOT MATCHING CI");
not_matching(I, CI).tap(logrv);

console.log("All characters with an item that deals more than 30 damage:")
console.log("C MATCHING (CI JOIN (I WHERE {Damage > 30}))")
matching(C, join(CI, where(I, i=>i.Damage > 30))).tap(logrv)

console.log("Just their names:")
console.log("(C MATCHING (CI JOIN (I WHERE {Damage > 30}))){Cname}")
matching(C, join(CI, where(I, i=>i.Damage > 30))).tap(_sel("Cname")).tap(logrv)

where(I, i=>i.Damage > 30)
.tap(_j(CI))
.tap(cii => matching(C, cii))
.tap(_sel("Cname"))
.tap(logrv);

console.log("Characters and the items they DON'T have: ")
console.log("((C{C#} JOIN I{I#}) MINUS (CI{C#, I#}) JOIN C{C#, Cname} JOIN I{I#, Iname}) {Cname, Iname} RENAME {Cname AS Character, Iname AS Missing Item}")
join(selection(C, "C#"), selection(I, "I#"))
.tap(_minus( selection(CI, "C#", "I#") ))
.tap(_j( selection(C, "C#", "Cname"),  selection(I, "I#", "Iname") ))
.tap(_sel("Cname", "Iname"))
.tap(_ren(["Cname", "Character"], ["Iname", "Missing Item"]))
.tap(logrv);

console.log("Fix overpowered items: ")
console.log("UPDATE I WHERE Damage > 30: {Damage := Damage / 2}")
update(I, i=>i.Damage > 30, ["Damage", i => Math.floor(i.Damage/2)])
.tap(logrv);

console.log("Show Damage Per Durability (DPD): ")
console.log("EXTEND I: {DPD := Damage / Durability}");
extend(I, ["DPD", "number", i=>Math.round(i.Damage/i.Durability)])
.tap(logrv);


console.log("Count of all character-item combos:\n\tCOUNT ( CI ) =", count(CI));
console.log("Count of all distinct slots in character-item combos:\n\tCOUNT ( CI { Slot } ) :", count(CI, "Slot"));
console.log("Sum of all item damage:\n\tSUM (I, Damage) =", sum(I, "Damage"));
console.log("Sum of all distinct item damage values:\n\tSUM (I, { Damage }) =", sum(I, ["Damage"]));
console.log("Sum of tripled item damage:\n\tSUM (I, 3 * Damage) = ", sum(I, "Damage", i=>i.Damage*3) )
console.log("Sum of all distinct tripled item damage:\n\tSUM( EXTEND I : { Damage := Damage * 3 }, {Damage}) = ", sum(extend(I, ["Damage", "number", i=>i.Damage*3]) , ["Damage"]))

console.log("My D implementation is behind on most features, but the plumbing exists: ");
console.log("(C join CI join I){ALL BUT C#, HP, I#}");
run(GameDB, "(C join I join CI){ALL BUT C#, HP, I#}").tap(logrv);

// With D, I hope to be able to allow constraints to be written in it
// as a simple solution rather than needing the prefab or custom JS.


// Add a uniqueness constraint to I.

const uniq_i = (rvs) => count(rvs.I) == count(rvs.I, "I#");
var {db: GameDB} = assign_js_constraint(GameDB, ["UNIQ_I", uniq_i]);

console.log("Attempt to violate the constraint:");
const new_i = union(I, {tuples: [{"I#": 7, "Iname": "Floppy Disk", "Durability": 40, "Damage": 3}] });
const attempt_assign = assign_rv(GameDB, ["I", new_i]);
console.dir(attempt_assign.c == "FAILED" ? attempt_assign.issue : "Oops. This shouldn't happen.");

console.log("Concede, follow the constraint:");
const new_i_2 = union(I, {tuples: [{"I#": 8, "Iname": "Floppy Disk", "Durability": 40, "Damage": 3}] });
const this_should_work = assign_rv(GameDB, ["I", new_i_2]);
logrv(this_should_work.db.data.relvars["I"]);

// Initialize an existing DB with supplied constraints: 

var my_constraints = {
    "UNIQ_I": uniq_i
};

var newGame = db(require("path").join(__dirname, "test2.yaml")
            , {name: "NewGame", supplied: {constraints_js: my_constraints}});

    // I don't recommend you do this normally.
    // This is just to ensure that the fresh file has the constraint and data.
    // Your usual DB will already have the constraint in its data.
    // if you've already built test2.yaml, you could comment these three lines out, and it'll still work.
    newGame.data.relvars = GameDB.data.relvars;
    newGame.data.constraints_js = ["UNIQ_I"];
    save_db(newGame);

console.log("Attempt to violate again on new DB:");
const attempt_assign_2 = assign_rv(newGame, ["I", new_i]);
console.dir(attempt_assign_2.c == "FAILED" ? attempt_assign_2.issue : "Oops. This shouldn't happen.");

Output:

C:
┌────────────┬──────────────────────┬────────────┐
│ C#::number │ Cname::string        │ HP::number │
├────────────┼──────────────────────┼────────────┤
│ 1          │ Reese of Wellington  │ 4          │
├────────────┼──────────────────────┼────────────┤
│ 2          │ Sir Nic of the Weils │ 18         │
└────────────┴──────────────────────┴────────────┘
C join CI:
┌────────────┬──────────────────────┬────────────┬────────────┬──────────────┐
│ C#::number │ Cname::string        │ HP::number │ I#::number │ Slot::number │
├────────────┼──────────────────────┼────────────┼────────────┼──────────────┤
│ 1          │ Reese of Wellington  │ 4          │ 1          │ 3            │
├────────────┼──────────────────────┼────────────┼────────────┼──────────────┤
│ 1          │ Reese of Wellington  │ 4          │ 3          │ 4            │
├────────────┼──────────────────────┼────────────┼────────────┼──────────────┤
│ 2          │ Sir Nic of the Weils │ 18         │ 2          │ 4            │
├────────────┼──────────────────────┼────────────┼────────────┼──────────────┤
│ 2          │ Sir Nic of the Weils │ 18         │ 4          │ 3            │
├────────────┼──────────────────────┼────────────┼────────────┼──────────────┤
│ 2          │ Sir Nic of the Weils │ 18         │ 1          │ 10           │
└────────────┴──────────────────────┴────────────┴────────────┴──────────────┘
C{C#, Cname} join CI
┌────────────┬──────────────────────┬────────────┬──────────────┐
│ C#::number │ Cname::string        │ I#::number │ Slot::number │
├────────────┼──────────────────────┼────────────┼──────────────┤
│ 1          │ Reese of Wellington  │ 1          │ 3            │
├────────────┼──────────────────────┼────────────┼──────────────┤
│ 1          │ Reese of Wellington  │ 3          │ 4            │
├────────────┼──────────────────────┼────────────┼──────────────┤
│ 2          │ Sir Nic of the Weils │ 2          │ 4            │
├────────────┼──────────────────────┼────────────┼──────────────┤
│ 2          │ Sir Nic of the Weils │ 4          │ 3            │
├────────────┼──────────────────────┼────────────┼──────────────┤
│ 2          │ Sir Nic of the Weils │ 1          │ 10           │
└────────────┴──────────────────────┴────────────┴──────────────┘
C join CI join I
┌────────────┬──────────────────────┬────────────┬────────────┬──────────────┬────────────────────┬────────────────────┬────────────────┐
│ C#::number │ Cname::string        │ HP::number │ I#::number │ Slot::number │ Iname::string      │ Durability::number │ Damage::number │
├────────────┼──────────────────────┼────────────┼────────────┼──────────────┼────────────────────┼────────────────────┼────────────────┤
│ 1          │ Reese of Wellington  │ 4          │ 1          │ 3            │ Bronze Poniard     │ 10                 │ 10             │
├────────────┼──────────────────────┼────────────┼────────────┼──────────────┼────────────────────┼────────────────────┼────────────────┤
│ 1          │ Reese of Wellington  │ 4          │ 3          │ 4            │ Venom-Soaked Blade │ 5                  │ 25             │
├────────────┼──────────────────────┼────────────┼────────────┼──────────────┼────────────────────┼────────────────────┼────────────────┤
│ 2          │ Sir Nic of the Weils │ 18         │ 2          │ 4            │ Glazed Donut       │ 1                  │ 1              │
├────────────┼──────────────────────┼────────────┼────────────┼──────────────┼────────────────────┼────────────────────┼────────────────┤
│ 2          │ Sir Nic of the Weils │ 18         │ 4          │ 3            │ Ancient Spoon      │ 1                  │ 80             │
├────────────┼──────────────────────┼────────────┼────────────┼──────────────┼────────────────────┼────────────────────┼────────────────┤
│ 2          │ Sir Nic of the Weils │ 18         │ 1          │ 10           │ Bronze Poniard     │ 10                 │ 10             │
└────────────┴──────────────────────┴────────────┴────────────┴──────────────┴────────────────────┴────────────────────┴────────────────┘
(C join CI join I){ALL BUT C#, HP, I#}
┌──────────────────────┬──────────────┬────────────────────┬────────────────────┬────────────────┐
│ Cname::string        │ Slot::number │ Iname::string      │ Durability::number │ Damage::number │
├──────────────────────┼──────────────┼────────────────────┼────────────────────┼────────────────┤
│ Reese of Wellington  │ 3            │ Bronze Poniard     │ 10                 │ 10             │
├──────────────────────┼──────────────┼────────────────────┼────────────────────┼────────────────┤
│ Reese of Wellington  │ 4            │ Venom-Soaked Blade │ 5                  │ 25             │
├──────────────────────┼──────────────┼────────────────────┼────────────────────┼────────────────┤
│ Sir Nic of the Weils │ 4            │ Glazed Donut       │ 1                  │ 1              │
├──────────────────────┼──────────────┼────────────────────┼────────────────────┼────────────────┤
│ Sir Nic of the Weils │ 3            │ Ancient Spoon      │ 1                  │ 80             │
├──────────────────────┼──────────────┼────────────────────┼────────────────────┼────────────────┤
│ Sir Nic of the Weils │ 10           │ Bronze Poniard     │ 10                 │ 10             │
└──────────────────────┴──────────────┴────────────────────┴────────────────────┴────────────────┘
Variant to test/display commutative properties of join:
(C join I join CI){ALL BUT C#, HP, I#}
┌──────────────────────┬────────────────────┬────────────────────┬────────────────┬──────────────┐
│ Cname::string        │ Iname::string      │ Durability::number │ Damage::number │ Slot::number │
├──────────────────────┼────────────────────┼────────────────────┼────────────────┼──────────────┤
│ Reese of Wellington  │ Bronze Poniard     │ 10                 │ 10             │ 3            │
├──────────────────────┼────────────────────┼────────────────────┼────────────────┼──────────────┤
│ Reese of Wellington  │ Venom-Soaked Blade │ 5                  │ 25             │ 4            │
├──────────────────────┼────────────────────┼────────────────────┼────────────────┼──────────────┤
│ Sir Nic of the Weils │ Bronze Poniard     │ 10                 │ 10             │ 10           │
├──────────────────────┼────────────────────┼────────────────────┼────────────────┼──────────────┤
│ Sir Nic of the Weils │ Glazed Donut       │ 1                  │ 1              │ 4            │
├──────────────────────┼────────────────────┼────────────────────┼────────────────┼──────────────┤
│ Sir Nic of the Weils │ Ancient Spoon      │ 1                  │ 80             │ 3            │
└──────────────────────┴────────────────────┴────────────────────┴────────────────┴──────────────┘
Give me all items that are owned by no character:
I NOT MATCHING CI
┌────────────┬──────────────────────────────┬────────────────────┬────────────────┐
│ I#::number │ Iname::string                │ Durability::number │ Damage::number │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┤
│ 5          │ Holy Hand Grenade of Antioch │ 1                  │ 999            │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┤
│ 6          │ Common Shortsword            │ 30                 │ 15             │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┤
│ 7          │ Rot-Hilted Axe               │ 8                  │ 15             │
└────────────┴──────────────────────────────┴────────────────────┴────────────────┘
All characters with an item that deals more than 30 damage:
C MATCHING (CI JOIN (I WHERE {Damage > 30}))
┌────────────┬──────────────────────┬────────────┐
│ C#::number │ Cname::string        │ HP::number │
├────────────┼──────────────────────┼────────────┤
│ 2          │ Sir Nic of the Weils │ 18         │
└────────────┴──────────────────────┴────────────┘
Just their names:
(C MATCHING (CI JOIN (I WHERE {Damage > 30}))){Cname}
┌──────────────────────┐
│ Cname::string        │
├──────────────────────┤
│ Sir Nic of the Weils │
└──────────────────────┘
┌──────────────────────┐
│ Cname::string        │
├──────────────────────┤
│ Sir Nic of the Weils │
└──────────────────────┘
Characters and the items they DON'T have:
((C{C#} JOIN I{I#}) MINUS (CI{C#, I#}) JOIN C{C#, Cname} JOIN I{I#, Iname}) {Cname, Iname} RENAME {Cname AS Character, Iname AS Missing Item}
┌──────────────────────┬──────────────────────────────┐
│ Character::string    │ Missing Item::string         │
├──────────────────────┼──────────────────────────────┤
│ Reese of Wellington  │ Glazed Donut                 │
├──────────────────────┼──────────────────────────────┤
│ Reese of Wellington  │ Ancient Spoon                │
├──────────────────────┼──────────────────────────────┤
│ Reese of Wellington  │ Holy Hand Grenade of Antioch │
├──────────────────────┼──────────────────────────────┤
│ Reese of Wellington  │ Common Shortsword            │
├──────────────────────┼──────────────────────────────┤
│ Reese of Wellington  │ Rot-Hilted Axe               │
├──────────────────────┼──────────────────────────────┤
│ Sir Nic of the Weils │ Venom-Soaked Blade           │
├──────────────────────┼──────────────────────────────┤
│ Sir Nic of the Weils │ Holy Hand Grenade of Antioch │
├──────────────────────┼──────────────────────────────┤
│ Sir Nic of the Weils │ Common Shortsword            │
├──────────────────────┼──────────────────────────────┤
│ Sir Nic of the Weils │ Rot-Hilted Axe               │
└──────────────────────┴──────────────────────────────┘
Fix overpowered items:
UPDATE I WHERE Damage > 30: {Damage := Damage / 2}
┌────────────┬──────────────────────────────┬────────────────────┬────────────────┐
│ I#::number │ Iname::string                │ Durability::number │ Damage::number │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┤
│ 1          │ Bronze Poniard               │ 10                 │ 10             │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┤
│ 2          │ Glazed Donut                 │ 1                  │ 1              │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┤
│ 3          │ Venom-Soaked Blade           │ 5                  │ 25             │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┤
│ 4          │ Ancient Spoon                │ 1                  │ 40             │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┤
│ 5          │ Holy Hand Grenade of Antioch │ 1                  │ 499            │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┤
│ 6          │ Common Shortsword            │ 30                 │ 15             │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┤
│ 7          │ Rot-Hilted Axe               │ 8                  │ 15             │
└────────────┴──────────────────────────────┴────────────────────┴────────────────┘
Show Damage Per Durability (DPD): 
EXTEND I: {DPD := Damage / Durability}
┌────────────┬──────────────────────────────┬────────────────────┬────────────────┬─────────────┐
│ I#::number │ Iname::string                │ Durability::number │ Damage::number │ DPD::number │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┼─────────────┤
│ 1          │ Bronze Poniard               │ 10                 │ 10             │ 1           │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┼─────────────┤
│ 2          │ Glazed Donut                 │ 1                  │ 1              │ 1           │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┼─────────────┤
│ 3          │ Venom-Soaked Blade           │ 5                  │ 25             │ 5           │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┼─────────────┤
│ 4          │ Ancient Spoon                │ 1                  │ 80             │ 80          │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┼─────────────┤
│ 5          │ Holy Hand Grenade of Antioch │ 1                  │ 999            │ 999         │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┼─────────────┤
│ 6          │ Common Shortsword            │ 30                 │ 15             │ 1           │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┼─────────────┤
│ 7          │ Rot-Hilted Axe               │ 8                  │ 15             │ 2           │
└────────────┴──────────────────────────────┴────────────────────┴────────────────┴─────────────┘
Count of all character-item combos:
        COUNT ( CI ) = 5
Count of all distinct slots in character-item combos:
        COUNT ( CI { Slot } ) : 3
Sum of all item damage:
        SUM (I, Damage) = 1145
Sum of all distinct item damage values:
        SUM (I, { Damage }) = 1130
Sum of tripled item damage:
        SUM (I, 3 * Damage) =  3435
Sum of all distinct tripled item damage:
        SUM( EXTEND I : { Damage := Damage * 3 }, {Damage}) =  3390
My D implementation is behind on most features, but the plumbing exists:
(C join CI join I){ALL BUT C#, HP, I#}
┌──────────────────────┬────────────────────┬────────────────────┬────────────────┬──────────────┐
│ Cname::string        │ Iname::string      │ Durability::number │ Damage::number │ Slot::number │
├──────────────────────┼────────────────────┼────────────────────┼────────────────┼──────────────┤
│ Reese of Wellington  │ Bronze Poniard     │ 10                 │ 10             │ 3            │
├──────────────────────┼────────────────────┼────────────────────┼────────────────┼──────────────┤
│ Reese of Wellington  │ Venom-Soaked Blade │ 5                  │ 25             │ 4            │
├──────────────────────┼────────────────────┼────────────────────┼────────────────┼──────────────┤
│ Sir Nic of the Weils │ Bronze Poniard     │ 10                 │ 10             │ 10           │
├──────────────────────┼────────────────────┼────────────────────┼────────────────┼──────────────┤
│ Sir Nic of the Weils │ Glazed Donut       │ 1                  │ 1              │ 4            │
├──────────────────────┼────────────────────┼────────────────────┼────────────────┼──────────────┤
│ Sir Nic of the Weils │ Ancient Spoon      │ 1                  │ 80             │ 3            │
└──────────────────────┴────────────────────┴────────────────────┴────────────────┴──────────────┘
Attempt to violate the constraint:
{ c: 'CONSTRAINTS_FAILED', constraint_names: [ 'UNIQ_I' ] }
Concede, follow the constraint:
┌────────────┬──────────────────────────────┬────────────────────┬────────────────┐
│ I#::number │ Iname::string                │ Durability::number │ Damage::number │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┤
│ 1          │ Bronze Poniard               │ 10                 │ 10             │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┤
│ 2          │ Glazed Donut                 │ 1                  │ 1              │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┤
│ 3          │ Venom-Soaked Blade           │ 5                  │ 25             │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┤
│ 4          │ Ancient Spoon                │ 1                  │ 80             │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┤
│ 5          │ Holy Hand Grenade of Antioch │ 1                  │ 999            │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┤
│ 6          │ Common Shortsword            │ 30                 │ 15             │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┤
│ 7          │ Rot-Hilted Axe               │ 8                  │ 15             │
├────────────┼──────────────────────────────┼────────────────────┼────────────────┤
│ 8          │ Floppy Disk                  │ 40                 │ 3              │
└────────────┴──────────────────────────────┴────────────────────┴────────────────┘
Attempt to violate again on new DB:
{ c: 'CONSTRAINTS_FAILED', constraint_names: [ 'UNIQ_I' ] }

About

Primitive, Amateur Relational Database in JS

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published