From 6291173cacf77d58886b3b93b387e4407c4c4547 Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Sun, 30 Aug 2020 01:33:48 +0200 Subject: [PATCH 01/10] tests: adde some query and find tests --- src/types/SBTree/methods/findDocuments.spec.js | 17 +++++++++++++++++ src/types/SBTree/ops/query.js | 9 +++++++++ src/types/SBTree/ops/query.spec.js | 5 +++++ 3 files changed, 31 insertions(+) create mode 100644 src/types/SBTree/methods/findDocuments.spec.js diff --git a/src/types/SBTree/methods/findDocuments.spec.js b/src/types/SBTree/methods/findDocuments.spec.js new file mode 100644 index 0000000..2f04d6c --- /dev/null +++ b/src/types/SBTree/methods/findDocuments.spec.js @@ -0,0 +1,17 @@ +const { expect } = require('chai'); + +const findDocuments = require('./findDocuments'); +const mock = { + state:{ + isReady: true + } +} +describe('.findDocuments()', ()=>{ + it('should find documents', function () { + + }); + it('should return null on non found documents', async function () { + const response = await findDocuments.apply(mock, {age: 13}); + expect(response).to.deep.equal([]); + }); +}); diff --git a/src/types/SBTree/ops/query.js b/src/types/SBTree/ops/query.js index 20fe2eb..c64e606 100644 --- a/src/types/SBTree/ops/query.js +++ b/src/types/SBTree/ops/query.js @@ -20,7 +20,13 @@ const findIntersectingIdentifiers = (listOfListOfIdentifiers) => { return intersection(...identifiers); }; +/** + * + * @param query + * @returns {Promise<[]>} + */ async function query(query) { + const self = this; const findNested = async function (_promises, _queryFieldName, _queryFieldValue) { for (const nestedQueryFieldName in _queryFieldValue) { @@ -40,8 +46,10 @@ async function query(query) { } } }; + if(!query) return []; const fields = Object.keys(query); + const fieldsResults = {}; const result = []; @@ -90,6 +98,7 @@ async function query(query) { } }); + let intermediateIdentifiers = []; await Promise .all(promises) diff --git a/src/types/SBTree/ops/query.spec.js b/src/types/SBTree/ops/query.spec.js index 7383a80..81307cc 100644 --- a/src/types/SBTree/ops/query.spec.js +++ b/src/types/SBTree/ops/query.spec.js @@ -22,6 +22,7 @@ const fakeSelf = { getFieldTree: (fieldName) => { return { find: (key, operator) => { + if(fieldName === 'city') return {identifiers: [], keys: []}; calledFn.push([fieldName, 'find', key, operator]); return {identifiers:['5d6ebb7e21f1df6ff7482631'],keys:[33]} } @@ -39,6 +40,10 @@ describe('SBTree - ops - query', () => { expect(calledFn[1]).to.deep.equal(['age','find',33, '$gte']); expect(calledFn[2]).to.deep.equal(['age','find',50, '$lte']); }); + it('should return empty array when no answers', async function () { + const res3 = await querySpec.call(fakeSelf, {city: 'Nowhere'}); + expect(res3).to.deep.equal([]); + }); it('should detect nested object', async function () { //TODO // const res3 = await query.call(fakeSelf, {address: {country:{$in:['France', 'Russia']}}}); From c3427b99868c0f364d3baf1bb4192a85ea1c0ce1 Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Sun, 30 Aug 2020 01:52:31 +0200 Subject: [PATCH 02/10] docs: added core concepts explains logic --- docs/_sidebar.md | 2 ++ docs/getting-started/core-concepts.md | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 docs/getting-started/core-concepts.md diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 0cb6f2a..5f1583a 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -1,3 +1,5 @@ +- Getting Started + - [Core Concepts](getting-started/core-concepts.md) - Primitives - [SBTree](primitives/SBTree.md) - Usage diff --git a/docs/getting-started/core-concepts.md b/docs/getting-started/core-concepts.md new file mode 100644 index 0000000..201130c --- /dev/null +++ b/docs/getting-started/core-concepts.md @@ -0,0 +1,25 @@ +## Core Concepts + +SBTree allows you to insert, delete, update, replace and find withing a balance B+ tree supporting uniqueness, and optional fields. + +SBTree holds set of SBFTree (F stands for Field). + +On each insertion of a document, we parse all individual field, if we did not yet have a SBFTree with this fieldName, we creates. + +All further instruction (finding, inserting,...) are directed to this tree manager. + +## SBFTree + +A SBFTree is unique for each fields, for nested object, each field of this object also have their own SBFTree. + +Using B+Tree, we have a root, that lead to leaf, or intermediaries node (holding leafs) and we manage indexing this way. + +In those nodes (SBFRoot, SBFLeaf, SBFNode), we hold childrens reference (half splitting), set of keys (in a SBFTree of names, that would be ["Jean Valjean",...]) and identifiers (_id of the document). + +The logic is then that we parse query into those fieldTrees to output identifiers, and we resolves those identifiers by looking for a get of those document by Id. + +## Adapters + +This system allows to have a indices db locally, that support a MongoDB style command (but without any thing to install). + +Due to the fact that it request persistance on a key-value way (via document id), it can be adaptable to a range of usage (local memory with single file save persistance, fs-adapter with single file / id, localstorage, mongodb,...); From abdfdb749abfd47988558e02183a7cedeab20329 Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Thu, 3 Sep 2020 23:26:50 +0200 Subject: [PATCH 03/10] fix!(SBTree): .insertDocument to return array on single inclusion --- docs/primitives/SBTree.md | 5 +- src/types/SBTree/methods/insertDocuments.js | 8 +-- .../SBTree/methods/insertDocuments.spec.js | 51 +++++++++++++++++++ 3 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 src/types/SBTree/methods/insertDocuments.spec.js diff --git a/docs/primitives/SBTree.md b/docs/primitives/SBTree.md index 71cde78..19a3192 100644 --- a/docs/primitives/SBTree.md +++ b/docs/primitives/SBTree.md @@ -12,10 +12,11 @@ const tree = new SBTree([props]); - `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). + #### async tree.insertDocuments(documents) -Allow to process one or an array of documents and insert them. -It returns the inserted object with their _id if not specified in document. +Allow to process an array (or unique) of documents and get them inserted. +It returns the inserted array containing mutated (with their _id if not specified in document) values. ```js await tree.insertDocuments({age:33, name:"Jean",_id:'507f191e810c19729de860ea'}) diff --git a/src/types/SBTree/methods/insertDocuments.js b/src/types/SBTree/methods/insertDocuments.js index fbe0045..224cafe 100644 --- a/src/types/SBTree/methods/insertDocuments.js +++ b/src/types/SBTree/methods/insertDocuments.js @@ -10,11 +10,13 @@ async function insertDocuments(documents) { } if (Array.isArray(documents)) { + let insertedDocumentsResultats = []; for (const document of documents) { - await this.insertDocuments(document); + insertedDocumentsResultats.push(...await this.insertDocuments(document)); } - return documents; + return insertedDocumentsResultats; } + const document = cloneDeep(documents); if (!document._id) { @@ -24,6 +26,6 @@ async function insertDocuments(documents) { this.size += 1; - return document; + return [document]; } module.exports = insertDocuments; diff --git a/src/types/SBTree/methods/insertDocuments.spec.js b/src/types/SBTree/methods/insertDocuments.spec.js new file mode 100644 index 0000000..855dfbc --- /dev/null +++ b/src/types/SBTree/methods/insertDocuments.spec.js @@ -0,0 +1,51 @@ +const { expect } = require('chai'); +const insertDocuments = require('./insertDocuments'); + +const doc1 = { + value: 'Sunny sun' +}; + +const docNb1 = { + number: 1 +}; +const docNb2 = { + number: 2 +}; +const docNb3 = { + number: 3 +}; +const mock = { + getFieldTree: ()=> {}, + setFieldTree: ()=> {}, + insertDocuments, + adapter:{ + saveDocument: ()=> {} + }, + state:{ + isReady: true + }, +} + +describe('.insertDocuments', ()=>{ + it('should insert a single document', async function () { + const insert = await insertDocuments.call(mock, doc1); + expect(insert.length).to.equal(1); + expect(insert[0].value).to.deep.equal(doc1.value); + expect(insert[0]._id).to.be.an('string'); + }); + it('should insert multiple documents', async function () { + const insertMultiples = await insertDocuments.call(mock, [docNb1, docNb2, docNb3]); + + expect(insertMultiples.length).to.equal(3); + + expect(insertMultiples[0].number).to.deep.equal(docNb1.number); + expect(insertMultiples[0]._id).to.be.an('string'); + + expect(insertMultiples[1].number).to.deep.equal(docNb2.number); + expect(insertMultiples[1]._id).to.be.an('string'); + + expect(insertMultiples[2].number).to.deep.equal(docNb3.number); + expect(insertMultiples[2]._id).to.be.an('string'); + + }); +}); From c67aa14a89e255eb7e4ce52c72dbdebc581e0dd5 Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Thu, 3 Sep 2020 23:27:12 +0200 Subject: [PATCH 04/10] chore: update dependencies --- package-lock.json | 49 ++++++++++++++++++++++++++++++++++++----------- package.json | 2 +- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0aae7f9..141c0eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -230,6 +230,24 @@ "to-fast-properties": "^2.0.0" } }, + "@eslint/eslintrc": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz", + "integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + } + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -818,12 +836,13 @@ "dev": true }, "eslint": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.7.0.tgz", - "integrity": "sha512-1KUxLzos0ZVsyL81PnRN335nDtQ8/vZUD6uMtWbF+5zDtjKcsklIi78XoE0MVL93QvWTu+E5y44VyyCsOMBrIg==", + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.8.1.tgz", + "integrity": "sha512-/2rX2pfhyUG0y+A123d0ccXtMm7DV7sH1m3lk9nk2DZ2LReq39FXHueR9xZwshE5MdfSf0xunSaMWRqyIA6M1w==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", + "@eslint/eslintrc": "^0.1.3", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -833,7 +852,7 @@ "eslint-scope": "^5.1.0", "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^1.3.0", - "espree": "^7.2.0", + "espree": "^7.3.0", "esquery": "^1.2.0", "esutils": "^2.0.2", "file-entry-cache": "^5.0.1", @@ -1156,12 +1175,20 @@ } }, "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "requires": { - "estraverse": "^4.1.0" + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } } }, "estraverse": { @@ -3119,9 +3146,9 @@ } }, "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", "dev": true, "requires": { "punycode": "^2.1.0" diff --git a/package.json b/package.json index 00ba3b0..11fb82b 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "devDependencies": { "chai": "^4.2.0", "chance": "^1.1.7", - "eslint": "^7.7.0", + "eslint": "^7.8.1", "eslint-plugin-import": "^2.22.0", "eslint-config-airbnb-base": "^14.2.0", "mocha": "^8.1.3", From 10fc0630e902cafe167694dddc83675903a64719 Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Fri, 4 Sep 2020 00:06:50 +0200 Subject: [PATCH 05/10] doc: additional primitives doc --- LICENCE => LICENSE | 0 docs/_sidebar.md | 12 +++- docs/primitives/SBFLeaf/SBFLeaf.md | 6 ++ docs/primitives/SBFNode/SBFNode.md | 5 ++ docs/primitives/SBFRoot/SBFRoot.md | 6 ++ docs/primitives/SBFTree/SBFTree.md | 26 ++++++++ docs/primitives/SBTree.md | 63 ------------------- docs/primitives/SBTree/SBTree.md | 26 ++++++++ .../SBTree/methods/deleteDocuments.md | 10 +++ .../SBTree/methods/findDocuments.md | 11 ++++ docs/primitives/SBTree/methods/getDocument.md | 9 +++ .../SBTree/methods/insertDocuments.md | 9 +++ docs/primitives/SBTree/methods/toJSON.md | 7 +++ src/types/SBTree/methods/insertDocuments.js | 6 ++ 14 files changed, 132 insertions(+), 64 deletions(-) rename LICENCE => LICENSE (100%) create mode 100644 docs/primitives/SBFLeaf/SBFLeaf.md create mode 100644 docs/primitives/SBFNode/SBFNode.md create mode 100644 docs/primitives/SBFRoot/SBFRoot.md create mode 100644 docs/primitives/SBFTree/SBFTree.md delete mode 100644 docs/primitives/SBTree.md create mode 100644 docs/primitives/SBTree/SBTree.md create mode 100644 docs/primitives/SBTree/methods/deleteDocuments.md create mode 100644 docs/primitives/SBTree/methods/findDocuments.md create mode 100644 docs/primitives/SBTree/methods/getDocument.md create mode 100644 docs/primitives/SBTree/methods/insertDocuments.md create mode 100644 docs/primitives/SBTree/methods/toJSON.md diff --git a/LICENCE b/LICENSE similarity index 100% rename from LICENCE rename to LICENSE diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 5f1583a..f1359de 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -1,7 +1,17 @@ - Getting Started - [Core Concepts](getting-started/core-concepts.md) - Primitives - - [SBTree](primitives/SBTree.md) + - [SBTree](primitives/SBTree/SBTree.md) + - [deleteDocuments](primitives/SBTree/methods/deleteDocuments) + - [findDocuments](primitives/SBTree/methods/findDocuments) + - [getDocument](primitives/SBTree/methods/getDocument) + - [insertDocuments](primitives/SBTree/methods/insertDocuments) + - [toJSON](primitives/SBTree/methods/toJSON) + + - [SBFTree](primitives/SBFTree/SBFTree.md) + - [SBFRoot](primitives/SBFRoot/SBFRoot.md) + - [SBFNode](primitives/SBFNode/SBFNode.md) + - [SBFLeaf](primitives/SBFLeaf/SBFLeaf.md) - Usage - [Events](usage/events.md) - [Queries](usage/queries.md) diff --git a/docs/primitives/SBFLeaf/SBFLeaf.md b/docs/primitives/SBFLeaf/SBFLeaf.md new file mode 100644 index 0000000..61c3866 --- /dev/null +++ b/docs/primitives/SBFLeaf/SBFLeaf.md @@ -0,0 +1,6 @@ +## SBFLeaf + +SBFLeaf represents the end of the tree. +They contain a size, the identifier array, and the set of keys. + +They rely on the adapter to fetch these informations back. diff --git a/docs/primitives/SBFNode/SBFNode.md b/docs/primitives/SBFNode/SBFNode.md new file mode 100644 index 0000000..5d689eb --- /dev/null +++ b/docs/primitives/SBFNode/SBFNode.md @@ -0,0 +1,5 @@ +## SBFNode + +SBFNode represents a part of the tree that is not a Leaf (intermediary nodes). + +They contains the splitting keys (split of keys in the form of a binary search) and the references to the childrens nodes. diff --git a/docs/primitives/SBFRoot/SBFRoot.md b/docs/primitives/SBFRoot/SBFRoot.md new file mode 100644 index 0000000..2170d22 --- /dev/null +++ b/docs/primitives/SBFRoot/SBFRoot.md @@ -0,0 +1,6 @@ +## SBFRoot + +SBFRoot represents the first node of our tree, the starting point from which we perform the insertion, replacement, deletion or search. + +It contains multiples SBFNode (or Leaf) childrens, and a tiny subset of data in a keys and identifiers array. + diff --git a/docs/primitives/SBFTree/SBFTree.md b/docs/primitives/SBFTree/SBFTree.md new file mode 100644 index 0000000..e6d93b8 --- /dev/null +++ b/docs/primitives/SBFTree/SBFTree.md @@ -0,0 +1,26 @@ +## SBFTree API + +```js +const fielTree = new SBFTree([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) + - `isUnique` Bool - (def: false) + - `root` SBFRoot - (def: null) - Allow to set a root to the fieldTree. + - `exclude` Array - (def: []) - Allow to exclude from indexing some field (important if you expect field value to be huge or nested). + - `id` FieldId - Allow to identify a FieldTree by an id; useful especially for adapter. + +### Usage + +The SBFTree is an interface instance specific to a field. For a document {age, name}, you would have two SBFTree, one for age, one for name. + +The Tree has two components, an identifier (of the document), and a value and use a B+Tree. + +#### .find +#### .get +#### .insert +#### .remove +#### .replace diff --git a/docs/primitives/SBTree.md b/docs/primitives/SBTree.md deleted file mode 100644 index 19a3192..0000000 --- a/docs/primitives/SBTree.md +++ /dev/null @@ -1,63 +0,0 @@ -## SBTree API - -```js -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). - - -#### async tree.insertDocuments(documents) - -Allow to process an array (or unique) of documents and get them inserted. -It returns the inserted array containing mutated (with their _id if not specified in document) values. - -```js - await tree.insertDocuments({age:33, name:"Jean",_id:'507f191e810c19729de860ea'}) -``` - - -#### async tree.getDocument(_id) - -Allow to fetch a specific document by it's specific id. - -```js - await tree.getDocument('507f191e810c19729de860ea') -``` - - -#### async tree.findDocuments(query) - -Allow to find all documents matching the query - -See more info on [queries](/docs/usage/queries.md) - - -```js - await tree.findDocuments({age:33}); -``` - -#### async tree.deleteDocuments(query) - -Will delete all documents matching the query. - -See more info on [queries](/docs/usage/queries.md) - -```js - await tree.deleteDocuments({age:33}); -``` - - -#### tree.toJSON() - -Allow to return a representation in JSON of the tree. Useful for storing it, as it's result are valid params for the SBTree constructor. - -```js - tree.toJSON() -``` diff --git a/docs/primitives/SBTree/SBTree.md b/docs/primitives/SBTree/SBTree.md new file mode 100644 index 0000000..63c5ba2 --- /dev/null +++ b/docs/primitives/SBTree/SBTree.md @@ -0,0 +1,26 @@ +## SBTree API + +```js +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). + +### Usage + +The SBTree is the instance that will allow you to insert, get, find, replace or delete documents. +It internally hold multiple instances of [SBFTree](primitives/SBFTree.md) and works as a router, interfacing with the users. + +### Methods + +- [deleteDocuments](primitives/SBTree/methods/deleteDocuments) +- [findDocuments](primitives/SBTree/methods/findDocuments) +- [getDocument](primitives/SBTree/methods/getDocument) +- [insertDocuments](primitives/SBTree/methods/insertDocuments) +- [toJSON](primitives/SBTree/methods/toJSON) diff --git a/docs/primitives/SBTree/methods/deleteDocuments.md b/docs/primitives/SBTree/methods/deleteDocuments.md new file mode 100644 index 0000000..ed6a7b9 --- /dev/null +++ b/docs/primitives/SBTree/methods/deleteDocuments.md @@ -0,0 +1,10 @@ +#### async tree.deleteDocuments(query) + +Will delete all documents matching the query. + +See more info on [queries](/docs/usage/queries.md) + +```js + await tree.deleteDocuments({age:33}); +``` + diff --git a/docs/primitives/SBTree/methods/findDocuments.md b/docs/primitives/SBTree/methods/findDocuments.md new file mode 100644 index 0000000..6499cb3 --- /dev/null +++ b/docs/primitives/SBTree/methods/findDocuments.md @@ -0,0 +1,11 @@ +#### async tree.findDocuments(query) + +Allow to find all documents matching the query + +See more info on [queries](/docs/usage/queries.md) + + +```js + await tree.findDocuments({age:33}); +``` + diff --git a/docs/primitives/SBTree/methods/getDocument.md b/docs/primitives/SBTree/methods/getDocument.md new file mode 100644 index 0000000..e0156ee --- /dev/null +++ b/docs/primitives/SBTree/methods/getDocument.md @@ -0,0 +1,9 @@ + +#### async tree.getDocument(_id) + +Allow to fetch a specific document by it's specific id. + +```js + await tree.getDocument('507f191e810c19729de860ea') +``` + diff --git a/docs/primitives/SBTree/methods/insertDocuments.md b/docs/primitives/SBTree/methods/insertDocuments.md new file mode 100644 index 0000000..326f015 --- /dev/null +++ b/docs/primitives/SBTree/methods/insertDocuments.md @@ -0,0 +1,9 @@ +#### async tree.insertDocuments(documents) + +Allow to process an array (or unique) of documents and get them inserted. +It returns the inserted array containing mutated (with their _id if not specified in document) values. + +```js + await tree.insertDocuments({age:33, name:"Jean",_id:'507f191e810c19729de860ea'}) +``` + diff --git a/docs/primitives/SBTree/methods/toJSON.md b/docs/primitives/SBTree/methods/toJSON.md new file mode 100644 index 0000000..9e47078 --- /dev/null +++ b/docs/primitives/SBTree/methods/toJSON.md @@ -0,0 +1,7 @@ +#### tree.toJSON() + +Allow to return a representation in JSON of the tree. Useful for storing it, as it's result are valid params for the SBTree constructor. + +```js + tree.toJSON() +``` diff --git a/src/types/SBTree/methods/insertDocuments.js b/src/types/SBTree/methods/insertDocuments.js index 224cafe..1b1f2f9 100644 --- a/src/types/SBTree/methods/insertDocuments.js +++ b/src/types/SBTree/methods/insertDocuments.js @@ -2,6 +2,12 @@ const ObjectId = require('mongo-objectid'); const cloneDeep = require('lodash.clonedeep'); const insert = require('../ops/insert'); +/** + * Allow to insert of or multiple documents + * + * @param documents + * @returns {Promise<[{documents}]>} - copy of the inserted (mutated with _id) document. + */ async function insertDocuments(documents) { // This will wait for SBTree to have isReady = true. // When so, it will then perform the insertion. From cf97358529c6b84260657ef21e3fd8332ccddb29 Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Fri, 4 Sep 2020 00:52:55 +0200 Subject: [PATCH 06/10] doc: improve navigation --- README.md | 2 +- docs/_sidebar.md | 6 +++++- docs/primitives/SBFLeaf/SBFLeaf.md | 2 +- docs/primitives/SBFNode/SBFNode.md | 2 +- docs/primitives/SBFRoot/SBFRoot.md | 2 +- docs/primitives/SBFTree/SBFTree.md | 15 +++++++++------ docs/primitives/SBFTree/methods/find.md | 8 ++++++++ docs/primitives/SBFTree/methods/get.md | 8 ++++++++ docs/primitives/SBFTree/methods/insert.md | 8 ++++++++ docs/primitives/SBFTree/methods/remove.md | 10 ++++++++++ docs/primitives/SBFTree/methods/replace.md | 8 ++++++++ docs/primitives/SBTree/SBTree.md | 2 +- docs/usage/events.md | 6 +++--- docs/usage/queries.md | 8 ++++---- 14 files changed, 68 insertions(+), 19 deletions(-) create mode 100644 docs/primitives/SBFTree/methods/find.md create mode 100644 docs/primitives/SBFTree/methods/get.md create mode 100644 docs/primitives/SBFTree/methods/insert.md create mode 100644 docs/primitives/SBFTree/methods/remove.md create mode 100644 docs/primitives/SBFTree/methods/replace.md diff --git a/README.md b/README.md index abc0964..31f7c60 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ > State : Optimisation, features and stability works in progress. -> Documentation : https://alex-werner.github.io/SBTree +Documentation : https://alex-werner.github.io/SBTree 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. diff --git a/docs/_sidebar.md b/docs/_sidebar.md index f1359de..ecb5d89 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -7,8 +7,12 @@ - [getDocument](primitives/SBTree/methods/getDocument) - [insertDocuments](primitives/SBTree/methods/insertDocuments) - [toJSON](primitives/SBTree/methods/toJSON) - - [SBFTree](primitives/SBFTree/SBFTree.md) + - [find](primitives/SBFTree/methods/find) + - [get](primitives/SBFTree/methods/get) + - [insert](primitives/SBFTree/methods/insert) + - [remove](primitives/SBFTree/methods/remove) + - [replace](primitives/SBFTree/methods/replace) - [SBFRoot](primitives/SBFRoot/SBFRoot.md) - [SBFNode](primitives/SBFNode/SBFNode.md) - [SBFLeaf](primitives/SBFLeaf/SBFLeaf.md) diff --git a/docs/primitives/SBFLeaf/SBFLeaf.md b/docs/primitives/SBFLeaf/SBFLeaf.md index 61c3866..8404a23 100644 --- a/docs/primitives/SBFLeaf/SBFLeaf.md +++ b/docs/primitives/SBFLeaf/SBFLeaf.md @@ -1,4 +1,4 @@ -## SBFLeaf +### SBFLeaf SBFLeaf represents the end of the tree. They contain a size, the identifier array, and the set of keys. diff --git a/docs/primitives/SBFNode/SBFNode.md b/docs/primitives/SBFNode/SBFNode.md index 5d689eb..b4dc8da 100644 --- a/docs/primitives/SBFNode/SBFNode.md +++ b/docs/primitives/SBFNode/SBFNode.md @@ -1,4 +1,4 @@ -## SBFNode +### SBFNode SBFNode represents a part of the tree that is not a Leaf (intermediary nodes). diff --git a/docs/primitives/SBFRoot/SBFRoot.md b/docs/primitives/SBFRoot/SBFRoot.md index 2170d22..b2721db 100644 --- a/docs/primitives/SBFRoot/SBFRoot.md +++ b/docs/primitives/SBFRoot/SBFRoot.md @@ -1,4 +1,4 @@ -## SBFRoot +### SBFRoot SBFRoot represents the first node of our tree, the starting point from which we perform the insertion, replacement, deletion or search. diff --git a/docs/primitives/SBFTree/SBFTree.md b/docs/primitives/SBFTree/SBFTree.md index e6d93b8..1ec588c 100644 --- a/docs/primitives/SBFTree/SBFTree.md +++ b/docs/primitives/SBFTree/SBFTree.md @@ -1,4 +1,4 @@ -## SBFTree API +### SBFTree API ```js const fielTree = new SBFTree([props]); @@ -19,8 +19,11 @@ The SBFTree is an interface instance specific to a field. For a document {age, n The Tree has two components, an identifier (of the document), and a value and use a B+Tree. -#### .find -#### .get -#### .insert -#### .remove -#### .replace + +### Methods + +- [find](primitives/SBFTree/methods/find) +- [get](primitives/SBFTree/methods/get) +- [insert](primitives/SBFTree/methods/insert) +- [remove](primitives/SBFTree/methods/remove) +- [replace](primitives/SBFTree/methods/replace) diff --git a/docs/primitives/SBFTree/methods/find.md b/docs/primitives/SBFTree/methods/find.md new file mode 100644 index 0000000..2dfdd30 --- /dev/null +++ b/docs/primitives/SBFTree/methods/find.md @@ -0,0 +1,8 @@ +#### async tree.find(value, operator) + +Will find a value using a specific operator. + +```js + await tree.find(33, '$eq'); // Return set of identifiers and keys matching the find query. +``` + diff --git a/docs/primitives/SBFTree/methods/get.md b/docs/primitives/SBFTree/methods/get.md new file mode 100644 index 0000000..2ba1c2b --- /dev/null +++ b/docs/primitives/SBFTree/methods/get.md @@ -0,0 +1,8 @@ +#### async tree.get(identifier) + +Will get back a specific identifier's value + +```js + await tree.get('5d6755b71f9edbc997c8d156'); +``` + diff --git a/docs/primitives/SBFTree/methods/insert.md b/docs/primitives/SBFTree/methods/insert.md new file mode 100644 index 0000000..54d4613 --- /dev/null +++ b/docs/primitives/SBFTree/methods/insert.md @@ -0,0 +1,8 @@ +#### async tree.insert(identifier, value) + +Will insert a set of identifier and value into the fieldTree. + +```js + await tree.insert('5d6755b71f9edbc997c8d156', 33); +``` + diff --git a/docs/primitives/SBFTree/methods/remove.md b/docs/primitives/SBFTree/methods/remove.md new file mode 100644 index 0000000..b2de0df --- /dev/null +++ b/docs/primitives/SBFTree/methods/remove.md @@ -0,0 +1,10 @@ +#### async tree.remove(identifier) + +Will remove a document from an identifier. +The RemoveCommand instance holds the different fields to delete the identifiers from. + +```js + const removeCommand = new RemoveCommand() + await tree.remove(removeCommand); +``` + diff --git a/docs/primitives/SBFTree/methods/replace.md b/docs/primitives/SBFTree/methods/replace.md new file mode 100644 index 0000000..0cb6bc2 --- /dev/null +++ b/docs/primitives/SBFTree/methods/replace.md @@ -0,0 +1,8 @@ +#### async tree.replace(identifier, value) + +Will replace the value for an identifier. + +```js + await tree.replace('5d6755b71f9edbc997c8d156', 60); +``` + diff --git a/docs/primitives/SBTree/SBTree.md b/docs/primitives/SBTree/SBTree.md index 63c5ba2..e7cc9fe 100644 --- a/docs/primitives/SBTree/SBTree.md +++ b/docs/primitives/SBTree/SBTree.md @@ -1,4 +1,4 @@ -## SBTree API +### SBTree ```js const tree = new SBTree([props]); diff --git a/docs/usage/events.md b/docs/usage/events.md index cf38b84..8a69960 100644 --- a/docs/usage/events.md +++ b/docs/usage/events.md @@ -1,8 +1,8 @@ -## Events +### Events -### SBTree Events +#### SBTree Events -#### tree.on(event) +##### tree.on(event) - eventTypes : - ready : Emitted when the Tree has a parent attached and is ready for usage diff --git a/docs/usage/queries.md b/docs/usage/queries.md index 4a0ddb6..9c78ee4 100644 --- a/docs/usage/queries.md +++ b/docs/usage/queries.md @@ -1,10 +1,10 @@ -## Queries +### Queries Most of the methods in SBTree are straightforward `insert` expect a document to insert, `get` a document id to fetch. However the only exception might be for the query syntax, which is actually a subset of the MongoDB query syntax. -### Comparators +#### Comparators | Name | Description | Examples | |-------------- |------------------------------------------------------------------------- |------------------------------------------------ | @@ -19,7 +19,7 @@ However the only exception might be for the query syntax, which is actually a su | $nin | Matches documents that do not have any elements of the specified array. | `.findDocuments({country:{$nin:["Antarctica"]})` | -### Additionals +#### Additionals N.B : Not available yet. Only comparators are available to use. This documentation documents forthcoming features. @@ -32,6 +32,6 @@ TODO - $exists - $regex -### Caveats +#### Caveats Nested object query is not yet an available feature, but it is high on our priority list. From 5419761fb8fe179b0877e1df6bbdef82146e92ef Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Fri, 4 Sep 2020 00:53:35 +0200 Subject: [PATCH 07/10] fix: improve fieldName fetching from query --- src/types/SBTree/ops/query.js | 3 +- .../SBTree/utils/getFieldNamesFromQuery.js | 12 +++++++ .../utils/getFieldNamesFromQuery.spec.js | 19 +++++++++++ test/functional/use.case.3.spec.js | 17 +++++----- test/functional/use.case.5.spec.js | 33 ++++++++++++++++--- 5 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 src/types/SBTree/utils/getFieldNamesFromQuery.js create mode 100644 src/types/SBTree/utils/getFieldNamesFromQuery.spec.js diff --git a/src/types/SBTree/ops/query.js b/src/types/SBTree/ops/query.js index c64e606..873d5ec 100644 --- a/src/types/SBTree/ops/query.js +++ b/src/types/SBTree/ops/query.js @@ -20,6 +20,7 @@ const findIntersectingIdentifiers = (listOfListOfIdentifiers) => { return intersection(...identifiers); }; +const getFieldNamesFromQuery = require('../utils/getFieldNamesFromQuery'); /** * * @param query @@ -48,7 +49,7 @@ async function query(query) { }; if(!query) return []; - const fields = Object.keys(query); + const fields = getFieldNamesFromQuery(query); const fieldsResults = {}; const result = []; diff --git a/src/types/SBTree/utils/getFieldNamesFromQuery.js b/src/types/SBTree/utils/getFieldNamesFromQuery.js new file mode 100644 index 0000000..b456386 --- /dev/null +++ b/src/types/SBTree/utils/getFieldNamesFromQuery.js @@ -0,0 +1,12 @@ +module.exports = function getFieldNamesFromQuery(query){ + const fieldNames = []; + Object.entries(query).forEach((field)=>{ + let fieldName = field[0]; + + if(field[1].constructor === Object){ + fieldName+='.'+getFieldNamesFromQuery(field[1]); + } + fieldNames.push(fieldName); + }); + return fieldNames; +} diff --git a/src/types/SBTree/utils/getFieldNamesFromQuery.spec.js b/src/types/SBTree/utils/getFieldNamesFromQuery.spec.js new file mode 100644 index 0000000..4287878 --- /dev/null +++ b/src/types/SBTree/utils/getFieldNamesFromQuery.spec.js @@ -0,0 +1,19 @@ +const { expect } = require('chai'); + +const getFieldNamesFromQuery = require('./getFieldNamesFromQuery'); + +describe('utils - .getFieldNamesFromQuery', ()=>{ + it('should works', ()=>{ + const res = getFieldNamesFromQuery({age:33}); + expect(res).to.deep.equal(['age']); + + const res2 = getFieldNamesFromQuery({infos:{age:33}}); + expect(res2).to.deep.equal(['infos.age']); + + const res3 = getFieldNamesFromQuery({infos:{job:{sector:"IT"}}}); + expect(res3).to.deep.equal(['infos.job.sector']); + + const res4 = getFieldNamesFromQuery({age:23,infos:{job:{sector:"IT"}}}); + expect(res4).to.deep.equal(['age','infos.job.sector']); + }) +}) diff --git a/test/functional/use.case.3.spec.js b/test/functional/use.case.3.spec.js index 4da8208..e7d975a 100644 --- a/test/functional/use.case.3.spec.js +++ b/test/functional/use.case.3.spec.js @@ -18,8 +18,8 @@ describe('E2E - Classic UseCase', function suite() { "country": "Georgia", "_id": "5d6ebb7e21f1df6ff7482631" }; - const lilith = { - "name": "Lilith", + const lilia = { + "name": "Lilia", "age": 25, "gender": "Female", "country": "Armenia", @@ -28,8 +28,10 @@ describe('E2E - Classic UseCase', function suite() { it('should insert', async function () { await customTree.insertDocuments(devan); // Inserting any document without a _id will generate it one - const insertedDoc = await customTree.insertDocuments(lilith); - insertedDocId = insertedDoc._id; + const insertedDoc = await customTree.insertDocuments(lilia); + expect(insertedDoc.length).to.equal(1); + expect(insertedDoc[0]._id).to.be.an('string'); + insertedDocId = insertedDoc[0]._id; console.log("Inserted doc ", insertedDocId) // Inserting in bulk is also possible. @@ -65,15 +67,14 @@ describe('E2E - Classic UseCase', function suite() { const res = await customTree.findDocuments({age: {$lt: 45}}) const res2 = await customTree.findDocuments({age: {$lte: 45}}) const res3 = await customTree.findDocuments({age: {$lt: 18}}) - const expectedFirstname = ["Julian","Lucy","Lilith","Alex","Jean","Anastasia","Alice","Devan","Brigitte","Chen","Pascal","Teneti"]; + const expectedFirstname = ["Julian","Lucy","Lilia","Alex","Jean","Anastasia","Alice","Devan","Brigitte","Chen","Pascal","Teneti"]; expect(toNames(res)).to.deep.equal(expectedFirstname); expect(toNames(res2)).to.deep.equal(expectedFirstname.concat('Bob')); expect(res3).to.deep.equal([]) - }); it('should be able to do query with id', async function () { const res = (await customTree.findDocuments({_id:insertedDocId})); - expect(res).to.deep.equal([Object.assign({_id:insertedDocId}, lilith)]) + expect(res).to.deep.equal([Object.assign({_id:insertedDocId}, lilia)]) }); it('should be able to do special query',async function () { const r = await customTree.findDocuments({country:"France"}) @@ -92,7 +93,7 @@ describe('E2E - Classic UseCase', function suite() { }); it('should be able to get doc by id',async function () { const res = (await customTree.getDocument(insertedDocId)) - expect(res).to.deep.equal(Object.assign({_id:insertedDocId}, lilith)) + expect(res).to.deep.equal(Object.assign({_id:insertedDocId}, lilia)) }); it('should be able to remove a document',async function () { const res = (await customTree.deleteDocuments({name:'Bob'})) diff --git a/test/functional/use.case.5.spec.js b/test/functional/use.case.5.spec.js index 36deb37..d3359ca 100644 --- a/test/functional/use.case.5.spec.js +++ b/test/functional/use.case.5.spec.js @@ -16,7 +16,15 @@ describe('E2E - UseCase nested document', function suite() { key: 'KqXIhr', value: { _id: 'KqXIhr', - _rev: '1-85e8ff864ae6d157217ad2e4d4117f5e', + _rev: '1-85e8ff864ae6d157217ad2e4d2e4d45e', + _list: ['2e4d4', '4d45e'], + _obj: { + _key: '1-85-01/001', + _value: 12345, + _meta:{ + ts: 1599167157 + } + } }, _created: ts, _updated: ts, @@ -24,8 +32,10 @@ describe('E2E - UseCase nested document', function suite() { }; let doc2; describe('Nested document', async () => { - it('should insert a simgle document', async function () { - await customTree.insertDocuments(doc); + it('should insert a single document', async function () { + const insertedRes = await customTree.insertDocuments(doc); + expect(insertedRes.length).to.equal(1); + expect(insertedRes[0]).to.deep.equal(doc); }); it('should get the document', async function () { const getDocRes = await customTree.getDocument(doc._id); @@ -36,10 +46,13 @@ describe('E2E - UseCase nested document', function suite() { expect(findDocRes[0]).to.deep.equal(doc); const findDocRes2 = await customTree.findDocuments({_created: {$lte: doc._created+1000}}); expect(findDocRes2[0]).to.deep.equal(doc); + const findDocRes3 = await customTree.findDocuments({value:{_obj:{_value: 12345}}}); + console.log({findDocRes3}); + expect(findDocRes3[0]).to.deep.equal(doc); }); it.skip('should update', function () { }); - it('should replace', async function (){ + it.skip('should replace', async function (){ doc2 = {...doc, _updated: doc._updated+1000}; doc2.value._count = 1; @@ -48,11 +61,21 @@ describe('E2E - UseCase nested document', function suite() { const getDocRes = await customTree.getDocument(doc._id); expect(getDocRes).to.deep.equal(doc2); }) - it('should delete the document', async function () { + it.skip('should delete the document', async function () { const delDocRes = await customTree.deleteDocuments({_created: {$gte: doc._updated}}); expect(delDocRes[0]).to.deep.equal(doc2); const getDocRes = await customTree.getDocument(doc._id); expect(getDocRes).to.deep.equal(null); }); }); + describe('Array', async ()=>{ + it.skip('should find the array back', async function () { + // const res = await customTree.findDocuments({value:{_list:['2e4d4', '4d45e']}}) + // console.log(res); + }); + it.skip('should find a member of array', async function () { + const res = await customTree.findDocuments({value:{_list:{$in:['4d45e']}}}) + console.log(res); + }); + }) }); From 5e1a07b54eced8356b66e889de1a2ee2215bb052 Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Fri, 4 Sep 2020 01:09:44 +0200 Subject: [PATCH 08/10] fix: finding recursive --- .../MemoryAdapter/methods/findInLeaf.js | 1 + src/types/SBTree/ops/query.js | 42 ++++++++++--------- .../SBTree/utils/getFieldNamesFromQuery.js | 12 ++++-- .../utils/getFieldNamesFromQuery.spec.js | 3 ++ test/functional/use.case.5.spec.js | 1 - 5 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/adapters/MemoryAdapter/methods/findInLeaf.js b/src/adapters/MemoryAdapter/methods/findInLeaf.js index 3ec1bbb..18bbdbc 100644 --- a/src/adapters/MemoryAdapter/methods/findInLeaf.js +++ b/src/adapters/MemoryAdapter/methods/findInLeaf.js @@ -20,6 +20,7 @@ module.exports = async function findInLeaf(leafId, value, op = '$eq') { const lastIdx = keys.lastIndexOf(value); const strictMatchingKeysLen = (firstIdx > -1) ? 1 + (lastIdx - firstIdx) : 0; + switch (op) { case '$eq': if (!strictMatchingKeysLen) { diff --git a/src/types/SBTree/ops/query.js b/src/types/SBTree/ops/query.js index 873d5ec..f06f970 100644 --- a/src/types/SBTree/ops/query.js +++ b/src/types/SBTree/ops/query.js @@ -21,20 +21,20 @@ const findIntersectingIdentifiers = (listOfListOfIdentifiers) => { }; const getFieldNamesFromQuery = require('../utils/getFieldNamesFromQuery'); + /** * * @param query * @returns {Promise<[]>} */ async function query(query) { - const self = this; const findNested = async function (_promises, _queryFieldName, _queryFieldValue) { for (const nestedQueryFieldName in _queryFieldValue) { const nestedQueryFieldValue = _queryFieldValue[nestedQueryFieldName]; const nestedQueryFieldType = typeof nestedQueryFieldValue; - if (['number', 'string', 'boolean'].includes(nestedQueryFieldType)) { + if (['number', 'string', 'boolean', 'object'].includes(nestedQueryFieldType)) { const fTree = self.getFieldTree(`${_queryFieldName}.${nestedQueryFieldName}`); // Sometimes, like when excluded, this can not resolve. if (fTree) { @@ -47,13 +47,10 @@ async function query(query) { } } }; - if(!query) return []; + if (!query) return []; const fields = getFieldNamesFromQuery(query); - const fieldsResults = {}; - const result = []; - // When our search is based on _id and only _id, we can just get document. if (fields.length === 1 && fields.indexOf('_id') > -1) { return [await get.call(this, query._id)]; @@ -62,9 +59,16 @@ async function query(query) { const promises = []; fields.forEach((queryFieldName) => { - const queryFieldValue = query[queryFieldName]; - const queryFieldType = typeof queryFieldValue; + let queryFieldValue; + + queryFieldName.split('.').forEach((subFieldName) => { + queryFieldValue = (queryFieldValue && queryFieldValue[subFieldName]) + ? queryFieldValue[subFieldName] + : query[subFieldName]; + }) + + const queryFieldType = typeof queryFieldValue; let fieldTree; switch (queryFieldType) { @@ -102,18 +106,18 @@ async function query(query) { let intermediateIdentifiers = []; await Promise - .all(promises) - .then((pResults) => { - for (const pResult of pResults) { - // Whenever we sees that, we can quickly answer an empty response, as [] intersect with nothing. - if (pResult.identifiers.length === 0) { - // We remove any previous findings - intermediateIdentifiers = []; - break; + .all(promises) + .then((pResults) => { + for (const pResult of pResults) { + // Whenever we sees that, we can quickly answer an empty response, as [] intersect with nothing. + if (pResult.identifiers.length === 0) { + // We remove any previous findings + intermediateIdentifiers = []; + break; + } + intermediateIdentifiers.push(pResult.identifiers); } - intermediateIdentifiers.push(pResult.identifiers); - } - }); + }); const matchingObjectIds = findIntersectingIdentifiers(intermediateIdentifiers); return resolveDocuments(this, matchingObjectIds); diff --git a/src/types/SBTree/utils/getFieldNamesFromQuery.js b/src/types/SBTree/utils/getFieldNamesFromQuery.js index b456386..5ea6a5a 100644 --- a/src/types/SBTree/utils/getFieldNamesFromQuery.js +++ b/src/types/SBTree/utils/getFieldNamesFromQuery.js @@ -1,10 +1,14 @@ -module.exports = function getFieldNamesFromQuery(query){ +module.exports = function getFieldNamesFromQuery(query) { const fieldNames = []; - Object.entries(query).forEach((field)=>{ + Object.entries(query).forEach((field) => { let fieldName = field[0]; + if (fieldName[0] === '$') return; - if(field[1].constructor === Object){ - fieldName+='.'+getFieldNamesFromQuery(field[1]); + if (field[1].constructor === Object) { + const _fields = getFieldNamesFromQuery(field[1]); + if (_fields[0]) { + fieldName += '.' + _fields; + } } fieldNames.push(fieldName); }); diff --git a/src/types/SBTree/utils/getFieldNamesFromQuery.spec.js b/src/types/SBTree/utils/getFieldNamesFromQuery.spec.js index 4287878..3f96394 100644 --- a/src/types/SBTree/utils/getFieldNamesFromQuery.spec.js +++ b/src/types/SBTree/utils/getFieldNamesFromQuery.spec.js @@ -15,5 +15,8 @@ describe('utils - .getFieldNamesFromQuery', ()=>{ const res4 = getFieldNamesFromQuery({age:23,infos:{job:{sector:"IT"}}}); expect(res4).to.deep.equal(['age','infos.job.sector']); + + const res5 = getFieldNamesFromQuery({ age: { '$gte': 33, '$lte': 50 } }); + expect(res5).to.deep.equal(['age']); }) }) diff --git a/test/functional/use.case.5.spec.js b/test/functional/use.case.5.spec.js index d3359ca..031bf96 100644 --- a/test/functional/use.case.5.spec.js +++ b/test/functional/use.case.5.spec.js @@ -47,7 +47,6 @@ describe('E2E - UseCase nested document', function suite() { const findDocRes2 = await customTree.findDocuments({_created: {$lte: doc._created+1000}}); expect(findDocRes2[0]).to.deep.equal(doc); const findDocRes3 = await customTree.findDocuments({value:{_obj:{_value: 12345}}}); - console.log({findDocRes3}); expect(findDocRes3[0]).to.deep.equal(doc); }); it.skip('should update', function () { From d4b65339e9675e5bf72987f2735a8fd8e92f063d Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Fri, 4 Sep 2020 01:43:24 +0200 Subject: [PATCH 09/10] fix: proper $in search --- src/types/SBFRoot/methods/find.js | 5 +++++ src/types/SBTree/ops/insert.js | 10 ++++++++-- src/utils/array.js | 5 +++-- test/functional/use.case.5.spec.js | 27 ++++++++++++++------------- 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/types/SBFRoot/methods/find.js b/src/types/SBFRoot/methods/find.js index be6d20d..f15d8c8 100644 --- a/src/types/SBFRoot/methods/find.js +++ b/src/types/SBFRoot/methods/find.js @@ -7,6 +7,10 @@ async function find(value, operator = '$eq') { const p = []; const results = { identifiers: [], keys: [] }; + const valueKeys = Object.keys(value); + if(valueKeys.includes('$in')){ + return find.call(this, value.$in, '$in'); + } switch (operator) { case '$eq': return findEquals.call(this, value); @@ -45,6 +49,7 @@ async function find(value, operator = '$eq') { }).catch((err) => { console.error('err', err); }); + return results; case '$nin': if (!Array.isArray(value)) throw new Error('$nin operator expect key to be an array'); diff --git a/src/types/SBTree/ops/insert.js b/src/types/SBTree/ops/insert.js index d3bf7fa..d237161 100644 --- a/src/types/SBTree/ops/insert.js +++ b/src/types/SBTree/ops/insert.js @@ -17,7 +17,7 @@ async function insert(document) { if (validTypes.includes(_fieldType)) { // When we have to deal with a nested object - if (_fieldType === 'object' && !Array.isArray(_fieldType)) { + if (_fieldType === 'object' && !Array.isArray(_fieldValue)) { // Then we create a nested field tree for each field of the nested object const self = this; @@ -30,7 +30,13 @@ async function insert(document) { if (fieldTree) { if (typeof _fieldValue[_propName] === 'object' && !Array.isArray(_fieldValue)) { for (const _childPropName in _fieldValue[_propName]) { - await insertNested(`${_fieldName}.${_propName}`, _fieldValue[_propName]); + if(Array.isArray(_fieldValue[_propName])){ + if(_childPropName === '0') { + await fieldTree.insert(id, _fieldValue[_propName]); + } + } else{ + await insertNested(`${_fieldName}.${_propName}`, _fieldValue[_propName]); + } } } else { await fieldTree.insert(id, _fieldValue[_propName]); diff --git a/src/utils/array.js b/src/utils/array.js index 7df3ebd..d2f2521 100644 --- a/src/utils/array.js +++ b/src/utils/array.js @@ -19,9 +19,10 @@ const array = { index = Math.floor((min + max) / 2); } if (Array.isArray(item)) { - throw new Error('Not handled'); + arr.splice(index, 0, ...item); + }else{ + arr.splice(index, 0, item); } - arr.splice(index, 0, item); return index; }, async forEach(array, eachFn) { diff --git a/test/functional/use.case.5.spec.js b/test/functional/use.case.5.spec.js index 031bf96..2c47f9d 100644 --- a/test/functional/use.case.5.spec.js +++ b/test/functional/use.case.5.spec.js @@ -49,9 +49,19 @@ describe('E2E - UseCase nested document', function suite() { const findDocRes3 = await customTree.findDocuments({value:{_obj:{_value: 12345}}}); expect(findDocRes3[0]).to.deep.equal(doc); }); - it.skip('should update', function () { + it.skip('should find the array back', async function () { + const res = await customTree.findDocuments({value:{_list:['2e4d4', '4d45e']}}) + // console.log({res}); + }); + it('should find a member of array', async function () { + const res = await customTree.findDocuments({value:{_list:{$in:['2e4d4']}}}) + expect(res[0]).to.deep.equal(doc); }); - it.skip('should replace', async function (){ + + it('should update', function () { + }); + + it('should replace', async function (){ doc2 = {...doc, _updated: doc._updated+1000}; doc2.value._count = 1; @@ -60,21 +70,12 @@ describe('E2E - UseCase nested document', function suite() { const getDocRes = await customTree.getDocument(doc._id); expect(getDocRes).to.deep.equal(doc2); }) - it.skip('should delete the document', async function () { + it('should delete the document', async function () { const delDocRes = await customTree.deleteDocuments({_created: {$gte: doc._updated}}); expect(delDocRes[0]).to.deep.equal(doc2); const getDocRes = await customTree.getDocument(doc._id); expect(getDocRes).to.deep.equal(null); }); }); - describe('Array', async ()=>{ - it.skip('should find the array back', async function () { - // const res = await customTree.findDocuments({value:{_list:['2e4d4', '4d45e']}}) - // console.log(res); - }); - it.skip('should find a member of array', async function () { - const res = await customTree.findDocuments({value:{_list:{$in:['4d45e']}}}) - console.log(res); - }); - }) + }); From b056e77cdef7e2ed89efdeccca9e196fc054bf46 Mon Sep 17 00:00:00 2001 From: Alex Werner Date: Fri, 4 Sep 2020 01:45:34 +0200 Subject: [PATCH 10/10] chore: release v.3.0.2 --- CHANGELOG.md | 8 ++++++++ package-lock.json | 2 +- package.json | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 486aa17..d9a160c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,10 @@ # Changelog +## 3.0.2 + +- fix!(SBTree): .insertDocument to return array on single inclusion +- fix: improve fieldName fetching from query +- fix: finding recursive and $in +- tests: added some query and find tests +- doc: primitives documentation +- doc: improved navigation diff --git a/package-lock.json b/package-lock.json index 141c0eb..a7aca27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "sbtree", - "version": "3.0.1", + "version": "3.0.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 11fb82b..1e93fa2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sbtree", - "version": "3.0.1", + "version": "3.0.2", "description": "Optimised document store using B+ Tree for fields. Adapters support for In-Memory and FileSystem", "main": "index.js", "scripts": {