Skip to content

Commit

Permalink
Fix #5
Browse files Browse the repository at this point in the history
  • Loading branch information
vhf committed Nov 23, 2018
1 parent 1dc182e commit 94f1b17
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 32 deletions.
39 changes: 25 additions & 14 deletions __tests__/bplustree.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -486,28 +486,39 @@ describe('BPlusTree', () => {

let tree;
describe('regression tests', () => {
beforeAll(() => {
tree = new BPlusTree({ order: 4 });
tree.store(1, 'one');
tree.store(2, 'two');
tree.store(3, 'three');
tree.store(4, 'four');
tree.store(5, 'five');
});
describe('works over ranges with a new leaf and a removed element in the first leaf', () => {
it.only('#1', () => {
beforeEach(() => {
tree = new BPlusTree({ order: 4 });
tree.store(1, 'one');
tree.store(2, 'two');
tree.store(3, 'three');
tree.store(4, 'four');
tree.store(5, 'five');
});
it('#1a', () => {
// https://github.com/vhf/bplustree/issues/5#issuecomment-440056079
debugger
tree.remove(3, 'three');
tree.fetchRange(1, 4);
expect(tree.fetchRange(1, 4)).toEqual(['one', 'two', 'four']);
expect(tree.fetchRange(1, 2)).toEqual(['one', 'two']);
expect(tree.fetchRange(1, 3)).toEqual(['one', 'two']);
expect(tree.fetchRange(1, 5)).toEqual(['one', 'two', 'four', 'five']);
});
it('#1b', () => {
tree.remove(2, 'two');
expect(tree.fetchRange(1, 5)).toEqual(['one', 'three', 'four', 'five']);
expect(tree.fetchRange(2, 5)).toEqual(['three', 'four', 'five']);
expect(tree.fetchRange(3, 5)).toEqual(['three', 'four', 'five']);
expect(tree.fetchRange(4, 5)).toEqual(['four', 'five']);
});
it('#2', () => {
tree.remove(5, 'five'); // Removing from the new leaf, instead of the old one
tree.fetchRange(1, 7);
// Removing from the new leaf, instead of the old one
tree.remove(5, 'five');
expect(tree.fetchRange(1, 7)).toEqual(['one', 'two', 'three', 'four']);
});
it('#3', () => {
tree.remove(3, 'three');
tree.fetchRange(1, 2); // Fetching a range that does not include the removed element
// Fetching a range that does not include the removed element
expect(tree.fetchRange(1, 2)).toEqual(['one', 'two']);
});
});
});
35 changes: 18 additions & 17 deletions dist/bplustree.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ class BPlusTree {

function walk(node) {
if (node.t === 'branch') {
const kids = node.v;
const children = node.v;

for (let i = 0, kl = kids.length; i < kl; i++) {
walk(kids[i]);
for (let i = 0, kl = children.length; i < kl; i++) {
walk(children[i]);
}
} else if (node.t === 'leaf') {
for (let i = 0, nkl = node.k.length; i < nkl; i++) {
Expand Down Expand Up @@ -156,7 +156,8 @@ class BPlusTree {

if (leaf.n !== null) {
leaf = this.fetch(leaf.n, {
getLeaf: true
getLeaf: true,
notFound: 'right'
});
index = 0;
} else {
Expand Down Expand Up @@ -341,21 +342,21 @@ class BPlusTree {
assert(hi.length === 0 || self.cmpFn(node.k[keysLength - 1], hi[0]) === -1, 'hi error');

if (node.t === 'branch') {
const kids = node.v;
const kidsLength = kids.length;
const children = node.v;
const childrenLength = children.length;

if (currentDepth === 0) {
assert(kidsLength >= 2, 'Underpopulated root');
assert(childrenLength >= 2, 'Underpopulated root');
} else {
assert(kidsLength >= self.minKeys, 'Underpopulated branch');
assert(childrenLength >= self.minKeys, 'Underpopulated branch');
}

assert(keysLength === kidsLength - 1, 'keys and kids don\'t correspond');
assert(keysLength === childrenLength - 1, 'keys and children don\'t correspond');

for (let i = 0; i < kidsLength; i++) {
for (let i = 0; i < childrenLength; i++) {
const newLo = i === 0 ? lo : [node.k[i - 1]];
const newHi = i === keysLength ? hi : [node.k[i]];
checking(self, kids[i], currentDepth + 1, newLo, newHi);
checking(self, children[i], currentDepth + 1, newLo, newHi);
}
} else if (node.t === 'leaf') {
const v = node.v;
Expand Down Expand Up @@ -386,7 +387,7 @@ class BPlusTree {
* @param {BPTree.tree} [options.root=this.tree] - Tree to search in
* @param {boolean} [options.getLeaf=false] - Return the leaf containing the value(s)
* @param {boolean} [options.getPath=false] - Return {val: value(s), leaf: leaf, path: pathFromRootToLeaf}
* @param {string} [options.notFound] - either 'left' or 'right' - Return what was found left or right from key which doesn't exist
* @param {string} [options.notFound] - either 'left' or 'right' - Return what was found left or right from key that doesn't exist
* @return {Value|Value[]|Leaf|Object|Boolean}
*/

Expand Down Expand Up @@ -475,7 +476,7 @@ class BPlusTree {
return val;
}
} else if (this.cmpFn(node.k[j], key) === 1) {
break; // just to finish quicker; not needed for correctness
break; // just to return early; not needed for correctness
}
}

Expand Down Expand Up @@ -640,13 +641,13 @@ class BPlusTree {

function walk(node, depth, path) {
if (node.t === 'branch') {
const kids = node.v;
const children = node.v;

for (let i = 0, kl = kids.length; i < kl; i++) {
if (kids[i].t === 'branch') {
for (let i = 0, kl = children.length; i < kl; i++) {
if (children[i].t === 'branch') {
const newPath = path.slice(0, depth).concat([i]);
result.push(newPath);
walk(kids[i], depth + 1, newPath);
walk(children[i], depth + 1, newPath);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/bplustree.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ class BPlusTree {
// if last key is smaller than upper bound, fetch next leaf and iterate
result.push(leaf.v.slice(index));
if (leaf.n !== null) {
leaf = this.fetch(leaf.n, { getLeaf: true });
leaf = this.fetch(leaf.n, { getLeaf: true, notFound: 'right' });
index = 0;
} else {
break;
Expand Down

0 comments on commit 94f1b17

Please sign in to comment.