UNPKG

@dstanesc/o-o-o-o-o-o-o

Version:

O-O-O-O-O-O-O is a collection of content addressed persistent data structures

705 lines 27.6 kB
var __asyncValues = (this && this.__asyncValues) || function (o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator], i; return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } }; var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { var i, p; return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; } }; var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var g = generator.apply(thisArg, _arguments || []), i, q = []; return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } function fulfill(value) { resume("next", value); } function reject(value) { resume("throw", value); } function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } }; import { Status, } from './types'; import { OFFSET_INCREMENTS } from './serde'; import { traverseVertices } from './depth-first'; function edgesOutgoing(vertex, accessor) { return __asyncGenerator(this, arguments, function* edgesOutgoing_1() { yield __await(yield* __asyncDelegator(__asyncValues(edgeSourceNext(vertex.nextEdge, accessor)))); }); } function edgeSourceNext(ref, accessor) { return __asyncGenerator(this, arguments, function* edgeSourceNext_1() { if (ref !== undefined) { const edge = yield __await(accessor.getEdge(ref)); yield yield __await(edge); yield __await(yield* __asyncDelegator(__asyncValues(edgeSourceNext(edge.sourceNext, accessor)))); } }); } function propsNext(ref, accessor) { return __asyncGenerator(this, arguments, function* propsNext_1() { if (ref !== undefined) { const prop = yield __await(accessor.getProp(ref)); yield yield __await(prop); yield __await(yield* __asyncDelegator(__asyncValues(propsNext(prop.nextProp, accessor)))); } }); } function incomingEdgesVisitor(incomingEdges) { const endEdge = async (edge) => { const edgeOffset = edge.offset; const targetOffset = edge.target; if (incomingEdges.has(targetOffset)) { const edgeSet = incomingEdges.get(targetOffset); edgeSet.add(edgeOffset); } else { const edgeSet = new Set(); edgeSet.add(edgeOffset); incomingEdges.set(targetOffset, edgeSet); } }; return { endEdge }; } class Graph { constructor({ versionSet, rootGet, }, { vertexGet, vertexRange, edgeGet, edgeRange, propGet, propRange, indexGet, offsetsGet, verticesAll, edgesAll, propsAll, commit, packGraph, packComputed, }, { indexCreate, indexSearch } = { indexCreate: undefined, indexSearch: undefined, }) { this.vertices = new Map(); this.edges = new Map(); this.props = new Map(); this.indices = new Map(); this.versionSet = versionSet; this.rootGet = rootGet; this.vertexGet = vertexGet; this.vertexRange = vertexRange; this.edgeGet = edgeGet; this.edgeRange = edgeRange; this.propGet = propGet; this.propRange = propRange; this.indexGet = indexGet; this.offsetsGet = offsetsGet; this.verticesAll = verticesAll; this.edgesAll = edgesAll; this.propsAll = propsAll; this.commit = commit; this.packGraph = packGraph; this.packComputed = packComputed; this.indexCreate = indexCreate; this.indexSearch = indexSearch; } async getVertex(ref) { let vertex = this.vertices.get(ref); if (vertex === undefined) { vertex = await this.vertexGet(await this.rootGet(), ref); this.vertices.set(ref, vertex); } return vertex; } async getVertexRange(startRef, vertexCount) { const vertices = await this.vertexRange(await this.rootGet(), startRef, vertexCount); let vertexRef = startRef; for (let i = 0; i < vertexCount; i++) { this.vertices.set(vertexRef, vertices[i]); vertexRef += OFFSET_INCREMENTS.VERTEX_INCREMENT; } return vertices; } async getEdge(ref) { let edge = this.edges.get(ref); if (edge === undefined) { edge = await this.edgeGet(await this.rootGet(), ref); this.edges.set(ref, edge); } return edge; } async getEdgeRange(startRef, edgeCount) { const edges = await this.edgeRange(await this.rootGet(), startRef, edgeCount); let edgeRef = startRef; for (let i = 0; i < edgeCount; i++) { this.edges.set(edgeRef, edges[i]); edgeRef += OFFSET_INCREMENTS.EDGE_INCREMENT; } return edges; } async getProp(ref) { let prop = this.props.get(ref); if (prop === undefined) { prop = await this.propGet(await this.rootGet(), ref); this.props.set(ref, prop); } return prop; } async getPropRange(startRef, propCount) { const props = await this.propRange(await this.rootGet(), startRef, propCount); let propRef = startRef; for (let i = 0; i < propCount; i++) { this.props.set(propRef, props[i]); propRef += OFFSET_INCREMENTS.PROP_INCREMENT; } return props; } async getIndex(ref) { let index = this.indices.get(ref); if (index === undefined) { index = await this.indexGet(await this.rootGet(), ref); this.indices.set(ref, index); } return index; } async allVertices() { return await this.verticesAll(await this.rootGet()); } async allEdges() { return await this.edgesAll(await this.rootGet()); } async allProps() { return await this.propsAll(await this.rootGet()); } async matchAnyEdgeProp(edge, predicate) { if (edge.nextProp !== undefined) return await this.matchNextProp(edge.nextProp, predicate); else return false; } async matchAnyVertexProp(vertex, predicate) { if (vertex.nextProp !== undefined) return await this.matchNextProp(vertex.nextProp, predicate); else return false; } async matchNextProp(ref, predicate) { let prop = await this.getProp(ref); if (predicate.keyType === prop.key && predicate.operation.predicate(prop.value)) { return true; } else if (prop.nextProp !== undefined) { return await this.matchNextProp(prop.nextProp, predicate); } return false; } async matchAnyIndex(vertex, key) { return await this.matchNextIndex(vertex.nextIndex, key); } async matchNextIndex(ref, key) { if (ref !== undefined) { const index = await this.getIndex(ref); if (index.key === key) return index; else return await this.matchNextIndex(index.nextIndex, key); } else return undefined; } async searchIndex(index, predicate) { const { keyType, operation } = predicate; const indexedValue = await this.indexSearch(index.value, operation.operand); return indexedValue; } async getVertexEdges(vertex) { const edges = []; if (vertex.nextEdge !== undefined) { await this.getNextEdges(vertex.nextEdge, edges); } return edges; } async getNextEdges(ref, edges) { const edge = await this.getEdge(ref); edges.push(edge); if (edge.nextEdge !== undefined) { await this.getNextEdges(edge.nextEdge, edges); } } async getVertexProps(vertex) { const props = []; if (vertex.nextProp !== undefined) { await this.getNextProps(vertex.nextProp, props); } return props; } async getEdgeProps(edge) { const props = []; if (edge.nextProp !== undefined) { await this.getNextProps(edge.nextProp, props); } return props; } async getNextProps(ref, props) { const prop = await this.getProp(ref); props.push(prop); if (prop.nextProp !== undefined) { await this.getNextProps(prop.nextProp, props); } } tx() { return new Tx(this); } /** * Pack a complete graph * @param versionRoot - the version root * @returns - a block containing the packed graph */ async packGraphVersion(versionRoot) { const { root } = versionRoot === undefined ? await this.rootGet() : { root: versionRoot }; return await this.packGraph(root); } /** * Pack a graph fragment * * @param vertexOffsetStart - the offset of the first vertex * @param vertexCount - the number of vertices, counting from the first vertex * @param graphDepth - the depth of the fragment * @param versionRoot - the version root * @returns - a block containing the packed graph fragment */ async packGraphFragment(vertexOffsetStart, vertexCount, graphDepth, versionRoot) { const { root } = versionRoot === undefined ? await this.rootGet() : { root: versionRoot }; return await this.packComputed(root, vertexOffsetStart, vertexCount, graphDepth); } } class Tx { constructor(graph) { this.graph = graph; } async start() { this.vertices = { added: new Map(), updated: new Map() }; this.edges = { added: new Map(), updated: new Map() }; this.props = { added: new Map(), updated: new Map() }; this.indices = { added: new Map(), updated: new Map() }; const { root, index } = await this.graph.rootGet(); const { vertexOffset, edgeOffset, propOffset, indexOffset } = root !== undefined ? await this.graph.offsetsGet({ root, index }) : { vertexOffset: 0, edgeOffset: 0, propOffset: 0, indexOffset: 0, }; this.vertexOffsetInit = vertexOffset; this.edgeOffsetInit = edgeOffset; this.propOffsetInit = propOffset; this.indexOffsetInit = indexOffset; this.vertexOffset = vertexOffset; this.edgeOffset = edgeOffset; this.propOffset = propOffset; this.indexOffset = indexOffset; return this; } nextVertexOffset() { const currentOffset = this.vertexOffset; this.vertexOffset += OFFSET_INCREMENTS.VERTEX_INCREMENT; return currentOffset; } nextEdgeOffset() { const currentOffset = this.edgeOffset; this.edgeOffset += OFFSET_INCREMENTS.EDGE_INCREMENT; return currentOffset; } nextPropOffset() { const currentOffset = this.propOffset; this.propOffset += OFFSET_INCREMENTS.PROP_INCREMENT; return currentOffset; } nextIndexOffset() { const currentOffset = this.indexOffset; this.indexOffset += OFFSET_INCREMENTS.INDEX_INCREMENT; return currentOffset; } async getVertex(ref) { let vertex; if (ref >= this.vertexOffsetInit) { vertex = this.vertices.added.get(ref); if (vertex === undefined) vertex = this.vertices.updated.get(ref); if (vertex === undefined) throw new Error(`invalid vertex ref ${ref}`); } else vertex = await this.graph.getVertex(ref); return vertex; } async getEdge(ref) { let edge; if (ref >= this.edgeOffsetInit) { edge = this.edges.added.get(ref); if (edge === undefined) edge = this.edges.updated.get(ref); if (edge === undefined) throw new Error(`invalid edge ref ${ref}`); } else edge = await this.graph.getEdge(ref); return edge; } async getProp(ref) { let prop; if (ref >= this.propOffsetInit) { prop = this.props.added.get(ref); if (prop === undefined) prop = this.props.updated.get(ref); if (prop === undefined) throw new Error(`invalid prop ref ${ref}`); } else prop = await this.graph.getProp(ref); return prop; } async getIndex(ref) { let index; if (ref >= this.propOffsetInit) { index = this.indices.added.get(ref); if (index === undefined) index = this.indices.updated.get(ref); if (index === undefined) throw new Error(`invalid index ref ${ref}`); } else index = await this.graph.getProp(ref); return index; } notifyVertexUpdate(vertex) { if (!this.vertices.added.has(vertex.offset)) { this.vertices.updated.set(vertex.offset, vertex); } } notifyEdgeUpdate(edge) { if (!this.edges.added.has(edge.offset)) { this.edges.updated.set(edge.offset, edge); } } notifyPropUpdate(prop) { if (!this.props.added.has(prop.offset)) { this.props.updated.set(prop.offset, prop); } } notifyIndexUpdate(index) { if (!this.indices.added.has(index.offset)) { this.indices.updated.set(index.offset, index); } } addVertex(type) { const offset = this.nextVertexOffset(); const vertex = { status: Status.CREATED, offset }; if (type !== undefined) vertex.type = type; this.vertices.added.set(offset, vertex); return vertex; } async addEdge(source, target, type) { const offset = this.nextEdgeOffset(); const edge = { status: Status.CREATED, offset, source: source.offset, target: target.offset, }; if (type !== undefined) edge.type = type; this.edges.added.set(edge.offset, edge); if (source.nextEdge !== undefined) { const nextEdge = await this.getEdge(source.nextEdge); await this.appendEdge(nextEdge, edge); } else { source.nextEdge = offset; this.notifyVertexUpdate(source); } return edge; } async linkVertexEdge(source, edgeRef) { const edge = await this.getEdge(edgeRef); if (source.nextEdge !== undefined) { const nextEdge = await this.getEdge(source.nextEdge); await this.appendEdge(nextEdge, edge); } else { source.nextEdge = edgeRef; this.notifyVertexUpdate(source); } } async addVertexProp(vertex, key, value, type) { const offset = this.nextPropOffset(); const prop = { status: Status.CREATED, offset, key, value }; if (type !== undefined) prop.type = type; this.props.added.set(prop.offset, prop); if (vertex.nextProp !== undefined) { const nextProp = await this.getProp(vertex.nextProp); await this.appendProp(nextProp, prop); } else { vertex.nextProp = offset; this.notifyVertexUpdate(vertex); } return prop; } async linkVertexProp(source, propRef) { const prop = await this.getProp(propRef); if (source.nextProp !== undefined) { const nextProp = await this.getProp(source.nextProp); await this.appendProp(nextProp, prop); } else { source.nextProp = propRef; this.notifyVertexUpdate(source); } } async addVertexIndex(vertex, key, value, type) { const offset = this.nextIndexOffset(); const index = { status: Status.CREATED, offset, key, value }; if (type !== undefined) index.type = type; this.indices.added.set(index.offset, index); if (vertex.nextIndex !== undefined) await this.appendVertexIndex(vertex.nextIndex, index); else { vertex.nextIndex = offset; this.notifyVertexUpdate(vertex); } return index; } async addEdgeProp(edge, key, value, type) { const offset = this.nextPropOffset(); const prop = { status: Status.CREATED, offset, key, value }; if (type !== undefined) prop.type = type; this.props.added.set(prop.offset, prop); if (edge.nextProp !== undefined) { const nextProp = await this.getProp(edge.nextProp); await this.appendProp(nextProp, prop); } else { edge.nextProp = offset; this.notifyEdgeUpdate(edge); } return prop; } async linkEdgeProp(edge, propRef) { const prop = await this.getProp(propRef); if (edge.nextProp !== undefined) { const nextProp = await this.getProp(edge.nextProp); await this.appendProp(nextProp, prop); } else { edge.nextProp = propRef; this.notifyEdgeUpdate(edge); } } async linkEdge(currentEdge, edgeRef) { const edge = await this.getEdge(edgeRef); await this.appendEdge(currentEdge, edge); } async appendEdge(currentEdge, newEdge) { if (currentEdge.offset === newEdge.offset) throw new Error(`Invalid edge append. Cannot append to itself`); if (currentEdge.sourceNext !== undefined) { const sourceNext = await this.getEdge(currentEdge.sourceNext); await this.appendEdge(sourceNext, newEdge); } else { currentEdge.sourceNext = newEdge.offset; newEdge.sourcePrev = currentEdge.offset; this.notifyEdgeUpdate(currentEdge); } } async linkProp(currentProp, propRef) { const prop = await this.getProp(propRef); await this.appendProp(currentProp, prop); } async appendProp(currentProp, newProp) { if (currentProp.offset === newProp.offset) throw new Error(`Invalid prop append. Cannot append to itself`); if (currentProp.nextProp !== undefined) { const nextProp = await this.getProp(currentProp.nextProp); await this.appendProp(nextProp, newProp); } else { currentProp.nextProp = newProp.offset; this.notifyPropUpdate(currentProp); } } async appendVertexIndex(currentRef, newIndex) { const currentIndex = await this.getIndex(currentRef); if (currentIndex.offset === newIndex.offset) throw new Error(`Invalid index append. Cannot append to itself`); if (currentIndex.nextIndex !== undefined) { await this.appendVertexIndex(currentIndex.nextIndex, newIndex); } else { currentIndex.nextIndex = newIndex.offset; this.notifyIndexUpdate(currentIndex); } } async uniqueIndex(outgoingFrom, key, type) { var _a, e_1, _b, _c, _d, e_2, _e, _f; if (this.graph.indexCreate === undefined) throw new Error('Please provide an index store to the graph'); const edgesOut = edgesOutgoing(outgoingFrom, this); const values = []; try { for (var _g = true, edgesOut_1 = __asyncValues(edgesOut), edgesOut_1_1; edgesOut_1_1 = await edgesOut_1.next(), _a = edgesOut_1_1.done, !_a;) { _c = edgesOut_1_1.value; _g = false; try { const edge = _c; const targetVertex = await this.getVertex(edge.target); const targetVertexProps = propsNext(targetVertex.nextProp, this); try { for (var _h = true, targetVertexProps_1 = (e_2 = void 0, __asyncValues(targetVertexProps)), targetVertexProps_1_1; targetVertexProps_1_1 = await targetVertexProps_1.next(), _d = targetVertexProps_1_1.done, !_d;) { _f = targetVertexProps_1_1.value; _h = false; try { const prop = _f; const propKey = prop.key; if (propKey === key) { const value = prop.value; values.push({ value, ref: edge.offset }); break; } } finally { _h = true; } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (!_h && !_d && (_e = targetVertexProps_1.return)) await _e.call(targetVertexProps_1); } finally { if (e_2) throw e_2.error; } } } finally { _g = true; } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (!_g && !_a && (_b = edgesOut_1.return)) await _b.call(edgesOut_1); } finally { if (e_1) throw e_1.error; } } if (values.length === 0) return undefined; else { const link = await this.graph.indexCreate(values); const index = await this.addVertexIndex(outgoingFrom, key, link, type); return index; } } async commit({ comment, tags, signer, }) { const verticesNew = new Map([ ...this.graph.vertices, ...this.vertices.updated, ...this.vertices.added, ]); const edgesNew = new Map([ ...this.graph.edges, ...this.edges.updated, ...this.edges.added, ]); const propsNew = new Map([ ...this.graph.props, ...this.props.updated, ...this.props.added, ]); const indicesNew = new Map([ ...this.graph.indices, ...this.indices.updated, ...this.indices.added, ]); const sourceVertexRefs = new Set(Array.from(edgesNew.values()).map((edge) => edge.source)); const targetVertexRefs = new Set(Array.from(edgesNew.values()).map((edge) => edge.target)); const editedVertexRefs = new Set(Array.from(verticesNew.keys())); const impactedVertices = new Set([ ...sourceVertexRefs, ...targetVertexRefs, ...editedVertexRefs, ]); await this.fillTargetLinks(impactedVertices); const rootBefore = await this.graph.rootGet(); const { root, index, blocks } = await this.graph.commit(rootBefore, { vertices: this.vertices, edges: this.edges, props: this.props, indices: this.indices, }); const versionDetails = { comment, tags, timestamp: Date.now(), }; if (signer !== undefined) { const signature = await signer.sign(root); versionDetails.signature = signature; const publicKey = await signer.exportPublicKey(); versionDetails.author = publicKey; } const version = { root, parent: rootBefore.root, details: versionDetails, }; await this.graph.versionSet({ version, index }); this.graph.vertices = verticesNew; this.graph.edges = edgesNew; this.graph.props = propsNew; this.graph.indices = indicesNew; return { root, index, blocks }; } async fillTargetLinks(refs) { var _a, e_3, _b, _c; const incomingEdges = new Map(); await traverseVertices(this, refs, incomingEdgesVisitor(incomingEdges)); for (const [vertexRef, incomingRefs] of incomingEdges.entries()) { const vertex = await this.getVertex(vertexRef); if (incomingRefs !== undefined) { const outgoingEdges = edgesOutgoing(vertex, this); try { // FIXME Poor alg assumes single incoming edge (ie tree struct), elaboration needed ... // Should we to follow Neo4j's RelationshipRecord structure to store // a bool when edge is first in chain (for both - src and target chains) ? for (var _d = true, outgoingEdges_1 = (e_3 = void 0, __asyncValues(outgoingEdges)), outgoingEdges_1_1; outgoingEdges_1_1 = await outgoingEdges_1.next(), _a = outgoingEdges_1_1.done, !_a;) { _c = outgoingEdges_1_1.value; _d = false; try { const outgoingEdge = _c; for (const incomingEdgeRef of incomingRefs.values()) { const incomingEdge = await this.getEdge(incomingEdgeRef); outgoingEdge.targetPrev = incomingEdge.offset; if (outgoingEdge.offset === vertex.nextEdge) { incomingEdge.targetNext = outgoingEdge.offset; } } } finally { _d = true; } } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (!_d && !_a && (_b = outgoingEdges_1.return)) await _b.call(outgoingEdges_1); } finally { if (e_3) throw e_3.error; } } } } } } export { Graph, Tx }; //# sourceMappingURL=graph.js.map