From 60aadc92f7a35944274ac7a3da36fd712dfaa914 Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 00:27:35 -0500 Subject: [PATCH 01/36] add c++17 std --- binding.gyp | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/binding.gyp b/binding.gyp index 867e7578..d545d039 100644 --- a/binding.gyp +++ b/binding.gyp @@ -2,13 +2,30 @@ "targets": [ { "target_name": "fuzzaldrinplusfast", - "sources": [ "src/fuzzaldrin.cc", "src/scorer.cc", "src/path_scorer.cc", "src/filter.cc", "src/query.cc", "src/matcher.cc" ], - "cflags!": [ "-fno-exceptions" ], - "cflags_cc!": [ "-fno-exceptions" ], + "sources": [ "src/fuzzaldrin.cc", "src/scorer.cc", "src/path_scorer.cc", "src/filter.cc", "src/query.cc", "src/matcher.cc", "src/tree.cc" ], "include_dirs": [ " Date: Sun, 1 Nov 2020 00:29:32 -0500 Subject: [PATCH 02/36] CandidateObject --- src/tree.cc | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/tree.cc diff --git a/src/tree.cc b/src/tree.cc new file mode 100644 index 00000000..13f9506d --- /dev/null +++ b/src/tree.cc @@ -0,0 +1,7 @@ +#include "common.h" + +struct CandidateObject { + CandidateString data; + size_t level = 0; + size_t index = 0; +}; From 8b71d16faa47590dc635f84ca9b37be137ca974f Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 00:29:48 -0500 Subject: [PATCH 03/36] Tree --- src/tree.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/tree.cc b/src/tree.cc index 13f9506d..8f867790 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -5,3 +5,11 @@ struct CandidateObject { size_t level = 0; size_t index = 0; }; + + +struct Tree { + Napi::Object tree; + string dataKey; + string childrenKey; + bool hasChildren = false; +}; From a623b0ba9e6dd1e7f91100cbd9b33ab2fd7b5a59 Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 00:30:55 -0500 Subject: [PATCH 04/36] Tree constructor --- src/tree.cc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/tree.cc b/src/tree.cc index 8f867790..57e9337a 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -12,4 +12,18 @@ struct Tree { string dataKey; string childrenKey; bool hasChildren = false; + + /** Parse a Tree object from JS */ + Tree(const Napi::CallbackInfo& info) { + if (info.Length() != 3 || !info[0].IsObject() || !info[1].IsString() || !info[2].IsString()) { + Napi::TypeError::New(info.Env(), "Invalid arguments").ThrowAsJavaScriptException(); + // default constructor + } + else { + tree = info[0].As(); + dataKey = info[1].As(); + childrenKey = info[2].As(); + } + }; + }; From 64172831d989a8912de47d13af0ba8c075eb33a2 Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 15:05:59 -0600 Subject: [PATCH 05/36] getChildren function --- src/tree.cc | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/tree.cc b/src/tree.cc index 57e9337a..ee5893ae 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -1,3 +1,4 @@ +#include #include "common.h" struct CandidateObject { @@ -13,6 +14,8 @@ struct Tree { string childrenKey; bool hasChildren = false; + vector entriesArray; + /** Parse a Tree object from JS */ Tree(const Napi::CallbackInfo& info) { if (info.Length() != 3 || !info[0].IsObject() || !info[1].IsString() || !info[2].IsString()) { @@ -24,6 +27,26 @@ struct Tree { dataKey = info[1].As(); childrenKey = info[2].As(); } - }; - + } }; + + +/** Get the children of a tree (Napi::Object) */ +std::optional getChildren(Napi::Object & tree, string & childrenKey) { + Napi::Array childrenArray; + + // determine if it has children + bool hasChildren = false; + if (tree.HasOwnProperty(childrenKey)) { + auto childrenRaw = tree.Get(childrenKey); + if (childrenRaw.IsArray()) { + childrenArray = childrenRaw.As(); + if (childrenArray.Length() != 0) { + hasChildren = true; + } + } + } + if (hasChildren) + return childrenArray; + return {}; +} \ No newline at end of file From baf32933783c33e6a37b85ce6f1c13177c6a5b31 Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 15:25:47 -0600 Subject: [PATCH 06/36] remove unused hasChildren --- src/tree.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tree.cc b/src/tree.cc index ee5893ae..94c479da 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -12,7 +12,6 @@ struct Tree { Napi::Object tree; string dataKey; string childrenKey; - bool hasChildren = false; vector entriesArray; From 138b884c9b93d6c98a8763e82a21e372a32f620c Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 15:34:42 -0600 Subject: [PATCH 07/36] rename tree to jsTree --- src/tree.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tree.cc b/src/tree.cc index 94c479da..3336bfe8 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -9,7 +9,7 @@ struct CandidateObject { struct Tree { - Napi::Object tree; + Napi::Object jsTree; string dataKey; string childrenKey; @@ -22,7 +22,7 @@ struct Tree { // default constructor } else { - tree = info[0].As(); + jsTree = info[0].As(); dataKey = info[1].As(); childrenKey = info[2].As(); } @@ -31,13 +31,13 @@ struct Tree { /** Get the children of a tree (Napi::Object) */ -std::optional getChildren(Napi::Object & tree, string & childrenKey) { +std::optional getChildren(Napi::Object & jsTree, string & childrenKey) { Napi::Array childrenArray; // determine if it has children bool hasChildren = false; - if (tree.HasOwnProperty(childrenKey)) { - auto childrenRaw = tree.Get(childrenKey); + if (jsTree.HasOwnProperty(childrenKey)) { + auto childrenRaw = jsTree.Get(childrenKey); if (childrenRaw.IsArray()) { childrenArray = childrenRaw.As(); if (childrenArray.Length() != 0) { From 4e06a13fdb83367b6e32e96177b6f721e050e3a8 Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 15:34:58 -0600 Subject: [PATCH 08/36] get const ref in getChildren --- src/tree.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tree.cc b/src/tree.cc index 3336bfe8..62651eaa 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -31,7 +31,7 @@ struct Tree { /** Get the children of a tree (Napi::Object) */ -std::optional getChildren(Napi::Object & jsTree, string & childrenKey) { +std::optional getChildren(Napi::Object const & jsTree, string const & childrenKey) { Napi::Array childrenArray; // determine if it has children From 892936065a037f3663f2da9124cd40fafd7035d1 Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 16:44:22 -0600 Subject: [PATCH 09/36] move getChildren code --- src/tree.cc | 47 ++++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/src/tree.cc b/src/tree.cc index 62651eaa..2b6a1cbe 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -1,20 +1,38 @@ #include #include "common.h" +/** Get the children of a jsTree (Napi::Object) */ +std::optional getChildren(Napi::Object const& jsTree, string const& childrenKey) { + Napi::Array childrenArray; + + // determine if it has children + bool hasChildren = false; + if (jsTree.HasOwnProperty(childrenKey)) { + auto childrenRaw = jsTree.Get(childrenKey); + if (childrenRaw.IsArray()) { + childrenArray = childrenRaw.As(); + if (childrenArray.Length() != 0) { + hasChildren = true; + } + } + } + if (hasChildren) + return childrenArray; + return {}; +} + + struct CandidateObject { CandidateString data; size_t level = 0; size_t index = 0; }; - struct Tree { Napi::Object jsTree; string dataKey; string childrenKey; - vector entriesArray; - /** Parse a Tree object from JS */ Tree(const Napi::CallbackInfo& info) { if (info.Length() != 3 || !info[0].IsObject() || !info[1].IsString() || !info[2].IsString()) { @@ -27,25 +45,4 @@ struct Tree { childrenKey = info[2].As(); } } -}; - - -/** Get the children of a tree (Napi::Object) */ -std::optional getChildren(Napi::Object const & jsTree, string const & childrenKey) { - Napi::Array childrenArray; - - // determine if it has children - bool hasChildren = false; - if (jsTree.HasOwnProperty(childrenKey)) { - auto childrenRaw = jsTree.Get(childrenKey); - if (childrenRaw.IsArray()) { - childrenArray = childrenRaw.As(); - if (childrenArray.Length() != 0) { - hasChildren = true; - } - } - } - if (hasChildren) - return childrenArray; - return {}; -} \ No newline at end of file +}; \ No newline at end of file From 7797b338d3c7fd2b317ec1fae26ce3d90d472d21 Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 16:51:41 -0600 Subject: [PATCH 10/36] makeEntriesArray --- src/tree.cc | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/tree.cc b/src/tree.cc index 2b6a1cbe..f212642c 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -25,7 +25,7 @@ std::optional getChildren(Napi::Object const& jsTree, string const& struct CandidateObject { CandidateString data; size_t level = 0; - size_t index = 0; + uint32_t index = 0; }; struct Tree { @@ -45,4 +45,25 @@ struct Tree { childrenKey = info[2].As(); } } + + /** an array of the CandidateObject which includes the data and its address (index, level) in the tree for each */ + vector entriesArray; + + /** Recursive function that fills the entriesArray from the given jsTreeArray */ + void makeEntriesArray(Napi::Array jsTreeArray, size_t level) { + for (uint32_t iEntry = 0, len = jsTreeArray.Length(); iEntry < len; iEntry++) { + auto jsTree = jsTreeArray.Get(iEntry).As(); + + // get the current data + CandidateString data = jsTree.Get(dataKey).As(); + entriesArray.push_back(CandidateObject(data, level, iEntry)); + + // add children if any + auto mayChildren = getChildren(jsTreeArray, childrenKey); + if (mayChildren.has_value()) { + // recurse + makeEntriesArray(mayChildren.value(), level+1); + } + } + } }; \ No newline at end of file From e114891677f41cc308aa43716391e65102885bd2 Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 16:55:09 -0600 Subject: [PATCH 11/36] add CandidateObject constructor --- src/tree.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tree.cc b/src/tree.cc index f212642c..ee734860 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -26,6 +26,9 @@ struct CandidateObject { CandidateString data; size_t level = 0; uint32_t index = 0; + + CandidateObject(CandidateString data, size_t level, uint32_t index) + : data{ data }, level{ level }, index{ index } {}; }; struct Tree { From 402268af8ea3a8d8a4fec674e04452f8540ff028 Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 17:04:20 -0600 Subject: [PATCH 12/36] move makeEntriesArray code --- src/tree.cc | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/tree.cc b/src/tree.cc index ee734860..b400a8bf 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -36,18 +36,6 @@ struct Tree { string dataKey; string childrenKey; - /** Parse a Tree object from JS */ - Tree(const Napi::CallbackInfo& info) { - if (info.Length() != 3 || !info[0].IsObject() || !info[1].IsString() || !info[2].IsString()) { - Napi::TypeError::New(info.Env(), "Invalid arguments").ThrowAsJavaScriptException(); - // default constructor - } - else { - jsTree = info[0].As(); - dataKey = info[1].As(); - childrenKey = info[2].As(); - } - } /** an array of the CandidateObject which includes the data and its address (index, level) in the tree for each */ vector entriesArray; @@ -69,4 +57,19 @@ struct Tree { } } } + + /** Parse a Tree object from JS */ + Tree(const Napi::CallbackInfo& info) { + if (info.Length() != 3 || !info[0].IsObject() || !info[1].IsString() || !info[2].IsString()) { + Napi::TypeError::New(info.Env(), "Invalid arguments").ThrowAsJavaScriptException(); + // default constructor + } + else { + jsTreeArray = info[0].As(); + dataKey = info[1].As(); + childrenKey = info[2].As(); + + makeEntriesArray(jsTreeArray, 0); + } + } }; \ No newline at end of file From 59a27cc9f69c04f5a9bf7ed88af954f70669aa0e Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 17:04:52 -0600 Subject: [PATCH 13/36] store jsTreeArray in the Tree --- src/tree.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tree.cc b/src/tree.cc index b400a8bf..a526a7cc 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -32,7 +32,7 @@ struct CandidateObject { }; struct Tree { - Napi::Object jsTree; + Napi::Array jsTreeArray; string dataKey; string childrenKey; @@ -41,7 +41,7 @@ struct Tree { vector entriesArray; /** Recursive function that fills the entriesArray from the given jsTreeArray */ - void makeEntriesArray(Napi::Array jsTreeArray, size_t level) { + void makeEntriesArray(Napi::Array & jsTreeArray, size_t level) { for (uint32_t iEntry = 0, len = jsTreeArray.Length(); iEntry < len; iEntry++) { auto jsTree = jsTreeArray.Get(iEntry).As(); @@ -53,7 +53,7 @@ struct Tree { auto mayChildren = getChildren(jsTreeArray, childrenKey); if (mayChildren.has_value()) { // recurse - makeEntriesArray(mayChildren.value(), level+1); + makeEntriesArray(mayChildren.value(), level + 1); } } } From 01c06523b9d9097cc197f001cfbb2d45eb65d121 Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 17:12:18 -0600 Subject: [PATCH 14/36] make a new Array if the input is a single object --- src/tree.cc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/tree.cc b/src/tree.cc index a526a7cc..7f325bdc 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -65,7 +65,15 @@ struct Tree { // default constructor } else { - jsTreeArray = info[0].As(); + if (info[0].IsArray()) { + jsTreeArray = info[0].As(); + } + else { + // if not Array make a new one and set its only entry + jsTreeArray = Napi::Array::New(info.Env(), 1); + jsTreeArray.Set(0u, info[0]); + } + dataKey = info[1].As(); childrenKey = info[2].As(); From efcbf749ff335612ac268340beed42f39b7e3546 Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 17:46:19 -0600 Subject: [PATCH 15/36] make a specialized function for single object inputs --- src/tree.cc | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/src/tree.cc b/src/tree.cc index 7f325bdc..df62612f 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -1,4 +1,5 @@ #include +#include #include "common.h" /** Get the children of a jsTree (Napi::Object) */ @@ -25,14 +26,14 @@ std::optional getChildren(Napi::Object const& jsTree, string const& struct CandidateObject { CandidateString data; size_t level = 0; - uint32_t index = 0; + int32_t index = -1; CandidateObject(CandidateString data, size_t level, uint32_t index) : data{ data }, level{ level }, index{ index } {}; }; struct Tree { - Napi::Array jsTreeArray; + std::variant jsTreeArrayOrObject; string dataKey; string childrenKey; @@ -44,17 +45,21 @@ struct Tree { void makeEntriesArray(Napi::Array & jsTreeArray, size_t level) { for (uint32_t iEntry = 0, len = jsTreeArray.Length(); iEntry < len; iEntry++) { auto jsTree = jsTreeArray.Get(iEntry).As(); + makeEntriesArray(jsTree, level, iEntry); + } + } - // get the current data - CandidateString data = jsTree.Get(dataKey).As(); - entriesArray.push_back(CandidateObject(data, level, iEntry)); + /** 1st argument is a single object */ + void makeEntriesArray(Napi::Object const &jsTree, size_t const level, uint32_t const iEntry = -1) { + // get the current data + CandidateString data = jsTree.Get(dataKey).As(); + entriesArray.push_back(CandidateObject(data, level, iEntry)); - // add children if any - auto mayChildren = getChildren(jsTreeArray, childrenKey); - if (mayChildren.has_value()) { - // recurse - makeEntriesArray(mayChildren.value(), level + 1); - } + // add children if any + auto mayChildren = getChildren(jsTree, childrenKey); + if (mayChildren.has_value()) { + // recurse + makeEntriesArray(mayChildren.value(), level + 1); } } @@ -65,19 +70,21 @@ struct Tree { // default constructor } else { + + dataKey = info[1].As(); + childrenKey = info[2].As(); + if (info[0].IsArray()) { - jsTreeArray = info[0].As(); + jsTreeArrayOrObject = info[0].As(); + makeEntriesArray(std::get(jsTreeArrayOrObject), 0); } else { - // if not Array make a new one and set its only entry - jsTreeArray = Napi::Array::New(info.Env(), 1); - jsTreeArray.Set(0u, info[0]); + // if the input is a single object skip looping + jsTreeArrayOrObject = info[0].As(); + makeEntriesArray(std::get(jsTreeArrayOrObject), 0); } - dataKey = info[1].As(); - childrenKey = info[2].As(); - makeEntriesArray(jsTreeArray, 0); } } }; \ No newline at end of file From f8c2b059045df1a7ecdb070b1845aee55ed5d6ed Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 17:47:41 -0600 Subject: [PATCH 16/36] const correctness --- src/tree.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tree.cc b/src/tree.cc index df62612f..28c7ad3b 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -3,7 +3,7 @@ #include "common.h" /** Get the children of a jsTree (Napi::Object) */ -std::optional getChildren(Napi::Object const& jsTree, string const& childrenKey) { +std::optional getChildren(Napi::Object const &jsTree, string const &childrenKey) { Napi::Array childrenArray; // determine if it has children @@ -28,7 +28,7 @@ struct CandidateObject { size_t level = 0; int32_t index = -1; - CandidateObject(CandidateString data, size_t level, uint32_t index) + CandidateObject(CandidateString const data, size_t const level, int32_t const index) : data{ data }, level{ level }, index{ index } {}; }; @@ -42,7 +42,7 @@ struct Tree { vector entriesArray; /** Recursive function that fills the entriesArray from the given jsTreeArray */ - void makeEntriesArray(Napi::Array & jsTreeArray, size_t level) { + void makeEntriesArray(Napi::Array const &jsTreeArray, size_t const level) { for (uint32_t iEntry = 0, len = jsTreeArray.Length(); iEntry < len; iEntry++) { auto jsTree = jsTreeArray.Get(iEntry).As(); makeEntriesArray(jsTree, level, iEntry); @@ -50,7 +50,7 @@ struct Tree { } /** 1st argument is a single object */ - void makeEntriesArray(Napi::Object const &jsTree, size_t const level, uint32_t const iEntry = -1) { + void makeEntriesArray(Napi::Object const &jsTree, size_t const level, int32_t const iEntry = -1) { // get the current data CandidateString data = jsTree.Get(dataKey).As(); entriesArray.push_back(CandidateObject(data, level, iEntry)); @@ -64,7 +64,7 @@ struct Tree { } /** Parse a Tree object from JS */ - Tree(const Napi::CallbackInfo& info) { + Tree(Napi::CallbackInfo const &info) { if (info.Length() != 3 || !info[0].IsObject() || !info[1].IsString() || !info[2].IsString()) { Napi::TypeError::New(info.Env(), "Invalid arguments").ThrowAsJavaScriptException(); // default constructor From f5d62b607c0ce14e29570f24519f5cf5a7638404 Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 17:54:03 -0600 Subject: [PATCH 17/36] fix the number of input arguments --- src/tree.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tree.cc b/src/tree.cc index 28c7ad3b..605b5ba6 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -65,7 +65,7 @@ struct Tree { /** Parse a Tree object from JS */ Tree(Napi::CallbackInfo const &info) { - if (info.Length() != 3 || !info[0].IsObject() || !info[1].IsString() || !info[2].IsString()) { + if (info.Length() != 4 || !info[0].IsObject() || !info[1].IsString() || !info[2].IsString()) { Napi::TypeError::New(info.Env(), "Invalid arguments").ThrowAsJavaScriptException(); // default constructor } From 355645900f9e3844eeb45b0bada8f9b63dffe65e Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 18:37:56 -0600 Subject: [PATCH 18/36] Create tree.h --- src/tree.h | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/tree.h diff --git a/src/tree.h b/src/tree.h new file mode 100644 index 00000000..be425168 --- /dev/null +++ b/src/tree.h @@ -0,0 +1,9 @@ +#include + +struct Tree { + std::variant jsTreeArrayOrObject; + string dataKey; + string childrenKey; + + Tree(Napi::Object const _jsTreeArrayOrObject, string const _dataKey, string const _childrenKey) {}; +}; \ No newline at end of file From ad3a4f74a728ba051c82e44f3c02088c41203d93 Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 18:38:20 -0600 Subject: [PATCH 19/36] SetCandidateTrees Moves the parsing from JS to SetCandidateTrees --- src/fuzzaldrin.cc | 14 ++++++++++++++ src/fuzzaldrin.h | 3 +++ src/tree.cc | 31 +++++++++++-------------------- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/fuzzaldrin.cc b/src/fuzzaldrin.cc index fd3001b2..b1c84b39 100644 --- a/src/fuzzaldrin.cc +++ b/src/fuzzaldrin.cc @@ -1,3 +1,4 @@ +#pragma once #include #include "fuzzaldrin.h" @@ -48,6 +49,19 @@ Napi::Value Fuzzaldrin::SetCandidates(const Napi::CallbackInfo& info) { return Napi::Boolean(); } +Napi::Value Fuzzaldrin::SetCandidateTrees(const Napi::CallbackInfo& info) { + if (info.Length() != 4 || !info[0].IsObject() || !info[1].IsString() || !info[2].IsString()) { + Napi::TypeError::New(info.Env(), "Invalid arguments").ThrowAsJavaScriptException(); + return Napi::Boolean(); + } + auto const jsTreeArrayOrObject = info[0].As(); + string const dataKey = info[1].As(); + string const childrenKey = info[2].As(); + auto tree = Tree(jsTreeArrayOrObject, dataKey, childrenKey); + return Napi::Boolean(); +} + + Napi::Number score(const Napi::CallbackInfo& info) { if (info.Length() != 4 || !info[0].IsString() || !info[1].IsString() || !info[2].IsBoolean() || !info[3].IsBoolean()) { diff --git a/src/fuzzaldrin.h b/src/fuzzaldrin.h index e089fc02..84ba9cd5 100644 --- a/src/fuzzaldrin.h +++ b/src/fuzzaldrin.h @@ -3,6 +3,7 @@ #include #include "common.h" +#include "tree.h" // Converted from the example at // https://github.com/nodejs/node-addon-examples/blob/master/6_object_wrap/node-addon-api/ @@ -15,6 +16,8 @@ class Fuzzaldrin : public Napi::ObjectWrap { Napi::Value Filter(const Napi::CallbackInfo& info); Napi::Value SetCandidates(const Napi::CallbackInfo& info); + Napi::Value SetCandidateTrees(const Napi::CallbackInfo& info); + private: vector> candidates_; }; diff --git a/src/tree.cc b/src/tree.cc index 605b5ba6..cb9b18f6 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -1,3 +1,4 @@ +#pragma once #include #include #include "common.h" @@ -44,7 +45,7 @@ struct Tree { /** Recursive function that fills the entriesArray from the given jsTreeArray */ void makeEntriesArray(Napi::Array const &jsTreeArray, size_t const level) { for (uint32_t iEntry = 0, len = jsTreeArray.Length(); iEntry < len; iEntry++) { - auto jsTree = jsTreeArray.Get(iEntry).As(); + auto jsTree = jsTreeArray[iEntry].As(); makeEntriesArray(jsTree, level, iEntry); } } @@ -64,27 +65,17 @@ struct Tree { } /** Parse a Tree object from JS */ - Tree(Napi::CallbackInfo const &info) { - if (info.Length() != 4 || !info[0].IsObject() || !info[1].IsString() || !info[2].IsString()) { - Napi::TypeError::New(info.Env(), "Invalid arguments").ThrowAsJavaScriptException(); - // default constructor + Tree(Napi::Object const _jsTreeArrayOrObject, string const _dataKey, string const _childrenKey) { + dataKey = _dataKey; + childrenKey = _childrenKey; + if (_jsTreeArrayOrObject.IsArray()) { + jsTreeArrayOrObject = _jsTreeArrayOrObject.As(); + makeEntriesArray(std::get(jsTreeArrayOrObject), 0); } else { - - dataKey = info[1].As(); - childrenKey = info[2].As(); - - if (info[0].IsArray()) { - jsTreeArrayOrObject = info[0].As(); - makeEntriesArray(std::get(jsTreeArrayOrObject), 0); - } - else { - // if the input is a single object skip looping - jsTreeArrayOrObject = info[0].As(); - makeEntriesArray(std::get(jsTreeArrayOrObject), 0); - } - - + // if the input is a single object skip looping + jsTreeArrayOrObject = _jsTreeArrayOrObject.As(); + makeEntriesArray(std::get(jsTreeArrayOrObject), 0); } } }; \ No newline at end of file From a1336527f36f9ba4837f23b1b6c17b7206aaba09 Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 18:47:37 -0600 Subject: [PATCH 20/36] make Tree a template Moves parsing of Array vs Object to setCandidateTrees --- src/fuzzaldrin.cc | 9 ++++++++- src/tree.cc | 16 +++++----------- src/tree.h | 5 +++-- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/fuzzaldrin.cc b/src/fuzzaldrin.cc index b1c84b39..a712e86b 100644 --- a/src/fuzzaldrin.cc +++ b/src/fuzzaldrin.cc @@ -57,7 +57,14 @@ Napi::Value Fuzzaldrin::SetCandidateTrees(const Napi::CallbackInfo& info) { auto const jsTreeArrayOrObject = info[0].As(); string const dataKey = info[1].As(); string const childrenKey = info[2].As(); - auto tree = Tree(jsTreeArrayOrObject, dataKey, childrenKey); + + if (jsTreeArrayOrObject.IsArray()) { + auto tree = Tree(jsTreeArrayOrObject.As(), dataKey, childrenKey); + } + else { + auto tree = Tree(jsTreeArrayOrObject.As(), dataKey, childrenKey); + } + return Napi::Boolean(); } diff --git a/src/tree.cc b/src/tree.cc index cb9b18f6..e4412cd1 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -33,8 +33,9 @@ struct CandidateObject { : data{ data }, level{ level }, index{ index } {}; }; +template struct Tree { - std::variant jsTreeArrayOrObject; + T jsTreeArrayOrObject; string dataKey; string childrenKey; @@ -65,17 +66,10 @@ struct Tree { } /** Parse a Tree object from JS */ - Tree(Napi::Object const _jsTreeArrayOrObject, string const _dataKey, string const _childrenKey) { + Tree(T const _jsTreeArrayOrObject, string const _dataKey, string const _childrenKey) { dataKey = _dataKey; childrenKey = _childrenKey; - if (_jsTreeArrayOrObject.IsArray()) { - jsTreeArrayOrObject = _jsTreeArrayOrObject.As(); - makeEntriesArray(std::get(jsTreeArrayOrObject), 0); - } - else { - // if the input is a single object skip looping - jsTreeArrayOrObject = _jsTreeArrayOrObject.As(); - makeEntriesArray(std::get(jsTreeArrayOrObject), 0); - } + jsTreeArrayOrObject = _jsTreeArrayOrObject; + makeEntriesArray(jsTreeArrayOrObject, 0); } }; \ No newline at end of file diff --git a/src/tree.h b/src/tree.h index be425168..a199755f 100644 --- a/src/tree.h +++ b/src/tree.h @@ -1,9 +1,10 @@ #include +template struct Tree { - std::variant jsTreeArrayOrObject; + T jsTreeArrayOrObject; string dataKey; string childrenKey; - Tree(Napi::Object const _jsTreeArrayOrObject, string const _dataKey, string const _childrenKey) {}; + Tree(T const _jsTreeArrayOrObject, string const _dataKey, string const _childrenKey) {}; }; \ No newline at end of file From d9a1a0bb77f9b6fdb601b1fb32adcc19002667b6 Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 20:19:40 -0600 Subject: [PATCH 21/36] add CandidateObject to tree.h --- src/tree.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/tree.h b/src/tree.h index a199755f..8614d7fa 100644 --- a/src/tree.h +++ b/src/tree.h @@ -1,10 +1,19 @@ #include +struct CandidateObject { + CandidateString data; + size_t level = 0; + int32_t index = -1; + + CandidateObject(CandidateString const data, size_t const level, int32_t const index); +}; + template struct Tree { T jsTreeArrayOrObject; string dataKey; string childrenKey; + vector entriesArray; Tree(T const _jsTreeArrayOrObject, string const _dataKey, string const _childrenKey) {}; }; \ No newline at end of file From 5a3fc941d7b397143f657b8b278c75094431d71c Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 20:20:14 -0600 Subject: [PATCH 22/36] FilterTree --- src/fuzzaldrin.cc | 64 +++++++++++++++++++++++++++++++++++++++-------- src/fuzzaldrin.h | 5 ++-- 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/src/fuzzaldrin.cc b/src/fuzzaldrin.cc index a712e86b..aa17e6ff 100644 --- a/src/fuzzaldrin.cc +++ b/src/fuzzaldrin.cc @@ -49,26 +49,68 @@ Napi::Value Fuzzaldrin::SetCandidates(const Napi::CallbackInfo& info) { return Napi::Boolean(); } -Napi::Value Fuzzaldrin::SetCandidateTrees(const Napi::CallbackInfo& info) { - if (info.Length() != 4 || !info[0].IsObject() || !info[1].IsString() || !info[2].IsString()) { +void Fuzzaldrin::SetCandidates(vector const &candidates) { + const size_t N = candidates.size(); // different + const size_t num_chunks = (N < 1000 * kMaxThreads) ? (N / 1000 + 1) : kMaxThreads; + candidates_.clear(); + candidates_.resize(num_chunks); + size_t cur_start = 0; + for (size_t i = 0; i < num_chunks; i++) { + size_t chunk_size = N / num_chunks; + // Distribute remainder among the chunks. + if (i < N % num_chunks) { + chunk_size++; + } + for (size_t j = cur_start; j < cur_start + chunk_size; j++) { + candidates_[i].push_back(candidates[j].data); // different + } + cur_start += chunk_size; + } +} + +Napi::Array Fuzzaldrin::FilterTree(const Napi::CallbackInfo& info) { + + // parse arguments + if (info.Length() != 5 || !info[0].IsArray() + || !info[1].IsString() || !info[2].IsString() || !info[3].IsString() + || !info[4].IsNumber() || !info[5].IsBoolean() || !info[6].IsBoolean() + ) { Napi::TypeError::New(info.Env(), "Invalid arguments").ThrowAsJavaScriptException(); - return Napi::Boolean(); + return Napi::Array::New(info.Env()); } - auto const jsTreeArrayOrObject = info[0].As(); + auto const jsTreeArray = info[0].As(); string const dataKey = info[1].As(); string const childrenKey = info[2].As(); - if (jsTreeArrayOrObject.IsArray()) { - auto tree = Tree(jsTreeArrayOrObject.As(), dataKey, childrenKey); - } - else { - auto tree = Tree(jsTreeArrayOrObject.As(), dataKey, childrenKey); - } + std::string query = info[0].As(); + size_t maxResults = info[1].As().Uint32Value(); + bool usePathScoring = info[2].As(); + bool useExtensionBonus = info[3].As(); - return Napi::Boolean(); + // create Tree and set candidates + auto tree = Tree(jsTreeArray, dataKey, childrenKey); + Fuzzaldrin::SetCandidates(tree.entriesArray); + + // create options + Options options(query, maxResults, usePathScoring, useExtensionBonus); + const auto matches = filter(candidates_, query, options); + + Napi::Array filteredCandidateObjects = Napi::Array::New(info.Env()); // array of objects + for (uint32_t i = 0, len = matches.size(); i < len; i++) { + auto entry = tree.entriesArray[matches[i]]; + auto obj = Napi::Object::New(info.Env()); + + obj.Set("data", entry.data); + obj.Set("index", entry.index); + obj.Set("level", entry.level); + + filteredCandidateObjects[i] = obj; + } + return filteredCandidateObjects; } + Napi::Number score(const Napi::CallbackInfo& info) { if (info.Length() != 4 || !info[0].IsString() || !info[1].IsString() || !info[2].IsBoolean() || !info[3].IsBoolean()) { diff --git a/src/fuzzaldrin.h b/src/fuzzaldrin.h index 84ba9cd5..2e2e00bd 100644 --- a/src/fuzzaldrin.h +++ b/src/fuzzaldrin.h @@ -15,11 +15,12 @@ class Fuzzaldrin : public Napi::ObjectWrap { Napi::Value Filter(const Napi::CallbackInfo& info); Napi::Value SetCandidates(const Napi::CallbackInfo& info); + void SetCandidates(vector const& candidates); - Napi::Value SetCandidateTrees(const Napi::CallbackInfo& info); + Napi::Array FilterTree(const Napi::CallbackInfo& info); private: vector> candidates_; }; -#endif // FUZZALDRIN_H +#endif // FUZZALDRIN_H \ No newline at end of file From 6c915698a8850c1db0165092118d66d987f52cb7 Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 20:21:27 -0600 Subject: [PATCH 23/36] remove excess pragma once --- src/fuzzaldrin.cc | 1 - src/tree.cc | 1 - 2 files changed, 2 deletions(-) diff --git a/src/fuzzaldrin.cc b/src/fuzzaldrin.cc index aa17e6ff..765c6ac7 100644 --- a/src/fuzzaldrin.cc +++ b/src/fuzzaldrin.cc @@ -1,4 +1,3 @@ -#pragma once #include #include "fuzzaldrin.h" diff --git a/src/tree.cc b/src/tree.cc index e4412cd1..b97f5104 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -1,4 +1,3 @@ -#pragma once #include #include #include "common.h" From 7220f7b2a81e13ee25deb8022b8d6744f62b9aa5 Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 20:23:51 -0600 Subject: [PATCH 24/36] convert to string rather using As --- src/tree.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tree.cc b/src/tree.cc index b97f5104..a754f550 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -53,7 +53,7 @@ struct Tree { /** 1st argument is a single object */ void makeEntriesArray(Napi::Object const &jsTree, size_t const level, int32_t const iEntry = -1) { // get the current data - CandidateString data = jsTree.Get(dataKey).As(); + CandidateString data = jsTree.Get(dataKey).ToString().Utf8Value(); entriesArray.push_back(CandidateObject(data, level, iEntry)); // add children if any From 4edee550d762fec41898cc652f22243c7a60e52c Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 21:29:25 -0600 Subject: [PATCH 25/36] fix argument order --- src/fuzzaldrin.cc | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/fuzzaldrin.cc b/src/fuzzaldrin.cc index 765c6ac7..e48a0732 100644 --- a/src/fuzzaldrin.cc +++ b/src/fuzzaldrin.cc @@ -67,24 +67,27 @@ void Fuzzaldrin::SetCandidates(vector const &candidates) { } } +/** (tree: Array, query: string, dataKey: string, childrenKey: string, options: Options) */ Napi::Array Fuzzaldrin::FilterTree(const Napi::CallbackInfo& info) { // parse arguments - if (info.Length() != 5 || !info[0].IsArray() - || !info[1].IsString() || !info[2].IsString() || !info[3].IsString() + if (info.Length() != 7 + || !info[0].IsArray() + || !info[1].IsString() || !info[2].IsString() || !info[3].IsString() || !info[4].IsNumber() || !info[5].IsBoolean() || !info[6].IsBoolean() ) { Napi::TypeError::New(info.Env(), "Invalid arguments").ThrowAsJavaScriptException(); return Napi::Array::New(info.Env()); } auto const jsTreeArray = info[0].As(); - string const dataKey = info[1].As(); - string const childrenKey = info[2].As(); + std::string query = info[1].As(); - std::string query = info[0].As(); - size_t maxResults = info[1].As().Uint32Value(); - bool usePathScoring = info[2].As(); - bool useExtensionBonus = info[3].As(); + string const dataKey = info[2].As(); + string const childrenKey = info[3].As(); + + size_t maxResults = info[4].As().Uint32Value(); + bool usePathScoring = info[5].As(); + bool useExtensionBonus = info[6].As(); // create Tree and set candidates auto tree = Tree(jsTreeArray, dataKey, childrenKey); From 01b6731d6854022ba601c98ec0d5366235014fd4 Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 21:29:38 -0600 Subject: [PATCH 26/36] add filterTree to JavaScript --- fuzzaldrin.js | 14 ++++++++++++++ src/fuzzaldrin.cc | 3 ++- src/fuzzaldrin.h | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/fuzzaldrin.js b/fuzzaldrin.js index eaa5b0ae..c7d98169 100644 --- a/fuzzaldrin.js +++ b/fuzzaldrin.js @@ -34,6 +34,12 @@ class FuzzaldrinPlusFast { Boolean(options.usePathScoring), Boolean(options.useExtensionBonus)) return res.map((ind) => this.candidates[ind]) } + + filterTree(candidatesTrees, query, dataKey = "data", childrenKey = "children", options = {}) { + options = parseOptions(options) + return this.obj.filterTree(candidatesTrees, query, dataKey, childrenKey, options.maxResults, + Boolean(options.usePathScoring), Boolean(options.useExtensionBonus)) + } } export const New = () => new FuzzaldrinPlusFast() @@ -46,6 +52,14 @@ export function filter (candidates, query, options = {}) { return obj.filter(query, options) } + +export function filterTree(candidatesTrees, query, dataKey = "data", childrenKey = "children", options = {}) { + if (!candidatesTrees || !query) + return [] + const obj = new FuzzaldrinPlusFast() + return obj.filterTree(candidatesTrees, query, dataKey, childrenKey, options) +} + export function score (candidate, query, options = {}) { if (!candidate || !query) return 0 diff --git a/src/fuzzaldrin.cc b/src/fuzzaldrin.cc index e48a0732..072184e1 100644 --- a/src/fuzzaldrin.cc +++ b/src/fuzzaldrin.cc @@ -68,7 +68,7 @@ void Fuzzaldrin::SetCandidates(vector const &candidates) { } /** (tree: Array, query: string, dataKey: string, childrenKey: string, options: Options) */ -Napi::Array Fuzzaldrin::FilterTree(const Napi::CallbackInfo& info) { +Napi::Value Fuzzaldrin::FilterTree(const Napi::CallbackInfo& info) { // parse arguments if (info.Length() != 7 @@ -174,6 +174,7 @@ Napi::Object Fuzzaldrin::Init(Napi::Env env, Napi::Object exports) { Napi::Function func = DefineClass(env, "Fuzzaldrin", { InstanceMethod("filter", &Fuzzaldrin::Filter), + InstanceMethod("filterTree", &Fuzzaldrin::FilterTree), InstanceMethod("setCandidates", &Fuzzaldrin::SetCandidates) }); diff --git a/src/fuzzaldrin.h b/src/fuzzaldrin.h index 2e2e00bd..020e2a15 100644 --- a/src/fuzzaldrin.h +++ b/src/fuzzaldrin.h @@ -17,7 +17,7 @@ class Fuzzaldrin : public Napi::ObjectWrap { Napi::Value SetCandidates(const Napi::CallbackInfo& info); void SetCandidates(vector const& candidates); - Napi::Array FilterTree(const Napi::CallbackInfo& info); + Napi::Value FilterTree(const Napi::CallbackInfo& info); private: vector> candidates_; From 5b46407a9386983cbeff19c5b905b3155d34c05e Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 21:41:25 -0600 Subject: [PATCH 27/36] add a basic example to spec --- spec/filter-tree.js | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 spec/filter-tree.js diff --git a/spec/filter-tree.js b/spec/filter-tree.js new file mode 100644 index 00000000..178e9919 --- /dev/null +++ b/spec/filter-tree.js @@ -0,0 +1,6 @@ +const {filterTree} = require('../fuzzaldrin-dist') + +candidates = [ + {data: "helloworld", children: []}, +] +console.log(filterTree(candidates, "hello", "data", "children")) From 6486fac3a1dddae2745e75eea722793a0e0c4873 Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 21:54:13 -0600 Subject: [PATCH 28/36] add types and comments for FilterArray --- src/fuzzaldrin.cc | 10 ++++++---- src/tree.cc | 4 ++-- types/fuzzaldrin-plus-fast.d.ts | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/fuzzaldrin.cc b/src/fuzzaldrin.cc index 072184e1..f174cbc0 100644 --- a/src/fuzzaldrin.cc +++ b/src/fuzzaldrin.cc @@ -71,7 +71,7 @@ void Fuzzaldrin::SetCandidates(vector const &candidates) { Napi::Value Fuzzaldrin::FilterTree(const Napi::CallbackInfo& info) { // parse arguments - if (info.Length() != 7 + if (info.Length() != 7 || !info[0].IsArray() || !info[1].IsString() || !info[2].IsString() || !info[3].IsString() || !info[4].IsNumber() || !info[5].IsBoolean() || !info[6].IsBoolean() @@ -97,11 +97,13 @@ Napi::Value Fuzzaldrin::FilterTree(const Napi::CallbackInfo& info) { Options options(query, maxResults, usePathScoring, useExtensionBonus); const auto matches = filter(candidates_, query, options); - Napi::Array filteredCandidateObjects = Napi::Array::New(info.Env()); // array of objects + // filter + Napi::Array filteredCandidateObjects = Napi::Array::New(info.Env()); // array of candidate objects (with their address in index and level) for (uint32_t i = 0, len = matches.size(); i < len; i++) { - auto entry = tree.entriesArray[matches[i]]; - auto obj = Napi::Object::New(info.Env()); + auto entry = tree.entriesArray[matches[i]]; // + // create {data, index, level} + auto obj = Napi::Object::New(info.Env()); obj.Set("data", entry.data); obj.Set("index", entry.index); obj.Set("level", entry.level); diff --git a/src/tree.cc b/src/tree.cc index a754f550..c9817635 100644 --- a/src/tree.cc +++ b/src/tree.cc @@ -64,11 +64,11 @@ struct Tree { } } - /** Parse a Tree object from JS */ + /** create a Tree object and make an entries array */ Tree(T const _jsTreeArrayOrObject, string const _dataKey, string const _childrenKey) { dataKey = _dataKey; childrenKey = _childrenKey; jsTreeArrayOrObject = _jsTreeArrayOrObject; makeEntriesArray(jsTreeArrayOrObject, 0); } -}; \ No newline at end of file +}; diff --git a/types/fuzzaldrin-plus-fast.d.ts b/types/fuzzaldrin-plus-fast.d.ts index b7abb6fc..9956e3d2 100644 --- a/types/fuzzaldrin-plus-fast.d.ts +++ b/types/fuzzaldrin-plus-fast.d.ts @@ -44,6 +44,24 @@ export function filter( options?: IFilterOptions ): T[] + +/** Sort and filter the given Tree candidates by matching them against the given query. +* A tree object is an object which each entry stores the data in its dataKey and it has (may have) some children (with the similar structure) in its childrenKey +* @param candidates An array of tree objects. +* @param query A string query to match each candidate against. +* @param dataKey the key of the object (and its children) which holds the data +* @param childrenKey the key of the object (and its children) which hold the children +* @param options options +* @return returns an array of candidates sorted by best match against the query. + */ +export function filterTree( + candidates: T[], + query: string, + dataKey: string, + childrenKey: string, + options?: IFilterOptions +): T[] + /** Score the given string against the given query. * @param str The string the score. * @param query The query to score the string against. From fb2fb8cd9322352682e3007a9c27e967f0310dd3 Mon Sep 17 00:00:00 2001 From: aminya Date: Sun, 1 Nov 2020 23:33:24 -0600 Subject: [PATCH 29/36] move tree code to tree.h --- binding.gyp | 2 +- src/common.h | 1 + src/fuzzaldrin.h | 1 + src/tree.cc | 74 ------------------------------------------------ src/tree.h | 62 ++++++++++++++++++++++++++++++++++++++-- 5 files changed, 62 insertions(+), 78 deletions(-) delete mode 100644 src/tree.cc diff --git a/binding.gyp b/binding.gyp index d545d039..9aa34722 100644 --- a/binding.gyp +++ b/binding.gyp @@ -2,7 +2,7 @@ "targets": [ { "target_name": "fuzzaldrinplusfast", - "sources": [ "src/fuzzaldrin.cc", "src/scorer.cc", "src/path_scorer.cc", "src/filter.cc", "src/query.cc", "src/matcher.cc", "src/tree.cc" ], + "sources": [ "src/fuzzaldrin.cc", "src/scorer.cc", "src/path_scorer.cc", "src/filter.cc", "src/query.cc", "src/matcher.cc", "src/tree.h" ], "include_dirs": [ " #include #include diff --git a/src/fuzzaldrin.h b/src/fuzzaldrin.h index 020e2a15..99c3716e 100644 --- a/src/fuzzaldrin.h +++ b/src/fuzzaldrin.h @@ -1,3 +1,4 @@ +#pragma once #ifndef FUZZALDRIN_H #define FUZZALDRIN_H diff --git a/src/tree.cc b/src/tree.cc deleted file mode 100644 index c9817635..00000000 --- a/src/tree.cc +++ /dev/null @@ -1,74 +0,0 @@ -#include -#include -#include "common.h" - -/** Get the children of a jsTree (Napi::Object) */ -std::optional getChildren(Napi::Object const &jsTree, string const &childrenKey) { - Napi::Array childrenArray; - - // determine if it has children - bool hasChildren = false; - if (jsTree.HasOwnProperty(childrenKey)) { - auto childrenRaw = jsTree.Get(childrenKey); - if (childrenRaw.IsArray()) { - childrenArray = childrenRaw.As(); - if (childrenArray.Length() != 0) { - hasChildren = true; - } - } - } - if (hasChildren) - return childrenArray; - return {}; -} - - -struct CandidateObject { - CandidateString data; - size_t level = 0; - int32_t index = -1; - - CandidateObject(CandidateString const data, size_t const level, int32_t const index) - : data{ data }, level{ level }, index{ index } {}; -}; - -template -struct Tree { - T jsTreeArrayOrObject; - string dataKey; - string childrenKey; - - - /** an array of the CandidateObject which includes the data and its address (index, level) in the tree for each */ - vector entriesArray; - - /** Recursive function that fills the entriesArray from the given jsTreeArray */ - void makeEntriesArray(Napi::Array const &jsTreeArray, size_t const level) { - for (uint32_t iEntry = 0, len = jsTreeArray.Length(); iEntry < len; iEntry++) { - auto jsTree = jsTreeArray[iEntry].As(); - makeEntriesArray(jsTree, level, iEntry); - } - } - - /** 1st argument is a single object */ - void makeEntriesArray(Napi::Object const &jsTree, size_t const level, int32_t const iEntry = -1) { - // get the current data - CandidateString data = jsTree.Get(dataKey).ToString().Utf8Value(); - entriesArray.push_back(CandidateObject(data, level, iEntry)); - - // add children if any - auto mayChildren = getChildren(jsTree, childrenKey); - if (mayChildren.has_value()) { - // recurse - makeEntriesArray(mayChildren.value(), level + 1); - } - } - - /** create a Tree object and make an entries array */ - Tree(T const _jsTreeArrayOrObject, string const _dataKey, string const _childrenKey) { - dataKey = _dataKey; - childrenKey = _childrenKey; - jsTreeArrayOrObject = _jsTreeArrayOrObject; - makeEntriesArray(jsTreeArrayOrObject, 0); - } -}; diff --git a/src/tree.h b/src/tree.h index 8614d7fa..1a24967b 100644 --- a/src/tree.h +++ b/src/tree.h @@ -1,11 +1,36 @@ +#pragma once +#include #include +#include "common.h" + +/** Get the children of a jsTree (Napi::Object) */ +std::optional getChildren(Napi::Object const& jsTree, string const& childrenKey) { + Napi::Array childrenArray; + + // determine if it has children + bool hasChildren = false; + if (jsTree.HasOwnProperty(childrenKey)) { + auto childrenRaw = jsTree.Get(childrenKey); + if (childrenRaw.IsArray()) { + childrenArray = childrenRaw.As(); + if (childrenArray.Length() != 0) { + hasChildren = true; + } + } + } + if (hasChildren) + return childrenArray; + return {}; +} + struct CandidateObject { CandidateString data; size_t level = 0; int32_t index = -1; - CandidateObject(CandidateString const data, size_t const level, int32_t const index); + CandidateObject(CandidateString const data, size_t const level, int32_t const index) + : data{ data }, level{ level }, index{ index } {}; }; template @@ -13,7 +38,38 @@ struct Tree { T jsTreeArrayOrObject; string dataKey; string childrenKey; + + + /** an array of the CandidateObject which includes the data and its address (index, level) in the tree for each */ vector entriesArray; - Tree(T const _jsTreeArrayOrObject, string const _dataKey, string const _childrenKey) {}; -}; \ No newline at end of file + /** Recursive function that fills the entriesArray from the given jsTreeArray */ + void makeEntriesArray(Napi::Array const& jsTreeArray, size_t const level) { + for (uint32_t iEntry = 0, len = jsTreeArray.Length(); iEntry < len; iEntry++) { + auto jsTree = jsTreeArray[iEntry].As(); + makeEntriesArray(jsTree, level, iEntry); + } + } + + /** 1st argument is a single object */ + void makeEntriesArray(Napi::Object const& jsTree, size_t const level, int32_t const iEntry = -1) { + // get the current data + CandidateString data = jsTree.Get(dataKey).ToString().Utf8Value(); + entriesArray.push_back(CandidateObject(data, level, iEntry)); + + // add children if any + auto mayChildren = getChildren(jsTree, childrenKey); + if (mayChildren.has_value()) { + // recurse + makeEntriesArray(mayChildren.value(), level + 1); + } + } + + /** create a Tree object and make an entries array */ + Tree(T const _jsTreeArrayOrObject, string const _dataKey, string const _childrenKey) { + dataKey = _dataKey; + childrenKey = _childrenKey; + jsTreeArrayOrObject = _jsTreeArrayOrObject; + makeEntriesArray(jsTreeArrayOrObject, 0); + } +}; From 88eb1fd065c739d6b9ce735c4735b5729f17c189 Mon Sep 17 00:00:00 2001 From: aminya Date: Mon, 2 Nov 2020 00:03:37 -0600 Subject: [PATCH 30/36] install deep-equal for testing --- package-lock.json | 186 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 187 insertions(+) diff --git a/package-lock.json b/package-lock.json index 7e8b3a0a..3fc52fec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5068,6 +5068,36 @@ "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", "dev": true }, + "deep-equal": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.4.tgz", + "integrity": "sha512-BUfaXrVoCfgkOQY/b09QdO9L3XNoF2XH0A3aY9IQwQL/ZjLOe8FQgCNVl1wiolhsFo8kFdO9zdPViCPbmaJA5w==", + "dev": true, + "requires": { + "es-abstract": "^1.18.0-next.1", + "es-get-iterator": "^1.1.0", + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.2", + "is-regex": "^1.1.1", + "isarray": "^2.0.5", + "object-is": "^1.1.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "regexp.prototype.flags": "^1.3.0", + "side-channel": "^1.0.3", + "which-boxed-primitive": "^1.0.1", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.2" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } + } + }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -5458,6 +5488,48 @@ "string.prototype.trimstart": "^1.0.1" } }, + "es-get-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", + "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", + "dev": true, + "requires": { + "es-abstract": "^1.17.4", + "has-symbols": "^1.0.1", + "is-arguments": "^1.0.4", + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-string": "^1.0.5", + "isarray": "^2.0.5" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } + } + }, "es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -6670,6 +6742,18 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "is-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.0.tgz", + "integrity": "sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g==", + "dev": true + }, + "is-boolean-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.1.tgz", + "integrity": "sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==", + "dev": true + }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -6829,6 +6913,12 @@ "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=", "dev": true }, + "is-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", + "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", + "dev": true + }, "is-nan": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.0.tgz", @@ -6856,6 +6946,12 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "is-number-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", + "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", + "dev": true + }, "is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", @@ -6906,12 +7002,24 @@ "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, + "is-set": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", + "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", + "dev": true + }, "is-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", "dev": true }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, "is-svg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", @@ -6975,6 +7083,18 @@ "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", "dev": true }, + "is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "dev": true + }, + "is-weakset": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.1.tgz", + "integrity": "sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==", + "dev": true + }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -10594,6 +10714,37 @@ "safe-regex": "^1.1.0" } }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, "regexpu-core": { "version": "4.7.1", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", @@ -11028,6 +11179,16 @@ "shelljs": "^0.8.4" } }, + "side-channel": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.3.tgz", + "integrity": "sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g==", + "dev": true, + "requires": { + "es-abstract": "^1.18.0-next.0", + "object-inspect": "^1.8.0" + } + }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -12433,6 +12594,31 @@ "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz", + "integrity": "sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ==", + "dev": true, + "requires": { + "is-bigint": "^1.0.0", + "is-boolean-object": "^1.0.0", + "is-number-object": "^1.0.3", + "is-string": "^1.0.4", + "is-symbol": "^1.0.2" + } + }, + "which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "dev": true, + "requires": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + } + }, "which-typed-array": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.2.tgz", diff --git a/package.json b/package.json index 1808ce34..8b8b3383 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "devDependencies": { "babel-preset-atomic": "^3.0.0", "cross-env": "^7.0.2", + "deep-equal": "^2.0.4", "fuzzaldrin-plus": "^0.6.0", "growl": ">=1.10.5", "jasmine-node": "^3.0.0", From 0a22179164419a3a8affef3398b995ce951771fe Mon Sep 17 00:00:00 2001 From: aminya Date: Mon, 2 Nov 2020 00:12:23 -0600 Subject: [PATCH 31/36] add test for array of children-less objects --- spec/filter-tree-spec.js | 29 +++++++++++++++++++++++++++++ spec/filter-tree.js | 6 ------ 2 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 spec/filter-tree-spec.js delete mode 100644 spec/filter-tree.js diff --git a/spec/filter-tree-spec.js b/spec/filter-tree-spec.js new file mode 100644 index 00000000..a167f790 --- /dev/null +++ b/spec/filter-tree-spec.js @@ -0,0 +1,29 @@ +const {filterTree} = require('../fuzzaldrin-dist') +const DeepEqual = require('deep-equal'); + +describe("filterTree", () => { + it("can search in an array of children-less objects", () =>{ + const candidates = [ + {data: "helloworld"}, + {data: "bye"}, + {data: "hello"}, + ] + expect(DeepEqual( + filterTree(candidates, "hello", "data", "children"), + [ + { data: 'hello', index: 2, level: 0 }, + { data: 'helloworld', index: 0, level: 0 }, + ] + )).toBe(true) + + // test default values + expect(DeepEqual( + filterTree(candidates, "hello"), + [ + { data: 'hello', index: 2, level: 0 }, + { data: 'helloworld', index: 0, level: 0 }, + ] + )).toBe(true) + }) + +}) diff --git a/spec/filter-tree.js b/spec/filter-tree.js deleted file mode 100644 index 178e9919..00000000 --- a/spec/filter-tree.js +++ /dev/null @@ -1,6 +0,0 @@ -const {filterTree} = require('../fuzzaldrin-dist') - -candidates = [ - {data: "helloworld", children: []}, -] -console.log(filterTree(candidates, "hello", "data", "children")) From c84556e9e399c1084eb87d5920fce136814d812a Mon Sep 17 00:00:00 2001 From: aminya Date: Mon, 2 Nov 2020 00:17:08 -0600 Subject: [PATCH 32/36] add test for array of tree objects --- spec/filter-tree-spec.js | 48 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/spec/filter-tree-spec.js b/spec/filter-tree-spec.js index a167f790..f071863b 100644 --- a/spec/filter-tree-spec.js +++ b/spec/filter-tree-spec.js @@ -2,6 +2,54 @@ const {filterTree} = require('../fuzzaldrin-dist') const DeepEqual = require('deep-equal'); describe("filterTree", () => { + it("can fuzzy search in an array tree objects", () =>{ + candidates = [ + {data: "bye1", children: [{data: "hello"}]}, + {data: "Bye2", children: [{data: "_bye4"}, {data: "hel"}]}, + {data: "eye"}, + ] + + expect(DeepEqual( + filterTree(candidates, "hello", "data", "children"), + [ { data: 'hello', index: 0, level: 1 } ] + )).toBe(true) + + expect(DeepEqual( + filterTree(candidates, "hel", "data", "children"), + [ + { data: 'hel', index: 1, level: 1 }, + { data: 'hello', index: 0, level: 1 } + ] + )).toBe(true) + + expect(DeepEqual( + filterTree(candidates, "he", "data", "children"), + [ + { data: 'hel', index: 1, level: 1 }, + { data: 'hello', index: 0, level: 1 } + ] + )).toBe(true) + + expect(DeepEqual( + filterTree(candidates, "bye", "data", "children"), + [ + { data: 'bye1', index: 0, level: 0 }, + { data: '_bye4', index: 0, level: 1 }, + { data: 'Bye2', index: 1, level: 0 } + ] + )).toBe(true) + + expect(DeepEqual( + filterTree(candidates, "ye", "data", "children"), + [ + { data: 'eye', index: 2, level: 0 }, + { data: 'bye1', index: 0, level: 0 }, + { data: 'Bye2', index: 1, level: 0 }, + { data: '_bye4', index: 0, level: 1 } + ] + )).toBe(true) + }) + it("can search in an array of children-less objects", () =>{ const candidates = [ {data: "helloworld"}, From 9299b0851d8089319157c0880e2223f28e5fc7f2 Mon Sep 17 00:00:00 2001 From: aminya Date: Mon, 2 Nov 2020 00:17:13 -0600 Subject: [PATCH 33/36] add maxResults test --- spec/filter-tree-spec.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/spec/filter-tree-spec.js b/spec/filter-tree-spec.js index f071863b..26977dc3 100644 --- a/spec/filter-tree-spec.js +++ b/spec/filter-tree-spec.js @@ -48,6 +48,26 @@ describe("filterTree", () => { { data: '_bye4', index: 0, level: 1 } ] )).toBe(true) + + // test maxResults + expect(DeepEqual( + filterTree(candidates, "bye", "data", "children", {maxResults: 2}), + [ + { data: 'bye1', index: 0, level: 0 }, + { data: '_bye4', index: 0, level: 1 }, + ] + )).toBe(true) + + expect(DeepEqual( + filterTree(candidates, "ye", "data", "children", {maxResults: 3}), + [ + { data: 'eye', index: 2, level: 0 }, + { data: 'bye1', index: 0, level: 0 }, + { data: 'Bye2', index: 1, level: 0 }, + ] + )).toBe(true) + + }) it("can search in an array of children-less objects", () =>{ From 86af1ae139c42eed315430d36dd6bc7298c6562a Mon Sep 17 00:00:00 2001 From: aminya Date: Mon, 2 Nov 2020 00:24:24 -0600 Subject: [PATCH 34/36] bump MacOS version --- binding.gyp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/binding.gyp b/binding.gyp index 9aa34722..d5fc5435 100644 --- a/binding.gyp +++ b/binding.gyp @@ -15,7 +15,7 @@ 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES', "CLANG_CXX_LIBRARY": "libc++", "CLANG_CXX_LANGUAGE_STANDARD":"c++17", - 'MACOSX_DEPLOYMENT_TARGET': '10.14' + 'MACOSX_DEPLOYMENT_TARGET': '10.15' } }], ['OS=="win"', { From 0f8219758b98fe42409cec94f4a80b32a3e32263 Mon Sep 17 00:00:00 2001 From: aminya Date: Mon, 2 Nov 2020 00:41:21 -0600 Subject: [PATCH 35/36] Add filterTree to readme --- README.md | 41 ++++++++++++++++++++++++++++++--- types/fuzzaldrin-plus-fast.d.ts | 7 +++--- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3685c059..aba6e7d3 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,7 @@ Fast fuzzy-search - the native replacement for `fuzzaldrin-plus` * Fuzzaldrin plus is an awesome library that provides fuzzy-search that is more targeted towards filenames. * Fuzzaldrin-plus-fast is a rewrite of the library in native C++ to make it fast. The goal is to make it a few hundred millisecond filter times for a dataset with 1M entries. This performance is helpful in Atom's fuzzy finder to open files from large projects such as Chrome/Mozilla. -This project potentially solves the following Atom fuzzy-finder issues if used. -https://github.com/atom/fuzzy-finder/issues/271 -https://github.com/atom/fuzzy-finder/issues/88 +Fuzzaldrin-plus-fast also provides an additional `filterTree` function which allows to fuzzy filter text in nested tree-like objects. # How performance is improved? Fuzzaldrin-plus-fast achieves 10x-20x performance improvement over Fuzzaldrin plus for chromium project with 300K files. This high performance is achieved using the following techniques. @@ -16,6 +14,9 @@ Fuzzaldrin-plus-fast achieves 10x-20x performance improvement over Fuzzaldrin pl * Use multiple threads to parallelize computation to achieve another 4x performance benefit. Currently, up to 8 threads are used if there are more than 10K candidates to filter. * Some miscellaneous improvements provide additional benefit. +This project potentially solves the following Atom fuzzy-finder issues if used. +https://github.com/atom/fuzzy-finder/issues/271 and https://github.com/atom/fuzzy-finder/issues/88 + # Is the API the same? API is backward compatible with Fuzzaldrin and Fuzzaldrin-plus. Additional functions are provided to achieve better performance that could suit your needs @@ -42,6 +43,7 @@ Sort and filter the given candidates by matching them against the given query. * `candidates` - An array of strings or objects. * `query` - A string query to match each candidate against. +* `options` options. You should provide a `key` in the options if an array of objects are passed. Returns an array of candidates sorted by best match against the query. @@ -61,6 +63,39 @@ candidates = [ results = filter(candidates, 'me', {key: 'name'}) // [{name: 'Me', id: 2}, {name: 'Maybe', id: 3}] ``` +### filterTree(candidates, query, dataKey, childrenKey, options = {}) + +Sort and filter the given Tree candidates by matching them against the given query. + +A **tree object** is an object in which each entry stores the data in its dataKey and it has (may have) some children (with a similar structure) in its childrenKey. See the following example. + +* `candidates` An array of tree objects. +* `query` A string query to match each candidate against. +* `dataKey` the key of the object (and its children) which holds the data +* `childrenKey` the key of the object (and its children) which hold the children +* `options` options +* `returns` An array of candidate objects in form of `{data, index, level}` sorted by best match against the query. Each objects has the address of the object in the tree using `index` and `level`. + +```js +const { filterTree } = require('fuzzaldrin-plus-fast') + +candidates = [ + {data: "bye1", children: [{data: "hello"}]}, + {data: "Bye2", children: [{data: "_bye4"}, {data: "hel"}]}, + {data: "eye"}, +] + +filterTree(candidates, "he", "data", "children") // [ { data: 'hel', index: 1, level: 1 }, { data: 'hello', index: 0, level: 1 }] + +// With an array of objects (similar to providing `key` to `filter` function) +const candidates = [ + {data: "helloworld"}, + {data: "bye"}, + {data: "hello"}, +] +results = filter(candidates, 'hello', {key: 'name'}) // [ { data: 'hello', index: 2, level: 0 }, { data: 'helloworld', index: 0, level: 0 } ] +``` + ### score(string, query, options = {}) Score the given string against the given query. diff --git a/types/fuzzaldrin-plus-fast.d.ts b/types/fuzzaldrin-plus-fast.d.ts index 9956e3d2..e7fc63da 100644 --- a/types/fuzzaldrin-plus-fast.d.ts +++ b/types/fuzzaldrin-plus-fast.d.ts @@ -36,23 +36,24 @@ export type IFilterOptions = IOptions & { /** Sort and filter the given candidates by matching them against the given query. * @param candidates An array of strings or objects. * @param query A string query to match each candidate against. +* @param options options * @return returns an array of candidates sorted by best match against the query. */ export function filter( - data: T[], + candidates: T[], query: string, options?: IFilterOptions ): T[] /** Sort and filter the given Tree candidates by matching them against the given query. -* A tree object is an object which each entry stores the data in its dataKey and it has (may have) some children (with the similar structure) in its childrenKey +* A tree object is an object in which each entry stores the data in its dataKey and it has (may have) some children (with a similar structure) in its childrenKey * @param candidates An array of tree objects. * @param query A string query to match each candidate against. * @param dataKey the key of the object (and its children) which holds the data * @param childrenKey the key of the object (and its children) which hold the children * @param options options -* @return returns an array of candidates sorted by best match against the query. +* @return An array of candidate objects in form of `{data, index, level}` sorted by best match against the query. Each objects has the address of the object in the tree using `index` and `level`. */ export function filterTree( candidates: T[], From c1d3c3d6b546c5170a7c9ec0bae511de2cf23c36 Mon Sep 17 00:00:00 2001 From: aminya Date: Mon, 2 Nov 2020 05:33:22 -0600 Subject: [PATCH 36/36] fix macos tests --- spec/filter-tree-spec.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/spec/filter-tree-spec.js b/spec/filter-tree-spec.js index 26977dc3..3e3b7575 100644 --- a/spec/filter-tree-spec.js +++ b/spec/filter-tree-spec.js @@ -41,14 +41,24 @@ describe("filterTree", () => { expect(DeepEqual( filterTree(candidates, "ye", "data", "children"), + process.platform !== "darwin" + ? [ { data: 'eye', index: 2, level: 0 }, { data: 'bye1', index: 0, level: 0 }, { data: 'Bye2', index: 1, level: 0 }, { data: '_bye4', index: 0, level: 1 } ] + : + [ + { data: 'eye', index: 2, level: 0 }, + { data: 'Bye2', index: 1, level: 0 }, + { data: 'bye1', index: 0, level: 0 }, + { data: '_bye4', index: 0, level: 1 } + ] )).toBe(true) + // test maxResults expect(DeepEqual( filterTree(candidates, "bye", "data", "children", {maxResults: 2}), @@ -58,16 +68,24 @@ describe("filterTree", () => { ] )).toBe(true) + expect(DeepEqual( filterTree(candidates, "ye", "data", "children", {maxResults: 3}), + process.platform !== "darwin" + ? [ { data: 'eye', index: 2, level: 0 }, { data: 'bye1', index: 0, level: 0 }, { data: 'Bye2', index: 1, level: 0 }, ] + : + [ + { data: 'eye', index: 2, level: 0 }, + { data: 'Bye2', index: 1, level: 0 }, + { data: 'bye1', index: 0, level: 0 } + ] )).toBe(true) - }) it("can search in an array of children-less objects", () =>{