@x5e/gink
Version:
an eventually consistent database
192 lines (190 loc) • 10.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.EdgeType = void 0;
const lodash_1 = require("lodash");
const Container_1 = require("./Container");
const builders_1 = require("./builders");
const Bundler_1 = require("./Bundler");
const utils_1 = require("./utils");
const Edge_1 = require("./Edge");
const builders_2 = require("./builders");
const builders_3 = require("./builders");
const builders_4 = require("./builders");
const factories_1 = require("./factories");
const store_utils_1 = require("./store_utils");
class EdgeType extends Container_1.Container {
constructor(database, address, containerBuilder) {
super(database, address, builders_1.Behavior.EDGE_TYPE);
if (this.address.timestamp < 0) {
(0, utils_1.ensure)(address.offset === builders_1.Behavior.EDGE_TYPE);
}
else if (containerBuilder) {
(0, utils_1.ensure)(containerBuilder.getBehavior() === builders_1.Behavior.EDGE_TYPE);
}
}
async createEdge(source, target, value, change) {
const muid = await this.addEntry([source, target], value, change);
const edgeData = {
source: source.address,
target: target.address,
action: this.address,
value,
};
return new Edge_1.Edge(this.database, muid, edgeData);
}
async reset(args) {
var _a;
const toTime = args === null || args === void 0 ? void 0 : args.toTime;
const bundlerOrComment = args === null || args === void 0 ? void 0 : args.bundlerOrComment;
const skipProperties = args === null || args === void 0 ? void 0 : args.skipProperties;
const recurse = args === null || args === void 0 ? void 0 : args.recurse;
const seen = recurse ? ((_a = args === null || args === void 0 ? void 0 : args.seen) !== null && _a !== void 0 ? _a : new Set()) : undefined;
if (seen) {
seen.add((0, utils_1.muidToString)(this.address));
}
let immediate = false;
let bundler;
if (bundlerOrComment instanceof Bundler_1.Bundler) {
bundler = bundlerOrComment;
}
else {
immediate = true;
bundler = new Bundler_1.Bundler(bundlerOrComment);
}
if (!toTime) {
// If no time is specified, we are resetting to epoch, which is just a clear
this.clear(false, bundler);
}
else {
const entriesThen = await this.database.store.getOrderedEntries(this.address, Infinity, toTime);
const entriesNow = await this.database.store.getOrderedEntries(this.address, Infinity);
for (const [key, entry] of entriesThen) {
(0, utils_1.ensure)((0, lodash_1.isEqual)((0, utils_1.muidToTuple)(this.address), entry.containerId), "entry's containerId doesn't match this edgeType's address");
const edgeData = (0, utils_1.entryToEdgeData)(entry);
const effectiveThen = entry.storageKey;
const placementNow = await this.database.store.getLocation((0, utils_1.muidTupleToMuid)(entry.entryId));
const effectiveNow = placementNow
? placementNow.key
: undefined;
if (!effectiveNow) {
// This entry existed then, but has since been deleted
// Need to re-add it to the previous location
const entryBuilder = new builders_2.EntryBuilder();
entryBuilder.setContainer((0, utils_1.muidToBuilder)(this.address));
/*
This confused me while writing this, so hopefully it helps
whoever is reading this:
The effective timestamp is set here, but it will also later
be the storageKey when the entry is converted from EntryBuilder
to Entry.
Setting the key here directly will get overwritten by setPair
since they are oneof in the proto definition.
*/
entryBuilder.setEffective(effectiveThen);
entryBuilder.setBehavior(entry.behavior);
if (entry.value) {
entryBuilder.setValue((0, utils_1.wrapValue)(entry.value));
}
const pairBuilder = new builders_4.PairBuilder();
pairBuilder.setLeft((0, utils_1.muidToBuilder)(edgeData.source));
pairBuilder.setRite((0, utils_1.muidToBuilder)(edgeData.target));
entryBuilder.setPair(pairBuilder);
const changeBuilder = new builders_3.ChangeBuilder();
changeBuilder.setEntry(entryBuilder);
const changeMuid = bundler.addChange(changeBuilder);
// Copy properties from the edge that previously existed
const propertiesThen = await this.database.store.getContainerProperties((0, utils_1.muidTupleToMuid)(entry.entryId), toTime);
// Not using this.createEdge because we already added the
// entry to the bundler. this.createEdge does not allow
// us to adjust the position of the edge.
const newEdge = new Edge_1.Edge(this.database, changeMuid, edgeData);
for (const [key, value] of propertiesThen.entries()) {
const property = (await (0, factories_1.construct)(this.database, (0, utils_1.strToMuid)(key)));
(0, utils_1.ensure)(property.behavior === builders_1.Behavior.PROPERTY, "constructed container isn't a property?");
await property.set(newEdge, value, bundler);
}
}
else {
if (effectiveNow && effectiveNow !== effectiveThen) {
// This entry exists, but has been moved
// Need to move it back
await (0, store_utils_1.movementHelper)(bundler, (0, utils_1.muidTupleToMuid)(entry.entryId), this.address, effectiveThen, false);
}
// reset the properties of this edge
await this.resetEdgeProperties((0, utils_1.muidTupleToMuid)(entry.entryId), edgeData, toTime, bundler);
// Need to remove the current entry from entriesNow if
// 1) the entry exists but was moved, or 2) the entry is untouched
(0, utils_1.ensure)(entriesNow.delete(`${effectiveNow},${(0, utils_1.muidTupleToString)(placementNow.placement)}`), "entry not found in entriesNow");
}
}
// We will need to loop through the remaining entries in entriesNow
// to delete them, since we know they weren't active at toTime
for (const [key, entry] of entriesNow) {
await (0, store_utils_1.movementHelper)(bundler, (0, utils_1.muidTupleToMuid)(entry.entryId), this.address, undefined, false);
}
}
if (!skipProperties) {
// Reset the properties of this edgeType
await this.resetProperties(toTime, bundler);
}
if (immediate) {
await this.database.addBundler(bundler);
}
}
/**
* Specific property reset method to reset the properties of an edge. Resets the properties
* associated with the edge to toTime.
*
* This is separated from the container reset method due to Edges being handled differently from
* containers (edges are not stored in the internal container database like other containers).
* @param edgeMuid the muid of the edge to reset
* @param edgeData the data of the edge to reset
* @param toTime optional timestamp to reset the properties to
* @param bundlerOrComment optional bundler or comment
*/
async resetEdgeProperties(edgeMuid, edgeData, toTime, bundlerOrComment) {
let immediate = false;
let bundler;
if (bundlerOrComment instanceof Bundler_1.Bundler) {
bundler = bundlerOrComment;
}
else {
immediate = true;
bundler = new Bundler_1.Bundler(bundlerOrComment);
}
const edge = new Edge_1.Edge(this.database, edgeMuid, edgeData);
const propertiesNow = await this.database.store.getContainerProperties(edge.address);
if (!toTime) {
// Resetting to epoch, so just delete all properties
for (const [key, _] of propertiesNow.entries()) {
const property = (await (0, factories_1.construct)(this.database, (0, utils_1.strToMuid)(key)));
(0, utils_1.ensure)(property.behavior === builders_1.Behavior.PROPERTY, "constructed container isn't a property?");
await property.delete(edge, bundler);
}
}
else {
const propertiesThen = await this.database.store.getContainerProperties(edge, toTime);
for (const [key, value] of propertiesThen.entries()) {
if (value !== propertiesNow.get(key)) {
const property = (await (0, factories_1.construct)(this.database, (0, utils_1.strToMuid)(key)));
(0, utils_1.ensure)(property.behavior === builders_1.Behavior.PROPERTY, "constructed container isn't a property?");
await property.set(edge, value, bundler);
}
// Remove from propertiesNow so we can delete the rest
// after this iteration
propertiesNow.delete(key);
}
// Now loop through the remaining entries in propertiesNow and delete them
for (const [key, _] of propertiesNow.entries()) {
const property = (await (0, factories_1.construct)(this.database, (0, utils_1.strToMuid)(key)));
(0, utils_1.ensure)(property.behavior === builders_1.Behavior.PROPERTY, "constructed container isn't a property?");
await property.delete(edge, bundler);
}
}
if (immediate) {
await this.database.addBundler(bundler);
}
}
}
exports.EdgeType = EdgeType;
//# sourceMappingURL=EdgeType.js.map