@@ -5,41 +5,167 @@ const inherits = require('inherits')
55const events = require ( 'events' )
66const SparqlIterator = require ( 'sparql-iterator' )
77
8+ const constants = require ( './lib/constants' )
89const utils = require ( './lib/utils' )
10+ const prefixes = require ( './lib/prefixes' )
911const Variable = require ( './lib/Variable' )
1012const HyperdbReadTransform = require ( './lib/HyperdbReadTransform' )
1113const JoinStream = require ( './lib/JoinStream' )
1214const planner = require ( './lib/planner' )
13- const attachCreateReadStream = require ( './lib/hyperdbModifier' ) . attachCreateReadStream
15+ const pkg = require ( './package.json' )
1416
1517const Transform = stream . Transform
1618const PassThrough = stream . PassThrough
1719
18- // temporarily augment hyperdb prototype to include createReadStream
19- if ( ! hyperdb . createReadStream ) {
20- attachCreateReadStream ( hyperdb )
21- }
22-
2320function Graph ( storage , key , opts ) {
2421 if ( ! ( this instanceof Graph ) ) return new Graph ( storage , key , opts )
2522 events . EventEmitter . call ( this )
23+
24+ if ( typeof key === 'string' ) key = Buffer . from ( key , 'hex' )
25+
26+ if ( ! Buffer . isBuffer ( key ) && ! opts ) {
27+ opts = key
28+ key = null
29+ }
30+
31+ if ( ! opts ) opts = { }
2632 this . db = hyperdb ( storage , key , opts )
33+ this . _prefixes = Object . assign ( { } , opts . prefixes || constants . DEFAULT_PREFIXES )
34+ this . _basename = opts . name || constants . DEFAULT_BASE
35+ this . _prefixes . _ = this . _basename
36+ this . _indexType = opts . index === 'tri' ? 'tri' : 'hex'
37+ this . _indexes = opts . index === 'tri'
38+ ? constants . HEXSTORE_INDEXES_REDUCED
39+ : constants . HEXSTORE_INDEXES
40+ this . _indexKeys = Object . keys ( this . _indexes )
2741
2842 this . db . on ( 'error' , ( e ) => {
2943 this . emit ( 'error' , e )
3044 } )
3145 this . db . on ( 'ready' , ( e ) => {
32- this . emit ( 'ready' , e )
46+ if ( utils . isNewDatabase ( this . db ) ) {
47+ this . _onNew ( ( err ) => {
48+ if ( err ) return this . emit ( 'error' , err )
49+ this . emit ( 'ready' , e )
50+ } )
51+ } else {
52+ this . _onInit ( ( err ) => {
53+ if ( err ) return this . emit ( 'error' , err )
54+ this . emit ( 'ready' , e )
55+ } )
56+ }
3357 } )
3458}
3559
3660inherits ( Graph , events . EventEmitter )
3761
62+ Graph . prototype . _onNew = function ( cb ) {
63+ this . _version = pkg . version
64+ const metadata = [
65+ [ '@version' , pkg . version ] ,
66+ [ '@index' , this . _indexType ] ,
67+ [ '@name' , this . _basename ]
68+ ]
69+ Object . keys ( this . _prefixes ) . forEach ( ( key ) => {
70+ if ( key !== '_' ) {
71+ metadata . push ( [ prefixes . toKey ( key ) , this . _prefixes [ key ] ] )
72+ }
73+ } )
74+ utils . put ( this . db , metadata , cb )
75+ }
76+
77+ Graph . prototype . _onInit = function ( cb ) {
78+ // get and set graph version
79+ this . _version = null
80+ this . _basename = null
81+ this . _indexType = null
82+
83+ let missing = 4
84+ let error = null
85+ // get and set version
86+ this . graphVersion ( ( err , version ) => {
87+ if ( err ) error = err
88+ this . _version = version
89+ maybeDone ( )
90+ } )
91+ // get and set graph name
92+ this . name ( ( err , name ) => {
93+ if ( err ) error = err
94+ this . _basename = name || constants . DEFAULT_BASE
95+ // modify prefixes to ensure correct namespacing
96+ this . _prefixes . _ = this . _basename
97+ maybeDone ( )
98+ } )
99+ // get and set graph indexation
100+ this . indexType ( ( err , index ) => {
101+ if ( err ) error = err
102+ this . _indexType = index || 'hex'
103+ this . _indexes = index === 'tri'
104+ ? constants . HEXSTORE_INDEXES_REDUCED
105+ : constants . HEXSTORE_INDEXES
106+ this . _indexKeys = Object . keys ( this . _indexes )
107+ maybeDone ( )
108+ } )
109+ // get and set prefixes
110+ this . prefixes ( ( err , prefixes ) => {
111+ if ( err ) error = err
112+ this . _prefixes = Object . assign ( { _ : this . _prefixes } , prefixes )
113+ maybeDone ( )
114+ } )
115+ function maybeDone ( ) {
116+ missing --
117+ if ( ! missing ) {
118+ cb ( error )
119+ }
120+ }
121+ }
122+
38123Graph . prototype . v = ( name ) => new Variable ( name )
39124
125+ function returnValueAsString ( cb ) {
126+ return ( err , nodes ) => {
127+ if ( err ) return cb ( err )
128+ if ( ! nodes || nodes . length === 0 ) return cb ( null , null )
129+ cb ( null , nodes [ 0 ] . value . toString ( ) )
130+ }
131+ }
132+
133+ Graph . prototype . graphVersion = function ( cb ) {
134+ if ( this . _version ) return cb ( null , this . _version )
135+ this . db . get ( '@version' , returnValueAsString ( cb ) )
136+ }
137+
138+ Graph . prototype . name = function ( cb ) {
139+ if ( this . _basename ) return cb ( null , this . _basename )
140+ this . db . get ( '@name' , returnValueAsString ( cb ) )
141+ }
142+
143+ Graph . prototype . indexType = function ( cb ) {
144+ if ( this . _indexType ) return cb ( null , this . _indexType )
145+ this . db . get ( '@index' , returnValueAsString ( cb ) )
146+ }
147+
148+ Graph . prototype . prefixes = function ( callback ) {
149+ // should cache this somehow
150+ const prefixStream = this . db . createReadStream ( constants . PREFIX_KEY )
151+ utils . collect ( prefixStream , ( err , data ) => {
152+ if ( err ) return callback ( err )
153+ var names = data . reduce ( ( p , nodes ) => {
154+ var data = prefixes . fromNodes ( nodes )
155+ p [ data . prefix ] = data . uri
156+ return p
157+ } , { } )
158+ callback ( null , names )
159+ } )
160+ }
161+
162+ Graph . prototype . addPrefix = function ( prefix , uri , cb ) {
163+ this . db . put ( prefixes . toKey ( prefix ) , uri , cb )
164+ }
165+
40166Graph . prototype . getStream = function ( triple , opts ) {
41- const stream = this . db . createReadStream ( utils . createQuery ( triple ) )
42- return stream . pipe ( new HyperdbReadTransform ( this . db , opts ) )
167+ const stream = this . db . createReadStream ( this . _createQuery ( triple , { encode : ( ! opts || opts . encode === undefined ) ? true : opts . encode } ) )
168+ return stream . pipe ( new HyperdbReadTransform ( this . db , this . _basename , opts ) )
43169}
44170
45171Graph . prototype . get = function ( triple , opts , callback ) {
@@ -52,21 +178,22 @@ function doAction (action) {
52178 if ( ! triples ) return callback ( new Error ( 'Must pass triple' ) )
53179 let entries = ( ! triples . reduce ) ? [ triples ] : triples
54180 entries = entries . reduce ( ( prev , triple ) => {
55- return prev . concat ( this . generateBatch ( triple , action ) )
181+ return prev . concat ( this . _generateBatch ( triple , action ) )
56182 } , [ ] )
57183 this . db . batch ( entries . reverse ( ) , callback )
58184 }
59185}
60186
61187function doActionStream ( action ) {
62188 return function ( ) {
189+ const self = this
63190 const transform = new Transform ( {
64191 objectMode : true ,
65192 transform ( triples , encoding , done ) {
66193 if ( ! triples ) return done ( )
67194 let entries = ( ! triples . reduce ) ? [ triples ] : triples
68195 entries = entries . reduce ( ( prev , triple ) => {
69- return prev . concat ( utils . generateBatch ( triple , action ) )
196+ return prev . concat ( self . _generateBatch ( triple , action ) )
70197 } , [ ] )
71198 this . push ( entries . reverse ( ) )
72199 done ( )
@@ -95,8 +222,7 @@ Graph.prototype.searchStream = function (query, options) {
95222 } else if ( ! Array . isArray ( query ) ) {
96223 query = [ query ]
97224 }
98- const plannedQuery = planner ( query )
99-
225+ const plannedQuery = planner ( query , this . _prefixes )
100226 var streams = plannedQuery . map ( ( triple , i ) => {
101227 const limit = ( options && i === plannedQuery . length - 1 ) ? options . limit : undefined
102228 return new JoinStream ( {
@@ -131,10 +257,62 @@ Graph.prototype.query = function (query, callback) {
131257 utils . collect ( this . queryStream ( query ) , callback )
132258}
133259
134- Graph . prototype . generateBatch = utils . generateBatch
135-
136260Graph . prototype . close = function ( callback ) {
137261 callback ( )
138262}
139263
264+ /* PRIVATE FUNCTIONS */
265+
266+ Graph . prototype . _generateBatch = function ( triple , action ) {
267+ if ( ! action ) action = 'put'
268+ var data = null
269+ if ( action === 'put' ) {
270+ data = JSON . stringify ( utils . extraDataMask ( triple ) )
271+ }
272+ return this . _encodeKeys ( triple ) . map ( key => ( {
273+ type : 'put' , // no delete in hyperdb so just putting nulls
274+ key : key ,
275+ value : data
276+ } ) )
277+ }
278+
279+ Graph . prototype . _encodeKeys = function ( triple ) {
280+ const encodedTriple = utils . encodeTriple ( triple , this . _prefixes )
281+ return this . _indexKeys . map ( key => utils . encodeKey ( key , encodedTriple ) )
282+ }
283+
284+ Graph . prototype . _createQuery = function ( pattern , options ) {
285+ var types = utils . typesFromPattern ( pattern )
286+ var preferedIndex = options && options . index
287+ var index = this . _findIndex ( types , preferedIndex )
288+ const encodedTriple = utils . encodeTriple ( pattern , options . encode ? this . _prefixes : { _ : this . _basename } )
289+ var key = utils . encodeKey ( index , encodedTriple )
290+ return key
291+ }
292+
293+ Graph . prototype . _possibleIndexes = function ( types ) {
294+ var result = this . _indexKeys . filter ( ( key ) => {
295+ var matches = 0
296+ return this . _indexes [ key ] . every ( function ( e , i ) {
297+ if ( types . indexOf ( e ) >= 0 ) {
298+ matches ++
299+ return true
300+ }
301+ if ( matches === types . length ) {
302+ return true
303+ }
304+ } )
305+ } )
306+ result . sort ( )
307+ return result
308+ }
309+
310+ Graph . prototype . _findIndex = function ( types , preferedIndex ) {
311+ var result = this . _possibleIndexes ( types )
312+ if ( preferedIndex && result . some ( r => r === preferedIndex ) ) {
313+ return preferedIndex
314+ }
315+ return result [ 0 ]
316+ }
317+
140318module . exports = Graph
0 commit comments