Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(remove): adding ability to remove key #4

Merged
merged 30 commits into from
Oct 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
2005a21
tests(MemoryAdapter): added tests
Alex-Werner Sep 24, 2019
5c0577e
feat(MemoryAdapter): remove in leaf
Alex-Werner Sep 25, 2019
f67a128
breaking(addInLeaf): removed unnecessary duplicate fieldName
Alex-Werner Sep 25, 2019
71c48dc
chore(TODO): removed todo
Alex-Werner Sep 27, 2019
75152fe
breaking(leafName): moved leafName to leafId + removed fieldName
Alex-Werner Sep 27, 2019
9da7610
test(MemoryAdapter): fixed test
Alex-Werner Sep 27, 2019
1ff034f
test(SBFLeaf): test for SBFLeaf find, split, insert
Alex-Werner Sep 27, 2019
c4be251
breaking(findAll): renaming findAll to getAll
Alex-Werner Sep 27, 2019
b0435a1
feat(isAtLeastHalfFull): added feature + tests
Alex-Werner Sep 30, 2019
466211a
typo: this.name -> this.id
Alex-Werner Sep 30, 2019
486098c
feat(fillFactor): allow to set fillFactor
Alex-Werner Sep 30, 2019
9e3a873
feat(redistribute/remove): wip + tests + moved file struct
Alex-Werner Sep 30, 2019
6b23687
feat(getLeft/Right): added new feature + MemoryAdapter implementation
Alex-Werner Oct 2, 2019
b535e1a
breaking(getAllInLeaf): changed format of returned object
Alex-Werner Oct 2, 2019
9dfdd56
feat(getLeft/Right): Added getLeft/getRight to FsAdapter
Alex-Werner Oct 2, 2019
165fb69
feat(getFillStatus): added getFillStatus reports
Alex-Werner Oct 2, 2019
1d86cd0
feat(mergeWithSiblings): partial left implementation
Alex-Werner Oct 3, 2019
d4347b1
feat(draw): added utils draw ascii for debug purpose
Alex-Werner Oct 3, 2019
ef1963a
impr(remove): use draw in e2e test + improve removal
Alex-Werner Oct 3, 2019
7f98d38
test: added snapshots
Alex-Werner Oct 3, 2019
d34784d
fix: moved id rand to production able
Alex-Werner Oct 3, 2019
2f9f8f8
feat(mergeUp): for SBFNode + getFillStatus + fix for root
Alex-Werner Oct 3, 2019
7af00a6
fix: fixed export toJSON and import
Alex-Werner Oct 7, 2019
b24e41f
fix: fillStatus
Alex-Werner Oct 7, 2019
f8bbdc3
fix: tests
Alex-Werner Oct 7, 2019
1fb3083
doc(readme): set state status as not for production
Alex-Werner Oct 7, 2019
4b1565e
fix: bigger timeout
Alex-Werner Oct 7, 2019
42289a1
feat(waitFor): added utils fn waitFor prop
Alex-Werner Oct 8, 2019
29d1296
fix(FsAdapter): save/load + waiter before inserting
Alex-Werner Oct 8, 2019
a18049f
test: update changed method
Alex-Werner Oct 8, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
> Fast document store using B+ Tree for fields. Adapters support for In-Memory and FileSystem

Complexity : `O(log(n))` - Help welcome to pass it `O(n)`.
State : NOT FOR PRODUCTION (see remaining TODO / FIXME / ISSUES).

This library's goal is to provide a way to quickly store document-based data in-memory or on the filesystem.
It uses a field-specific indexing system relaying on B+Tree structure.
Expand Down
13 changes: 0 additions & 13 deletions TODO.md

This file was deleted.

