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

581 lines (480 loc) 15.7 kB
import { compute_chunks } from '@dstanesc/wasm-chunking-fastcdc-node' import { LinkCodec, linkCodecFactory, valueCodecFactory, ValueCodec, } from '../codecs' import { graphStore } from '../graph-store' import { chunkerFactory } from '../chunking' import { Graph } from '../graph' import { BlockStore, memoryBlockStoreFactory } from '../block-store' import { RequestBuilder, navigateVertices, PathElemType } from '../navigate' import { OFFSET_INCREMENTS } from '../serde' import * as assert from 'assert' import { protoGremlinFactory, ProtoGremlin, ProtoGremlinFactory, } from '../api/proto-gremlin' import { incl, isTrue } from '../ops' import { VersionStore, versionStoreFactory } from '../version-store' const { chunk } = chunkerFactory(1024, compute_chunks) const linkCodec: LinkCodec = linkCodecFactory() const valueCodec: ValueCodec = valueCodecFactory() describe('Api', function () { test('internal, minimal creation, proto-schema aware, deterministic root and offsets', async () => { enum ObjectTypes { TWEET = 1, } enum RlshpTypes { COMMENT_TO = 1, } enum PropTypes { COMMENT = 1, } enum KeyTypes { JSON = 1, } const blockStore: BlockStore = memoryBlockStoreFactory() const story: VersionStore = await versionStoreFactory({ chunk, linkCodec, valueCodec, blockStore, }) const store = graphStore({ chunk, linkCodec, valueCodec, blockStore, }) const graph = new Graph(story, store) const tx = graph.tx() await tx.start() const v1 = tx.addVertex(ObjectTypes.TWEET) const v2 = tx.addVertex(ObjectTypes.TWEET) const v3 = tx.addVertex(ObjectTypes.TWEET) await tx.addEdge(v1, v2, RlshpTypes.COMMENT_TO) await tx.addEdge(v1, v3, RlshpTypes.COMMENT_TO) await tx.addVertexProp( v2, KeyTypes.JSON, { hello: 'v2' }, PropTypes.COMMENT ) await tx.addVertexProp( v2, KeyTypes.JSON, { hello: 'v3' }, PropTypes.COMMENT ) const { root, index, blocks } = await tx.commit({}) const { vertexOffset, edgeOffset, propOffset } = index assert.equal(OFFSET_INCREMENTS.VERTEX_INCREMENT * 3, vertexOffset) assert.equal(OFFSET_INCREMENTS.EDGE_INCREMENT * 2, edgeOffset) assert.equal(OFFSET_INCREMENTS.PROP_INCREMENT * 2, propOffset) assert.equal( 'bafkreihfd57xnxauil7q4cqr6hj5cnf4geer7pte2v54i7gaijw7lhkndu', root.toString() ) }) test('equivalent proto-gremlin, minimal creation, proto-schema aware, deterministic root and offsets', async () => { enum ObjectTypes { TWEET = 1, } enum RlshpTypes { COMMENT_TO = 1, } enum PropTypes { COMMENT = 1, } enum KeyTypes { JSON = 1, } const blockStore: BlockStore = memoryBlockStoreFactory() const versionStore: VersionStore = await versionStoreFactory({ chunk, linkCodec, valueCodec, blockStore, }) const gf: ProtoGremlinFactory = protoGremlinFactory({ chunk, linkCodec, valueCodec, blockStore, versionStore, }) const g: ProtoGremlin = gf.g() const tx = await g.tx() const v1 = await tx.addV(ObjectTypes.TWEET).next() const v2 = await tx .addV(ObjectTypes.TWEET) .property(KeyTypes.JSON, { hello: 'v2' }, PropTypes.COMMENT) .property(KeyTypes.JSON, { hello: 'v3' }, PropTypes.COMMENT) .next() const v3 = await tx.addV(ObjectTypes.TWEET).next() const e1 = await tx.addE(RlshpTypes.COMMENT_TO).from(v1).to(v2).next() const e2 = await tx.addE(RlshpTypes.COMMENT_TO).from(v1).to(v3).next() const { root, index, blocks } = await tx.commit({}) const { vertexOffset, edgeOffset, propOffset } = index assert.equal(OFFSET_INCREMENTS.VERTEX_INCREMENT * 3, vertexOffset) assert.equal(OFFSET_INCREMENTS.EDGE_INCREMENT * 2, edgeOffset) assert.equal(OFFSET_INCREMENTS.PROP_INCREMENT * 2, propOffset) assert.equal( 'bafkreihfd57xnxauil7q4cqr6hj5cnf4geer7pte2v54i7gaijw7lhkndu', root.toString() ) }) test('internal minimal creation, proto-schema aware w/ additional edge properties', async () => { enum ObjectTypes { TWEET = 1, } enum RlshpTypes { COMMENT_TO = 1, } enum PropTypes { COMMENT = 1, WEIGHT = 2, } enum KeyTypes { JSON = 1, TEXT = 3, VALUE = 33, } const blockStore: BlockStore = memoryBlockStoreFactory() const story: VersionStore = await versionStoreFactory({ chunk, linkCodec, valueCodec, blockStore, }) const store = graphStore({ chunk, linkCodec, valueCodec, blockStore, }) const graph = new Graph(story, store) const tx = graph.tx() await tx.start() const v1 = tx.addVertex(ObjectTypes.TWEET) const v2 = tx.addVertex(ObjectTypes.TWEET) const v3 = tx.addVertex(ObjectTypes.TWEET) const e1 = await tx.addEdge(v1, v2, RlshpTypes.COMMENT_TO) const e2 = await tx.addEdge(v1, v3, RlshpTypes.COMMENT_TO) const p1 = await tx.addEdgeProp( e1, KeyTypes.VALUE, 55, PropTypes.WEIGHT ) const p2 = await tx.addEdgeProp( e2, KeyTypes.VALUE, 33, PropTypes.WEIGHT ) await tx.addVertexProp( v2, KeyTypes.TEXT, 'hello world from v2', PropTypes.COMMENT ) await tx.addVertexProp( v3, KeyTypes.TEXT, 'hello world from v3', PropTypes.COMMENT ) const { root, index } = await tx.commit({}) const { vertexRoot, vertexOffset, vertexIndex, edgeRoot, edgeOffset, edgeIndex, propRoot, propOffset, propIndex, } = index assert.equal(OFFSET_INCREMENTS.VERTEX_INCREMENT * 3, vertexOffset) assert.equal(OFFSET_INCREMENTS.EDGE_INCREMENT * 2, edgeOffset) assert.equal(OFFSET_INCREMENTS.PROP_INCREMENT * 4, propOffset) assert.equal( 'bafkreihahkgbiitsq6vew5utryjcknd4tfppi3feke7izcelwgs25xjv7q', root.toString() ) }) test('proto-schema aware creation, w/ navigation to edge ', async () => { const { v1, graph } = await createSchemaAwareGraph() const request = new RequestBuilder() .add(PathElemType.VERTEX) .type(1) .add(PathElemType.EDGE) .type(1) .maxResults(10) .get() const edgeResults = [] for await (const result of navigateVertices( graph, [v1.offset], request )) { edgeResults.push(result) } // first edge assert.equal(0, edgeResults[0].offset) assert.equal(0, edgeResults[0].source) assert.equal(25, edgeResults[0].target) assert.equal(45, edgeResults[0].sourceNext) // second edge assert.equal(45, edgeResults[1].offset) assert.equal(0, edgeResults[1].source) assert.equal(50, edgeResults[1].target) assert.equal(0, edgeResults[1].sourcePrev) assert.equal(32, edgeResults[1].nextProp) }) test('proto-schema aware creation, w/ navigation to vertex ', async () => { const { v1, graph } = await createSchemaAwareGraph() enum ObjectTypes { TWEET = 1, } enum RlshpTypes { COMMENT_TO = 1, } enum PropTypes { COMMENT = 1, WEIGHT = 2, } enum KeyTypes { JSON = 1, TEXT = 3, VALUE = 33, } const request = new RequestBuilder() .add(PathElemType.VERTEX) .type(ObjectTypes.TWEET) .add(PathElemType.EDGE) .type(RlshpTypes.COMMENT_TO) .propPred(KeyTypes.VALUE, incl([55, 33])) .add(PathElemType.VERTEX) .type(ObjectTypes.TWEET) .propPred(KeyTypes.TEXT, isTrue()) .maxResults(10) .get() const vertexResults = [] for await (const result of navigateVertices( graph, [v1.offset], request )) { vertexResults.push(result) } // first vertex assert.equal(25, vertexResults[0].offset) assert.equal(64, vertexResults[0].nextProp) // second vertex assert.equal(50, vertexResults[1].offset) assert.equal(160, vertexResults[1].nextProp) }) test('proto-schema aware creation, w/ extraction of properties ', async () => { const { v1, graph } = await createSchemaAwareGraph() enum ObjectTypes { TWEET = 1, } enum RlshpTypes { COMMENT_TO = 1, } enum PropTypes { COMMENT = 1, WEIGHT = 2, } enum KeyTypes { JSON = 1, TEXT = 3, VALUE = 33, } const request = new RequestBuilder() .add(PathElemType.VERTEX) .type(1) .add(PathElemType.EDGE) .type(1) .propPred(KeyTypes.VALUE, isTrue()) .add(PathElemType.VERTEX) .type(1) .propPred(KeyTypes.TEXT, isTrue()) .extract(3) .maxResults(10) .get() const propResults = [] for await (const result of navigateVertices( graph, [v1.offset], request )) { propResults.push(result) } assert.equal(4, propResults.length) assert.equal('comment 1 from v2', propResults[0].value) assert.equal('comment 2 from v2', propResults[1].value) assert.equal('comment 3 from v2', propResults[2].value) assert.equal('comment 1 from v3', propResults[3].value) }) test('proto-schema aware creation, w/ reduction on properties ', async () => { enum ObjectTypes { TWEET = 1, } enum RlshpTypes { COMMENT_TO = 1, } enum PropTypes { KPI = 1, WEIGHT = 2, } enum KeyTypes { VALUE = 33, } const blockStore: BlockStore = memoryBlockStoreFactory() const story: VersionStore = await versionStoreFactory({ chunk, linkCodec, valueCodec, blockStore, }) const store = graphStore({ chunk, linkCodec, valueCodec, blockStore }) const graph = new Graph(story, store) const tx = graph.tx() await tx.start() const v1 = tx.addVertex(ObjectTypes.TWEET) const v2 = tx.addVertex(ObjectTypes.TWEET) const v3 = tx.addVertex(ObjectTypes.TWEET) const e1 = await tx.addEdge(v1, v2, RlshpTypes.COMMENT_TO) const e2 = await tx.addEdge(v1, v3, RlshpTypes.COMMENT_TO) const p1 = await tx.addEdgeProp( e1, KeyTypes.VALUE, 55, PropTypes.WEIGHT ) const p2 = await tx.addEdgeProp( e2, KeyTypes.VALUE, 33, PropTypes.WEIGHT ) await tx.addVertexProp(v2, KeyTypes.VALUE, 100, PropTypes.KPI) await tx.addVertexProp(v2, KeyTypes.VALUE, 200, PropTypes.KPI) await tx.addVertexProp(v2, KeyTypes.VALUE, 300, PropTypes.KPI) await tx.addVertexProp(v3, KeyTypes.VALUE, 700, PropTypes.KPI) await tx.addVertexProp(v3, KeyTypes.VALUE, 50, PropTypes.KPI) const { root, index } = await tx.commit({}) const request = new RequestBuilder() .add(PathElemType.VERTEX) .type(1) .add(PathElemType.EDGE) .type(1) .propPred(KeyTypes.VALUE, isTrue()) .add(PathElemType.VERTEX) .type(1) .propPred(KeyTypes.VALUE, isTrue()) .extract(KeyTypes.VALUE) .reduce((previous, current) => { if (previous === undefined) previous = 0 previous += current return previous }) .maxResults(10) .get() const propResults = [] for await (const result of navigateVertices( graph, [v1.offset], request )) { propResults.push(result) } assert.equal(2, propResults.length) assert.equal(v2.offset, propResults[0].context.offset) assert.equal(v3.offset, propResults[1].context.offset) assert.equal(600, propResults[0].value) assert.equal(750, propResults[1].value) }) }) async function createSchemaAwareGraph() { enum ObjectTypes { TWEET = 1, } enum RlshpTypes { COMMENT_TO = 1, } enum PropTypes { COMMENT = 1, WEIGHT = 2, } enum KeyTypes { JSON = 1, TEXT = 3, VALUE = 33, } const blockStore: BlockStore = memoryBlockStoreFactory() const story: VersionStore = await versionStoreFactory({ chunk, linkCodec, valueCodec, blockStore, }) const store = graphStore({ chunk, linkCodec, valueCodec, blockStore }) const graph = new Graph(story, store) const tx = graph.tx() await tx.start() const v1 = tx.addVertex(ObjectTypes.TWEET) const v2 = tx.addVertex(ObjectTypes.TWEET) const v3 = tx.addVertex(ObjectTypes.TWEET) const e1 = await tx.addEdge(v1, v2, RlshpTypes.COMMENT_TO) const e2 = await tx.addEdge(v1, v3, RlshpTypes.COMMENT_TO) const p1 = await tx.addEdgeProp(e1, KeyTypes.VALUE, 55, PropTypes.WEIGHT) const p2 = await tx.addEdgeProp(e2, KeyTypes.VALUE, 33, PropTypes.WEIGHT) await tx.addVertexProp( v2, KeyTypes.TEXT, 'comment 1 from v2', PropTypes.COMMENT ) await tx.addVertexProp( v2, KeyTypes.TEXT, 'comment 2 from v2', PropTypes.COMMENT ) await tx.addVertexProp( v2, KeyTypes.TEXT, 'comment 3 from v2', PropTypes.COMMENT ) await tx.addVertexProp( v3, KeyTypes.TEXT, 'comment 1 from v3', PropTypes.COMMENT ) const { root, index } = await tx.commit({}) const { vertexRoot, vertexOffset, vertexIndex, edgeRoot, edgeOffset, edgeIndex, propRoot, propOffset, propIndex, } = index assert.equal(OFFSET_INCREMENTS.VERTEX_INCREMENT * 3, vertexOffset) assert.equal(OFFSET_INCREMENTS.EDGE_INCREMENT * 2, edgeOffset) assert.equal(OFFSET_INCREMENTS.PROP_INCREMENT * 6, propOffset) return { v1, graph } }