@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
JavaScript
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