1 change: 1 addition & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const tree = new SBTree([props]);
- Constructor options :
- `adapter` Adapter - (def: MemoryAdapter) : Allow to specific another adapter to use
- `order` Number - (def: 511) : Primordial for the performance, the closest to L1 the better. Chose below 2^n.
- `fillFactor` Float - (def: 0.5) : Used for balancing the tree. Should not be less than 0.5 (50%).
- `verbose` Bool - (def: false)
- `uniques` Array - (def: []) - Allow to set some field unique by adding them to this array
- `exclude` Array - (def: []) - Allow to exclude from indexing some field (important if you expect field value to be huge or nested).
Expand Down
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const SBTree = require('./src/types/SBTree/SBTree');
const adapters = require('./src/adapters/index');
const utils = require('./src/utils/index');
module.exports = {SBTree, adapters};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Optimised document store using B+ Tree for fields. Adapters support for In-Memory and FileSystem",
"main": "index.js",
"scripts": {
"test": "mocha test/ --recursive",
"test": "mocha test/ --recursive ",
"benchmark": "mocha test/z_benchmark --recursive"
},
"engines": {
Expand Down
36 changes: 24 additions & 12 deletions src/adapters/FsAdapter/FsAdapter.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,46 @@
const {FSLock} = require('fslockjs');
const EventEmitter = require('events');
const defaultProps = {
options: {
path: '.db',
//TODO : Ideally, when false, we keep a set of deferred job that we execute once saveDatabase is called.
autoSave: true,
autoSaveInterval: 5000,
autoLoad: true,
autoLoadCallback: null,
}
}

class FsAdapter extends EventEmitter {
#parent;
constructor(props = {}) {
super();
this.name = "FsAdapter";

if(props.parent){
this.parent = props.parent
this.setParent(props.parent)
}
this.leafs = (props.leafs) ? props.leafs : {};
this.options = {
path: (props.path) ? (props.path) : defaultProps.options.path,
autoSave: (props.autoSave !== undefined) ? (props.autoSave) : defaultProps.options.autoSave,
autoSaveInterval: (props.autoSaveInterval !== undefined) ? (props.autoSaveInterval) : defaultProps.options.autoSaveInterval,
autoLoad: (props.autoLoad !== undefined) ? (props.autoLoad) : defaultProps.options.autoLoad,
autoLoadCallback: (props.autoLoadCallback !== undefined) ? (props.autoLoadCallback) : defaultProps.options.autoLoadCallback,
}
if (!this.options.autoLoad && this.options.autoLoadForceOverwrite === undefined) {
this.path= (props.path) ? (props.path) : defaultProps.path;
this.autoSave= (props.autoSave !== undefined) ? (props.autoSave) : defaultProps.autoSave;
this.autoSaveInterval= (props.autoSaveInterval !== undefined) ? (props.autoSaveInterval) : defaultProps.autoSaveInterval;
this.autoLoad= (props.autoLoad !== undefined) ? (props.autoLoad) : defaultProps.autoLoad;
this.autoLoadCallback= (props.autoLoadCallback !== undefined) ? (props.autoLoadCallback) : defaultProps.autoLoadCallback;

if (!this.autoLoad && this.autoLoadForceOverwrite === undefined) {
throw new Error('Not implemented : Overwrite graceful handle. Pass autoLoadForceOverwrite to force.');
}
this.lastChange = null;
this.lastSave = null;
this.queue = new FSLock();
this.isReady = true;
if(props.leafs){
this.isReady = false;
}
}
setParent(parent){
this.#parent = parent;
}
getParent(){
return this.#parent;
}
};
//TODO : Optimization possible by just removing the LeafMeta from memory for disk instead, but existance search will be slower.
Expand All @@ -40,7 +50,9 @@ FsAdapter.prototype.attachParent = require('./methods/attachParent');
FsAdapter.prototype.addInLeaf = require('./methods/addInLeaf')
FsAdapter.prototype.createLeaf = require('./methods/createLeaf')
FsAdapter.prototype.findInLeaf = require('./methods/findInLeaf')
FsAdapter.prototype.findAllInLeaf = require('./methods/findAllInLeaf')
FsAdapter.prototype.getAllInLeaf = require('./methods/getAllInLeaf')
FsAdapter.prototype.getLeftInLeaf = require('./methods/getLeftInLeaf')
FsAdapter.prototype.getRightInLeaf = require('./methods/getRightInLeaf')
FsAdapter.prototype.getDocument = require('./methods/getDocument')
FsAdapter.prototype.insertSortedInLeaf = require('./methods/insertSortedInLeaf')
FsAdapter.prototype.loadDatabase = require('./methods/loadDatabase')
Expand Down
6 changes: 3 additions & 3 deletions src/adapters/FsAdapter/methods/addInLeaf.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
const {insertSorted} = require('../../../utils/array');

async function addInLeaf(leafName, field, identifier, key) {
async function addInLeaf(leafName, identifier, value) {
if (!this.leafs[leafName]) {
await this.createLeaf(leafName);
}
if(this.leafs[leafName].meta.identifiers.includes(identifier)){
//TODO : except unique:false?
throw new Error(`Key ${identifier} already exist`);
throw new Error(`Identifier ${identifier} already exist`);
}
const index = await this.insertSortedInLeaf(leafName, key)
const index = await this.insertSortedInLeaf(leafName, value)
this.leafs[leafName].meta.size += 1;
this.leafs[leafName].meta.identifiers.splice(index, 0, identifier);

Expand Down
6 changes: 3 additions & 3 deletions src/adapters/FsAdapter/methods/attachParent.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const autosave = require('./ops/autosave')
async function attachParent(parent) {
this.parent = parent;
this.setParent(parent);

if (this.options.autoLoad) {
if (this.autoLoad) {
try{
await this.loadDatabase()
if (this.autoLoadCallback && typeof this.autoLoadCallback === 'function') {
Expand All @@ -13,7 +13,7 @@ async function attachParent(parent) {
process.exit(1);
}
};
if (this.options.autoSave === true) {
if (this.autoSave === true) {
autosave(this);
}
this.emit('ready');
Expand Down
8 changes: 4 additions & 4 deletions src/adapters/FsAdapter/methods/createLeaf.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
const LeafData = require('../types/LeafData/LeafData')
const LeafMeta = require('../types/LeafMeta/LeafMeta')
module.exports = async function createLeaf(leafName){
this.leafs[leafName] = {
name: leafName,
module.exports = async function createLeaf(leafId){
this.leafs[leafId] = {
id: leafId,
meta: new LeafMeta()
};

const data = new LeafData();
await this.saveLeafData(leafName, data)
await this.saveLeafData(leafId, data)
}
10 changes: 0 additions & 10 deletions src/adapters/FsAdapter/methods/findAllInLeaf.js

This file was deleted.

65 changes: 46 additions & 19 deletions src/adapters/FsAdapter/methods/findInLeaf.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
const getStrictMatchingKeys = require('./ops/getStrictMatchingKeys');
const lowerThanKeys = require('./ops/lowerThanKeys');
const greaterThanKeys = require('./ops/greaterThanKeys');
module.exports = async function findInLeaf(leafName, key, op = '$eq') {
let {keys} = await this.openLeafData(leafName);
module.exports = async function findInLeaf(leafId, value, op = '$eq') {
const result = {
identifiers:[],
keys:[]
};
let {keys} = await this.openLeafData(leafId);
if (!keys) {
console.error(`Leafname ${leafName} was not present, had to recreate`)
await this.createLeaf(leafName);
return this.findInLeaf(leafName, key, op);
console.error(`leafId ${leafId} was not present, had to recreate`)
await this.createLeaf(leafId);
return this.findInLeaf(leafId, value, op);
}
const strictMatchingKeys = getStrictMatchingKeys(keys, key);
const strictMatchingKeys = getStrictMatchingKeys(keys, value);

switch (op) {
case "$eq":
Expand All @@ -17,34 +21,57 @@ module.exports = async function findInLeaf(leafName, key, op = '$eq') {
}
const start = strictMatchingKeys[0];
const end = strictMatchingKeys[0]+strictMatchingKeys.length;
return this.leafs[leafName].meta.identifiers.slice(start, end);

result.identifiers.push(...this.leafs[leafId].meta.identifiers.slice(start, end));
result.keys.push(...keys.slice(start, end));

return result;
// return this.leafs[leafName].meta.identifiers.slice(start, end);
case "$lte":
let resLte = [];
resLte = resLte.concat(await this.findInLeaf(leafName, key, '$lt'));
resLte = resLte.concat(await this.findInLeaf(leafName, key, '$eq'));
resLte = resLte.concat(await this.findInLeaf(leafId, value, '$lt'));
resLte = resLte.concat(await this.findInLeaf(leafId, value, '$eq'));
throw new Error('Modification to new format')
return resLte;
case "$gte":
let resGte = [];
resGte = resGte.concat(await this.findInLeaf(leafName, key, '$eq'));
resGte = resGte.concat(await this.findInLeaf(leafName, key, '$gt'));
resGte = resGte.concat(await this.findInLeaf(leafId, value, '$eq'));
resGte = resGte.concat(await this.findInLeaf(leafId, value, '$gt'));
throw new Error('Modification to new format')
return resGte;
case "$lt":
if(strictMatchingKeys.length){
const localIndex = keys.indexOf(key);
return (localIndex===0) ? [] : this.leafs[leafName].meta.identifiers.slice(0, localIndex);
const localIndex = keys.indexOf(value);
if(localIndex!==0){
result.identifiers.push(...this.leafs[leafId].meta.identifiers.slice(0, localIndex));
result.keys.push(...keys.slice(0, localIndex));
}
// return (localIndex===0) ? [] : this.leafs[leafId].meta.identifiers.slice(0, localIndex);
}else{
const ltKeys = lowerThanKeys(keys, key);
return this.leafs[leafName].meta.identifiers.slice(0, ltKeys.length);
const ltKeys = lowerThanKeys(keys, value);

result.identifiers.push(...this.leafs[leafId].meta.identifiers.slice(0, ltKeys.length));
result.keys.push(...keys.slice(0, ltKeys.length));

// return this.leafs[leafId].meta.identifiers.slice(0, ltKeys.length);
}
return result;
case "$gt":
if(strictMatchingKeys.length){
const localIndex = keys.indexOf(key);
return (localIndex===-1) ? [] : this.leafs[leafName].meta.identifiers.slice(localIndex+strictMatchingKeys.length);
const localIndex = keys.indexOf(value);
if(localIndex !== -1){
result.identifiers.push(...this.leafs[leafId].meta.identifiers.slice(localIndex+strictMatchingKeys.length));
result.keys.push(...keys.slice(localIndex+strictMatchingKeys.length));
}
}else{
const _keys = greaterThanKeys(keys, key);
const _keys = greaterThanKeys(keys, value);
const len = (_keys.length<=0) ? 0 : _keys.length;
return (len===0) ? [] :this.leafs[leafName].meta.identifiers.slice(-len);
if(leafId!==0){
result.identifiers.push(...this.leafs[leafId].meta.identifiers.slice(-len));
result.keys.push(...keys.slice(-len));
}
}
return result;
default:
throw new Error(`Unsupported operator ${op}`);
}
Expand Down
10 changes: 10 additions & 0 deletions src/adapters/FsAdapter/methods/getAllInLeaf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module.exports = async function getAllInLeaf(leafId){

let {keys} = await this.openLeafData(leafId);
if(!keys){
console.error(`leafId ${leafId} was not present, had to recreate`)
await this.createLeaf(leafId);
return this.getAllInLeaf(leafId);
}
return JSON.parse(JSON.stringify({identifiers:this.leafs[leafId].meta.identifiers, keys:keys}));
}
2 changes: 1 addition & 1 deletion src/adapters/FsAdapter/methods/getDocument.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module.exports = async function getDocument(identifier){
return this.openDocument(identifier);
return JSON.parse(JSON.stringify(this.openDocument(identifier)));
}
15 changes: 15 additions & 0 deletions src/adapters/FsAdapter/methods/getLeftInLeaf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module.exports = async function getLeftInLeaf(leafId){

let {keys} = await this.openLeafData(leafId);
if(!keys){
console.error(`leafId ${leafId} was not present, had to recreate`)
await this.createLeaf(leafId);
return this.getLeftInLeaf(leafId);
}

const leaf = this.leafs[leafId];
const identifier = leaf.meta.identifiers[0];
const key = leaf.data.keys[0];

return JSON.parse(JSON.stringify({identifier, key }))
}
16 changes: 16 additions & 0 deletions src/adapters/FsAdapter/methods/getRightInLeaf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module.exports = async function getRightInLeaf(leafId){

let {keys} = await this.openLeafData(leafId);
if(!keys){
console.error(`leafId ${leafId} was not present, had to recreate`)
await this.createLeaf(leafId);
return this.getLeftInLeaf(leafId);
}

const leaf = this.leafs[leafId];
const len = leaf.meta.identifiers.length;
const identifier = leaf.meta.identifiers[len-1];
const key = leaf.data.keys[len-1];

return JSON.parse(JSON.stringify({identifier, key }))
}
14 changes: 7 additions & 7 deletions src/adapters/FsAdapter/methods/insertSortedInLeaf.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
const {insertSorted} = require('../../../utils/array')
module.exports = async function insertSortedInLeaf(leafName, key){
const data = await this.openLeafData(leafName);
module.exports = async function insertSortedInLeaf(leafId, value){
const data = await this.openLeafData(leafId);
if(!data || !data.keys){
console.error(`Leafname ${leafName} was not present, had to recreate`)
await this.createLeaf(leafName);
return this.insertSortedInLeaf(leafName, key)
console.error(`leafId ${leafId} was not present, had to recreate`)
await this.createLeaf(leafId);
return this.insertSortedInLeaf(leafId, value)
}
const index = insertSorted(data.keys,key);
await this.saveLeafData(leafName, data)
const index = insertSorted(data.keys,value);
await this.saveLeafData(leafId, data)
return index;
}
Loading