Skip to content

Commit

Permalink
feat: Merges pull request #33 from atom-ide-community/tree-search
Browse files Browse the repository at this point in the history
  • Loading branch information
aminya authored Nov 2, 2020
2 parents 9aa6c70 + c1d3c3d commit 8ba03cd
Show file tree
Hide file tree
Showing 11 changed files with 544 additions and 8 deletions.
41 changes: 38 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ 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.
* Converting Javascript/CoffeeScript code to native C++ bindings provides 4x performance benefit.
* 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

Expand All @@ -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.

Expand All @@ -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.
Expand Down
23 changes: 20 additions & 3 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -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.h" ],
"include_dirs": [
"<!@(node -p \"require('node-addon-api').include\")"
],
'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],
"cflags": [ "-fno-exceptions" ],
"cflags_cc": [ "-fno-exceptions", "-std=c++17" ],
"conditions": [
['OS=="mac"', {
"xcode_settings": {
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
"CLANG_CXX_LIBRARY": "libc++",
"CLANG_CXX_LANGUAGE_STANDARD":"c++17",
'MACOSX_DEPLOYMENT_TARGET': '10.15'
}
}],
['OS=="win"', {
"msvs_settings": {
"VCCLCompilerTool": {
"AdditionalOptions": [ "-std:c++latest", ],
},
},
}]
]
}
]
}
14 changes: 14 additions & 0 deletions fuzzaldrin.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
Expand Down
186 changes: 186 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 8ba03cd

Please sign in to comment.