Skip to content

Commit 7d610fd

Browse files
author
Benjamin Forster
committed
reduce index size by extracting triple from key
1 parent 6a8b946 commit 7d610fd

4 files changed

Lines changed: 103 additions & 18 deletions

File tree

lib/HyperdbReadTransform.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const Transform = require('readable-stream').Transform
22
const inherits = require('inherits')
3+
const utils = require('./utils')
34

45
function HyperdbReadTransform (db, options) {
56
if (!(this instanceof HyperdbReadTransform)) {
@@ -31,7 +32,9 @@ HyperdbReadTransform.prototype._transform = function transform (nodes, encoding,
3132
return
3233
}
3334
var value = nodes[0].value && JSON.parse(nodes[0].value.toString())
34-
if (value !== null && (!this._filter || this._filter(value))) {
35+
if (value === null) return done()
36+
value = Object.assign(value, utils.decodeKey(nodes[0].key))
37+
if (!this._filter || this._filter(value)) {
3538
if (this._count >= this._offset) {
3639
this.push(value)
3740
}

lib/utils.js

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ const defs = {
88
ops: ['object', 'predicate', 'subject'], // [optional]
99
osp: ['object', 'subject', 'predicate']
1010
}
11+
const tripleAliasMap = {
12+
s: 'subject',
13+
p: 'predicate',
14+
o: 'object'
15+
}
1116
const defKeys = Object.keys(defs)
1217

1318
function collect (stream, cb) {
@@ -20,7 +25,7 @@ function collect (stream, cb) {
2025
function namespaceBlankVariables (triple) {
2126
Object.keys(triple).forEach((key) => {
2227
if (triple[key].startsWith && triple[key].startsWith('_:')) {
23-
triple[key] = triple[key].replace(/^_:/, 'hypergraph:')
28+
triple[key] = triple[key].replace(/^_:/, 'hg:')
2429
}
2530
})
2631
return triple
@@ -36,21 +41,37 @@ function filterTriple (triple) {
3641
return filtered
3742
}
3843

39-
function escape (value) {
44+
function escapeKeyValue (value) {
4045
if (typeof value === 'string' || value instanceof String) {
41-
return value.replace(/(\\|\/)/g, '\\$&')
46+
return value.replace(/(\/)/g, '%2F')
4247
}
4348
return value
4449
}
4550

46-
function genKey (key, triple) {
47-
var result = '/' + key
51+
function unescapeKeyValue (value) {
52+
return value.replace(/%2F/g, '/')
53+
}
54+
55+
function decodeKey (key) {
56+
const values = key.split('/')
57+
if (values.length < 4) throw new Error('Key is not in triple form')
58+
const order = values[0]
59+
const triple = {}
60+
for (var i = 0; i < 3; i++) {
61+
const k = tripleAliasMap[order[i]]
62+
triple[k] = unescapeKeyValue(values[i + 1])
63+
}
64+
return triple
65+
}
66+
67+
function encodeKey (key, triple) {
68+
var result = key
4869
var def = defs[key]
4970
var i = 0
5071
var value = triple[def[i]]
5172
// need to handle this smarter
5273
while (value) {
53-
result += '/' + escape(value)
74+
result += '/' + escapeKeyValue(value)
5475
i += 1
5576
value = triple[def[i]]
5677
}
@@ -60,17 +81,19 @@ function genKey (key, triple) {
6081
return result
6182
}
6283

63-
function genKeys (triple) {
64-
return defKeys.map(key => genKey(key, triple))
84+
function encodeKeys (triple) {
85+
return defKeys.map(key => encodeKey(key, triple))
6586
}
6687

6788
function generateBatch (triple, action) {
89+
const namespacedTriple = namespaceBlankVariables(triple)
6890
if (!action) action = 'put'
69-
var data = (action === 'put')
70-
? JSON.stringify(namespaceBlankVariables(triple))
71-
: null
72-
return genKeys(triple).map(key => ({
73-
type: 'put', // no delete in hyperdb so just puting nulls
91+
var data = null
92+
if (action === 'put') {
93+
data = JSON.stringify(extraDataMask(triple))
94+
}
95+
return encodeKeys(namespacedTriple).map(key => ({
96+
type: 'put', // no delete in hyperdb so just putting nulls
7497
key: key,
7598
value: data
7699
}))
@@ -122,7 +145,7 @@ function createQuery (pattern, options) {
122145
var types = typesFromPattern(pattern)
123146
var preferedIndex = options && options.index
124147
var index = findIndex(types, preferedIndex)
125-
var key = genKey(index, pattern)
148+
var key = encodeKey(index, pattern)
126149
return key
127150
}
128151

@@ -134,10 +157,23 @@ function keyIsNotAObject (tripleKey) {
134157
return typeof tripleKey !== 'object'
135158
}
136159

160+
function not (fn) {
161+
return (...args) => !fn(...args)
162+
}
163+
137164
function keyIsAVariable (tripleKey) {
138165
return tripleKey instanceof Variable
139166
}
140167

168+
function extraDataMask (obj) {
169+
return Object.keys(obj)
170+
.filter(not(keyIsAVariable))
171+
.reduce((prev, key) => {
172+
prev[key] = obj[key]
173+
return prev
174+
}, {})
175+
}
176+
141177
function objectMask (criteria, obj) {
142178
return Object.keys(obj)
143179
.filter(hasKey)
@@ -157,6 +193,7 @@ function variableNames (obj) {
157193
function queryMask (object) {
158194
return objectMask(keyIsNotAObject, object)
159195
};
196+
160197
function variablesMask (object) {
161198
return objectMask(keyIsAVariable, object)
162199
};
@@ -203,11 +240,13 @@ function materializer (pattern, data) {
203240
module.exports = {
204241
defs,
205242
defKeys,
206-
genKey,
243+
encodeKey,
244+
decodeKey,
207245
filterTriple,
208246
collect,
209247
generateBatch,
210248
createQuery,
249+
extraDataMask,
211250
queryMask,
212251
variablesMask,
213252
variableNames,

test/sparql.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describe('hypergraph.queryStream', () => {
4545
it('performs CONSTRUCT query type with UNION operator', (done) => {
4646
console.time('query')
4747
var expected = [
48-
{ subject: 'hypergraph:b0_b',
48+
{ subject: 'hg:b0_b',
4949
predicate: 'http://www.w3.org/2001/vcard-rdf/3.0#N',
5050
object: '_:b0' },
5151
{ subject: '_:b0',
@@ -54,7 +54,7 @@ describe('hypergraph.queryStream', () => {
5454
{ subject: '_:b0',
5555
predicate: 'http://www.w3.org/2001/vcard-rdf/3.0#familyName',
5656
object: '"Hacker"' },
57-
{ subject: 'hypergraph:b0_a',
57+
{ subject: 'hg:b0_a',
5858
predicate: 'http://www.w3.org/2001/vcard-rdf/3.0#N',
5959
object: '_:b1' },
6060
{ subject: '_:b1',

test/utils.spec.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/* eslint-env mocha */
2+
const expect = require('chai').expect
3+
const utils = require('../lib/utils')
4+
5+
describe('encodeKey', () => {
6+
it('generates a unique index key for a triple (spo)', () => {
7+
var key = utils.encodeKey('spo', { subject: 'a-subject', object: 'a-object', predicate: 'a-predicate' })
8+
expect(key).to.eql('spo/a-subject/a-predicate/a-object')
9+
})
10+
it('generates a unique index key for a triple (spo)', () => {
11+
var key = utils.encodeKey('sop', { subject: 'a-subject', object: 'a-object', predicate: 'a-predicate' })
12+
expect(key).to.eql('sop/a-subject/a-object/a-predicate')
13+
})
14+
it('generates a unique index key for a triple (osp)', () => {
15+
var key = utils.encodeKey('osp', { subject: 'a-subject', object: 'a-object', predicate: 'a-predicate' })
16+
expect(key).to.eql('osp/a-object/a-subject/a-predicate')
17+
})
18+
19+
it('escapes forward slashes in the triple', () => {
20+
var key = utils.encodeKey('spo', { subject: 'a/subject' })
21+
expect(key).to.eql('spo/a%2Fsubject/')
22+
})
23+
})
24+
25+
describe('decodeKey', () => {
26+
it('generates a triple from a index key (spo)', () => {
27+
var triple = utils.decodeKey('spo/a-subject/a-predicate/a-object')
28+
expect(triple).to.eql({ subject: 'a-subject', object: 'a-object', predicate: 'a-predicate' })
29+
})
30+
it('generates a triple from a index key (spo)', () => {
31+
var triple = utils.decodeKey('sop/a-subject/a-object/a-predicate')
32+
expect(triple).to.eql({ subject: 'a-subject', object: 'a-object', predicate: 'a-predicate' })
33+
})
34+
it('generates a triple from a index key (osp)', () => {
35+
var triple = utils.decodeKey('osp/a-object/a-subject/a-predicate')
36+
expect(triple).to.eql({ subject: 'a-subject', object: 'a-object', predicate: 'a-predicate' })
37+
})
38+
39+
it('unescapes escaped /‘s from index key (spo)', () => {
40+
var triple = utils.decodeKey('spo/a%2Fsubject/a%2Fpredicate/a%2Fobject')
41+
expect(triple).to.eql({ subject: 'a/subject', object: 'a/object', predicate: 'a/predicate' })
42+
})
43+
})

0 commit comments

Comments
 (0)