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

551 lines (480 loc) 17.6 kB
import { protoGremlinFactory, ProtoGremlin } from '../api/proto-gremlin' import { Block, Link, RootIndex, Vertex, VertexRef } from '../types' import { compute_chunks } from '@dstanesc/wasm-chunking-fastcdc-node' import { chunkerFactory } from '../chunking' import { BlockStore, MemoryBlockStore, memoryBlockStoreFactory, } from '../block-store' import { create as ipfsApi } from 'ipfs-http-client' import { blockStore as ipfsBlockStore } from '@dstanesc/ipfs-block-store' import { LinkCodec, linkCodecFactory, valueCodecFactory, ValueCodec, } from '../codecs' import { eq } from '../ops' import * as assert from 'assert' import bent from 'bent' import { VersionStore, versionStoreFactory } from '../version-store' import { graphPackerFactory } from '../graph-packer' const getStream = bent('https://raw.githubusercontent.com') enum ObjectTypes { ROOT = 1, BOOK = 2, CHAPTER = 3, VERSE = 4, } enum RlshpTypes { book = 1, chapter = 2, verse = 3, } enum KeyTypes { ID = 1, NAME = 2, TEXT = 3, } enum PropTypes { ANY = 1, } async function queryVerse( g: ProtoGremlin, rootOffset: VertexRef, book: string, chapter: number, verse: number ): Promise<{ result: string; time: number }> { const vr = [] const startTime = new Date().getTime() for await (const result of g .V([rootOffset]) .out(RlshpTypes.book) .has(ObjectTypes.BOOK, { keyType: KeyTypes.ID, operation: eq(book) }) .out(RlshpTypes.chapter) .has(ObjectTypes.CHAPTER, { keyType: KeyTypes.ID, operation: eq(chapter), }) .out(RlshpTypes.verse) .has(ObjectTypes.VERSE, { keyType: KeyTypes.ID, operation: eq(verse) }) .values(KeyTypes.TEXT) .maxResults(1) .exec()) { vr.push(result) } const endTime = new Date().getTime() const time = endTime - startTime return { result: vr[0].value, time } } async function queryVerseFromBook( g: ProtoGremlin, bookOffset: VertexRef, chapter: number, verse: number ): Promise<{ result: string; time: number }> { const vr = [] const startTime = new Date().getTime() for await (const result of g .V([bookOffset]) .out(RlshpTypes.chapter) .has(ObjectTypes.CHAPTER, { keyType: KeyTypes.ID, operation: eq(chapter), }) .out(RlshpTypes.verse) .has(ObjectTypes.VERSE, { keyType: KeyTypes.ID, operation: eq(verse) }) .values(KeyTypes.TEXT) .maxResults(1) .exec()) { vr.push(result) } const endTime = new Date().getTime() const time = endTime - startTime return { result: vr[0].value, time } } async function quickVerse( g: ProtoGremlin, verseOffset: VertexRef ): Promise<{ result: string; time: number }> { const vr = [] const startTime = new Date().getTime() for await (const result of g .V([verseOffset]) .values(KeyTypes.TEXT) .maxResults(1) .exec()) { vr.push(result) } const endTime = new Date().getTime() const time = endTime - startTime return { result: vr[0].value, time } } describe('Graph packer', function () { test('pack and restore random blocks', async () => { const cache = {} const ipfs = ipfsApi({ url: process.env.IPFS_API }) const linkCodec: LinkCodec = linkCodecFactory() const blockStore: BlockStore = ipfsBlockStore({ cache, ipfs }) const bytes1 = await blockStore.get( 'bafkreigu3fodriojosw3irmjz6v2znxli243n3xstcg575taxxjmctwu64' ) const bytes2 = await blockStore.get( 'bafkreihxekffa3coj4gpdmx2x5vteix5t5exrxfd7t4erqibj3mvw4rzm4' ) const bytes3 = await blockStore.get( 'bafkreib55oovsq4oxgiszoi43bsavb4raonyydlcyk6wx75hmb2ehkkikm' ) const block1 = { bytes: bytes1, cid: linkCodec.parseString( 'bafkreigu3fodriojosw3irmjz6v2znxli243n3xstcg575taxxjmctwu64' ), } const block2 = { bytes: bytes2, cid: linkCodec.parseString( 'bafkreihxekffa3coj4gpdmx2x5vteix5t5exrxfd7t4erqibj3mvw4rzm4' ), } const block3 = { bytes: bytes3, cid: linkCodec.parseString( 'bafkreib55oovsq4oxgiszoi43bsavb4raonyydlcyk6wx75hmb2ehkkikm' ), } const { packRandomBlocks, restoreRandomBlocks } = graphPackerFactory(linkCodec) const bundle: Block = await packRandomBlocks([block1, block2, block3]) const memStore: BlockStore = memoryBlockStoreFactory() const restored: Block[] = await restoreRandomBlocks( bundle.bytes, memStore ) expect(restored.length).toBe(3) expect(restored[0].cid.toString()).toBe( 'bafkreigu3fodriojosw3irmjz6v2znxli243n3xstcg575taxxjmctwu64' ) expect(restored[1].cid.toString()).toBe( 'bafkreihxekffa3coj4gpdmx2x5vteix5t5exrxfd7t4erqibj3mvw4rzm4' ) expect(restored[2].cid.toString()).toBe( 'bafkreib55oovsq4oxgiszoi43bsavb4raonyydlcyk6wx75hmb2ehkkikm' ) }) test('pack and restore version store', async () => { const cache = {} const ipfs = ipfsApi({ url: process.env.IPFS_API }) const { chunk } = chunkerFactory(1024 * 48, compute_chunks) const linkCodec: LinkCodec = linkCodecFactory() const valueCodec: ValueCodec = valueCodecFactory() const blockStore: BlockStore = ipfsBlockStore({ cache, ipfs }) const versionStoreRoot = linkCodec.parseString( 'bafkreiarwxmqgb7aroqi4wocldxcub25yclf6iz3ey4gmy4fqlnmkqvq3u' ) /* * Packing and restoring */ const { packVersionStore, restoreSingleIndex: restoreVersionStore } = graphPackerFactory(linkCodec) const bundle: Block = await packVersionStore( versionStoreRoot, blockStore, chunk, valueCodec ) const memStore: BlockStore = memoryBlockStoreFactory() const { root: storeRoot } = await restoreVersionStore( bundle.bytes, memStore ) const versionStore: VersionStore = await versionStoreFactory({ storeRoot: storeRoot, chunk, linkCodec, valueCodec, blockStore: memStore, }) assert.equal( 'bafkreielwa556n5ax2rngrplokqypcds2ypxnq5niufa4ls4l73flwt73i', versionStore.id() ) assert.equal( 'bafkreiarwxmqgb7aroqi4wocldxcub25yclf6iz3ey4gmy4fqlnmkqvq3u', versionStore.versionStoreRoot() ) assert.equal(1, versionStore.log().length) assert.equal( 'bafkreiegljjns2rqb3z5mtdyvq2u6u2cvsahyez6bqsdjibo6737vrqhbi', versionStore.currentRoot().toString() ) }) test('pack and restore full graph', async () => { const cache = {} const ipfs = ipfsApi({ url: process.env.IPFS_API }) const { chunk } = chunkerFactory(1024 * 48, compute_chunks) const linkCodec: LinkCodec = linkCodecFactory() const valueCodec: ValueCodec = valueCodecFactory() const blockStore: BlockStore = ipfsBlockStore({ cache, ipfs }) const versionRoot = linkCodec.parseString( 'bafkreiegljjns2rqb3z5mtdyvq2u6u2cvsahyez6bqsdjibo6737vrqhbi' ) const versionStore: VersionStore = await versionStoreFactory({ versionRoot, chunk, linkCodec, valueCodec, blockStore, }) /* * Packing and restoring */ const { packGraph, restore } = graphPackerFactory(linkCodec) const bundle: Block = await packGraph(versionRoot, blockStore) const memStore: BlockStore = memoryBlockStoreFactory() const { root, index, blocks } = await restore(bundle.bytes, memStore) /** * Verifying */ const g2: ProtoGremlin = protoGremlinFactory({ chunk, linkCodec, valueCodec, blockStore: memStore, versionStore, }).g() const { result, time } = await queryVerse(g2, 0, 'Rev', 22, 21) assert.strictEqual( result, 'The grace of our Lord Jesus Christ be with you all. Amen.' ) assert.ok(time < 3000) // 3sec /** * More concise packing */ const g: ProtoGremlin = protoGremlinFactory({ chunk, linkCodec, valueCodec, blockStore, versionStore, }).g() const bundle2 = await g.pack(versionRoot) assert.deepStrictEqual(bundle, bundle2) }) test('pack and restore single commit', async () => { const stream = await getStream( '/dstanesc/bibleapi-bibles-json/legacy/kjv.json' ) const str = (await stream.text()).trim() const lines = str.split(/\r?\n/g) const { chunk } = chunkerFactory(1024 * 48, compute_chunks) const linkCodec: LinkCodec = linkCodecFactory() const valueCodec: ValueCodec = valueCodecFactory() const blockStore: MemoryBlockStore = memoryBlockStoreFactory() const versionStore: VersionStore = await versionStoreFactory({ chunk, linkCodec, valueCodec, blockStore, }) const g: ProtoGremlin = protoGremlinFactory({ chunk, linkCodec, valueCodec, blockStore, versionStore, }).g() const tx = await g.tx() const bible = await tx.addV(ObjectTypes.ROOT).next() let book: any, book_id: string let chapter: any, chapter_id: number let verse: any, verse_id: number for (const line of lines) { const entry = JSON.parse(line) if (book === undefined || book_id !== entry.book_id) { book = await tx .addV(ObjectTypes.BOOK) .property(KeyTypes.ID, entry.book_id, PropTypes.ANY) .property(KeyTypes.NAME, entry.book_name, PropTypes.ANY) .next() book_id = entry.book_id await tx.addE(RlshpTypes.book).from(bible).to(book).next() } if (chapter === undefined || chapter_id !== entry.chapter) { chapter = await tx .addV(ObjectTypes.CHAPTER) .property(KeyTypes.ID, entry.chapter, PropTypes.ANY) .next() chapter_id = entry.chapter await tx.addE(RlshpTypes.chapter).from(book).to(chapter).next() } if (verse === undefined || verse_id !== entry.verse) { verse = await tx .addV(ObjectTypes.VERSE) .property(KeyTypes.ID, entry.verse, PropTypes.ANY) .property(KeyTypes.TEXT, entry.text, PropTypes.ANY) .next() verse_id = entry.verse await tx.addE(RlshpTypes.verse).from(chapter).to(verse).next() } // console.log( // `Book:${entry.book_name}: ${book.offset} Chapter: ${entry.chapter}: ${chapter.offset} Verse: ${entry.verse}: ${verse.offset}` // ) } const commit = await tx.commit({}) const { packCommit, restore } = graphPackerFactory(linkCodec) const bundle: Block = await packCommit(commit) const emptyStore: BlockStore = memoryBlockStoreFactory() const { root, index, blocks } = await restore(bundle.bytes, emptyStore) const g2: ProtoGremlin = protoGremlinFactory({ chunk, linkCodec, valueCodec, blockStore: emptyStore, versionStore, }).g() { const { result, time } = await queryVerse(g2, 0, 'Rev', 22, 21) assert.strictEqual( result, 'The grace of our Lord Jesus Christ be with you all. Amen.' ) assert.ok(time < 3000) // 3sec } }) test('pack and restore computed range, graph depth 1', async () => { const cache = {} const ipfs = ipfsApi({ url: process.env.IPFS_API }) const { chunk } = chunkerFactory(1024 * 48, compute_chunks) const linkCodec: LinkCodec = linkCodecFactory() const valueCodec: ValueCodec = valueCodecFactory() const blockStore: BlockStore = ipfsBlockStore({ cache, ipfs }) const versionRoot = linkCodec.parseString( 'bafkreiegljjns2rqb3z5mtdyvq2u6u2cvsahyez6bqsdjibo6737vrqhbi' ) const versionStore: VersionStore = await versionStoreFactory({ versionRoot, chunk, linkCodec, valueCodec, blockStore, }) const { packComputed, unpack, restore } = graphPackerFactory(linkCodec) const bundle: Block = await packComputed( versionRoot, 808775, // first vertex ref 1, // vertex count 1, // graph depth blockStore, chunk, valueCodec ) const emptyStore: MemoryBlockStore = memoryBlockStoreFactory() { const { root, index, blocks } = await restore( bundle.bytes, emptyStore ) assert.strictEqual( root.toString(), 'bafkreiegljjns2rqb3z5mtdyvq2u6u2cvsahyez6bqsdjibo6737vrqhbi' ) assert.strictEqual(blocks.length, 9) const g2: ProtoGremlin = protoGremlinFactory({ chunk, linkCodec, valueCodec, blockStore: emptyStore, versionStore, }).g() const { result, time } = await quickVerse(g2, 808800) assert.strictEqual( result, 'The grace of our Lord Jesus Christ be with you all. Amen.' ) assert.ok(time < 10) // 10ms } { const { root, index, blocks } = await unpack(bundle.bytes) assert.strictEqual( root.toString(), 'bafkreiegljjns2rqb3z5mtdyvq2u6u2cvsahyez6bqsdjibo6737vrqhbi' ) assert.strictEqual(blocks.length, 9) const otherStore: MemoryBlockStore = memoryBlockStoreFactory() for (const block of blocks) { await otherStore.put(block) } assert.deepStrictEqual(emptyStore.size(), otherStore.size()) } { /** * More concise fragment packing */ const g: ProtoGremlin = protoGremlinFactory({ chunk, linkCodec, valueCodec, blockStore, versionStore, }).g() const bundle2 = await g.packFragment(808775, 1, 1, versionRoot) assert.deepStrictEqual(bundle, bundle2) } }) test('pack and restore computed range, graph depth 3', async () => { const cache = {} const ipfs = ipfsApi({ url: process.env.IPFS_API }) const { chunk } = chunkerFactory(1024 * 48, compute_chunks) const linkCodec: LinkCodec = linkCodecFactory() const valueCodec: ValueCodec = valueCodecFactory() const blockStore: BlockStore = ipfsBlockStore({ cache, ipfs }) const versionRoot = linkCodec.parseString( 'bafkreiegljjns2rqb3z5mtdyvq2u6u2cvsahyez6bqsdjibo6737vrqhbi' ) const versionStore: VersionStore = await versionStoreFactory({ versionRoot, chunk, linkCodec, valueCodec, blockStore, }) const g: ProtoGremlin = protoGremlinFactory({ chunk, linkCodec, valueCodec, blockStore, versionStore, }).g() // Packing single (Revelation) book, w/ depth 3 (book, chapters, verses) - vertex offset is 798175 const bundle = await g.packFragment(798175, 1, 3, versionRoot) const emptyStore: MemoryBlockStore = memoryBlockStoreFactory() const { restore } = graphPackerFactory(linkCodec) await restore(bundle.bytes, emptyStore) const g2: ProtoGremlin = protoGremlinFactory({ chunk, linkCodec, valueCodec, blockStore: emptyStore, versionStore, }).g() const { result: r1, time } = await queryVerseFromBook( g2, 798175, 22, 21 ) assert.strictEqual( r1, 'The grace of our Lord Jesus Christ be with you all. Amen.' ) const { result: r2 } = await queryVerseFromBook(g2, 798175, 22, 11) assert.strictEqual( r2, 'He that is unjust, let him be unjust still: and he which is filthy, let him be filthy still: and he that is righteous, let him be righteous still: and he that is holy, let him be holy still.' ) }) })