├── .gitignore ├── .npmignore ├── README.md ├── index.js ├── package.json ├── test-keys.js ├── test.js ├── testdb.js ├── testdump.js └── testsetnul.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *~ 3 | sql.config 4 | gun.db* 5 | data.json* 6 | zz* 7 | xx* 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | .npmignore 3 | *~ 4 | gun.db 5 | gun.db-journal 6 | data.json* 7 | zz* 8 | xx* 9 | sql.config 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Join the chat at https://gitter.im/sack-vfs/Lobby](https://badges.gitter.im/sack-vfs/gun-db.svg)](https://gitter.im/sack-vfs/gun-db?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 3 | 4 | # gun-db 5 | ODBC/Sqlite native persistence layer for [gun](https://github.com/amark/gun)! GUN is an Open Source Firebase with swappable storage engines (level, SQLite, etc.) that handles data synchronization across machines / devices. 6 | 7 | 8 | Get it by 9 | 10 | `npm install gun-db` 11 | 12 | Use by 13 | 14 | ```javascript 15 | var Gun = require('gun'); 16 | require('gun-db'); 17 | 18 | var gun = Gun({ 19 | file: false // turn off pesky file.js data.json default 20 | , db: { 21 | file: "gun.db" 22 | exclusive : false // default 23 | } 24 | }); 25 | ``` 26 | 27 | If you want to have maximum speed, you can set exclusive, which will gain about 30-40% speed; but you're only allowed one instance of Gun against this database. 28 | You can open multiple instances if they don't have the same database name. 29 | 30 | Check the gun docs on how to read/write data, it will then handle sync automatically for you (even to the browser!). Tip: It is a graph database, so you can do key/value, document, relational, or graph based data - here is a [crash course](https://github.com/amark/gun/wiki/graphs) on how to use it. 31 | 32 | Enjoy! 33 | 34 | Or: Complain about bugs. :) 35 | 36 | 37 | # notes 38 | If the filename is '*.db' it defaults to sqlite if it's not it tries it as a DSN (data source name) and then if that doesn't work falls back to use sqlite filename. 39 | ODBC can be provided by providing unixodbc on linux, but requires modifying the build to enable; it is by default only enabled for windows. 40 | 41 | It also ends up writing a sql.config file somewhere ... there's options you can set there to enable sql logging (optionally with data returned) which goes to stderr 42 | under windows this goes to (/programdata/freedom collective/node/...) probably. If your node.exe is not what your running it will be in a folder that is whatever the program name is minus the last (.*) 43 | under not windows it probably just goes to ~ 44 | 45 | ## VFS Usage 46 | 47 | This is an example of how to open the sqlite database in a virtual filesystem storage; the access to the sqlite database 48 | is then memory mapped. 49 | 50 | ``` 51 | var vfs = require( "sack.vfs" ); 52 | var vol = vfs.Volume( "MountName", "vfsFile.dat" ); 53 | 54 | var Gun = require('gun'); 55 | require('gun-db'); 56 | 57 | var gun = Gun({ 58 | file: false // turn off pesky file.js data.json default 59 | , db: { 60 | file: "$sack@MountName$gun.db" 61 | } 62 | }); 63 | 64 | /* ... your appcode ... */ 65 | 66 | ``` 67 | 68 | 69 | # Changelog 70 | - 1.0.571 Fix depending on gun from past 0.9.96 is kinda required (moved symbols) 71 | - 1.0.570 Fix depending on gun in the future (98 it's only 96) 72 | - 1.0.569 Update to gun 0.9.98 (internal constant moved) 73 | - 1.0.568 Fix generating input to wrong object (should be ctx, not gun) 74 | - 1.0.567 update to gun 0.9.x 75 | - 1.0.566 for very large nodes, batch results into a single 'in' to gun. 76 | - 1.0.565 disable exclusive by default; add option to enable it 77 | - 1.0.564 test sqlite database without exclusive, but with URI filename and nolock option. 78 | - 1.0.563 remove excess logging 79 | - 1.0.562 store json string in data otherwise simple number types come back as numbers and not strings (invalid graph! error) 80 | - 1.0.561 fix last minute typo 81 | - 1.0.56 fix writing null value and relation; fixed relation restore; remove unused code; optimize existance check 82 | - 1.0.55 More cleanup; if database open fails, don't register handlers. 83 | - 1.0.54 fixed typo 84 | - 1.0.53 a little cleanup; move varibes to closure (bad debug typo) 85 | - 1.0.52 remove noisy logging when record already up to date; post reply acking the transation. 86 | - 1.0.51 Update docs; add gitter badge 87 | - 1.0.4 fix excessively slow load; misported from sqlite.gun. 88 | - 1.0.3 fix database performance options. 89 | - 1.0.2 update to Gun 0.8.3 90 | - 1.0.1 First usable version 91 | 92 | 93 | 94 | 95 | 96 | ### Benchmark Results 97 | 98 | ``` 99 | (sqlite, native filesystem, windows) 100 | __ Small Nodes: 10 Properties Each __ 101 | Write 10000 nodes: : 19841ms; 19.841s; 1.984 ms/node; errors: 0. 102 | Read 10000 nodes: : 7690ms; 7.69s; 0.769 ms/node; errors: 0. 103 | Update 10000 nodes: : 22273ms; 22.273s; 2.227 ms/node; errors: 0. 104 | Update single field on 10000 nodes: : 3215ms; 3.215s; 0.321 ms/node; errors: 0. 105 | __ Medium Nodes: 1000 Properties Each __ 106 | Write 100 nodes: : 18155ms; 18.155s; 179.752 ms/node; errors: 0. 107 | Read 100 nodes: : 15554ms; 15.554s; 154.000 ms/node; errors: 0. 108 | Update 100 nodes: : 21044ms; 21.044s; 208.356 ms/node; errors: 0. 109 | Update single field on 100 nodes: : 245ms; 0.245s; 2.426 ms/node; errors: 0. 110 | __ Large Nodes: 10000 Properties Each __ 111 | Write 10 nodes: : 20773ms; 20.773s; 1888.455 ms/node; errors: 0. 112 | Read 10 nodes: : 17342ms; 17.342s; 1576.545 ms/node; errors: 0. 113 | Update 10 nodes: : 22796ms; 22.796s; 2072.364 ms/node; errors: 0. 114 | Update single field on 10 nodes: : 1919ms; 1.919s; 174.455 ms/node; errors: 0. 115 | 116 | 117 | (sqlite, vfs) 118 | __ Small Nodes: 10 Properties Each __ 119 | Write 10000 nodes: : 11667ms; 11.667s; 1.167 ms/node; errors: 0. 120 | Read 10000 nodes: : 7086ms; 7.086s; 0.709 ms/node; errors: 0. 121 | Update 10000 nodes: : 13439ms; 13.439s; 1.344 ms/node; errors: 0. 122 | Update single field on 10000 nodes: : 2530ms; 2.53s; 0.253 ms/node; errors: 0. 123 | __ Medium Nodes: 1000 Properties Each __ 124 | Write 100 nodes: : 12935ms; 12.935s; 128.069 ms/node; errors: 0. 125 | Read 100 nodes: : 5792ms; 5.792s; 57.347 ms/node; errors: 0. 126 | Update 100 nodes: : 16061ms; 16.061s; 159.020 ms/node; errors: 0. 127 | Update single field on 100 nodes: : 200ms; 0.2s; 1.980 ms/node; errors: 0. 128 | __ Large Nodes: 10000 Properties Each __ 129 | Write 10 nodes: : 16134ms; 16.134s; 1466.727 ms/node; errors: 0. 130 | Read 10 nodes: : 7653ms; 7.653s; 695.727 ms/node; errors: 0. 131 | Update 10 nodes: : 21076ms; 21.076s; 1916.000 ms/node; errors: 0. 132 | Update single field on 10 nodes: : 1962ms; 1.962s; 178.364 ms/node; errors: 0. 133 | 134 | 135 | (sqlite, vfs, with encryption) 136 | __ Small Nodes: 10 Properties Each __ 137 | Write 10000 nodes: : 14899ms; 14.899s; 1.490 ms/node; errors: 0. 138 | Read 10000 nodes: : 7301ms; 7.301s; 0.730 ms/node; errors: 0. 139 | Update 10000 nodes: : 18248ms; 18.248s; 1.825 ms/node; errors: 0. 140 | Update single field on 10000 nodes: : 3224ms; 3.224s; 0.322 ms/node; errors: 0. 141 | __ Medium Nodes: 1000 Properties Each __ 142 | Write 100 nodes: : 20645ms; 20.645s; 204.406 ms/node; errors: 0. 143 | Read 100 nodes: : 5952ms; 5.952s; 58.931 ms/node; errors: 0. 144 | Update 100 nodes: : 27732ms; 27.732s; 274.574 ms/node; errors: 0. 145 | Update single field on 100 nodes: : 350ms; 0.35s; 3.465 ms/node; errors: 0. 146 | __ Large Nodes: 10000 Properties Each __ 147 | Write 10 nodes: : 28288ms; 28.288s; 2571.636 ms/node; errors: 0. 148 | Read 10 nodes: : 9496ms; 9.496s; 863.273 ms/node; errors: 0. 149 | Update 10 nodes: : 37709ms; 37.709s; 3428.091 ms/node; errors: 0. 150 | Update single field on 10 nodes: : 3587ms; 3.587s; 326.091 ms/node; errors: 0. 151 | 152 | ``` -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | //process.on( "warning", (warning)=>{console.trace( "WARNING:", warning ); } ); 3 | //process.on( "error", (warning)=>{console.trace( "ERROR PROCESS:", warning ); } ); 4 | //process.on( "exit", (warning)=>{console.trace( "EXIT:", warning ); } ); 5 | 6 | const Gun = require('gun/gun'); 7 | const vfs = require("sack.vfs"); 8 | 9 | var _debug_counter = 0; 10 | var __debug_counter = 0; 11 | var _debug_tick = Date.now(); 12 | const _debug = false; 13 | 14 | const rel_ = Gun.val.rel._; // '#' 15 | const val_ = Gun.obj.has._; // '.' 16 | const node_ = Gun.node._; // '_' 17 | const state_ = Gun.state._;// '>'; 18 | const soul_ = Gun.node.soul._;// '#'; 19 | 20 | const ACK_ = '@'; 21 | const SEQ_ = '#'; 22 | 23 | Gun.on('opt', function(ctx){ 24 | this.to.next(ctx); 25 | if(ctx.once){ return } 26 | var opt = ctx.opt.db || (ctx.opt.db = {}); 27 | //opt.file = opt.file || ('file:gun.db?nolock=1'); 28 | opt.file = opt.file || ('gun.db'); 29 | var client = vfs.Sqlite(opt.file); 30 | var gun = ctx.gun; 31 | if( !client ) { 32 | console.log( "Failed to open database:", opt.file ); 33 | return; 34 | } 35 | //client.transaction(); 36 | client.makeTable( `create table record ( 37 | soul char, 38 | field char, 39 | value char, 40 | relation char, 41 | state char, 42 | constraint record_unique unique(soul,field) 43 | )` ); 44 | 45 | client.do( "create index if not exists soul_index on record(soul)"); 46 | //client.do( "PRAGMA mmap_size=16777216" ); 47 | client.do( "PRAGMA journal_mode=PERSIST" ); 48 | //client.do( "PRAGMA journal_mode=WAL" ); 49 | client.do( "PRAGMA synchronous = 0"); // necessary for perf! 50 | if( opt.exclusive ) 51 | client.do( "PRAGMA locking_mode = EXCLUSIVE" ); 52 | 53 | //client.do( "create index if not exists soul_field_index on record(soul,field)"); 54 | //client.commit(); 55 | //client.autoTransact( true ); 56 | var skip_put = null; 57 | 58 | ctx.on('put', function(at){ 59 | this.to.next(at); 60 | if( skip_put && skip_put == at[ACK_] ) { 61 | if( _debug ) { 62 | var now = Date.now(); 63 | if( now - _debug_tick > 1000 ) { 64 | console.log( "N in M", _debug_counter - __debug_counter, now-_debug_tick, (_debug_counter - __debug_counter)/( now-_debug_tick) ); 65 | _debug_tick = now; 66 | __debug_counter = _debug_counter; 67 | } 68 | _debug_counter++; 69 | console.log( new Date(), "skipping put in-get:", _debug_counter, " get putting:", skip_put, at[ACK_], JSON.stringify( at.put ) ); 70 | } 71 | return; 72 | } 73 | _debug && console.log( new Date(), "PUT", at[SEQ_], at[ACK_], JSON.stringify( at.put ) ); 74 | Gun.graph.is(at.put, null, function(value, field, node, soul){ var id; 75 | // kinda hate to always do a select just to see that the new update is newer than what was there. 76 | //console.log( "do select soul field", field, `select state from Record where soul='${client.escape(soul)}' and field='${client.escape(field)}'` ); 77 | var record = client.do( `select state from record where soul='${client.escape(soul)}' and field='${client.escape(field)}'` ); 78 | { 79 | var dataRelation, dataValue, tmp; 80 | var state = Gun.state.is(node, field); 81 | // Check to see if what we have on disk is more recent. 82 | //console.log( "result?", record ) 83 | if(record && record.length && state <= record[0].state){ 84 | _debug && console.log( new Date(), "already newer in database.." ); 85 | ctx.on('in', {[ACK_]: at[rel_], ok: 1}); 86 | return 87 | } 88 | if(value && (tmp = value[rel_])){ // TODO: Don't hardcode. 89 | dataRelation = "'" + client.escape(JSON.stringify(tmp)) + "'"; 90 | dataValue = "NULL" 91 | } else if( value ) { 92 | dataRelation = "NULL"; 93 | dataValue = "'" + client.escape(JSON.stringify(value)) + "'"; 94 | } else { 95 | dataRelation = "NULL"; 96 | dataValue = "NULL" 97 | } 98 | try { 99 | _debug && console.log( new Date(), "Do replace field soul:", soul, " field:", field, "val:", dataValue ); 100 | client.do( `replace into record (soul,field,value,relation,state) values('${client.escape(soul)}','${client.escape(field)}',${dataValue},${dataRelation},${state})` ); 101 | ctx.on('in', {[ACK_]: at[rel_], ok: 1}); 102 | } catch( e ) { 103 | ctx.on('in', {[ACK_]: at[rel_], err: e}); 104 | } 105 | } 106 | }); 107 | _debug && console.log( new Date(), " : Put done" ); 108 | }); 109 | 110 | ctx.on('get', function(at){ 111 | this.to.next(at); 112 | if(!client){ console.log( "Lost the database somehow" ); return } 113 | var lex = at.get, u; 114 | if(!lex){ return } 115 | var soul = lex[soul_]; 116 | var field = lex[val_]; 117 | _debug && console.log( new Date(), "doing get...for soul:", soul, "field:",field ); 118 | if(node_ === field){ 119 | var record = client.do( `select 1 from record where soul='${client.escape(soul)}' limit 1` ); 120 | _debug && console.log( new Date(), "select result:", record ); 121 | if(!record || !record.length){ 122 | _debug && console.log( "So, result with an in?" ); 123 | return ctx.on('in', {[ACK_]: at[SEQ_]}); 124 | } 125 | _debug && console.log( new Date(), "give back empty" ); 126 | return ctx.on('in', {[ACK_]: at[SEQ_], put: { [soul]: { [node_]:{ [rel_]:soul, [state_]:{}} }}}); 127 | } 128 | if(field){ 129 | _debug && console.log( new Date(), " field...", field ); 130 | var record = client.do( `select * from record where soul='${client.escape(soul)}' and field='${client.escape(field)}'` ); 131 | if( record && record.length ) { 132 | _debug && console.log( new Date(), "Specific field?", record ); 133 | let rec= record[0] 134 | var msg; 135 | if( rec.relation ) 136 | msg = { [rec.soul]: { [node_]:{ [rel_]:rec.soul, [state_]:{[rec.field]:rec.state }}, [rec.field]:{[rel_]:JSON.parse(rec.relation)} } }; 137 | else if( rec.value ) 138 | msg = { [rec.soul]: { [node_]:{ [rel_]:rec.soul, [state_]:{[rec.field]:rec.state }}, [rec.field]:JSON.parse(rec.value) } }; 139 | else 140 | msg = { [rec.soul]: { [node_]:{ [rel_]:rec.soul, [state_]:{[rec.field]:rec.state }}, [rec.field]:null } }; 141 | skip_put = at[SEQ_]; 142 | console.log( new Date(), msg ); 143 | ctx.on('in', {[ACK_]: at[SEQ_], put: msg}); 144 | skip_put = null; 145 | } 146 | return 147 | } 148 | _debug && console.log( new Date(), "select all fields...", soul ); 149 | var record = client.do( `select * from record where soul='${client.escape(soul)}'` ); 150 | if( !record || !record.length){ 151 | _debug && console.log( new Date(), "nothing... So, result with an in?" ); 152 | ctx.on('in', {[ACK_]: at[SEQ_]}); 153 | } 154 | else { 155 | _debug && console.log( new Date(), "got result" ); 156 | if( record.length > 1 ) { 157 | var state, node; 158 | var rec = { [soul] : { [node_] : { [rel_]: soul, [state_] : {} } } }; 159 | node = rec[soul]; 160 | state = node[node_][state_]; 161 | record.forEach(function(record){ 162 | state[record.field] = parseFloat(record.state); 163 | if( record.relation ) 164 | node[record.field] = {[rel_]:JSON.parse(record.relation)}; 165 | else if( record.value ) 166 | node[record.field] = JSON.parse(record.value); 167 | else 168 | node[record.field] = null; 169 | } ); 170 | //console.log( new Date(), "Node is now ------------\n", JSON.stringify(rec) ); 171 | skip_put = at[SEQ_]; 172 | _debug && console.log( new Date(), "put to gun" ); 173 | ctx.on('in', {ACK_: at[SEQ_], put: rec }); 174 | skip_put = null; 175 | } 176 | else record.forEach(function(record){ 177 | var msg; 178 | if( record.relation ) 179 | msg = { [soul]: { [node_]:{ [rel_]:record.soul, [state_]:{[record.field]:parseFloat(record.state) }}, [record.field]:{[rel_]:JSON.parse(record.relation)} } }; 180 | else if( record.value ) 181 | msg = { [soul]: { [node_]:{ [rel_]:record.soul, [state_]:{[record.field]:parseFloat(record.state) }}, [record.field]:JSON.parse(record.value) } }; 182 | else 183 | msg = { [soul]: { [node_]:{ [rel_]:record.soul, [state_]:{[record.field]:parseFloat(record.state) }}, [record.field]:null } }; 184 | //console.log( "State is:", typeof( record.state ) ); 185 | //console.log( new Date(), " From Nodify", JSON.stringify(msg) ); 186 | skip_put = at[SEQ_]; 187 | _debug && console.log( new Date(), "put to gun" ); 188 | result = ctx.on('in', {[ACK_]: at[SEQ_], put: msg } ); 189 | skip_put = null; 190 | }); 191 | _debug && console.log( new Date(), "put into gun done" ); 192 | } 193 | }); 194 | 195 | 196 | }); 197 | 198 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gun-db", 3 | "version": "1.0.572", 4 | "description": "ODBC/Sqlite persistence layer for gun", 5 | "main": "index.js", 6 | "keywords": [ 7 | "gun", 8 | "gundb", 9 | "sqlite", 10 | "sqlite3", 11 | "driver", 12 | "vfs" 13 | ], 14 | "author": "d3x0r", 15 | "license": "MIT", 16 | "dependencies": { 17 | "gun": "^0.2019.416", 18 | "sack.vfs": "^0.9.146" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/d3x0r/gun-db" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test-keys.js: -------------------------------------------------------------------------------- 1 | 2 | var Gun = require( "gun/gun" ); 3 | var gunNot = require('gun/lib/not') 4 | var gunDb = require( "." ); 5 | 6 | 7 | //var vfs = require( "sack.vfs" ); 8 | //var vol = vfs.Volume( "Mount", "data.vfs", "key1", "key2" ); 9 | //var gun = new Gun( { db:{ file:'$sack@Mount$gun.db' } } ); 10 | 11 | var gun = new Gun( /*{ db:{ file:'gun.db' } }*/ ); 12 | 13 | var root = gun.get( 'db' ); 14 | 15 | var tick = Date.now(); 16 | for( var i = 1; i < 1000; i++ ) { 17 | var obj = {}; 18 | for( var j = 1; j < 1000; j++ ) { 19 | obj["key"+j] = "Some 10 character value"; 20 | } 21 | root.get( i.toString() ).put( obj ); 22 | if( i % 1001 == 1000 ) { 23 | console.log( new Date(), "did ", i%1001, " in ", Date.now() - tick ); 24 | tick = Date.now(); 25 | } 26 | } 27 | console.log( "done" ); 28 | 29 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | try{require('fs').unlinkSync('gun.db'); 2 | }catch(e){} 3 | 4 | require('./index'); 5 | 6 | global.Gun = require('gun/gun'); 7 | 8 | require('gun/test/abc'); -------------------------------------------------------------------------------- /testdb.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | var Gun = require( "gun/gun" ); 4 | var gunNot = require('gun/lib/not') 5 | var gunDb = require( "." ); 6 | 7 | //var vfs = require( "sack.vfs" ); 8 | //var vol = vfs.Volume( "Mount", "data.vfs" ); 9 | //var gun = new Gun( { db:{ file:'$sack@Mount$gun.db' } } ); 10 | 11 | var gun = new Gun( /*{ db:{ file:'gun.db' } }*/ ); 12 | 13 | console.log( new Date(), "Initialized gun instance" ); 14 | var root = gun.get( "db" ); 15 | 16 | console.log( new Date(), "got root db" ); 17 | 18 | var notStart = Date.now(); 19 | root.not( ()=>{ 20 | console.log( "not happened." ); 21 | root.put( { hello:"world" } ); 22 | root.put( { other:"test" } ); 23 | root.set( { field: "randomkey" } ); 24 | root.set( { field: "randomkey" } ); 25 | root.set( { field: "randomkey" } ); 26 | } ); 27 | console.log( "waited in not?", Date.now() - notStart ); 28 | 29 | var count = 0; 30 | var start = Date.now(); 31 | var first = false; 32 | 33 | var done = false; 34 | function showItems() { 35 | console.log( new Date(), "Got", count, "items" ); 36 | //dumpDatabase(); 37 | if( !done ) 38 | setTimeout( showItems, 1000 ); 39 | } 40 | timeout = setTimeout( showItems, 100 ); 41 | var timeout; 42 | var tick = Date.now(); 43 | console.log( new Date(), "marked tick." ); 44 | var _n = 0; 45 | var gotHello = false; 46 | root.map( (field,val)=>{ 47 | if( !first ) { 48 | console.log( "first map in ", Date.now() - start ); 49 | first = true; 50 | } 51 | count++; 52 | if( count % 3000 === 0 ) console.log( new Date(), "count:", count ); 53 | //console.log( "Got:", val, field ) 54 | if( val == "hello" && !gotHello ) { 55 | gotHello = true; 56 | console.log( new Date(), "Got:", val, field ) 57 | for( var n = 0; n < 30000; n++ ) { 58 | if( (n % 1000) === 0 ) { 59 | console.log( new Date(), "new items:", n , Date.now() - tick, (n-_n)/(Date.now() - tick) ); 60 | tick = Date.now(); 61 | _n = n; 62 | } 63 | root.set( { field: "randomkey" } ); 64 | } 65 | done = true; 66 | } 67 | } ) 68 | 69 | 70 | -------------------------------------------------------------------------------- /testdump.js: -------------------------------------------------------------------------------- 1 | 2 | var vfs = require( "sack.vfs" ); 3 | //var vol = vfs.Volume( "Mount", "data.vfs", "key1" ); 4 | 5 | function dumpDatabase() { 6 | var db = vfs.Sqlite( "gun.db" ); 7 | //var db = vfs.Sqlite( "$sack@Mount$gun.db" ); 8 | var tables = db.do( "select tbl_name from sqlite_master where type='table'" ); 9 | console.log( "Tables:", tables ); 10 | var records = db.do( "select * from record" ); 11 | console.log( "records:" ); 12 | records.forEach( rec=>{ 13 | console.log( ` s:${rec.soul} f:${rec.field} v:${rec.value} r:${rec.relation} s:${rec.state}` ); 14 | } ); 15 | 16 | } 17 | 18 | dumpDatabase() -------------------------------------------------------------------------------- /testsetnul.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | var add = false; 4 | var del = false; 5 | if( process.argv[2] == "add" ) 6 | add = true; 7 | else if( process.argv[2] == "del" ) 8 | del = true; 9 | else { 10 | console.log( "Must supply parameter 'add' or 'del'" ); 11 | //process.exit(0); 12 | } 13 | 14 | var Gun = require( "gun/gun" ); 15 | var gunDb = require( "." ); 16 | 17 | //var Gun = require( "gun" ); 18 | 19 | require('gun/lib/not') 20 | require( "gun-unset" ); 21 | 22 | 23 | const rel_ = Gun.val.rel._; // '#' 24 | const val_ = Gun._.field; // '.' 25 | const node_ = Gun.node._; // '_' 26 | 27 | var gun = new Gun( /*{ db:{ file:'gun.db' } }*/ ); 28 | 29 | console.log( new Date(), "Initialized gun instance" ); 30 | var root = gun.get( "db" ); 31 | 32 | var alice = root.get( "alice" ) 33 | alice.not( (val)=>{ 34 | console.log( "init alice data", val ); 35 | alice.put( { name: 'alice', dob: 'before now', whatever: 'one' } ); 36 | } ); 37 | var bob = root.get( "bob" ) 38 | bob.not( (val)=>{ 39 | console.log( "init bob data" , val); 40 | bob.put( { name:'bob', dob: "yes", color: "purple" } ); 41 | } ); 42 | if( add ) { 43 | console.log( "adding bob friend" ); 44 | alice.get( 'friends' ).set( bob ); 45 | } 46 | 47 | var charlie = root.get( "charlie" ) 48 | charlie.not( (val)=>{ 49 | console.log( "init charlie data", val ); 50 | charlie.put( {name:"charlie", dob : "not yet", extra: "banana" } ); 51 | } ); 52 | 53 | if( add ) { 54 | console.log( "adding charlie friend" ); 55 | alice.get( 'friends' ).set( charlie ); 56 | } 57 | 58 | var deleting = false; 59 | var deleted = false; 60 | 61 | if( del ) 62 | alice.get( 'friends' ).unset( bob ); 63 | 64 | alice.get( 'friends' ).map( ).val( function(val, field, ctx) { 65 | if( !val ) 66 | console.log( "(val)alice lost a friend." ); 67 | else 68 | console.log( "(val)alice has a friend", val.name ); 69 | } ); 70 | 71 | --------------------------------------------------------------------------------