Skip to content

Commit

Permalink
decouple from level-sublevel
Browse files Browse the repository at this point in the history
  • Loading branch information
bcomnes committed Dec 30, 2016
1 parent 6d0da2d commit 9b2f914
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 35 deletions.
73 changes: 68 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ var db = sub(level(__dirname + '/db', {
var posts = db.sublevel('posts');

// add a title index
posts.byTitle = Secondary(posts, 'title');
posts.byTitle = Secondary(posts, db.sublevel('title'), 'title');

// add a length index
// append the post.id for unique indexes with possibly overlapping values
posts.byLength = Secondary(posts, 'length', function(post){
posts.byLength = Secondary(posts, db.sublevel('length'), function(post){
return post.body.length + '!' + post.id;
});

Expand Down Expand Up @@ -69,12 +69,33 @@ posts.put('1337', {
});
```

Same arrangement as above, but using a separate index level and [subleveldown](https://github.com/mafintosh/subleveldown) instead of [level-sublevel](https://github.com/dominictarr/level-sublevel).

```js
var level = require('level');
var Secondary = require('level-secondary');
var sub = require('subleveldown');

var db = level(__dirname + '/db');
var idb = level(__dirname + '/idb');

var posts = sub(db, 'posts', { valueEncoding: 'json' });

// add a title index
posts.byTitle = Secondary(posts, sub(idb, 'title', { valueEncoding: 'json' }), 'title');

// add a length index
// append the post.id for unique indexes with possibly overlapping values
posts.byLength = Secondary(posts, sub(idb, 'length', { valueEncoding: 'json' }), function(post){
return post.body.length + '!' + post.id;
});
```

## API

### Secondary(db, name[, reduce])
### Secondary(db, idb[, reduce])

Return a secondary index that either indexes property `name` or uses a custom
`reduce` function to map values to indexes.
Takes any level as `db` and any level as `idb` to use as a secondary index that either indexes a property name if `reduce` is a string or uses a custom `reduce` function to map values to indexes. The level that gets passed as `db` gets mutated by [`dominictarr/level-hooks`](https://github.com/dominictarr/level-hooks) in order to capture db events. Secondary returns a wrapped `idb` level that helps prune old index values.

### Secondary#get(key, opts[, cb])

Expand All @@ -90,6 +111,48 @@ A [level manifest](https://github.com/dominictarr/level-manifest) that you can p

## Breaking changes

### 2.0.0

You are now in charge of creating isolated levels for indexes yourself now. This allows for greater flexibility on key and value encodings, and lets you separate out storage locations, as well as use other `sublevel` solutions.

What used to be

```js
var level = require('level');
var Secondary = require('level-secondary');
var sub = require('level-sublevel');

var db = sub(level(__dirname + '/db', {
valueEncoding: 'json'
}));

var posts = db.sublevel('posts');

posts.byTitle = Secondary(posts, 'title');
posts.byLength = Secondary(posts, 'length', function(post){
return post.body.length + '!' + post.id;
});
```

is now

```js
var level = require('level');
var Secondary = require('level-secondary');
var sub = require('level-sublevel');

var db = sub(level(__dirname + '/db', {
valueEncoding: 'json'
}));

var posts = db.sublevel('posts');

posts.byTitle = Secondary(posts, db.sublevel('title'), 'title');
posts.byLength = Secondary(posts, db.sublevel('length'), function(post){
return post.body.length + '!' + post.id;
});
```

### 1.0.0

What used to be
Expand Down
41 changes: 24 additions & 17 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,39 @@
var extend = require('xtend');
var hooks = require('level-hooks');
var Transform = require('stream').Transform
|| require('readable-stream').Transform;

module.exports = Secondary;

function Secondary(db, name, reduce) {
var sub = db.sublevel(name);
function Secondary(db, idb, reduce) {
if (db.sublevel && !db.hooks) {
db.hooks = {}
db.hooks.pre = db.pre
db.hooks.post = db.post
}

if (!db.hooks) {
hooks(db)
}

if (!reduce) {
if (reduce && typeof reduce === 'string') {
var reducerString = reduce
reduce = function(value) {
return value[name];
return value[reducerString]
};
}

db.pre(function(change, add) {
if (change.type != 'put') return;
if (typeof reduce !== 'function') {
throw new Error('Reduce argument must be a string or function')
}

add({
type: 'put',
key: reduce(change.value),
value: change.key,
prefix: sub
});
db.hooks.pre(function(change, add) {
if (change.type != 'put') return;
idb.put(reduce(change.value), change.key)
});

var secondary = {};

secondary.manifest = {
methods: {
get: { type: 'async' },
Expand All @@ -46,7 +54,7 @@ function Secondary(db, name, reduce) {
opts = {};
}

sub.get(key, function(err, value) {
idb.get(key, function(err, value) {
if (err) return fn(err);
db[type](value, opts, fn);
});
Expand All @@ -69,15 +77,14 @@ function Secondary(db, name, reduce) {

tr._transform = function(chunk, enc, done) {
var key = chunk.value;

if (opts.values === false) {
done(null, key);
return;
}

db.get(key, function(err, value) {
if (err && err.type == 'NotFoundError') {
sub.del(key, done);
idb.del(key, done);
} else if (err) {
done(err);
} else {
Expand All @@ -99,7 +106,7 @@ function Secondary(db, name, reduce) {

var opts2 = extend({}, opts);
opts2.keys = opts2.values = true;
sub.createReadStream(opts2).pipe(tr);
idb.createReadStream(opts2).pipe(tr);

return tr;
};
Expand Down
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@
"test": "tape test/*.js"
},
"dependencies": {
"xtend": "~4.0.1",
"readable-stream": "~2.2.0"
"level-hooks": "^4.5.0",
"readable-stream": "~2.2.0",
"xtend": "~4.0.1"
},
"devDependencies": {
"tape": "*",
"level-sublevel": "~6.6.1",
"memdb": "1.3.1",
"multilevel": "~7.3.0"
"multilevel": "~7.3.0",
"subleveldown": "^2.1.0",
"tape": "*"
},
"keywords": [
"level",
Expand Down
3 changes: 2 additions & 1 deletion test/del.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ var test = require('tape');
test('del', function(t) {
t.plan(5);
var db = sub(level({ valueEncoding: 'json' }));
var idb = db.sublevel('title')

var posts = db.sublevel('posts');
posts.byTitle = Secondary(posts, 'title');
posts.byTitle = Secondary(posts, idb, 'title');

posts.put('1337', {
title: 'a title',
Expand Down
14 changes: 11 additions & 3 deletions test/get.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ var sub = require('level-sublevel');
var test = require('tape');

test('get', function(t) {
t.plan(3);
t.plan(5);
var db = sub(level({ valueEncoding: 'json' }));
var index = {
title: db.sublevel('title'),
len: db.sublevel('length')
}

var posts = db.sublevel('posts');
posts.byTitle = Secondary(posts, 'title');
posts.byLength = Secondary(posts, 'length', function(post){
posts.byTitle = Secondary(posts, index.title, 'title');
posts.byLength = Secondary(posts, index.len, function(post){
return post.body.length;
});

Expand All @@ -24,6 +28,10 @@ test('get', function(t) {
posts.byTitle.get('a title', function(err, _post) {
t.error(err);
t.deepEqual(_post, post);
posts.byLength.get(post.body.length, function(err, _post) {
t.error(err)
t.deepEqual(_post, post)
})
});
});
});
Expand Down
5 changes: 3 additions & 2 deletions test/multilevel.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ var multilevel = require('multilevel');

test('multilevel', function(t) {
t.plan(3);

var db = sub(level({ valueEncoding: 'json' }));
var byTitle = Secondary(db, 'title');
var idb = db.sublevel('title')
var byTitle = Secondary(db, idb, 'title');
var server = multilevel.server(byTitle);
var client = multilevel.client(byTitle.manifest);

Expand Down
9 changes: 6 additions & 3 deletions test/read-stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ var test = require('tape');
test('read streams', function(t) {
t.plan(4);
var db = sub(level({ valueEncoding: 'json' }));
var index = {
title: db.sublevel('title'),
len: db.sublevel('length')
};

var posts = db.sublevel('posts');
posts.byTitle = Secondary(posts, 'title');
posts.byLength = Secondary(posts, 'length', function(post){
posts.byTitle = Secondary(posts, index.title, 'title');
posts.byLength = Secondary(posts, index.len, function(post){
return post.body.length;
});

Expand All @@ -20,7 +24,6 @@ test('read streams', function(t) {

posts.put('1337', post, function(err) {
t.error(err);

posts.byLength.createReadStream({
start: 10,
end: 20
Expand Down
54 changes: 54 additions & 0 deletions test/subleveldown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
var level = require('memdb');
var Secondary = require('..');
var sub = require('subleveldown');
var test = require('tape');

test('subleveldown', function(t) {
t.plan(4);
var db = level();
var idb = level();

var index = {
title: sub(idb, 'title', { valueEncoding: 'json' }),
len: sub(idb, 'length', { valueEncoding: 'json' })
};

var posts = sub(db, 'posts', { valueEncoding: 'json' });
posts.byTitle = Secondary(posts, index.title, 'title');
posts.byLength = Secondary(posts, index.len, function(post){
return post.body.length;
});

var post = {
title: 'a title',
body: 'lorem ipsum'
};

posts.put('1337', post, function(err) {
t.error(err);
posts.byLength.createReadStream({
start: 10,
end: 20
}).on('data', function(data) {
t.deepEqual(data, {
key: '1337',
value: post
});
});

posts.byLength.createKeyStream({
start: 10,
end: 20
}).on('data', function(data) {
t.equal(data, '1337');
});

posts.byLength.createValueStream({
start: 10,
end: 20
}).on('data', function(data) {
t.deepEqual(data, post);
});
});
});

0 comments on commit 9b2f914

Please sign in to comment.