UNPKG

@x5e/gink

Version:

an eventually consistent database

192 lines (190 loc) 10.4 kB
"use strict"; 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