@instantdb/core
Advanced tools
Comparing version 0.5.5 to 0.5.6
import { test, expect } from "vitest"; | ||
import movieTriples from "./data/movieTriples.json"; | ||
import movieAttrs from "./data/movies/attrs.json" | ||
import movieTriples from "./data/movies/triples.json"; | ||
import { | ||
@@ -10,4 +11,5 @@ matchPattern, | ||
import { createStore } from "../../src/store"; | ||
import { getAttrByFwdIdentName } from "../../src/instaml"; | ||
const store = createStore({}, movieTriples); | ||
const store = createStore(movieAttrs, movieTriples); | ||
@@ -31,10 +33,31 @@ test("matchPattern", () => { | ||
function aid(friendlyName) { | ||
const [etype, label] = friendlyName.split("/"); | ||
const attr = getAttrByFwdIdentName(store.attrs, etype, label); | ||
return attr.id | ||
} | ||
function mid(movieName) { | ||
const [eid] = querySingle(store, ["?eid", aid("movie/title"), movieName], {}); | ||
return eid["?eid"]; | ||
} | ||
function pid(movieName) { | ||
const [eid] = querySingle(store, ["?eid", aid("person/name"), movieName], {}); | ||
return eid["?eid"]; | ||
} | ||
test("querySingle", () => { | ||
expect(querySingle(store, ["?movieId", "movie/year", 1987], {})).toEqual([ | ||
{ "?movieId": 202 }, | ||
{ "?movieId": 203 }, | ||
{ "?movieId": 204 }, | ||
]); | ||
const res = querySingle(store, ["?movieId", aid("movie/year"), 1987], {}) | ||
const movieIds = res.map((x) => x["?movieId"]).sort(); | ||
expect(movieIds).toEqual( | ||
[ | ||
mid("Predator"), | ||
mid("RoboCop"), | ||
mid("Lethal Weapon") | ||
].sort() | ||
); | ||
}); | ||
test("queryWhere", () => { | ||
@@ -45,5 +68,5 @@ expect( | ||
[ | ||
["?movieId", "movie/title", "The Terminator"], | ||
["?movieId", "movie/director", "?directorId"], | ||
["?directorId", "person/name", "?directorName"], | ||
["?movieId", aid("movie/title"), "The Terminator"], | ||
["?movieId", aid("movie/director"), "?directorId"], | ||
["?directorId", aid("person/name"), "?directorName"], | ||
], | ||
@@ -53,3 +76,3 @@ {} | ||
).toEqual([ | ||
{ "?movieId": 200, "?directorId": 100, "?directorName": "James Cameron" }, | ||
{ "?movieId": mid("The Terminator"), "?directorId": pid("James Cameron"), "?directorName": "James Cameron" }, | ||
]); | ||
@@ -63,5 +86,5 @@ }); | ||
where: [ | ||
["?movieId", "movie/title", "The Terminator"], | ||
["?movieId", "movie/director", "?directorId"], | ||
["?directorId", "person/name", "?directorName"], | ||
["?movieId", aid("movie/title"), "The Terminator"], | ||
["?movieId", aid("movie/director"), "?directorId"], | ||
["?directorId", aid("person/name"), "?directorName"], | ||
], | ||
@@ -83,4 +106,4 @@ }) | ||
where: [ | ||
["?id", "movie/title", "Alien"], | ||
["?id", "movie/year", "?year"], | ||
["?id", aid("movie/title"), "Alien"], | ||
["?id", aid("movie/year"), "?year"], | ||
], | ||
@@ -93,5 +116,5 @@ }) | ||
where: [ | ||
["?movieId", "movie/title", "RoboCop"], | ||
["?movieId", "movie/director", "?directorId"], | ||
["?directorId", "person/name", "?directorName"], | ||
["?movieId", aid("movie/title"), "RoboCop"], | ||
["?movieId", aid("movie/director"), "?directorId"], | ||
["?directorId", aid("person/name"), "?directorName"], | ||
], | ||
@@ -101,19 +124,17 @@ }) | ||
expect( | ||
new Set( | ||
query(store, { | ||
find: ["?attr", "?value"], | ||
where: [[200, "?attr", "?value"]], | ||
}) | ||
) | ||
).toEqual( | ||
new Set([ | ||
["movie/title", "The Terminator"], | ||
["movie/year", 1984], | ||
["movie/director", 100], | ||
["movie/cast", 101], | ||
["movie/cast", 102], | ||
["movie/cast", 103], | ||
["movie/sequel", 207], | ||
]) | ||
); | ||
query(store, { | ||
find: ["?value"], | ||
where: [ | ||
[mid("The Terminator"), "?attr", "?value"], | ||
], | ||
}).map((x) => x[0]).sort()).toEqual([ | ||
"The Terminator", | ||
1984, | ||
pid("James Cameron"), | ||
pid("Arnold Schwarzenegger"), | ||
pid("Linda Hamilton"), | ||
pid("Michael Biehn"), | ||
mid("The Terminator"), | ||
mid("Terminator 2: Judgment Day"), | ||
].sort()) | ||
expect( | ||
@@ -124,7 +145,7 @@ new Set( | ||
where: [ | ||
["?arnoldId", "person/name", "Arnold Schwarzenegger"], | ||
["?movieId", "movie/cast", "?arnoldId"], | ||
["?movieId", "movie/title", "?movieTitle"], | ||
["?movieId", "movie/director", "?directorId"], | ||
["?directorId", "person/name", "?directorName"], | ||
["?arnoldId", aid("person/name"), "Arnold Schwarzenegger"], | ||
["?movieId", aid("movie/cast"), "?arnoldId"], | ||
["?movieId", aid("movie/title"), "?movieTitle"], | ||
["?movieId", aid("movie/director"), "?directorId"], | ||
["?directorId", aid("person/name"), "?directorName"], | ||
], | ||
@@ -131,0 +152,0 @@ }) |
export function matchPattern(pattern: any, triple: any, context: any): any; | ||
export function querySingle(store: any, pattern: any, context: any): any; | ||
export function querySingle(store: any, pattern: any, context: any): any[]; | ||
export function queryWhere(store: any, patterns: any): any; | ||
@@ -4,0 +4,0 @@ export function query(store: any, { find, where }: { |
"use strict"; | ||
// 1. patternMatch | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.query = exports.queryWhere = exports.querySingle = exports.matchPattern = void 0; | ||
// 1. patternMatch | ||
const store_js_1 = require("./store.js"); | ||
function isVariable(x) { | ||
@@ -32,3 +33,3 @@ return typeof x === "string" && x.startsWith("?"); | ||
function querySingle(store, pattern, context) { | ||
return relevantTriples(store, pattern) | ||
return relevantTriples(store, pattern, context) | ||
.map((triple) => matchPattern(pattern, triple, context)) | ||
@@ -58,5 +59,5 @@ .filter((x) => x); | ||
// 5. Index | ||
function relevantTriples(store, _pattern) { | ||
return store.triples; | ||
function relevantTriples(store, pattern, context) { | ||
return (0, store_js_1.getTriples)(store, actualize(context, pattern)); | ||
} | ||
//# sourceMappingURL=datalog.js.map |
export function matchPattern(pattern: any, triple: any, context: any): any; | ||
export function querySingle(store: any, pattern: any, context: any): any; | ||
export function querySingle(store: any, pattern: any, context: any): any[]; | ||
export function queryWhere(store: any, patterns: any): any; | ||
@@ -4,0 +4,0 @@ export function query(store: any, { find, where }: { |
// 1. patternMatch | ||
import { getTriples } from "./store.js"; | ||
function isVariable(x) { | ||
@@ -28,3 +29,3 @@ return typeof x === "string" && x.startsWith("?"); | ||
export function querySingle(store, pattern, context) { | ||
return relevantTriples(store, pattern) | ||
return relevantTriples(store, pattern, context) | ||
.map((triple) => matchPattern(pattern, triple, context)) | ||
@@ -51,5 +52,5 @@ .filter((x) => x); | ||
// 5. Index | ||
function relevantTriples(store, _pattern) { | ||
return store.triples; | ||
function relevantTriples(store, pattern, context) { | ||
return getTriples(store, actualize(context, pattern)); | ||
} | ||
//# sourceMappingURL=datalog.js.map |
@@ -44,13 +44,3 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
_extractTriplesHelper(idNodes, triples); | ||
const seen = {}; | ||
return triples.filter((x) => { | ||
const k = weakHash(x); | ||
if (seen[k]) { | ||
return false; | ||
} | ||
else { | ||
seen[k] = true; | ||
return true; | ||
} | ||
}); | ||
return triples; | ||
} | ||
@@ -102,3 +92,3 @@ // PersistedObjects save data outside of memory | ||
// (XXX): This could fail, we should handle this | ||
this._persister.setItem(this._key, this.toJSON(this.currentValue)); | ||
setTimeout(() => this._persister.setItem(this._key, this.toJSON(this.currentValue)), 0); | ||
} | ||
@@ -220,3 +210,3 @@ } | ||
} | ||
this._persister = new Storage(`instant_${config.appId}`); | ||
this._persister = new Storage(`instant_${config.appId}_3`); | ||
this.querySubs = new PersistedObject(this._persister, "querySubs", {}, this._onMergeQuerySubs); | ||
@@ -223,0 +213,0 @@ this.pendingMutations = new PersistedObject(this._persister, "pendingMutations", new Map(), this._onMergePendingMutations, (x) => { |
@@ -1,6 +0,4 @@ | ||
export function createStore(attrs: any, triples: any): { | ||
attrs: any; | ||
triples: any; | ||
}; | ||
export function createStore(attrs: any, triples: any): any; | ||
export function getTriples(store: any, [e]: [any]): any[]; | ||
export function transact(store: any, txSteps: any): (base?: (draft: any) => void, ...args: unknown[]) => ((draft: any) => void) | Promise<(draft: any) => void>; | ||
//# sourceMappingURL=store.d.ts.map |
import { produce } from "immer"; | ||
function hasEA(attr) { | ||
return attr["cardinality"] === "one"; | ||
} | ||
function isRef(attr) { | ||
return attr["value-type"] === "ref"; | ||
} | ||
function getAttrAsserting(attrs, attrId) { | ||
const attr = attrs[attrId]; | ||
if (!attr) | ||
throw new Error(`no such attr: ${attrId}`); | ||
return attr; | ||
} | ||
function setIn(obj, path, value) { | ||
if (path.length === 0) | ||
throw new Error("path must have at least one element"); | ||
if (path.length === 1) { | ||
obj[path[0]] = value; | ||
return; | ||
} | ||
const [head, ...tail] = path; | ||
if (!obj[head]) | ||
obj[head] = {}; | ||
setIn(obj[head], tail, value); | ||
} | ||
function deleteIn(obj, path) { | ||
if (path.length === 0) | ||
throw new Error("path must have at least one element"); | ||
if (path.length === 1) { | ||
delete obj[path[0]]; | ||
return; | ||
} | ||
const [head, ...tail] = path; | ||
if (!obj[head]) | ||
return; | ||
deleteIn(obj[head], tail); | ||
} | ||
function createIndexMap(attrs, triples) { | ||
return triples.reduce((indexMap, triple) => { | ||
const [eid, aid, v] = triple; | ||
const attr = getAttrAsserting(attrs, aid); | ||
if (hasEA(attr)) { | ||
setIn(indexMap, ["ea", eid, aid], triple); | ||
} | ||
if (isRef(attr)) { | ||
setIn(indexMap, ["eav", eid, aid, v], triple); | ||
setIn(indexMap, ["vae", v, aid, eid], triple); | ||
} | ||
return indexMap; | ||
}, { ea: {}, eav: {}, vae: {} }); | ||
} | ||
export function createStore(attrs, triples) { | ||
return { attrs, triples }; | ||
const store = createIndexMap(attrs, triples); | ||
store.attrs = attrs; | ||
return store; | ||
} | ||
function retractTriple(store, triple) { | ||
store.triples = store.triples.filter((x) => !x.every((y, i) => y === triple[i])); | ||
const [eid, aid, v] = triple; | ||
const attr = getAttrAsserting(store.attrs, aid); | ||
if (hasEA(attr)) { | ||
deleteIn(store.ea, [eid, aid]); | ||
} | ||
if (isRef(attr)) { | ||
deleteIn(store.eav, [eid, aid, v]); | ||
deleteIn(store.vae, [v, aid, eid]); | ||
} | ||
} | ||
function addTriple(store, triple) { | ||
store.triples.push(triple); | ||
const [eid, aid, v] = triple; | ||
const attr = getAttrAsserting(store.attrs, aid); | ||
if (hasEA(attr)) { | ||
setIn(store.ea, [eid, aid], triple); | ||
} | ||
if (isRef(attr)) { | ||
setIn(store.eav, [eid, aid, v], triple); | ||
setIn(store.vae, [v, aid, eid], triple); | ||
} | ||
} | ||
function retractCardinalityOne(store, [e, a]) { | ||
const attr = store.attrs[a]; | ||
if (attr["cardinality"] !== "one") | ||
return; | ||
store.triples = store.triples.filter((x) => !(x[0] === e && x[1] == a)); | ||
} | ||
function deleteEntity(store, [id]) { | ||
store.triples = store.triples.filter((x) => x[0] !== id && x[2] !== id); | ||
// delete object attributes + cardinality one links | ||
deleteIn(store.ea, [id]); | ||
// delete forward links | ||
deleteIn(store.eav, [id]); | ||
// delete reverse links | ||
const vaeTriples = store.vae[id] && | ||
Object.values(store.vae[id]).flatMap(vMap => Object.values(vMap)); | ||
if (vaeTriples) { | ||
vaeTriples.forEach((triple) => { | ||
deleteIn(store.eav, triple); | ||
}); | ||
} | ||
deleteIn(store.vae, [id]); | ||
} | ||
@@ -27,5 +101,2 @@ function addAttr(store, [attr]) { | ||
case "add-triple": | ||
retractCardinalityOne(store, args); | ||
// to handle dups, we always remove first. | ||
retractTriple(store, args); | ||
addTriple(store, args); | ||
@@ -46,2 +117,25 @@ break; | ||
} | ||
function getEATriples(store, e) { | ||
const aMap = store.ea[e]; | ||
return aMap | ||
? Object.values(aMap) | ||
: Object.values(store.ea).flatMap(aMap => Object.values(aMap)); | ||
} | ||
function getEAVTriples(store, e) { | ||
const aMap = store.eav[e]; | ||
return aMap | ||
? Object.values(aMap) | ||
.flatMap(vMap => Object.values(vMap)) | ||
: Object.values(store.eav) | ||
.flatMap(aMap => { | ||
return Object.values(aMap) | ||
.flatMap(vMap => Object.values(vMap)); | ||
}); | ||
} | ||
export function getTriples(store, [e]) { | ||
const eaTriples = getEATriples(store, e); | ||
const eavTriples = getEAVTriples(store, e) | ||
.filter(([_, aid]) => !hasEA(getAttrAsserting(store.attrs, aid))); | ||
return [...eaTriples, ...eavTriples]; | ||
} | ||
export function transact(store, txSteps) { | ||
@@ -48,0 +142,0 @@ return produce(store, (draft) => { |
@@ -72,13 +72,3 @@ "use strict"; | ||
_extractTriplesHelper(idNodes, triples); | ||
const seen = {}; | ||
return triples.filter((x) => { | ||
const k = (0, weakHash_1.default)(x); | ||
if (seen[k]) { | ||
return false; | ||
} | ||
else { | ||
seen[k] = true; | ||
return true; | ||
} | ||
}); | ||
return triples; | ||
} | ||
@@ -130,3 +120,3 @@ // PersistedObjects save data outside of memory | ||
// (XXX): This could fail, we should handle this | ||
this._persister.setItem(this._key, this.toJSON(this.currentValue)); | ||
setTimeout(() => this._persister.setItem(this._key, this.toJSON(this.currentValue)), 0); | ||
} | ||
@@ -248,3 +238,3 @@ } | ||
} | ||
this._persister = new Storage(`instant_${config.appId}`); | ||
this._persister = new Storage(`instant_${config.appId}_3`); | ||
this.querySubs = new PersistedObject(this._persister, "querySubs", {}, this._onMergeQuerySubs); | ||
@@ -251,0 +241,0 @@ this.pendingMutations = new PersistedObject(this._persister, "pendingMutations", new Map(), this._onMergePendingMutations, (x) => { |
@@ -1,6 +0,4 @@ | ||
export function createStore(attrs: any, triples: any): { | ||
attrs: any; | ||
triples: any; | ||
}; | ||
export function createStore(attrs: any, triples: any): any; | ||
export function getTriples(store: any, [e]: [any]): any[]; | ||
export function transact(store: any, txSteps: any): (base?: (draft: any) => void, ...args: unknown[]) => ((draft: any) => void) | Promise<(draft: any) => void>; | ||
//# sourceMappingURL=store.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.transact = exports.createStore = void 0; | ||
exports.transact = exports.getTriples = exports.createStore = void 0; | ||
const immer_1 = require("immer"); | ||
function hasEA(attr) { | ||
return attr["cardinality"] === "one"; | ||
} | ||
function isRef(attr) { | ||
return attr["value-type"] === "ref"; | ||
} | ||
function getAttrAsserting(attrs, attrId) { | ||
const attr = attrs[attrId]; | ||
if (!attr) | ||
throw new Error(`no such attr: ${attrId}`); | ||
return attr; | ||
} | ||
function setIn(obj, path, value) { | ||
if (path.length === 0) | ||
throw new Error("path must have at least one element"); | ||
if (path.length === 1) { | ||
obj[path[0]] = value; | ||
return; | ||
} | ||
const [head, ...tail] = path; | ||
if (!obj[head]) | ||
obj[head] = {}; | ||
setIn(obj[head], tail, value); | ||
} | ||
function deleteIn(obj, path) { | ||
if (path.length === 0) | ||
throw new Error("path must have at least one element"); | ||
if (path.length === 1) { | ||
delete obj[path[0]]; | ||
return; | ||
} | ||
const [head, ...tail] = path; | ||
if (!obj[head]) | ||
return; | ||
deleteIn(obj[head], tail); | ||
} | ||
function createIndexMap(attrs, triples) { | ||
return triples.reduce((indexMap, triple) => { | ||
const [eid, aid, v] = triple; | ||
const attr = getAttrAsserting(attrs, aid); | ||
if (hasEA(attr)) { | ||
setIn(indexMap, ["ea", eid, aid], triple); | ||
} | ||
if (isRef(attr)) { | ||
setIn(indexMap, ["eav", eid, aid, v], triple); | ||
setIn(indexMap, ["vae", v, aid, eid], triple); | ||
} | ||
return indexMap; | ||
}, { ea: {}, eav: {}, vae: {} }); | ||
} | ||
function createStore(attrs, triples) { | ||
return { attrs, triples }; | ||
const store = createIndexMap(attrs, triples); | ||
store.attrs = attrs; | ||
return store; | ||
} | ||
exports.createStore = createStore; | ||
function retractTriple(store, triple) { | ||
store.triples = store.triples.filter((x) => !x.every((y, i) => y === triple[i])); | ||
const [eid, aid, v] = triple; | ||
const attr = getAttrAsserting(store.attrs, aid); | ||
if (hasEA(attr)) { | ||
deleteIn(store.ea, [eid, aid]); | ||
} | ||
if (isRef(attr)) { | ||
deleteIn(store.eav, [eid, aid, v]); | ||
deleteIn(store.vae, [v, aid, eid]); | ||
} | ||
} | ||
function addTriple(store, triple) { | ||
store.triples.push(triple); | ||
const [eid, aid, v] = triple; | ||
const attr = getAttrAsserting(store.attrs, aid); | ||
if (hasEA(attr)) { | ||
setIn(store.ea, [eid, aid], triple); | ||
} | ||
if (isRef(attr)) { | ||
setIn(store.eav, [eid, aid, v], triple); | ||
setIn(store.vae, [v, aid, eid], triple); | ||
} | ||
} | ||
function retractCardinalityOne(store, [e, a]) { | ||
const attr = store.attrs[a]; | ||
if (attr["cardinality"] !== "one") | ||
return; | ||
store.triples = store.triples.filter((x) => !(x[0] === e && x[1] == a)); | ||
} | ||
function deleteEntity(store, [id]) { | ||
store.triples = store.triples.filter((x) => x[0] !== id && x[2] !== id); | ||
// delete object attributes + cardinality one links | ||
deleteIn(store.ea, [id]); | ||
// delete forward links | ||
deleteIn(store.eav, [id]); | ||
// delete reverse links | ||
const vaeTriples = store.vae[id] && | ||
Object.values(store.vae[id]).flatMap(vMap => Object.values(vMap)); | ||
if (vaeTriples) { | ||
vaeTriples.forEach((triple) => { | ||
deleteIn(store.eav, triple); | ||
}); | ||
} | ||
deleteIn(store.vae, [id]); | ||
} | ||
@@ -31,5 +105,2 @@ function addAttr(store, [attr]) { | ||
case "add-triple": | ||
retractCardinalityOne(store, args); | ||
// to handle dups, we always remove first. | ||
retractTriple(store, args); | ||
addTriple(store, args); | ||
@@ -50,2 +121,26 @@ break; | ||
} | ||
function getEATriples(store, e) { | ||
const aMap = store.ea[e]; | ||
return aMap | ||
? Object.values(aMap) | ||
: Object.values(store.ea).flatMap(aMap => Object.values(aMap)); | ||
} | ||
function getEAVTriples(store, e) { | ||
const aMap = store.eav[e]; | ||
return aMap | ||
? Object.values(aMap) | ||
.flatMap(vMap => Object.values(vMap)) | ||
: Object.values(store.eav) | ||
.flatMap(aMap => { | ||
return Object.values(aMap) | ||
.flatMap(vMap => Object.values(vMap)); | ||
}); | ||
} | ||
function getTriples(store, [e]) { | ||
const eaTriples = getEATriples(store, e); | ||
const eavTriples = getEAVTriples(store, e) | ||
.filter(([_, aid]) => !hasEA(getAttrAsserting(store.attrs, aid))); | ||
return [...eaTriples, ...eavTriples]; | ||
} | ||
exports.getTriples = getTriples; | ||
function transact(store, txSteps) { | ||
@@ -52,0 +147,0 @@ return (0, immer_1.produce)(store, (draft) => { |
{ | ||
"name": "@instantdb/core", | ||
"version": "0.5.5", | ||
"version": "0.5.6", | ||
"description": "Instant's core local abstraction", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
// 1. patternMatch | ||
import { getTriples } from "./store.js"; | ||
@@ -33,3 +34,3 @@ function isVariable(x) { | ||
export function querySingle(store, pattern, context) { | ||
return relevantTriples(store, pattern) | ||
return relevantTriples(store, pattern, context) | ||
.map((triple) => matchPattern(pattern, triple, context)) | ||
@@ -68,4 +69,4 @@ .filter((x) => x); | ||
function relevantTriples(store, _pattern) { | ||
return store.triples; | ||
function relevantTriples(store, pattern, context) { | ||
return getTriples(store, actualize(context, pattern)); | ||
} |
@@ -38,12 +38,3 @@ import log from "./utils/log"; | ||
_extractTriplesHelper(idNodes, triples); | ||
const seen = {}; | ||
return triples.filter((x) => { | ||
const k = weakHash(x); | ||
if (seen[k]) { | ||
return false; | ||
} else { | ||
seen[k] = true; | ||
return true; | ||
} | ||
}); | ||
return triples | ||
} | ||
@@ -106,3 +97,3 @@ | ||
// (XXX): This could fail, we should handle this | ||
this._persister.setItem(this._key, this.toJSON(this.currentValue)); | ||
setTimeout(() => this._persister.setItem(this._key, this.toJSON(this.currentValue)), 0); | ||
} | ||
@@ -142,3 +133,3 @@ } | ||
} | ||
this._persister = new Storage(`instant_${config.appId}`); | ||
this._persister = new Storage(`instant_${config.appId}_3`); | ||
this.querySubs = new PersistedObject( | ||
@@ -145,0 +136,0 @@ this._persister, |
130
src/store.js
import { produce } from "immer"; | ||
function hasEA(attr) { | ||
return attr["cardinality"] === "one"; | ||
} | ||
function isRef(attr) { | ||
return attr["value-type"] === "ref"; | ||
} | ||
function getAttrAsserting(attrs, attrId) { | ||
const attr = attrs[attrId]; | ||
if (!attr) throw new Error(`no such attr: ${attrId}`); | ||
return attr; | ||
} | ||
function setIn(obj, path, value) { | ||
if (path.length === 0) throw new Error("path must have at least one element"); | ||
if (path.length === 1) { | ||
obj[path[0]] = value; | ||
return; | ||
} | ||
const [head, ...tail] = path; | ||
if (!obj[head]) obj[head] = {}; | ||
setIn(obj[head], tail, value); | ||
} | ||
function deleteIn(obj, path) { | ||
if (path.length === 0) throw new Error("path must have at least one element"); | ||
if (path.length === 1) { | ||
delete obj[path[0]]; | ||
return; | ||
} | ||
const [head, ...tail] = path; | ||
if (!obj[head]) return; | ||
deleteIn(obj[head], tail); | ||
} | ||
function createIndexMap(attrs, triples) { | ||
return triples.reduce((indexMap, triple) => { | ||
const [eid, aid, v] = triple; | ||
const attr = getAttrAsserting(attrs, aid); | ||
if (hasEA(attr)) { | ||
setIn(indexMap, ["ea", eid, aid], triple); | ||
} | ||
if (isRef(attr)) { | ||
setIn(indexMap, ["eav", eid, aid, v], triple); | ||
setIn(indexMap, ["vae", v, aid, eid], triple); | ||
} | ||
return indexMap; | ||
}, { ea: {}, eav: {}, vae: {} }); | ||
} | ||
export function createStore(attrs, triples) { | ||
return { attrs, triples }; | ||
const store = createIndexMap(attrs, triples); | ||
store.attrs = attrs | ||
return store | ||
} | ||
function retractTriple(store, triple) { | ||
store.triples = store.triples.filter( | ||
(x) => !x.every((y, i) => y === triple[i]) | ||
); | ||
const [eid, aid, v] = triple; | ||
const attr = getAttrAsserting(store.attrs, aid); | ||
if (hasEA(attr)) { | ||
deleteIn(store.ea, [eid, aid]); | ||
} | ||
if (isRef(attr)) { | ||
deleteIn(store.eav, [eid, aid, v]); | ||
deleteIn(store.vae, [v, aid, eid]); | ||
} | ||
} | ||
function addTriple(store, triple) { | ||
store.triples.push(triple); | ||
const [eid, aid, v] = triple; | ||
const attr = getAttrAsserting(store.attrs, aid); | ||
if (hasEA(attr)) { | ||
setIn(store.ea, [eid, aid], triple); | ||
} | ||
if (isRef(attr)) { | ||
setIn(store.eav, [eid, aid, v], triple); | ||
setIn(store.vae, [v, aid, eid], triple); | ||
} | ||
} | ||
function retractCardinalityOne(store, [e, a]) { | ||
const attr = store.attrs[a]; | ||
if (attr["cardinality"] !== "one") return; | ||
store.triples = store.triples.filter((x) => !(x[0] === e && x[1] == a)); | ||
} | ||
function deleteEntity(store, [id]) { | ||
// delete object attributes + cardinality one links | ||
deleteIn(store.ea, [id]); | ||
function deleteEntity(store, [id]) { | ||
store.triples = store.triples.filter((x) => x[0] !== id && x[2] !== id); | ||
// delete forward links | ||
deleteIn(store.eav, [id]); | ||
// delete reverse links | ||
const vaeTriples = store.vae[id] && | ||
Object.values(store.vae[id]).flatMap(vMap => Object.values(vMap)); | ||
if (vaeTriples) { | ||
vaeTriples.forEach((triple) => { | ||
deleteIn(store.eav, triple); | ||
}) | ||
} | ||
deleteIn(store.vae, [id]); | ||
} | ||
@@ -35,5 +111,2 @@ | ||
case "add-triple": | ||
retractCardinalityOne(store, args); | ||
// to handle dups, we always remove first. | ||
retractTriple(store, args); | ||
addTriple(store, args); | ||
@@ -55,2 +128,28 @@ break; | ||
function getEATriples(store, e) { | ||
const aMap = store.ea[e]; | ||
return aMap | ||
? Object.values(aMap) | ||
: Object.values(store.ea).flatMap(aMap => Object.values(aMap)); | ||
} | ||
function getEAVTriples(store, e) { | ||
const aMap = store.eav[e]; | ||
return aMap | ||
? Object.values(aMap) | ||
.flatMap(vMap => Object.values(vMap)) | ||
: Object.values(store.eav) | ||
.flatMap(aMap => { | ||
return Object.values(aMap) | ||
.flatMap(vMap => Object.values(vMap)) | ||
}); | ||
} | ||
export function getTriples(store, [e]) { | ||
const eaTriples = getEATriples(store, e); | ||
const eavTriples = getEAVTriples(store, e) | ||
.filter(([_, aid]) => !hasEA(getAttrAsserting(store.attrs, aid))); | ||
return [...eaTriples, ...eavTriples]; | ||
} | ||
export function transact(store, txSteps) { | ||
@@ -63,1 +162,2 @@ return produce(store, (draft) => { | ||
} | ||
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
1333344
159
28083
3