initial vitepress site with basic nav
This commit is contained in:
parent
a7df2e049d
commit
2029f16583
1900 changed files with 1014692 additions and 0 deletions
398
node_modules/minisearch/CHANGELOG.md
generated
vendored
Normal file
398
node_modules/minisearch/CHANGELOG.md
generated
vendored
Normal file
|
@ -0,0 +1,398 @@
|
|||
# Changelog
|
||||
|
||||
`MiniSearch` follows [semantic versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
# v6.1.0
|
||||
|
||||
- Add `getStoredFields` method to retrieve the stored fields for a document
|
||||
given its ID.
|
||||
|
||||
- Pass stored fields to the `boostDocument` callback function, making it
|
||||
easier to perform dynamic document boosting.
|
||||
|
||||
# v6.0.1
|
||||
|
||||
- [fix] The `boost` search option now does not interfere with the `fields`
|
||||
search option: if `fields` is specified, boosting a field that is not
|
||||
included in `fields` has no effect, and will not include such boosted field
|
||||
in the search.
|
||||
- [fix] When using `search` with a `QuerySpec`, the `combineWith` option is
|
||||
now properly taking its default from the `SearchOptions` given as the second
|
||||
argument.
|
||||
|
||||
# v6.0.0
|
||||
|
||||
This is a major release. The most notable change is the addition of `discard`,
|
||||
`discardAll`, and `replace`. These method make it more convenient and performant
|
||||
to remove or replace documents.
|
||||
|
||||
This release is almost completely backwards compatible with `v5`, apart from one
|
||||
breaking change in the behavior of `add` when the document ID already exists.
|
||||
|
||||
Changes:
|
||||
|
||||
- [breaking change] `add`, `addAll`, and `addAllAsync` now throw an error on
|
||||
duplicate document IDs. When necessary, it is now possible to check for the
|
||||
existence of a document with a certain ID with the new method `has`.
|
||||
- Add `discard` method to remove documents by ID. This is a convenient
|
||||
alternative to `remove` that takes only the ID of the documents to remove,
|
||||
as opposed to the whole document. The visible effect is the same as
|
||||
`remove`. The difference is that `remove` immediately mutates the index,
|
||||
while `discard` marks the current document version as discarded, so it is
|
||||
immedately ignored by searches, but delays modifying the index until a
|
||||
certain number of documents are discarded. At that point, a vacuuming is
|
||||
triggered, cleaning up the index from obsolete references and allowing
|
||||
memory to be released.
|
||||
- Add `discardAll` and `replace` methods, built on top of `discard`
|
||||
- Add vacuuming of references to discarded documents from the index. Vacuuming
|
||||
is performed automatically by default when the number of discarded documents
|
||||
reaches a threshold (controlled by the new `autoVacuum` constructor option),
|
||||
or can be triggered manually by calling the `vacuum` method. The new
|
||||
`dirtCount` and `dirtFactor` properties give the current value of the
|
||||
parameters used to decide whether to trigger an automatic vacuuming.
|
||||
- Add `termCount` property, giving the number of distinct terms present in the
|
||||
index
|
||||
- Allow customizing the parameters of the BM25+ scoring algorithm via the
|
||||
`bm25` search option.
|
||||
- Improve TypeScript type of some methods by marking the given array argument
|
||||
as `readonly`, signaling that it won't be mutated, and allowing passing
|
||||
readonly arrays.
|
||||
- Make it possible to overload the `loadJS` static method in subclasses
|
||||
|
||||
# v5.1.0
|
||||
|
||||
- The `processTerm` option can now also expand a single term into several
|
||||
terms by returning an array of strings.
|
||||
- Add `logger` option to pass a custom logger function.
|
||||
|
||||
# v5.0.0
|
||||
|
||||
This is a major release. The main change is an improved scoring algorithm based
|
||||
on [BM25+](https://en.wikipedia.org/wiki/Okapi_BM25). The new algorithm will
|
||||
cause the scoring and sorting of search results to be different than in previous
|
||||
versions (generally better), and need less aggressive boosting.
|
||||
|
||||
- [breaking change] Use the [BM25+
|
||||
algorithm](https://en.wikipedia.org/wiki/Okapi_BM25) to score search
|
||||
results, improving their quality over the previous implementation. Note
|
||||
that, if you were using field boosting, you might need to re-adjust the
|
||||
boosting amounts, since their effect is now different.
|
||||
|
||||
- [breaking change] auto suggestions now default to `combineWith: 'AND'`
|
||||
instead of `'OR'`, requiring all the query terms to match. The old defaults
|
||||
can be replicated by passing a new `autoSuggestOptions` option to the
|
||||
constructor, with value `{ autoSuggestOptions: { combineWith: 'OR' } }`.
|
||||
|
||||
- Possibility to set the default auto suggest options in the constructor.
|
||||
|
||||
- Remove redundant fields in the index data. This also changes the
|
||||
serialization format, but serialized indexes created with `v4.x.y` are still
|
||||
deserialized correctly.
|
||||
|
||||
- Define `exports` entry points in `package.json`, to require MiniSearch as a
|
||||
commonjs package or import it as a ES module.
|
||||
|
||||
# v4.0.3
|
||||
|
||||
- [fix] Fix regression causing stored fields not being saved in some
|
||||
situations.
|
||||
|
||||
# v4.0.2
|
||||
|
||||
- [fix] Fix match data on mixed prefix and fuzzy search
|
||||
|
||||
# v4.0.1
|
||||
|
||||
- [fix] Fix an issue with scoring, causing a result matching both fuzzy and
|
||||
prefix search to be scored higher than an exact match.
|
||||
|
||||
- [breaking change] `SearchableMap` method `fuzzyGet` now returns a `Map`
|
||||
instead of an object. This is a breaking change only if you directly use
|
||||
`SearchableMap`, not if you use `MiniSearch`, and is considered part of
|
||||
version 4.
|
||||
|
||||
# v4.0.0
|
||||
|
||||
- [breaking change] The serialization format was changed, to abstract away the
|
||||
internal implementation details of the index data structure. This allows for
|
||||
present and future optimizations without breaking backward compatibility
|
||||
again. Moreover, the new format is simpler, facilitating the job of tools
|
||||
that create a serialized MiniSearch index in other languages.
|
||||
|
||||
- [performance] Large performance improvements on indexing (at least 4 time
|
||||
faster in the official benchmark) and search, due to changes to the internal
|
||||
data structures and the code.
|
||||
|
||||
- [peformance] The fuzzy search algorithm has been updated to work like
|
||||
outlined in [this blog post by Steve
|
||||
Hanov](http://stevehanov.ca/blog/?id=114), improving its performance by
|
||||
several times, especially on large maximum edit distances.
|
||||
|
||||
- [fix] The `weights` search option did not have an effect due to a bug. Now
|
||||
it works as documented. Note that, due to this, the relative scoring of
|
||||
fuzzy vs. prefix search matches might change compared to previous versions.
|
||||
This change also brings a further performance improvement of both fuzzy and
|
||||
prefix search.
|
||||
|
||||
**Migration notes:**
|
||||
|
||||
If you have an index serialized with a previous version of MiniSearch, you will
|
||||
need to re-create it when you upgrade to MiniSearch `v4`.
|
||||
|
||||
Also note that loading a pre-serialized index is _slower_ in `v4` than in
|
||||
previous versions, but there are much larger performance gains on indexing and
|
||||
search speed. If you serialized an index on the server-side, it is worth
|
||||
checking if it is now fast enough for your use case to index on the client side:
|
||||
it would save you from having to re-serialize the index every time something
|
||||
changes.
|
||||
|
||||
**Acknowledgements:**
|
||||
|
||||
Many thanks to [rolftimmermans](https://github.com/rolftimmermans) for
|
||||
contributing the fixes and outstanding performance improvements that are part of
|
||||
this release.
|
||||
|
||||
|
||||
# v3.3.0
|
||||
|
||||
- Add `maxFuzzy` search option, to limit the maximum edit distance for fuzzy
|
||||
search when using fractional fuzziness
|
||||
|
||||
# v3.2.0
|
||||
|
||||
- Add AND_NOT combinator to subtract results of a subquery from another (for
|
||||
example to find documents that match one term and not another)
|
||||
|
||||
# v3.1.0
|
||||
|
||||
- Add possibility for advanced combination of subqueries as query expression
|
||||
trees
|
||||
|
||||
# v3.0.4
|
||||
|
||||
- [fix] Keep radix tree property (no node with a single child) after removal
|
||||
of an entry
|
||||
|
||||
# v3.0.3
|
||||
|
||||
- [fix] Adjust data about field lengths upon document removal
|
||||
|
||||
# v3.0.2
|
||||
|
||||
- [fix] `addAllAsync` now allows events to be processed between chunks, avoid
|
||||
blocking the UI (by [@grimmen](https://github.com/grimmen))
|
||||
|
||||
# v3.0.1
|
||||
|
||||
- [fix] Fix type signature of `removeAll` to allow calling it with no
|
||||
arguments. Also, throw a more informative error if called with a falsey
|
||||
value. Thanks to [https://github.com/nilclass](@nilclass).
|
||||
|
||||
# v3.0.0
|
||||
|
||||
This major version ports the source code to TypeScript. That made it possible
|
||||
to improve types and documentation, making sure that both are in sync with the
|
||||
actual code. It is mostly backward compatible: JavaScript users should
|
||||
experience no breaking change, while TypeScript users _might_ have toadapt
|
||||
some types.
|
||||
|
||||
- Port source to [TypeScript](https://www.typescriptlang.org), adding type
|
||||
safety
|
||||
- Improved types and documentation (now generated with [TypeDoc](http://typedoc.org))
|
||||
- [breaking change, fix] TypeScript `SearchOptions` type is not generic
|
||||
anymore
|
||||
- [breaking change] `SearchableMap` is not a static field of `MiniSearch`
|
||||
anymore: it can instead be imported separately as `minisearch/SearchableMap`
|
||||
|
||||
# v2.6.2
|
||||
|
||||
- [fix] Improve TypeScript types: default generic document type is `any`, not `object`
|
||||
|
||||
# v2.6.1
|
||||
|
||||
- No change from 2.6.0
|
||||
|
||||
# v2.6.0
|
||||
|
||||
- Better TypeScript typings using generics, letting the user (optionally)
|
||||
specify the document type.
|
||||
|
||||
# v2.5.1
|
||||
|
||||
- [fix] Fix document removal when using a custom `extractField` function
|
||||
(thanks [@ahri](https://github.com/ahri) for reporting and reproducting)
|
||||
|
||||
# v2.5.0
|
||||
|
||||
- Make `idField` extraction customizeable and consistent with other fields,
|
||||
using `extractField`
|
||||
|
||||
# v2.4.1
|
||||
|
||||
- [fix] Fix issue with the term `constructor` (reported by
|
||||
[@scambier](https://github.com/scambier))
|
||||
|
||||
- [fix] Fix issues when a field is named like a default property of JavaScript
|
||||
objects
|
||||
|
||||
# v2.4.0
|
||||
|
||||
- Convert field value to string before tokenization and indexing. This makes
|
||||
a custom field extractor unnecessary for basic cases like integers or simple
|
||||
arrays.
|
||||
|
||||
# v2.3.1
|
||||
|
||||
- Version `v2.3.1` mistakenly did not contain the commit adding `removeAll`,
|
||||
this patch release fixes it.
|
||||
|
||||
# v2.3.0
|
||||
|
||||
- Add `removeAll` method, to remove many documents, or all documents, at once.
|
||||
|
||||
# v2.2.2
|
||||
|
||||
- Avoid destructuring variables named with an underscore prefix. This plays
|
||||
nicer to some common minifier and builder configurations.
|
||||
|
||||
- Performance improvement in `getDefault` (by
|
||||
[stalniy](https://github.com/stalniy))
|
||||
|
||||
- Fix the linter setup, to ensure code style consistency
|
||||
|
||||
## v2.2.1
|
||||
|
||||
- Add `"sideEffects": false` to `package.json` to allow bundlers to perform
|
||||
tree shaking
|
||||
|
||||
## v2.2.0
|
||||
|
||||
- [fix] Fix documentation of `SearchableMap.prototype.atPrefix` (by
|
||||
[@graphman65](https://github.com/graphman65))
|
||||
- Switch to Rollup for bundling (by [stalniy](https://github.com/stalniy)),
|
||||
reducing size of build and providing ES6 and ES5 module versions too.
|
||||
|
||||
## v2.1.4
|
||||
|
||||
- [fix] Fix document removal in presence of custom per field tokenizer, field
|
||||
extractor, or term processor (thanks [@CaptainChaos](https://github.com/CaptainChaos))
|
||||
|
||||
## v2.1.3
|
||||
|
||||
- [fix] Fix TypeScript definition for `storeFields` option (by
|
||||
[@ryan-codingintrigue](https://github.com/ryan-codingintrigue))
|
||||
|
||||
## v2.1.2
|
||||
|
||||
- [fix] Fix TypeScript definition for `fuzzy` option (by
|
||||
[@alessandrobardini](https://github.com/alessandrobardini))
|
||||
|
||||
## v2.1.1
|
||||
|
||||
- [fix] Fix TypeScript definitions adding `filter` and `storeFields` options
|
||||
(by [@emilianox](https://github.com/emilianox))
|
||||
|
||||
## v2.1.0
|
||||
|
||||
- [feature] Add support for stored fields
|
||||
|
||||
- [feature] Add filtering of search results and auto suggestions
|
||||
|
||||
## v2.0.6
|
||||
|
||||
- Better TypeScript definitions (by [@samuelmeuli](https://github.com/samuelmeuli))
|
||||
|
||||
## v2.0.5
|
||||
|
||||
- Add TypeScript definitions for ease of use in TypeScript projects
|
||||
|
||||
## v2.0.4
|
||||
|
||||
- [fix] tokenizer behavior with newline characters (by [@samuelmeuli](https://github.com/samuelmeuli))
|
||||
|
||||
## v2.0.3
|
||||
|
||||
- Fix small imprecision in documentation
|
||||
|
||||
## v2.0.2
|
||||
|
||||
- Add `addAllAsync` method, adding many documents asynchronously and in chunks
|
||||
to avoid blocking the main thread
|
||||
|
||||
## v2.0.1
|
||||
|
||||
- Throw a more descriptive error when `loadJSON` is called without options
|
||||
|
||||
## v2.0.0
|
||||
|
||||
This release introduces better defaults. It is considered a major release, as
|
||||
the default options are slightly different, but the API is not changed.
|
||||
|
||||
- *Breaking change*: default tokenizer splits by Unicode space or punctuation
|
||||
(before it was splitting by space, punctuation, or _symbol_). The difference
|
||||
is that currency symbols and other non-punctuation symbols will not be
|
||||
discarded: "it's 100€" is now tokenized as `["it", "s", "100€"]` instead of
|
||||
`["it", "s", "100"]`.
|
||||
|
||||
- *Breaking change*: default term processing does not discard 1-character
|
||||
words.
|
||||
|
||||
- *Breaking change*: auto suggestions by default perform prefix search only on
|
||||
the last term in the query. So "super cond" will suggest "super
|
||||
conductivity", but not "superposition condition".
|
||||
|
||||
## v1.3.1
|
||||
|
||||
- Better and more compact regular expression in the default tokenizer,
|
||||
separating on Unicode spaces, punctuation, and symbols
|
||||
|
||||
## v1.3.0
|
||||
|
||||
- Support for non-latin scripts
|
||||
|
||||
## v1.2.1
|
||||
|
||||
- Improve fuzzy search performance (common cases are now ~4x faster, as shown
|
||||
by the benchmark)
|
||||
|
||||
## v1.2.0
|
||||
|
||||
- Add possibility to configure a custom field extraction function by setting
|
||||
the `extractField` option (to support cases like nested fields, non-string
|
||||
fields, getter methods, field pre-processing, etc.)
|
||||
|
||||
## v1.1.2
|
||||
|
||||
- Add `getDefault` static method to get the default value of configuration options
|
||||
|
||||
## v1.1.1
|
||||
|
||||
- Do not minify library when published as NPM package. Run `yarn
|
||||
build-minified` (or `npm run build-minified`) to produce a minified build
|
||||
with source maps.
|
||||
- **Bugfix**: as per specification, `processTerm` is called with only one
|
||||
argument upon search (see [#5](https://github.com/lucaong/minisearch/issues/5))
|
||||
|
||||
## v1.1.0
|
||||
|
||||
- Add possibility to configure separate index-time and search-time
|
||||
tokenization and term processing functions
|
||||
- The `processTerm` function can now reject a term by returning a falsy value
|
||||
- Upon indexing, the `tokenize` and `processTerm` functions receive the field
|
||||
name as the second argument. This makes it possible to process or tokenize
|
||||
each field differently.
|
||||
|
||||
## v1.0.1
|
||||
|
||||
- Reduce bundle size by optimizing babel preset env options
|
||||
|
||||
## v1.0.0
|
||||
|
||||
Production-ready release.
|
||||
|
||||
Features:
|
||||
|
||||
- Space-optimized index
|
||||
- Exact match, prefix match, fuzzy search
|
||||
- Auto suggestions
|
||||
- Add/remove documents at any time
|
7
node_modules/minisearch/LICENSE.txt
generated
vendored
Normal file
7
node_modules/minisearch/LICENSE.txt
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
Copyright 2022 Luca Ongaro
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
346
node_modules/minisearch/README.md
generated
vendored
Normal file
346
node_modules/minisearch/README.md
generated
vendored
Normal file
|
@ -0,0 +1,346 @@
|
|||
# MiniSearch
|
||||
|
||||
[](https://github.com/lucaong/minisearch/actions)
|
||||
[](https://coveralls.io/github/lucaong/minisearch?branch=master)
|
||||
[](https://bundlephobia.com/result?p=minisearch)
|
||||
[](https://www.npmjs.com/package/minisearch)
|
||||
[](https://www.npmjs.com/package/minisearch)
|
||||
[](https://lucaong.github.io/minisearch/classes/_minisearch_.minisearch.html)
|
||||
|
||||
`MiniSearch` is a tiny but powerful in-memory fulltext search engine written in
|
||||
JavaScript. It is respectful of resources, and it can comfortably run both in
|
||||
Node and in the browser.
|
||||
|
||||
Try out the [demo application](https://lucaong.github.io/minisearch/examples/).
|
||||
|
||||
Find the complete [documentation and API reference
|
||||
here](https://lucaong.github.io/minisearch/classes/_minisearch_.minisearch.html),
|
||||
and more background about `MiniSearch`, including a comparison with other
|
||||
similar libraries, in [this blog
|
||||
post](https://lucaongaro.eu/blog/2019/01/30/minisearch-client-side-fulltext-search-engine.html).
|
||||
|
||||
`MiniSearch` follows [semantic versioning](https://semver.org/spec/v2.0.0.html),
|
||||
and documents releases and changes in the
|
||||
[changelog](https://github.com/lucaong/minisearch/blob/master/CHANGELOG.md).
|
||||
|
||||
|
||||
## Use case
|
||||
|
||||
`MiniSearch` addresses use cases where full-text search features are needed
|
||||
(e.g. prefix search, fuzzy search, ranking, boosting of fields…), but the data
|
||||
to be indexed can fit locally in the process memory. While you won't index the
|
||||
whole Internet with it, there are surprisingly many use cases that are served
|
||||
well by `MiniSearch`. By storing the index in local memory, `MiniSearch` can
|
||||
work offline, and can process queries quickly, without network latency.
|
||||
|
||||
A prominent use-case is real time search "as you type" in web and mobile
|
||||
applications, where keeping the index on the client enables fast and reactive
|
||||
UIs, removing the need to make requests to a search server.
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
* Memory-efficient index, designed to support memory-constrained use cases
|
||||
like mobile browsers.
|
||||
|
||||
* Exact match, prefix search, fuzzy match, field boosting.
|
||||
|
||||
* Auto-suggestion engine, for auto-completion of search queries.
|
||||
|
||||
* Modern search result ranking algorithm.
|
||||
|
||||
* Documents can be added and removed from the index at any time.
|
||||
|
||||
* Zero external dependencies.
|
||||
|
||||
`MiniSearch` strives to expose a simple API that provides the building blocks to
|
||||
build custom solutions, while keeping a small and well tested codebase.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
With `npm`:
|
||||
|
||||
```
|
||||
npm install minisearch
|
||||
```
|
||||
|
||||
With `yarn`:
|
||||
|
||||
```
|
||||
yarn add minisearch
|
||||
```
|
||||
|
||||
Then `require` or `import` it in your project:
|
||||
|
||||
```javascript
|
||||
// If you are using import:
|
||||
import MiniSearch from 'minisearch'
|
||||
|
||||
// If you are using require:
|
||||
const MiniSearch = require('minisearch')
|
||||
```
|
||||
|
||||
Alternatively, if you prefer to use a `<script>` tag, you can require MiniSearch
|
||||
[from a CDN](https://www.jsdelivr.com/package/npm/minisearch):
|
||||
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/minisearch@6.1.0/dist/umd/index.min.js"></script>
|
||||
```
|
||||
|
||||
In this case, `MiniSearch` will appear as a global variable in your project.
|
||||
|
||||
Finally, if you want to manually build the library, clone the repository and run
|
||||
`yarn build` (or `yarn build-minified` for a minified version + source maps).
|
||||
The compiled source will be created in the `dist` folder (UMD, ES6 and ES2015
|
||||
module versions are provided).
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic usage
|
||||
|
||||
```javascript
|
||||
// A collection of documents for our examples
|
||||
const documents = [
|
||||
{
|
||||
id: 1,
|
||||
title: 'Moby Dick',
|
||||
text: 'Call me Ishmael. Some years ago...',
|
||||
category: 'fiction'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Zen and the Art of Motorcycle Maintenance',
|
||||
text: 'I can see by my watch...',
|
||||
category: 'fiction'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Neuromancer',
|
||||
text: 'The sky above the port was...',
|
||||
category: 'fiction'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'Zen and the Art of Archery',
|
||||
text: 'At first sight it must seem...',
|
||||
category: 'non-fiction'
|
||||
},
|
||||
// ...and more
|
||||
]
|
||||
|
||||
let miniSearch = new MiniSearch({
|
||||
fields: ['title', 'text'], // fields to index for full-text search
|
||||
storeFields: ['title', 'category'] // fields to return with search results
|
||||
})
|
||||
|
||||
// Index all documents
|
||||
miniSearch.addAll(documents)
|
||||
|
||||
// Search with default options
|
||||
let results = miniSearch.search('zen art motorcycle')
|
||||
// => [
|
||||
// { id: 2, title: 'Zen and the Art of Motorcycle Maintenance', category: 'fiction', score: 2.77258, match: { ... } },
|
||||
// { id: 4, title: 'Zen and the Art of Archery', category: 'non-fiction', score: 1.38629, match: { ... } }
|
||||
// ]
|
||||
```
|
||||
|
||||
### Search options
|
||||
|
||||
`MiniSearch` supports several options for more advanced search behavior:
|
||||
|
||||
```javascript
|
||||
// Search only specific fields
|
||||
miniSearch.search('zen', { fields: ['title'] })
|
||||
|
||||
// Boost some fields (here "title")
|
||||
miniSearch.search('zen', { boost: { title: 2 } })
|
||||
|
||||
// Prefix search (so that 'moto' will match 'motorcycle')
|
||||
miniSearch.search('moto', { prefix: true })
|
||||
|
||||
// Search within a specific category
|
||||
miniSearch.search('zen', {
|
||||
filter: (result) => result.category === 'fiction'
|
||||
})
|
||||
|
||||
// Fuzzy search, in this example, with a max edit distance of 0.2 * term length,
|
||||
// rounded to nearest integer. The mispelled 'ismael' will match 'ishmael'.
|
||||
miniSearch.search('ismael', { fuzzy: 0.2 })
|
||||
|
||||
// You can set the default search options upon initialization
|
||||
miniSearch = new MiniSearch({
|
||||
fields: ['title', 'text'],
|
||||
searchOptions: {
|
||||
boost: { title: 2 },
|
||||
fuzzy: 0.2
|
||||
}
|
||||
})
|
||||
miniSearch.addAll(documents)
|
||||
|
||||
// It will now by default perform fuzzy search and boost "title":
|
||||
miniSearch.search('zen and motorcycles')
|
||||
```
|
||||
|
||||
### Auto suggestions
|
||||
|
||||
`MiniSearch` can suggest search queries given an incomplete query:
|
||||
|
||||
```javascript
|
||||
miniSearch.autoSuggest('zen ar')
|
||||
// => [ { suggestion: 'zen archery art', terms: [ 'zen', 'archery', 'art' ], score: 1.73332 },
|
||||
// { suggestion: 'zen art', terms: [ 'zen', 'art' ], score: 1.21313 } ]
|
||||
```
|
||||
|
||||
The `autoSuggest` method takes the same options as the `search` method, so you
|
||||
can get suggestions for misspelled words using fuzzy search:
|
||||
|
||||
```javascript
|
||||
miniSearch.autoSuggest('neromancer', { fuzzy: 0.2 })
|
||||
// => [ { suggestion: 'neuromancer', terms: [ 'neuromancer' ], score: 1.03998 } ]
|
||||
```
|
||||
|
||||
Suggestions are ranked by the relevance of the documents that would be returned
|
||||
by that search.
|
||||
|
||||
Sometimes, you might need to filter auto suggestions to, say, only a specific
|
||||
category. You can do so by providing a `filter` option:
|
||||
|
||||
```javascript
|
||||
miniSearch.autoSuggest('zen ar', {
|
||||
filter: (result) => result.category === 'fiction'
|
||||
})
|
||||
// => [ { suggestion: 'zen art', terms: [ 'zen', 'art' ], score: 1.21313 } ]
|
||||
```
|
||||
|
||||
### Field extraction
|
||||
|
||||
By default, documents are assumed to be plain key-value objects with field names
|
||||
as keys and field values as simple values. In order to support custom field
|
||||
extraction logic (for example for nested fields, or non-string field values that
|
||||
need processing before tokenization), a custom field extractor function can be
|
||||
passed as the `extractField` option:
|
||||
|
||||
```javascript
|
||||
// Assuming that our documents look like:
|
||||
const documents = [
|
||||
{ id: 1, title: 'Moby Dick', author: { name: 'Herman Melville' }, pubDate: new Date(1851, 9, 18) },
|
||||
{ id: 2, title: 'Zen and the Art of Motorcycle Maintenance', author: { name: 'Robert Pirsig' }, pubDate: new Date(1974, 3, 1) },
|
||||
{ id: 3, title: 'Neuromancer', author: { name: 'William Gibson' }, pubDate: new Date(1984, 6, 1) },
|
||||
{ id: 4, title: 'Zen in the Art of Archery', author: { name: 'Eugen Herrigel' }, pubDate: new Date(1948, 0, 1) },
|
||||
// ...and more
|
||||
]
|
||||
|
||||
// We can support nested fields (author.name) and date fields (pubDate) with a
|
||||
// custom `extractField` function:
|
||||
|
||||
let miniSearch = new MiniSearch({
|
||||
fields: ['title', 'author.name', 'pubYear'],
|
||||
extractField: (document, fieldName) => {
|
||||
// If field name is 'pubYear', extract just the year from 'pubDate'
|
||||
if (fieldName === 'pubYear') {
|
||||
const pubDate = document['pubDate']
|
||||
return pubDate && pubDate.getFullYear().toString()
|
||||
}
|
||||
|
||||
// Access nested fields
|
||||
return fieldName.split('.').reduce((doc, key) => doc && doc[key], document)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
The default field extractor can be obtained by calling
|
||||
`MiniSearch.getDefault('extractField')`.
|
||||
|
||||
### Tokenization
|
||||
|
||||
By default, documents are tokenized by splitting on Unicode space or punctuation
|
||||
characters. The tokenization logic can be easily changed by passing a custom
|
||||
tokenizer function as the `tokenize` option:
|
||||
|
||||
```javascript
|
||||
// Tokenize splitting by hyphen
|
||||
let miniSearch = new MiniSearch({
|
||||
fields: ['title', 'text'],
|
||||
tokenize: (string, _fieldName) => string.split('-')
|
||||
})
|
||||
```
|
||||
|
||||
Upon search, the same tokenization is used by default, but it is possible to
|
||||
pass a `tokenize` search option in case a different search-time tokenization is
|
||||
necessary:
|
||||
|
||||
```javascript
|
||||
// Tokenize splitting by hyphen
|
||||
let miniSearch = new MiniSearch({
|
||||
fields: ['title', 'text'],
|
||||
tokenize: (string) => string.split('-'), // indexing tokenizer
|
||||
searchOptions: {
|
||||
tokenize: (string) => string.split(/[\s-]+/) // search query tokenizer
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
The default tokenizer can be obtained by calling
|
||||
`MiniSearch.getDefault('tokenize')`.
|
||||
|
||||
### Term processing
|
||||
|
||||
Terms are downcased by default. No stemming is performed, and no stop-word list
|
||||
is applied. To customize how the terms are processed upon indexing, for example
|
||||
to normalize them, filter them, or to apply stemming, the `processTerm` option
|
||||
can be used. The `processTerm` function should return the processed term as a
|
||||
string, or a falsy value if the term should be discarded:
|
||||
|
||||
```javascript
|
||||
let stopWords = new Set(['and', 'or', 'to', 'in', 'a', 'the', /* ...and more */ ])
|
||||
|
||||
// Perform custom term processing (here discarding stop words and downcasing)
|
||||
let miniSearch = new MiniSearch({
|
||||
fields: ['title', 'text'],
|
||||
processTerm: (term, _fieldName) =>
|
||||
stopWords.has(term) ? null : term.toLowerCase()
|
||||
})
|
||||
```
|
||||
|
||||
By default, the same processing is applied to search queries. In order to apply
|
||||
a different processing to search queries, supply a `processTerm` search option:
|
||||
|
||||
```javascript
|
||||
let miniSearch = new MiniSearch({
|
||||
fields: ['title', 'text'],
|
||||
processTerm: (term) =>
|
||||
stopWords.has(term) ? null : term.toLowerCase(), // index term processing
|
||||
searchOptions: {
|
||||
processTerm: (term) => term.toLowerCase() // search query processing
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
The default term processor can be obtained by calling
|
||||
`MiniSearch.getDefault('processTerm')`.
|
||||
|
||||
### API Documentation
|
||||
|
||||
Refer to the [API
|
||||
documentation](https://lucaong.github.io/minisearch/classes/_minisearch_.minisearch.html)
|
||||
for details about configuration options and methods.
|
||||
|
||||
|
||||
## Browser compatibility
|
||||
|
||||
`MiniSearch` natively supports all modern browsers implementing JavaScript
|
||||
standards, but requires a polyfill when used in Internet Explorer, as it makes
|
||||
use functions like `Object.entries`, `Array.includes`, and `Array.from`, which
|
||||
are standard but not available on older browsers. The package
|
||||
[`core-js`](https://github.com/zloirock/core-js) is one such polyfill that can
|
||||
be used to provide those functions.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions to `MiniSearch` are welcome! Please read the [contributions
|
||||
guidelines](https://github.com/lucaong/minisearch/blob/master/CONTRIBUTING.md).
|
||||
Reading the [design
|
||||
document](https://github.com/lucaong/minisearch/blob/master/DESIGN_DOCUMENT.md) is
|
||||
also useful to understand the project goals and the technical implementation.
|
680
node_modules/minisearch/dist/cjs/SearchableMap.cjs
generated
vendored
Normal file
680
node_modules/minisearch/dist/cjs/SearchableMap.cjs
generated
vendored
Normal file
|
@ -0,0 +1,680 @@
|
|||
'use strict';
|
||||
|
||||
/******************************************************************************
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
***************************************************************************** */
|
||||
/* global Reflect, Promise */
|
||||
|
||||
|
||||
function __values(o) {
|
||||
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
||||
if (m) return m.call(o);
|
||||
if (o && typeof o.length === "number") return {
|
||||
next: function () {
|
||||
if (o && i >= o.length) o = void 0;
|
||||
return { value: o && o[i++], done: !o };
|
||||
}
|
||||
};
|
||||
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
||||
}
|
||||
|
||||
function __read(o, n) {
|
||||
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
||||
if (!m) return o;
|
||||
var i = m.call(o), r, ar = [], e;
|
||||
try {
|
||||
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
||||
}
|
||||
catch (error) { e = { error: error }; }
|
||||
finally {
|
||||
try {
|
||||
if (r && !r.done && (m = i["return"])) m.call(i);
|
||||
}
|
||||
finally { if (e) throw e.error; }
|
||||
}
|
||||
return ar;
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
var ENTRIES = 'ENTRIES';
|
||||
/** @ignore */
|
||||
var KEYS = 'KEYS';
|
||||
/** @ignore */
|
||||
var VALUES = 'VALUES';
|
||||
/** @ignore */
|
||||
var LEAF = '';
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
var TreeIterator = /** @class */ (function () {
|
||||
function TreeIterator(set, type) {
|
||||
var node = set._tree;
|
||||
var keys = Array.from(node.keys());
|
||||
this.set = set;
|
||||
this._type = type;
|
||||
this._path = keys.length > 0 ? [{ node: node, keys: keys }] : [];
|
||||
}
|
||||
TreeIterator.prototype.next = function () {
|
||||
var value = this.dive();
|
||||
this.backtrack();
|
||||
return value;
|
||||
};
|
||||
TreeIterator.prototype.dive = function () {
|
||||
if (this._path.length === 0) {
|
||||
return { done: true, value: undefined };
|
||||
}
|
||||
var _a = last$1(this._path), node = _a.node, keys = _a.keys;
|
||||
if (last$1(keys) === LEAF) {
|
||||
return { done: false, value: this.result() };
|
||||
}
|
||||
var child = node.get(last$1(keys));
|
||||
this._path.push({ node: child, keys: Array.from(child.keys()) });
|
||||
return this.dive();
|
||||
};
|
||||
TreeIterator.prototype.backtrack = function () {
|
||||
if (this._path.length === 0) {
|
||||
return;
|
||||
}
|
||||
var keys = last$1(this._path).keys;
|
||||
keys.pop();
|
||||
if (keys.length > 0) {
|
||||
return;
|
||||
}
|
||||
this._path.pop();
|
||||
this.backtrack();
|
||||
};
|
||||
TreeIterator.prototype.key = function () {
|
||||
return this.set._prefix + this._path
|
||||
.map(function (_a) {
|
||||
var keys = _a.keys;
|
||||
return last$1(keys);
|
||||
})
|
||||
.filter(function (key) { return key !== LEAF; })
|
||||
.join('');
|
||||
};
|
||||
TreeIterator.prototype.value = function () {
|
||||
return last$1(this._path).node.get(LEAF);
|
||||
};
|
||||
TreeIterator.prototype.result = function () {
|
||||
switch (this._type) {
|
||||
case VALUES: return this.value();
|
||||
case KEYS: return this.key();
|
||||
default: return [this.key(), this.value()];
|
||||
}
|
||||
};
|
||||
TreeIterator.prototype[Symbol.iterator] = function () {
|
||||
return this;
|
||||
};
|
||||
return TreeIterator;
|
||||
}());
|
||||
var last$1 = function (array) {
|
||||
return array[array.length - 1];
|
||||
};
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
var fuzzySearch = function (node, query, maxDistance) {
|
||||
var results = new Map();
|
||||
if (query === undefined)
|
||||
return results;
|
||||
// Number of columns in the Levenshtein matrix.
|
||||
var n = query.length + 1;
|
||||
// Matching terms can never be longer than N + maxDistance.
|
||||
var m = n + maxDistance;
|
||||
// Fill first matrix row and column with numbers: 0 1 2 3 ...
|
||||
var matrix = new Uint8Array(m * n).fill(maxDistance + 1);
|
||||
for (var j = 0; j < n; ++j)
|
||||
matrix[j] = j;
|
||||
for (var i = 1; i < m; ++i)
|
||||
matrix[i * n] = i;
|
||||
recurse(node, query, maxDistance, results, matrix, 1, n, '');
|
||||
return results;
|
||||
};
|
||||
// Modified version of http://stevehanov.ca/blog/?id=114
|
||||
// This builds a Levenshtein matrix for a given query and continuously updates
|
||||
// it for nodes in the radix tree that fall within the given maximum edit
|
||||
// distance. Keeping the same matrix around is beneficial especially for larger
|
||||
// edit distances.
|
||||
//
|
||||
// k a t e <-- query
|
||||
// 0 1 2 3 4
|
||||
// c 1 1 2 3 4
|
||||
// a 2 2 1 2 3
|
||||
// t 3 3 2 1 [2] <-- edit distance
|
||||
// ^
|
||||
// ^ term in radix tree, rows are added and removed as needed
|
||||
var recurse = function (node, query, maxDistance, results, matrix, m, n, prefix) {
|
||||
var e_1, _a;
|
||||
var offset = m * n;
|
||||
try {
|
||||
key: for (var _b = __values(node.keys()), _c = _b.next(); !_c.done; _c = _b.next()) {
|
||||
var key = _c.value;
|
||||
if (key === LEAF) {
|
||||
// We've reached a leaf node. Check if the edit distance acceptable and
|
||||
// store the result if it is.
|
||||
var distance = matrix[offset - 1];
|
||||
if (distance <= maxDistance) {
|
||||
results.set(prefix, [node.get(key), distance]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Iterate over all characters in the key. Update the Levenshtein matrix
|
||||
// and check if the minimum distance in the last row is still within the
|
||||
// maximum edit distance. If it is, we can recurse over all child nodes.
|
||||
var i = m;
|
||||
for (var pos = 0; pos < key.length; ++pos, ++i) {
|
||||
var char = key[pos];
|
||||
var thisRowOffset = n * i;
|
||||
var prevRowOffset = thisRowOffset - n;
|
||||
// Set the first column based on the previous row, and initialize the
|
||||
// minimum distance in the current row.
|
||||
var minDistance = matrix[thisRowOffset];
|
||||
var jmin = Math.max(0, i - maxDistance - 1);
|
||||
var jmax = Math.min(n - 1, i + maxDistance);
|
||||
// Iterate over remaining columns (characters in the query).
|
||||
for (var j = jmin; j < jmax; ++j) {
|
||||
var different = char !== query[j];
|
||||
// It might make sense to only read the matrix positions used for
|
||||
// deletion/insertion if the characters are different. But we want to
|
||||
// avoid conditional reads for performance reasons.
|
||||
var rpl = matrix[prevRowOffset + j] + +different;
|
||||
var del = matrix[prevRowOffset + j + 1] + 1;
|
||||
var ins = matrix[thisRowOffset + j] + 1;
|
||||
var dist = matrix[thisRowOffset + j + 1] = Math.min(rpl, del, ins);
|
||||
if (dist < minDistance)
|
||||
minDistance = dist;
|
||||
}
|
||||
// Because distance will never decrease, we can stop. There will be no
|
||||
// matching child nodes.
|
||||
if (minDistance > maxDistance) {
|
||||
continue key;
|
||||
}
|
||||
}
|
||||
recurse(node.get(key), query, maxDistance, results, matrix, i, n, prefix + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A class implementing the same interface as a standard JavaScript
|
||||
* [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)
|
||||
* with string keys, but adding support for efficiently searching entries with
|
||||
* prefix or fuzzy search. This class is used internally by [[MiniSearch]] as
|
||||
* the inverted index data structure. The implementation is a radix tree
|
||||
* (compressed prefix tree).
|
||||
*
|
||||
* Since this class can be of general utility beyond _MiniSearch_, it is
|
||||
* exported by the `minisearch` package and can be imported (or required) as
|
||||
* `minisearch/SearchableMap`.
|
||||
*
|
||||
* @typeParam T The type of the values stored in the map.
|
||||
*/
|
||||
var SearchableMap = /** @class */ (function () {
|
||||
/**
|
||||
* The constructor is normally called without arguments, creating an empty
|
||||
* map. In order to create a [[SearchableMap]] from an iterable or from an
|
||||
* object, check [[SearchableMap.from]] and [[SearchableMap.fromObject]].
|
||||
*
|
||||
* The constructor arguments are for internal use, when creating derived
|
||||
* mutable views of a map at a prefix.
|
||||
*/
|
||||
function SearchableMap(tree, prefix) {
|
||||
if (tree === void 0) { tree = new Map(); }
|
||||
if (prefix === void 0) { prefix = ''; }
|
||||
this._size = undefined;
|
||||
this._tree = tree;
|
||||
this._prefix = prefix;
|
||||
}
|
||||
/**
|
||||
* Creates and returns a mutable view of this [[SearchableMap]], containing only
|
||||
* entries that share the given prefix.
|
||||
*
|
||||
* ### Usage:
|
||||
*
|
||||
* ```javascript
|
||||
* let map = new SearchableMap()
|
||||
* map.set("unicorn", 1)
|
||||
* map.set("universe", 2)
|
||||
* map.set("university", 3)
|
||||
* map.set("unique", 4)
|
||||
* map.set("hello", 5)
|
||||
*
|
||||
* let uni = map.atPrefix("uni")
|
||||
* uni.get("unique") // => 4
|
||||
* uni.get("unicorn") // => 1
|
||||
* uni.get("hello") // => undefined
|
||||
*
|
||||
* let univer = map.atPrefix("univer")
|
||||
* univer.get("unique") // => undefined
|
||||
* univer.get("universe") // => 2
|
||||
* univer.get("university") // => 3
|
||||
* ```
|
||||
*
|
||||
* @param prefix The prefix
|
||||
* @return A [[SearchableMap]] representing a mutable view of the original Map at the given prefix
|
||||
*/
|
||||
SearchableMap.prototype.atPrefix = function (prefix) {
|
||||
var e_1, _a;
|
||||
if (!prefix.startsWith(this._prefix)) {
|
||||
throw new Error('Mismatched prefix');
|
||||
}
|
||||
var _b = __read(trackDown(this._tree, prefix.slice(this._prefix.length)), 2), node = _b[0], path = _b[1];
|
||||
if (node === undefined) {
|
||||
var _c = __read(last(path), 2), parentNode = _c[0], key = _c[1];
|
||||
try {
|
||||
for (var _d = __values(parentNode.keys()), _e = _d.next(); !_e.done; _e = _d.next()) {
|
||||
var k = _e.value;
|
||||
if (k !== LEAF && k.startsWith(key)) {
|
||||
var node_1 = new Map();
|
||||
node_1.set(k.slice(key.length), parentNode.get(k));
|
||||
return new SearchableMap(node_1, prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
}
|
||||
}
|
||||
return new SearchableMap(node, prefix);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear
|
||||
*/
|
||||
SearchableMap.prototype.clear = function () {
|
||||
this._size = undefined;
|
||||
this._tree.clear();
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete
|
||||
* @param key Key to delete
|
||||
*/
|
||||
SearchableMap.prototype.delete = function (key) {
|
||||
this._size = undefined;
|
||||
return remove(this._tree, key);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries
|
||||
* @return An iterator iterating through `[key, value]` entries.
|
||||
*/
|
||||
SearchableMap.prototype.entries = function () {
|
||||
return new TreeIterator(this, ENTRIES);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach
|
||||
* @param fn Iteration function
|
||||
*/
|
||||
SearchableMap.prototype.forEach = function (fn) {
|
||||
var e_2, _a;
|
||||
try {
|
||||
for (var _b = __values(this), _c = _b.next(); !_c.done; _c = _b.next()) {
|
||||
var _d = __read(_c.value, 2), key = _d[0], value = _d[1];
|
||||
fn(key, value, this);
|
||||
}
|
||||
}
|
||||
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||
}
|
||||
finally { if (e_2) throw e_2.error; }
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Returns a Map of all the entries that have a key within the given edit
|
||||
* distance from the search key. The keys of the returned Map are the matching
|
||||
* keys, while the values are two-element arrays where the first element is
|
||||
* the value associated to the key, and the second is the edit distance of the
|
||||
* key to the search key.
|
||||
*
|
||||
* ### Usage:
|
||||
*
|
||||
* ```javascript
|
||||
* let map = new SearchableMap()
|
||||
* map.set('hello', 'world')
|
||||
* map.set('hell', 'yeah')
|
||||
* map.set('ciao', 'mondo')
|
||||
*
|
||||
* // Get all entries that match the key 'hallo' with a maximum edit distance of 2
|
||||
* map.fuzzyGet('hallo', 2)
|
||||
* // => Map(2) { 'hello' => ['world', 1], 'hell' => ['yeah', 2] }
|
||||
*
|
||||
* // In the example, the "hello" key has value "world" and edit distance of 1
|
||||
* // (change "e" to "a"), the key "hell" has value "yeah" and edit distance of 2
|
||||
* // (change "e" to "a", delete "o")
|
||||
* ```
|
||||
*
|
||||
* @param key The search key
|
||||
* @param maxEditDistance The maximum edit distance (Levenshtein)
|
||||
* @return A Map of the matching keys to their value and edit distance
|
||||
*/
|
||||
SearchableMap.prototype.fuzzyGet = function (key, maxEditDistance) {
|
||||
return fuzzySearch(this._tree, key, maxEditDistance);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get
|
||||
* @param key Key to get
|
||||
* @return Value associated to the key, or `undefined` if the key is not
|
||||
* found.
|
||||
*/
|
||||
SearchableMap.prototype.get = function (key) {
|
||||
var node = lookup(this._tree, key);
|
||||
return node !== undefined ? node.get(LEAF) : undefined;
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has
|
||||
* @param key Key
|
||||
* @return True if the key is in the map, false otherwise
|
||||
*/
|
||||
SearchableMap.prototype.has = function (key) {
|
||||
var node = lookup(this._tree, key);
|
||||
return node !== undefined && node.has(LEAF);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys
|
||||
* @return An `Iterable` iterating through keys
|
||||
*/
|
||||
SearchableMap.prototype.keys = function () {
|
||||
return new TreeIterator(this, KEYS);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set
|
||||
* @param key Key to set
|
||||
* @param value Value to associate to the key
|
||||
* @return The [[SearchableMap]] itself, to allow chaining
|
||||
*/
|
||||
SearchableMap.prototype.set = function (key, value) {
|
||||
if (typeof key !== 'string') {
|
||||
throw new Error('key must be a string');
|
||||
}
|
||||
this._size = undefined;
|
||||
var node = createPath(this._tree, key);
|
||||
node.set(LEAF, value);
|
||||
return this;
|
||||
};
|
||||
Object.defineProperty(SearchableMap.prototype, "size", {
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size
|
||||
*/
|
||||
get: function () {
|
||||
if (this._size) {
|
||||
return this._size;
|
||||
}
|
||||
/** @ignore */
|
||||
this._size = 0;
|
||||
var iter = this.entries();
|
||||
while (!iter.next().done)
|
||||
this._size += 1;
|
||||
return this._size;
|
||||
},
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
/**
|
||||
* Updates the value at the given key using the provided function. The function
|
||||
* is called with the current value at the key, and its return value is used as
|
||||
* the new value to be set.
|
||||
*
|
||||
* ### Example:
|
||||
*
|
||||
* ```javascript
|
||||
* // Increment the current value by one
|
||||
* searchableMap.update('somekey', (currentValue) => currentValue == null ? 0 : currentValue + 1)
|
||||
* ```
|
||||
*
|
||||
* If the value at the given key is or will be an object, it might not require
|
||||
* re-assignment. In that case it is better to use `fetch()`, because it is
|
||||
* faster.
|
||||
*
|
||||
* @param key The key to update
|
||||
* @param fn The function used to compute the new value from the current one
|
||||
* @return The [[SearchableMap]] itself, to allow chaining
|
||||
*/
|
||||
SearchableMap.prototype.update = function (key, fn) {
|
||||
if (typeof key !== 'string') {
|
||||
throw new Error('key must be a string');
|
||||
}
|
||||
this._size = undefined;
|
||||
var node = createPath(this._tree, key);
|
||||
node.set(LEAF, fn(node.get(LEAF)));
|
||||
return this;
|
||||
};
|
||||
/**
|
||||
* Fetches the value of the given key. If the value does not exist, calls the
|
||||
* given function to create a new value, which is inserted at the given key
|
||||
* and subsequently returned.
|
||||
*
|
||||
* ### Example:
|
||||
*
|
||||
* ```javascript
|
||||
* const map = searchableMap.fetch('somekey', () => new Map())
|
||||
* map.set('foo', 'bar')
|
||||
* ```
|
||||
*
|
||||
* @param key The key to update
|
||||
* @param defaultValue A function that creates a new value if the key does not exist
|
||||
* @return The existing or new value at the given key
|
||||
*/
|
||||
SearchableMap.prototype.fetch = function (key, initial) {
|
||||
if (typeof key !== 'string') {
|
||||
throw new Error('key must be a string');
|
||||
}
|
||||
this._size = undefined;
|
||||
var node = createPath(this._tree, key);
|
||||
var value = node.get(LEAF);
|
||||
if (value === undefined) {
|
||||
node.set(LEAF, value = initial());
|
||||
}
|
||||
return value;
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/values
|
||||
* @return An `Iterable` iterating through values.
|
||||
*/
|
||||
SearchableMap.prototype.values = function () {
|
||||
return new TreeIterator(this, VALUES);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/@@iterator
|
||||
*/
|
||||
SearchableMap.prototype[Symbol.iterator] = function () {
|
||||
return this.entries();
|
||||
};
|
||||
/**
|
||||
* Creates a [[SearchableMap]] from an `Iterable` of entries
|
||||
*
|
||||
* @param entries Entries to be inserted in the [[SearchableMap]]
|
||||
* @return A new [[SearchableMap]] with the given entries
|
||||
*/
|
||||
SearchableMap.from = function (entries) {
|
||||
var e_3, _a;
|
||||
var tree = new SearchableMap();
|
||||
try {
|
||||
for (var entries_1 = __values(entries), entries_1_1 = entries_1.next(); !entries_1_1.done; entries_1_1 = entries_1.next()) {
|
||||
var _b = __read(entries_1_1.value, 2), key = _b[0], value = _b[1];
|
||||
tree.set(key, value);
|
||||
}
|
||||
}
|
||||
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (entries_1_1 && !entries_1_1.done && (_a = entries_1.return)) _a.call(entries_1);
|
||||
}
|
||||
finally { if (e_3) throw e_3.error; }
|
||||
}
|
||||
return tree;
|
||||
};
|
||||
/**
|
||||
* Creates a [[SearchableMap]] from the iterable properties of a JavaScript object
|
||||
*
|
||||
* @param object Object of entries for the [[SearchableMap]]
|
||||
* @return A new [[SearchableMap]] with the given entries
|
||||
*/
|
||||
SearchableMap.fromObject = function (object) {
|
||||
return SearchableMap.from(Object.entries(object));
|
||||
};
|
||||
return SearchableMap;
|
||||
}());
|
||||
var trackDown = function (tree, key, path) {
|
||||
var e_4, _a;
|
||||
if (path === void 0) { path = []; }
|
||||
if (key.length === 0 || tree == null) {
|
||||
return [tree, path];
|
||||
}
|
||||
try {
|
||||
for (var _b = __values(tree.keys()), _c = _b.next(); !_c.done; _c = _b.next()) {
|
||||
var k = _c.value;
|
||||
if (k !== LEAF && key.startsWith(k)) {
|
||||
path.push([tree, k]); // performance: update in place
|
||||
return trackDown(tree.get(k), key.slice(k.length), path);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||
}
|
||||
finally { if (e_4) throw e_4.error; }
|
||||
}
|
||||
path.push([tree, key]); // performance: update in place
|
||||
return trackDown(undefined, '', path);
|
||||
};
|
||||
var lookup = function (tree, key) {
|
||||
var e_5, _a;
|
||||
if (key.length === 0 || tree == null) {
|
||||
return tree;
|
||||
}
|
||||
try {
|
||||
for (var _b = __values(tree.keys()), _c = _b.next(); !_c.done; _c = _b.next()) {
|
||||
var k = _c.value;
|
||||
if (k !== LEAF && key.startsWith(k)) {
|
||||
return lookup(tree.get(k), key.slice(k.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||
}
|
||||
finally { if (e_5) throw e_5.error; }
|
||||
}
|
||||
};
|
||||
// Create a path in the radix tree for the given key, and returns the deepest
|
||||
// node. This function is in the hot path for indexing. It avoids unnecessary
|
||||
// string operations and recursion for performance.
|
||||
var createPath = function (node, key) {
|
||||
var e_6, _a;
|
||||
var keyLength = key.length;
|
||||
outer: for (var pos = 0; node && pos < keyLength;) {
|
||||
try {
|
||||
for (var _b = (e_6 = void 0, __values(node.keys())), _c = _b.next(); !_c.done; _c = _b.next()) {
|
||||
var k = _c.value;
|
||||
// Check whether this key is a candidate: the first characters must match.
|
||||
if (k !== LEAF && key[pos] === k[0]) {
|
||||
var len = Math.min(keyLength - pos, k.length);
|
||||
// Advance offset to the point where key and k no longer match.
|
||||
var offset = 1;
|
||||
while (offset < len && key[pos + offset] === k[offset])
|
||||
++offset;
|
||||
var child_1 = node.get(k);
|
||||
if (offset === k.length) {
|
||||
// The existing key is shorter than the key we need to create.
|
||||
node = child_1;
|
||||
}
|
||||
else {
|
||||
// Partial match: we need to insert an intermediate node to contain
|
||||
// both the existing subtree and the new node.
|
||||
var intermediate = new Map();
|
||||
intermediate.set(k.slice(offset), child_1);
|
||||
node.set(key.slice(pos, pos + offset), intermediate);
|
||||
node.delete(k);
|
||||
node = intermediate;
|
||||
}
|
||||
pos += offset;
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_6_1) { e_6 = { error: e_6_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||
}
|
||||
finally { if (e_6) throw e_6.error; }
|
||||
}
|
||||
// Create a final child node to contain the final suffix of the key.
|
||||
var child = new Map();
|
||||
node.set(key.slice(pos), child);
|
||||
return child;
|
||||
}
|
||||
return node;
|
||||
};
|
||||
var remove = function (tree, key) {
|
||||
var _a = __read(trackDown(tree, key), 2), node = _a[0], path = _a[1];
|
||||
if (node === undefined) {
|
||||
return;
|
||||
}
|
||||
node.delete(LEAF);
|
||||
if (node.size === 0) {
|
||||
cleanup(path);
|
||||
}
|
||||
else if (node.size === 1) {
|
||||
var _b = __read(node.entries().next().value, 2), key_1 = _b[0], value = _b[1];
|
||||
merge(path, key_1, value);
|
||||
}
|
||||
};
|
||||
var cleanup = function (path) {
|
||||
if (path.length === 0) {
|
||||
return;
|
||||
}
|
||||
var _a = __read(last(path), 2), node = _a[0], key = _a[1];
|
||||
node.delete(key);
|
||||
if (node.size === 0) {
|
||||
cleanup(path.slice(0, -1));
|
||||
}
|
||||
else if (node.size === 1) {
|
||||
var _b = __read(node.entries().next().value, 2), key_2 = _b[0], value = _b[1];
|
||||
if (key_2 !== LEAF) {
|
||||
merge(path.slice(0, -1), key_2, value);
|
||||
}
|
||||
}
|
||||
};
|
||||
var merge = function (path, key, value) {
|
||||
if (path.length === 0) {
|
||||
return;
|
||||
}
|
||||
var _a = __read(last(path), 2), node = _a[0], nodeKey = _a[1];
|
||||
node.set(nodeKey + key, value);
|
||||
node.delete(nodeKey);
|
||||
};
|
||||
var last = function (array) {
|
||||
return array[array.length - 1];
|
||||
};
|
||||
|
||||
module.exports = SearchableMap;
|
||||
//# sourceMappingURL=SearchableMap.cjs.map
|
1
node_modules/minisearch/dist/cjs/SearchableMap.cjs.map
generated
vendored
Normal file
1
node_modules/minisearch/dist/cjs/SearchableMap.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2450
node_modules/minisearch/dist/cjs/index.cjs
generated
vendored
Normal file
2450
node_modules/minisearch/dist/cjs/index.cjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
node_modules/minisearch/dist/cjs/index.cjs.map
generated
vendored
Normal file
1
node_modules/minisearch/dist/cjs/index.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
678
node_modules/minisearch/dist/es/SearchableMap.js
generated
vendored
Normal file
678
node_modules/minisearch/dist/es/SearchableMap.js
generated
vendored
Normal file
|
@ -0,0 +1,678 @@
|
|||
/******************************************************************************
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
***************************************************************************** */
|
||||
/* global Reflect, Promise */
|
||||
|
||||
|
||||
function __values(o) {
|
||||
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
||||
if (m) return m.call(o);
|
||||
if (o && typeof o.length === "number") return {
|
||||
next: function () {
|
||||
if (o && i >= o.length) o = void 0;
|
||||
return { value: o && o[i++], done: !o };
|
||||
}
|
||||
};
|
||||
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
||||
}
|
||||
|
||||
function __read(o, n) {
|
||||
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
||||
if (!m) return o;
|
||||
var i = m.call(o), r, ar = [], e;
|
||||
try {
|
||||
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
||||
}
|
||||
catch (error) { e = { error: error }; }
|
||||
finally {
|
||||
try {
|
||||
if (r && !r.done && (m = i["return"])) m.call(i);
|
||||
}
|
||||
finally { if (e) throw e.error; }
|
||||
}
|
||||
return ar;
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
var ENTRIES = 'ENTRIES';
|
||||
/** @ignore */
|
||||
var KEYS = 'KEYS';
|
||||
/** @ignore */
|
||||
var VALUES = 'VALUES';
|
||||
/** @ignore */
|
||||
var LEAF = '';
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
var TreeIterator = /** @class */ (function () {
|
||||
function TreeIterator(set, type) {
|
||||
var node = set._tree;
|
||||
var keys = Array.from(node.keys());
|
||||
this.set = set;
|
||||
this._type = type;
|
||||
this._path = keys.length > 0 ? [{ node: node, keys: keys }] : [];
|
||||
}
|
||||
TreeIterator.prototype.next = function () {
|
||||
var value = this.dive();
|
||||
this.backtrack();
|
||||
return value;
|
||||
};
|
||||
TreeIterator.prototype.dive = function () {
|
||||
if (this._path.length === 0) {
|
||||
return { done: true, value: undefined };
|
||||
}
|
||||
var _a = last$1(this._path), node = _a.node, keys = _a.keys;
|
||||
if (last$1(keys) === LEAF) {
|
||||
return { done: false, value: this.result() };
|
||||
}
|
||||
var child = node.get(last$1(keys));
|
||||
this._path.push({ node: child, keys: Array.from(child.keys()) });
|
||||
return this.dive();
|
||||
};
|
||||
TreeIterator.prototype.backtrack = function () {
|
||||
if (this._path.length === 0) {
|
||||
return;
|
||||
}
|
||||
var keys = last$1(this._path).keys;
|
||||
keys.pop();
|
||||
if (keys.length > 0) {
|
||||
return;
|
||||
}
|
||||
this._path.pop();
|
||||
this.backtrack();
|
||||
};
|
||||
TreeIterator.prototype.key = function () {
|
||||
return this.set._prefix + this._path
|
||||
.map(function (_a) {
|
||||
var keys = _a.keys;
|
||||
return last$1(keys);
|
||||
})
|
||||
.filter(function (key) { return key !== LEAF; })
|
||||
.join('');
|
||||
};
|
||||
TreeIterator.prototype.value = function () {
|
||||
return last$1(this._path).node.get(LEAF);
|
||||
};
|
||||
TreeIterator.prototype.result = function () {
|
||||
switch (this._type) {
|
||||
case VALUES: return this.value();
|
||||
case KEYS: return this.key();
|
||||
default: return [this.key(), this.value()];
|
||||
}
|
||||
};
|
||||
TreeIterator.prototype[Symbol.iterator] = function () {
|
||||
return this;
|
||||
};
|
||||
return TreeIterator;
|
||||
}());
|
||||
var last$1 = function (array) {
|
||||
return array[array.length - 1];
|
||||
};
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
var fuzzySearch = function (node, query, maxDistance) {
|
||||
var results = new Map();
|
||||
if (query === undefined)
|
||||
return results;
|
||||
// Number of columns in the Levenshtein matrix.
|
||||
var n = query.length + 1;
|
||||
// Matching terms can never be longer than N + maxDistance.
|
||||
var m = n + maxDistance;
|
||||
// Fill first matrix row and column with numbers: 0 1 2 3 ...
|
||||
var matrix = new Uint8Array(m * n).fill(maxDistance + 1);
|
||||
for (var j = 0; j < n; ++j)
|
||||
matrix[j] = j;
|
||||
for (var i = 1; i < m; ++i)
|
||||
matrix[i * n] = i;
|
||||
recurse(node, query, maxDistance, results, matrix, 1, n, '');
|
||||
return results;
|
||||
};
|
||||
// Modified version of http://stevehanov.ca/blog/?id=114
|
||||
// This builds a Levenshtein matrix for a given query and continuously updates
|
||||
// it for nodes in the radix tree that fall within the given maximum edit
|
||||
// distance. Keeping the same matrix around is beneficial especially for larger
|
||||
// edit distances.
|
||||
//
|
||||
// k a t e <-- query
|
||||
// 0 1 2 3 4
|
||||
// c 1 1 2 3 4
|
||||
// a 2 2 1 2 3
|
||||
// t 3 3 2 1 [2] <-- edit distance
|
||||
// ^
|
||||
// ^ term in radix tree, rows are added and removed as needed
|
||||
var recurse = function (node, query, maxDistance, results, matrix, m, n, prefix) {
|
||||
var e_1, _a;
|
||||
var offset = m * n;
|
||||
try {
|
||||
key: for (var _b = __values(node.keys()), _c = _b.next(); !_c.done; _c = _b.next()) {
|
||||
var key = _c.value;
|
||||
if (key === LEAF) {
|
||||
// We've reached a leaf node. Check if the edit distance acceptable and
|
||||
// store the result if it is.
|
||||
var distance = matrix[offset - 1];
|
||||
if (distance <= maxDistance) {
|
||||
results.set(prefix, [node.get(key), distance]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Iterate over all characters in the key. Update the Levenshtein matrix
|
||||
// and check if the minimum distance in the last row is still within the
|
||||
// maximum edit distance. If it is, we can recurse over all child nodes.
|
||||
var i = m;
|
||||
for (var pos = 0; pos < key.length; ++pos, ++i) {
|
||||
var char = key[pos];
|
||||
var thisRowOffset = n * i;
|
||||
var prevRowOffset = thisRowOffset - n;
|
||||
// Set the first column based on the previous row, and initialize the
|
||||
// minimum distance in the current row.
|
||||
var minDistance = matrix[thisRowOffset];
|
||||
var jmin = Math.max(0, i - maxDistance - 1);
|
||||
var jmax = Math.min(n - 1, i + maxDistance);
|
||||
// Iterate over remaining columns (characters in the query).
|
||||
for (var j = jmin; j < jmax; ++j) {
|
||||
var different = char !== query[j];
|
||||
// It might make sense to only read the matrix positions used for
|
||||
// deletion/insertion if the characters are different. But we want to
|
||||
// avoid conditional reads for performance reasons.
|
||||
var rpl = matrix[prevRowOffset + j] + +different;
|
||||
var del = matrix[prevRowOffset + j + 1] + 1;
|
||||
var ins = matrix[thisRowOffset + j] + 1;
|
||||
var dist = matrix[thisRowOffset + j + 1] = Math.min(rpl, del, ins);
|
||||
if (dist < minDistance)
|
||||
minDistance = dist;
|
||||
}
|
||||
// Because distance will never decrease, we can stop. There will be no
|
||||
// matching child nodes.
|
||||
if (minDistance > maxDistance) {
|
||||
continue key;
|
||||
}
|
||||
}
|
||||
recurse(node.get(key), query, maxDistance, results, matrix, i, n, prefix + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A class implementing the same interface as a standard JavaScript
|
||||
* [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)
|
||||
* with string keys, but adding support for efficiently searching entries with
|
||||
* prefix or fuzzy search. This class is used internally by [[MiniSearch]] as
|
||||
* the inverted index data structure. The implementation is a radix tree
|
||||
* (compressed prefix tree).
|
||||
*
|
||||
* Since this class can be of general utility beyond _MiniSearch_, it is
|
||||
* exported by the `minisearch` package and can be imported (or required) as
|
||||
* `minisearch/SearchableMap`.
|
||||
*
|
||||
* @typeParam T The type of the values stored in the map.
|
||||
*/
|
||||
var SearchableMap = /** @class */ (function () {
|
||||
/**
|
||||
* The constructor is normally called without arguments, creating an empty
|
||||
* map. In order to create a [[SearchableMap]] from an iterable or from an
|
||||
* object, check [[SearchableMap.from]] and [[SearchableMap.fromObject]].
|
||||
*
|
||||
* The constructor arguments are for internal use, when creating derived
|
||||
* mutable views of a map at a prefix.
|
||||
*/
|
||||
function SearchableMap(tree, prefix) {
|
||||
if (tree === void 0) { tree = new Map(); }
|
||||
if (prefix === void 0) { prefix = ''; }
|
||||
this._size = undefined;
|
||||
this._tree = tree;
|
||||
this._prefix = prefix;
|
||||
}
|
||||
/**
|
||||
* Creates and returns a mutable view of this [[SearchableMap]], containing only
|
||||
* entries that share the given prefix.
|
||||
*
|
||||
* ### Usage:
|
||||
*
|
||||
* ```javascript
|
||||
* let map = new SearchableMap()
|
||||
* map.set("unicorn", 1)
|
||||
* map.set("universe", 2)
|
||||
* map.set("university", 3)
|
||||
* map.set("unique", 4)
|
||||
* map.set("hello", 5)
|
||||
*
|
||||
* let uni = map.atPrefix("uni")
|
||||
* uni.get("unique") // => 4
|
||||
* uni.get("unicorn") // => 1
|
||||
* uni.get("hello") // => undefined
|
||||
*
|
||||
* let univer = map.atPrefix("univer")
|
||||
* univer.get("unique") // => undefined
|
||||
* univer.get("universe") // => 2
|
||||
* univer.get("university") // => 3
|
||||
* ```
|
||||
*
|
||||
* @param prefix The prefix
|
||||
* @return A [[SearchableMap]] representing a mutable view of the original Map at the given prefix
|
||||
*/
|
||||
SearchableMap.prototype.atPrefix = function (prefix) {
|
||||
var e_1, _a;
|
||||
if (!prefix.startsWith(this._prefix)) {
|
||||
throw new Error('Mismatched prefix');
|
||||
}
|
||||
var _b = __read(trackDown(this._tree, prefix.slice(this._prefix.length)), 2), node = _b[0], path = _b[1];
|
||||
if (node === undefined) {
|
||||
var _c = __read(last(path), 2), parentNode = _c[0], key = _c[1];
|
||||
try {
|
||||
for (var _d = __values(parentNode.keys()), _e = _d.next(); !_e.done; _e = _d.next()) {
|
||||
var k = _e.value;
|
||||
if (k !== LEAF && k.startsWith(key)) {
|
||||
var node_1 = new Map();
|
||||
node_1.set(k.slice(key.length), parentNode.get(k));
|
||||
return new SearchableMap(node_1, prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
}
|
||||
}
|
||||
return new SearchableMap(node, prefix);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear
|
||||
*/
|
||||
SearchableMap.prototype.clear = function () {
|
||||
this._size = undefined;
|
||||
this._tree.clear();
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete
|
||||
* @param key Key to delete
|
||||
*/
|
||||
SearchableMap.prototype.delete = function (key) {
|
||||
this._size = undefined;
|
||||
return remove(this._tree, key);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries
|
||||
* @return An iterator iterating through `[key, value]` entries.
|
||||
*/
|
||||
SearchableMap.prototype.entries = function () {
|
||||
return new TreeIterator(this, ENTRIES);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach
|
||||
* @param fn Iteration function
|
||||
*/
|
||||
SearchableMap.prototype.forEach = function (fn) {
|
||||
var e_2, _a;
|
||||
try {
|
||||
for (var _b = __values(this), _c = _b.next(); !_c.done; _c = _b.next()) {
|
||||
var _d = __read(_c.value, 2), key = _d[0], value = _d[1];
|
||||
fn(key, value, this);
|
||||
}
|
||||
}
|
||||
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||
}
|
||||
finally { if (e_2) throw e_2.error; }
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Returns a Map of all the entries that have a key within the given edit
|
||||
* distance from the search key. The keys of the returned Map are the matching
|
||||
* keys, while the values are two-element arrays where the first element is
|
||||
* the value associated to the key, and the second is the edit distance of the
|
||||
* key to the search key.
|
||||
*
|
||||
* ### Usage:
|
||||
*
|
||||
* ```javascript
|
||||
* let map = new SearchableMap()
|
||||
* map.set('hello', 'world')
|
||||
* map.set('hell', 'yeah')
|
||||
* map.set('ciao', 'mondo')
|
||||
*
|
||||
* // Get all entries that match the key 'hallo' with a maximum edit distance of 2
|
||||
* map.fuzzyGet('hallo', 2)
|
||||
* // => Map(2) { 'hello' => ['world', 1], 'hell' => ['yeah', 2] }
|
||||
*
|
||||
* // In the example, the "hello" key has value "world" and edit distance of 1
|
||||
* // (change "e" to "a"), the key "hell" has value "yeah" and edit distance of 2
|
||||
* // (change "e" to "a", delete "o")
|
||||
* ```
|
||||
*
|
||||
* @param key The search key
|
||||
* @param maxEditDistance The maximum edit distance (Levenshtein)
|
||||
* @return A Map of the matching keys to their value and edit distance
|
||||
*/
|
||||
SearchableMap.prototype.fuzzyGet = function (key, maxEditDistance) {
|
||||
return fuzzySearch(this._tree, key, maxEditDistance);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get
|
||||
* @param key Key to get
|
||||
* @return Value associated to the key, or `undefined` if the key is not
|
||||
* found.
|
||||
*/
|
||||
SearchableMap.prototype.get = function (key) {
|
||||
var node = lookup(this._tree, key);
|
||||
return node !== undefined ? node.get(LEAF) : undefined;
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has
|
||||
* @param key Key
|
||||
* @return True if the key is in the map, false otherwise
|
||||
*/
|
||||
SearchableMap.prototype.has = function (key) {
|
||||
var node = lookup(this._tree, key);
|
||||
return node !== undefined && node.has(LEAF);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys
|
||||
* @return An `Iterable` iterating through keys
|
||||
*/
|
||||
SearchableMap.prototype.keys = function () {
|
||||
return new TreeIterator(this, KEYS);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set
|
||||
* @param key Key to set
|
||||
* @param value Value to associate to the key
|
||||
* @return The [[SearchableMap]] itself, to allow chaining
|
||||
*/
|
||||
SearchableMap.prototype.set = function (key, value) {
|
||||
if (typeof key !== 'string') {
|
||||
throw new Error('key must be a string');
|
||||
}
|
||||
this._size = undefined;
|
||||
var node = createPath(this._tree, key);
|
||||
node.set(LEAF, value);
|
||||
return this;
|
||||
};
|
||||
Object.defineProperty(SearchableMap.prototype, "size", {
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size
|
||||
*/
|
||||
get: function () {
|
||||
if (this._size) {
|
||||
return this._size;
|
||||
}
|
||||
/** @ignore */
|
||||
this._size = 0;
|
||||
var iter = this.entries();
|
||||
while (!iter.next().done)
|
||||
this._size += 1;
|
||||
return this._size;
|
||||
},
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
/**
|
||||
* Updates the value at the given key using the provided function. The function
|
||||
* is called with the current value at the key, and its return value is used as
|
||||
* the new value to be set.
|
||||
*
|
||||
* ### Example:
|
||||
*
|
||||
* ```javascript
|
||||
* // Increment the current value by one
|
||||
* searchableMap.update('somekey', (currentValue) => currentValue == null ? 0 : currentValue + 1)
|
||||
* ```
|
||||
*
|
||||
* If the value at the given key is or will be an object, it might not require
|
||||
* re-assignment. In that case it is better to use `fetch()`, because it is
|
||||
* faster.
|
||||
*
|
||||
* @param key The key to update
|
||||
* @param fn The function used to compute the new value from the current one
|
||||
* @return The [[SearchableMap]] itself, to allow chaining
|
||||
*/
|
||||
SearchableMap.prototype.update = function (key, fn) {
|
||||
if (typeof key !== 'string') {
|
||||
throw new Error('key must be a string');
|
||||
}
|
||||
this._size = undefined;
|
||||
var node = createPath(this._tree, key);
|
||||
node.set(LEAF, fn(node.get(LEAF)));
|
||||
return this;
|
||||
};
|
||||
/**
|
||||
* Fetches the value of the given key. If the value does not exist, calls the
|
||||
* given function to create a new value, which is inserted at the given key
|
||||
* and subsequently returned.
|
||||
*
|
||||
* ### Example:
|
||||
*
|
||||
* ```javascript
|
||||
* const map = searchableMap.fetch('somekey', () => new Map())
|
||||
* map.set('foo', 'bar')
|
||||
* ```
|
||||
*
|
||||
* @param key The key to update
|
||||
* @param defaultValue A function that creates a new value if the key does not exist
|
||||
* @return The existing or new value at the given key
|
||||
*/
|
||||
SearchableMap.prototype.fetch = function (key, initial) {
|
||||
if (typeof key !== 'string') {
|
||||
throw new Error('key must be a string');
|
||||
}
|
||||
this._size = undefined;
|
||||
var node = createPath(this._tree, key);
|
||||
var value = node.get(LEAF);
|
||||
if (value === undefined) {
|
||||
node.set(LEAF, value = initial());
|
||||
}
|
||||
return value;
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/values
|
||||
* @return An `Iterable` iterating through values.
|
||||
*/
|
||||
SearchableMap.prototype.values = function () {
|
||||
return new TreeIterator(this, VALUES);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/@@iterator
|
||||
*/
|
||||
SearchableMap.prototype[Symbol.iterator] = function () {
|
||||
return this.entries();
|
||||
};
|
||||
/**
|
||||
* Creates a [[SearchableMap]] from an `Iterable` of entries
|
||||
*
|
||||
* @param entries Entries to be inserted in the [[SearchableMap]]
|
||||
* @return A new [[SearchableMap]] with the given entries
|
||||
*/
|
||||
SearchableMap.from = function (entries) {
|
||||
var e_3, _a;
|
||||
var tree = new SearchableMap();
|
||||
try {
|
||||
for (var entries_1 = __values(entries), entries_1_1 = entries_1.next(); !entries_1_1.done; entries_1_1 = entries_1.next()) {
|
||||
var _b = __read(entries_1_1.value, 2), key = _b[0], value = _b[1];
|
||||
tree.set(key, value);
|
||||
}
|
||||
}
|
||||
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (entries_1_1 && !entries_1_1.done && (_a = entries_1.return)) _a.call(entries_1);
|
||||
}
|
||||
finally { if (e_3) throw e_3.error; }
|
||||
}
|
||||
return tree;
|
||||
};
|
||||
/**
|
||||
* Creates a [[SearchableMap]] from the iterable properties of a JavaScript object
|
||||
*
|
||||
* @param object Object of entries for the [[SearchableMap]]
|
||||
* @return A new [[SearchableMap]] with the given entries
|
||||
*/
|
||||
SearchableMap.fromObject = function (object) {
|
||||
return SearchableMap.from(Object.entries(object));
|
||||
};
|
||||
return SearchableMap;
|
||||
}());
|
||||
var trackDown = function (tree, key, path) {
|
||||
var e_4, _a;
|
||||
if (path === void 0) { path = []; }
|
||||
if (key.length === 0 || tree == null) {
|
||||
return [tree, path];
|
||||
}
|
||||
try {
|
||||
for (var _b = __values(tree.keys()), _c = _b.next(); !_c.done; _c = _b.next()) {
|
||||
var k = _c.value;
|
||||
if (k !== LEAF && key.startsWith(k)) {
|
||||
path.push([tree, k]); // performance: update in place
|
||||
return trackDown(tree.get(k), key.slice(k.length), path);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||
}
|
||||
finally { if (e_4) throw e_4.error; }
|
||||
}
|
||||
path.push([tree, key]); // performance: update in place
|
||||
return trackDown(undefined, '', path);
|
||||
};
|
||||
var lookup = function (tree, key) {
|
||||
var e_5, _a;
|
||||
if (key.length === 0 || tree == null) {
|
||||
return tree;
|
||||
}
|
||||
try {
|
||||
for (var _b = __values(tree.keys()), _c = _b.next(); !_c.done; _c = _b.next()) {
|
||||
var k = _c.value;
|
||||
if (k !== LEAF && key.startsWith(k)) {
|
||||
return lookup(tree.get(k), key.slice(k.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||
}
|
||||
finally { if (e_5) throw e_5.error; }
|
||||
}
|
||||
};
|
||||
// Create a path in the radix tree for the given key, and returns the deepest
|
||||
// node. This function is in the hot path for indexing. It avoids unnecessary
|
||||
// string operations and recursion for performance.
|
||||
var createPath = function (node, key) {
|
||||
var e_6, _a;
|
||||
var keyLength = key.length;
|
||||
outer: for (var pos = 0; node && pos < keyLength;) {
|
||||
try {
|
||||
for (var _b = (e_6 = void 0, __values(node.keys())), _c = _b.next(); !_c.done; _c = _b.next()) {
|
||||
var k = _c.value;
|
||||
// Check whether this key is a candidate: the first characters must match.
|
||||
if (k !== LEAF && key[pos] === k[0]) {
|
||||
var len = Math.min(keyLength - pos, k.length);
|
||||
// Advance offset to the point where key and k no longer match.
|
||||
var offset = 1;
|
||||
while (offset < len && key[pos + offset] === k[offset])
|
||||
++offset;
|
||||
var child_1 = node.get(k);
|
||||
if (offset === k.length) {
|
||||
// The existing key is shorter than the key we need to create.
|
||||
node = child_1;
|
||||
}
|
||||
else {
|
||||
// Partial match: we need to insert an intermediate node to contain
|
||||
// both the existing subtree and the new node.
|
||||
var intermediate = new Map();
|
||||
intermediate.set(k.slice(offset), child_1);
|
||||
node.set(key.slice(pos, pos + offset), intermediate);
|
||||
node.delete(k);
|
||||
node = intermediate;
|
||||
}
|
||||
pos += offset;
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_6_1) { e_6 = { error: e_6_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||
}
|
||||
finally { if (e_6) throw e_6.error; }
|
||||
}
|
||||
// Create a final child node to contain the final suffix of the key.
|
||||
var child = new Map();
|
||||
node.set(key.slice(pos), child);
|
||||
return child;
|
||||
}
|
||||
return node;
|
||||
};
|
||||
var remove = function (tree, key) {
|
||||
var _a = __read(trackDown(tree, key), 2), node = _a[0], path = _a[1];
|
||||
if (node === undefined) {
|
||||
return;
|
||||
}
|
||||
node.delete(LEAF);
|
||||
if (node.size === 0) {
|
||||
cleanup(path);
|
||||
}
|
||||
else if (node.size === 1) {
|
||||
var _b = __read(node.entries().next().value, 2), key_1 = _b[0], value = _b[1];
|
||||
merge(path, key_1, value);
|
||||
}
|
||||
};
|
||||
var cleanup = function (path) {
|
||||
if (path.length === 0) {
|
||||
return;
|
||||
}
|
||||
var _a = __read(last(path), 2), node = _a[0], key = _a[1];
|
||||
node.delete(key);
|
||||
if (node.size === 0) {
|
||||
cleanup(path.slice(0, -1));
|
||||
}
|
||||
else if (node.size === 1) {
|
||||
var _b = __read(node.entries().next().value, 2), key_2 = _b[0], value = _b[1];
|
||||
if (key_2 !== LEAF) {
|
||||
merge(path.slice(0, -1), key_2, value);
|
||||
}
|
||||
}
|
||||
};
|
||||
var merge = function (path, key, value) {
|
||||
if (path.length === 0) {
|
||||
return;
|
||||
}
|
||||
var _a = __read(last(path), 2), node = _a[0], nodeKey = _a[1];
|
||||
node.set(nodeKey + key, value);
|
||||
node.delete(nodeKey);
|
||||
};
|
||||
var last = function (array) {
|
||||
return array[array.length - 1];
|
||||
};
|
||||
|
||||
export { SearchableMap as default };
|
||||
//# sourceMappingURL=SearchableMap.js.map
|
1
node_modules/minisearch/dist/es/SearchableMap.js.map
generated
vendored
Normal file
1
node_modules/minisearch/dist/es/SearchableMap.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2448
node_modules/minisearch/dist/es/index.js
generated
vendored
Normal file
2448
node_modules/minisearch/dist/es/index.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
node_modules/minisearch/dist/es/index.js.map
generated
vendored
Normal file
1
node_modules/minisearch/dist/es/index.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
678
node_modules/minisearch/dist/es5m/SearchableMap.js
generated
vendored
Normal file
678
node_modules/minisearch/dist/es5m/SearchableMap.js
generated
vendored
Normal file
|
@ -0,0 +1,678 @@
|
|||
/******************************************************************************
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
***************************************************************************** */
|
||||
/* global Reflect, Promise */
|
||||
|
||||
|
||||
function __values(o) {
|
||||
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
||||
if (m) return m.call(o);
|
||||
if (o && typeof o.length === "number") return {
|
||||
next: function () {
|
||||
if (o && i >= o.length) o = void 0;
|
||||
return { value: o && o[i++], done: !o };
|
||||
}
|
||||
};
|
||||
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
||||
}
|
||||
|
||||
function __read(o, n) {
|
||||
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
||||
if (!m) return o;
|
||||
var i = m.call(o), r, ar = [], e;
|
||||
try {
|
||||
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
||||
}
|
||||
catch (error) { e = { error: error }; }
|
||||
finally {
|
||||
try {
|
||||
if (r && !r.done && (m = i["return"])) m.call(i);
|
||||
}
|
||||
finally { if (e) throw e.error; }
|
||||
}
|
||||
return ar;
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
var ENTRIES = 'ENTRIES';
|
||||
/** @ignore */
|
||||
var KEYS = 'KEYS';
|
||||
/** @ignore */
|
||||
var VALUES = 'VALUES';
|
||||
/** @ignore */
|
||||
var LEAF = '';
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
var TreeIterator = /** @class */ (function () {
|
||||
function TreeIterator(set, type) {
|
||||
var node = set._tree;
|
||||
var keys = Array.from(node.keys());
|
||||
this.set = set;
|
||||
this._type = type;
|
||||
this._path = keys.length > 0 ? [{ node: node, keys: keys }] : [];
|
||||
}
|
||||
TreeIterator.prototype.next = function () {
|
||||
var value = this.dive();
|
||||
this.backtrack();
|
||||
return value;
|
||||
};
|
||||
TreeIterator.prototype.dive = function () {
|
||||
if (this._path.length === 0) {
|
||||
return { done: true, value: undefined };
|
||||
}
|
||||
var _a = last$1(this._path), node = _a.node, keys = _a.keys;
|
||||
if (last$1(keys) === LEAF) {
|
||||
return { done: false, value: this.result() };
|
||||
}
|
||||
var child = node.get(last$1(keys));
|
||||
this._path.push({ node: child, keys: Array.from(child.keys()) });
|
||||
return this.dive();
|
||||
};
|
||||
TreeIterator.prototype.backtrack = function () {
|
||||
if (this._path.length === 0) {
|
||||
return;
|
||||
}
|
||||
var keys = last$1(this._path).keys;
|
||||
keys.pop();
|
||||
if (keys.length > 0) {
|
||||
return;
|
||||
}
|
||||
this._path.pop();
|
||||
this.backtrack();
|
||||
};
|
||||
TreeIterator.prototype.key = function () {
|
||||
return this.set._prefix + this._path
|
||||
.map(function (_a) {
|
||||
var keys = _a.keys;
|
||||
return last$1(keys);
|
||||
})
|
||||
.filter(function (key) { return key !== LEAF; })
|
||||
.join('');
|
||||
};
|
||||
TreeIterator.prototype.value = function () {
|
||||
return last$1(this._path).node.get(LEAF);
|
||||
};
|
||||
TreeIterator.prototype.result = function () {
|
||||
switch (this._type) {
|
||||
case VALUES: return this.value();
|
||||
case KEYS: return this.key();
|
||||
default: return [this.key(), this.value()];
|
||||
}
|
||||
};
|
||||
TreeIterator.prototype[Symbol.iterator] = function () {
|
||||
return this;
|
||||
};
|
||||
return TreeIterator;
|
||||
}());
|
||||
var last$1 = function (array) {
|
||||
return array[array.length - 1];
|
||||
};
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
var fuzzySearch = function (node, query, maxDistance) {
|
||||
var results = new Map();
|
||||
if (query === undefined)
|
||||
return results;
|
||||
// Number of columns in the Levenshtein matrix.
|
||||
var n = query.length + 1;
|
||||
// Matching terms can never be longer than N + maxDistance.
|
||||
var m = n + maxDistance;
|
||||
// Fill first matrix row and column with numbers: 0 1 2 3 ...
|
||||
var matrix = new Uint8Array(m * n).fill(maxDistance + 1);
|
||||
for (var j = 0; j < n; ++j)
|
||||
matrix[j] = j;
|
||||
for (var i = 1; i < m; ++i)
|
||||
matrix[i * n] = i;
|
||||
recurse(node, query, maxDistance, results, matrix, 1, n, '');
|
||||
return results;
|
||||
};
|
||||
// Modified version of http://stevehanov.ca/blog/?id=114
|
||||
// This builds a Levenshtein matrix for a given query and continuously updates
|
||||
// it for nodes in the radix tree that fall within the given maximum edit
|
||||
// distance. Keeping the same matrix around is beneficial especially for larger
|
||||
// edit distances.
|
||||
//
|
||||
// k a t e <-- query
|
||||
// 0 1 2 3 4
|
||||
// c 1 1 2 3 4
|
||||
// a 2 2 1 2 3
|
||||
// t 3 3 2 1 [2] <-- edit distance
|
||||
// ^
|
||||
// ^ term in radix tree, rows are added and removed as needed
|
||||
var recurse = function (node, query, maxDistance, results, matrix, m, n, prefix) {
|
||||
var e_1, _a;
|
||||
var offset = m * n;
|
||||
try {
|
||||
key: for (var _b = __values(node.keys()), _c = _b.next(); !_c.done; _c = _b.next()) {
|
||||
var key = _c.value;
|
||||
if (key === LEAF) {
|
||||
// We've reached a leaf node. Check if the edit distance acceptable and
|
||||
// store the result if it is.
|
||||
var distance = matrix[offset - 1];
|
||||
if (distance <= maxDistance) {
|
||||
results.set(prefix, [node.get(key), distance]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Iterate over all characters in the key. Update the Levenshtein matrix
|
||||
// and check if the minimum distance in the last row is still within the
|
||||
// maximum edit distance. If it is, we can recurse over all child nodes.
|
||||
var i = m;
|
||||
for (var pos = 0; pos < key.length; ++pos, ++i) {
|
||||
var char = key[pos];
|
||||
var thisRowOffset = n * i;
|
||||
var prevRowOffset = thisRowOffset - n;
|
||||
// Set the first column based on the previous row, and initialize the
|
||||
// minimum distance in the current row.
|
||||
var minDistance = matrix[thisRowOffset];
|
||||
var jmin = Math.max(0, i - maxDistance - 1);
|
||||
var jmax = Math.min(n - 1, i + maxDistance);
|
||||
// Iterate over remaining columns (characters in the query).
|
||||
for (var j = jmin; j < jmax; ++j) {
|
||||
var different = char !== query[j];
|
||||
// It might make sense to only read the matrix positions used for
|
||||
// deletion/insertion if the characters are different. But we want to
|
||||
// avoid conditional reads for performance reasons.
|
||||
var rpl = matrix[prevRowOffset + j] + +different;
|
||||
var del = matrix[prevRowOffset + j + 1] + 1;
|
||||
var ins = matrix[thisRowOffset + j] + 1;
|
||||
var dist = matrix[thisRowOffset + j + 1] = Math.min(rpl, del, ins);
|
||||
if (dist < minDistance)
|
||||
minDistance = dist;
|
||||
}
|
||||
// Because distance will never decrease, we can stop. There will be no
|
||||
// matching child nodes.
|
||||
if (minDistance > maxDistance) {
|
||||
continue key;
|
||||
}
|
||||
}
|
||||
recurse(node.get(key), query, maxDistance, results, matrix, i, n, prefix + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A class implementing the same interface as a standard JavaScript
|
||||
* [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)
|
||||
* with string keys, but adding support for efficiently searching entries with
|
||||
* prefix or fuzzy search. This class is used internally by [[MiniSearch]] as
|
||||
* the inverted index data structure. The implementation is a radix tree
|
||||
* (compressed prefix tree).
|
||||
*
|
||||
* Since this class can be of general utility beyond _MiniSearch_, it is
|
||||
* exported by the `minisearch` package and can be imported (or required) as
|
||||
* `minisearch/SearchableMap`.
|
||||
*
|
||||
* @typeParam T The type of the values stored in the map.
|
||||
*/
|
||||
var SearchableMap = /** @class */ (function () {
|
||||
/**
|
||||
* The constructor is normally called without arguments, creating an empty
|
||||
* map. In order to create a [[SearchableMap]] from an iterable or from an
|
||||
* object, check [[SearchableMap.from]] and [[SearchableMap.fromObject]].
|
||||
*
|
||||
* The constructor arguments are for internal use, when creating derived
|
||||
* mutable views of a map at a prefix.
|
||||
*/
|
||||
function SearchableMap(tree, prefix) {
|
||||
if (tree === void 0) { tree = new Map(); }
|
||||
if (prefix === void 0) { prefix = ''; }
|
||||
this._size = undefined;
|
||||
this._tree = tree;
|
||||
this._prefix = prefix;
|
||||
}
|
||||
/**
|
||||
* Creates and returns a mutable view of this [[SearchableMap]], containing only
|
||||
* entries that share the given prefix.
|
||||
*
|
||||
* ### Usage:
|
||||
*
|
||||
* ```javascript
|
||||
* let map = new SearchableMap()
|
||||
* map.set("unicorn", 1)
|
||||
* map.set("universe", 2)
|
||||
* map.set("university", 3)
|
||||
* map.set("unique", 4)
|
||||
* map.set("hello", 5)
|
||||
*
|
||||
* let uni = map.atPrefix("uni")
|
||||
* uni.get("unique") // => 4
|
||||
* uni.get("unicorn") // => 1
|
||||
* uni.get("hello") // => undefined
|
||||
*
|
||||
* let univer = map.atPrefix("univer")
|
||||
* univer.get("unique") // => undefined
|
||||
* univer.get("universe") // => 2
|
||||
* univer.get("university") // => 3
|
||||
* ```
|
||||
*
|
||||
* @param prefix The prefix
|
||||
* @return A [[SearchableMap]] representing a mutable view of the original Map at the given prefix
|
||||
*/
|
||||
SearchableMap.prototype.atPrefix = function (prefix) {
|
||||
var e_1, _a;
|
||||
if (!prefix.startsWith(this._prefix)) {
|
||||
throw new Error('Mismatched prefix');
|
||||
}
|
||||
var _b = __read(trackDown(this._tree, prefix.slice(this._prefix.length)), 2), node = _b[0], path = _b[1];
|
||||
if (node === undefined) {
|
||||
var _c = __read(last(path), 2), parentNode = _c[0], key = _c[1];
|
||||
try {
|
||||
for (var _d = __values(parentNode.keys()), _e = _d.next(); !_e.done; _e = _d.next()) {
|
||||
var k = _e.value;
|
||||
if (k !== LEAF && k.startsWith(key)) {
|
||||
var node_1 = new Map();
|
||||
node_1.set(k.slice(key.length), parentNode.get(k));
|
||||
return new SearchableMap(node_1, prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
}
|
||||
}
|
||||
return new SearchableMap(node, prefix);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear
|
||||
*/
|
||||
SearchableMap.prototype.clear = function () {
|
||||
this._size = undefined;
|
||||
this._tree.clear();
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete
|
||||
* @param key Key to delete
|
||||
*/
|
||||
SearchableMap.prototype.delete = function (key) {
|
||||
this._size = undefined;
|
||||
return remove(this._tree, key);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries
|
||||
* @return An iterator iterating through `[key, value]` entries.
|
||||
*/
|
||||
SearchableMap.prototype.entries = function () {
|
||||
return new TreeIterator(this, ENTRIES);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach
|
||||
* @param fn Iteration function
|
||||
*/
|
||||
SearchableMap.prototype.forEach = function (fn) {
|
||||
var e_2, _a;
|
||||
try {
|
||||
for (var _b = __values(this), _c = _b.next(); !_c.done; _c = _b.next()) {
|
||||
var _d = __read(_c.value, 2), key = _d[0], value = _d[1];
|
||||
fn(key, value, this);
|
||||
}
|
||||
}
|
||||
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||
}
|
||||
finally { if (e_2) throw e_2.error; }
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Returns a Map of all the entries that have a key within the given edit
|
||||
* distance from the search key. The keys of the returned Map are the matching
|
||||
* keys, while the values are two-element arrays where the first element is
|
||||
* the value associated to the key, and the second is the edit distance of the
|
||||
* key to the search key.
|
||||
*
|
||||
* ### Usage:
|
||||
*
|
||||
* ```javascript
|
||||
* let map = new SearchableMap()
|
||||
* map.set('hello', 'world')
|
||||
* map.set('hell', 'yeah')
|
||||
* map.set('ciao', 'mondo')
|
||||
*
|
||||
* // Get all entries that match the key 'hallo' with a maximum edit distance of 2
|
||||
* map.fuzzyGet('hallo', 2)
|
||||
* // => Map(2) { 'hello' => ['world', 1], 'hell' => ['yeah', 2] }
|
||||
*
|
||||
* // In the example, the "hello" key has value "world" and edit distance of 1
|
||||
* // (change "e" to "a"), the key "hell" has value "yeah" and edit distance of 2
|
||||
* // (change "e" to "a", delete "o")
|
||||
* ```
|
||||
*
|
||||
* @param key The search key
|
||||
* @param maxEditDistance The maximum edit distance (Levenshtein)
|
||||
* @return A Map of the matching keys to their value and edit distance
|
||||
*/
|
||||
SearchableMap.prototype.fuzzyGet = function (key, maxEditDistance) {
|
||||
return fuzzySearch(this._tree, key, maxEditDistance);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get
|
||||
* @param key Key to get
|
||||
* @return Value associated to the key, or `undefined` if the key is not
|
||||
* found.
|
||||
*/
|
||||
SearchableMap.prototype.get = function (key) {
|
||||
var node = lookup(this._tree, key);
|
||||
return node !== undefined ? node.get(LEAF) : undefined;
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has
|
||||
* @param key Key
|
||||
* @return True if the key is in the map, false otherwise
|
||||
*/
|
||||
SearchableMap.prototype.has = function (key) {
|
||||
var node = lookup(this._tree, key);
|
||||
return node !== undefined && node.has(LEAF);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys
|
||||
* @return An `Iterable` iterating through keys
|
||||
*/
|
||||
SearchableMap.prototype.keys = function () {
|
||||
return new TreeIterator(this, KEYS);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set
|
||||
* @param key Key to set
|
||||
* @param value Value to associate to the key
|
||||
* @return The [[SearchableMap]] itself, to allow chaining
|
||||
*/
|
||||
SearchableMap.prototype.set = function (key, value) {
|
||||
if (typeof key !== 'string') {
|
||||
throw new Error('key must be a string');
|
||||
}
|
||||
this._size = undefined;
|
||||
var node = createPath(this._tree, key);
|
||||
node.set(LEAF, value);
|
||||
return this;
|
||||
};
|
||||
Object.defineProperty(SearchableMap.prototype, "size", {
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size
|
||||
*/
|
||||
get: function () {
|
||||
if (this._size) {
|
||||
return this._size;
|
||||
}
|
||||
/** @ignore */
|
||||
this._size = 0;
|
||||
var iter = this.entries();
|
||||
while (!iter.next().done)
|
||||
this._size += 1;
|
||||
return this._size;
|
||||
},
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
/**
|
||||
* Updates the value at the given key using the provided function. The function
|
||||
* is called with the current value at the key, and its return value is used as
|
||||
* the new value to be set.
|
||||
*
|
||||
* ### Example:
|
||||
*
|
||||
* ```javascript
|
||||
* // Increment the current value by one
|
||||
* searchableMap.update('somekey', (currentValue) => currentValue == null ? 0 : currentValue + 1)
|
||||
* ```
|
||||
*
|
||||
* If the value at the given key is or will be an object, it might not require
|
||||
* re-assignment. In that case it is better to use `fetch()`, because it is
|
||||
* faster.
|
||||
*
|
||||
* @param key The key to update
|
||||
* @param fn The function used to compute the new value from the current one
|
||||
* @return The [[SearchableMap]] itself, to allow chaining
|
||||
*/
|
||||
SearchableMap.prototype.update = function (key, fn) {
|
||||
if (typeof key !== 'string') {
|
||||
throw new Error('key must be a string');
|
||||
}
|
||||
this._size = undefined;
|
||||
var node = createPath(this._tree, key);
|
||||
node.set(LEAF, fn(node.get(LEAF)));
|
||||
return this;
|
||||
};
|
||||
/**
|
||||
* Fetches the value of the given key. If the value does not exist, calls the
|
||||
* given function to create a new value, which is inserted at the given key
|
||||
* and subsequently returned.
|
||||
*
|
||||
* ### Example:
|
||||
*
|
||||
* ```javascript
|
||||
* const map = searchableMap.fetch('somekey', () => new Map())
|
||||
* map.set('foo', 'bar')
|
||||
* ```
|
||||
*
|
||||
* @param key The key to update
|
||||
* @param defaultValue A function that creates a new value if the key does not exist
|
||||
* @return The existing or new value at the given key
|
||||
*/
|
||||
SearchableMap.prototype.fetch = function (key, initial) {
|
||||
if (typeof key !== 'string') {
|
||||
throw new Error('key must be a string');
|
||||
}
|
||||
this._size = undefined;
|
||||
var node = createPath(this._tree, key);
|
||||
var value = node.get(LEAF);
|
||||
if (value === undefined) {
|
||||
node.set(LEAF, value = initial());
|
||||
}
|
||||
return value;
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/values
|
||||
* @return An `Iterable` iterating through values.
|
||||
*/
|
||||
SearchableMap.prototype.values = function () {
|
||||
return new TreeIterator(this, VALUES);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/@@iterator
|
||||
*/
|
||||
SearchableMap.prototype[Symbol.iterator] = function () {
|
||||
return this.entries();
|
||||
};
|
||||
/**
|
||||
* Creates a [[SearchableMap]] from an `Iterable` of entries
|
||||
*
|
||||
* @param entries Entries to be inserted in the [[SearchableMap]]
|
||||
* @return A new [[SearchableMap]] with the given entries
|
||||
*/
|
||||
SearchableMap.from = function (entries) {
|
||||
var e_3, _a;
|
||||
var tree = new SearchableMap();
|
||||
try {
|
||||
for (var entries_1 = __values(entries), entries_1_1 = entries_1.next(); !entries_1_1.done; entries_1_1 = entries_1.next()) {
|
||||
var _b = __read(entries_1_1.value, 2), key = _b[0], value = _b[1];
|
||||
tree.set(key, value);
|
||||
}
|
||||
}
|
||||
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (entries_1_1 && !entries_1_1.done && (_a = entries_1.return)) _a.call(entries_1);
|
||||
}
|
||||
finally { if (e_3) throw e_3.error; }
|
||||
}
|
||||
return tree;
|
||||
};
|
||||
/**
|
||||
* Creates a [[SearchableMap]] from the iterable properties of a JavaScript object
|
||||
*
|
||||
* @param object Object of entries for the [[SearchableMap]]
|
||||
* @return A new [[SearchableMap]] with the given entries
|
||||
*/
|
||||
SearchableMap.fromObject = function (object) {
|
||||
return SearchableMap.from(Object.entries(object));
|
||||
};
|
||||
return SearchableMap;
|
||||
}());
|
||||
var trackDown = function (tree, key, path) {
|
||||
var e_4, _a;
|
||||
if (path === void 0) { path = []; }
|
||||
if (key.length === 0 || tree == null) {
|
||||
return [tree, path];
|
||||
}
|
||||
try {
|
||||
for (var _b = __values(tree.keys()), _c = _b.next(); !_c.done; _c = _b.next()) {
|
||||
var k = _c.value;
|
||||
if (k !== LEAF && key.startsWith(k)) {
|
||||
path.push([tree, k]); // performance: update in place
|
||||
return trackDown(tree.get(k), key.slice(k.length), path);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||
}
|
||||
finally { if (e_4) throw e_4.error; }
|
||||
}
|
||||
path.push([tree, key]); // performance: update in place
|
||||
return trackDown(undefined, '', path);
|
||||
};
|
||||
var lookup = function (tree, key) {
|
||||
var e_5, _a;
|
||||
if (key.length === 0 || tree == null) {
|
||||
return tree;
|
||||
}
|
||||
try {
|
||||
for (var _b = __values(tree.keys()), _c = _b.next(); !_c.done; _c = _b.next()) {
|
||||
var k = _c.value;
|
||||
if (k !== LEAF && key.startsWith(k)) {
|
||||
return lookup(tree.get(k), key.slice(k.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||
}
|
||||
finally { if (e_5) throw e_5.error; }
|
||||
}
|
||||
};
|
||||
// Create a path in the radix tree for the given key, and returns the deepest
|
||||
// node. This function is in the hot path for indexing. It avoids unnecessary
|
||||
// string operations and recursion for performance.
|
||||
var createPath = function (node, key) {
|
||||
var e_6, _a;
|
||||
var keyLength = key.length;
|
||||
outer: for (var pos = 0; node && pos < keyLength;) {
|
||||
try {
|
||||
for (var _b = (e_6 = void 0, __values(node.keys())), _c = _b.next(); !_c.done; _c = _b.next()) {
|
||||
var k = _c.value;
|
||||
// Check whether this key is a candidate: the first characters must match.
|
||||
if (k !== LEAF && key[pos] === k[0]) {
|
||||
var len = Math.min(keyLength - pos, k.length);
|
||||
// Advance offset to the point where key and k no longer match.
|
||||
var offset = 1;
|
||||
while (offset < len && key[pos + offset] === k[offset])
|
||||
++offset;
|
||||
var child_1 = node.get(k);
|
||||
if (offset === k.length) {
|
||||
// The existing key is shorter than the key we need to create.
|
||||
node = child_1;
|
||||
}
|
||||
else {
|
||||
// Partial match: we need to insert an intermediate node to contain
|
||||
// both the existing subtree and the new node.
|
||||
var intermediate = new Map();
|
||||
intermediate.set(k.slice(offset), child_1);
|
||||
node.set(key.slice(pos, pos + offset), intermediate);
|
||||
node.delete(k);
|
||||
node = intermediate;
|
||||
}
|
||||
pos += offset;
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_6_1) { e_6 = { error: e_6_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||
}
|
||||
finally { if (e_6) throw e_6.error; }
|
||||
}
|
||||
// Create a final child node to contain the final suffix of the key.
|
||||
var child = new Map();
|
||||
node.set(key.slice(pos), child);
|
||||
return child;
|
||||
}
|
||||
return node;
|
||||
};
|
||||
var remove = function (tree, key) {
|
||||
var _a = __read(trackDown(tree, key), 2), node = _a[0], path = _a[1];
|
||||
if (node === undefined) {
|
||||
return;
|
||||
}
|
||||
node.delete(LEAF);
|
||||
if (node.size === 0) {
|
||||
cleanup(path);
|
||||
}
|
||||
else if (node.size === 1) {
|
||||
var _b = __read(node.entries().next().value, 2), key_1 = _b[0], value = _b[1];
|
||||
merge(path, key_1, value);
|
||||
}
|
||||
};
|
||||
var cleanup = function (path) {
|
||||
if (path.length === 0) {
|
||||
return;
|
||||
}
|
||||
var _a = __read(last(path), 2), node = _a[0], key = _a[1];
|
||||
node.delete(key);
|
||||
if (node.size === 0) {
|
||||
cleanup(path.slice(0, -1));
|
||||
}
|
||||
else if (node.size === 1) {
|
||||
var _b = __read(node.entries().next().value, 2), key_2 = _b[0], value = _b[1];
|
||||
if (key_2 !== LEAF) {
|
||||
merge(path.slice(0, -1), key_2, value);
|
||||
}
|
||||
}
|
||||
};
|
||||
var merge = function (path, key, value) {
|
||||
if (path.length === 0) {
|
||||
return;
|
||||
}
|
||||
var _a = __read(last(path), 2), node = _a[0], nodeKey = _a[1];
|
||||
node.set(nodeKey + key, value);
|
||||
node.delete(nodeKey);
|
||||
};
|
||||
var last = function (array) {
|
||||
return array[array.length - 1];
|
||||
};
|
||||
|
||||
export { SearchableMap as default };
|
||||
//# sourceMappingURL=SearchableMap.js.map
|
1
node_modules/minisearch/dist/es5m/SearchableMap.js.map
generated
vendored
Normal file
1
node_modules/minisearch/dist/es5m/SearchableMap.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2448
node_modules/minisearch/dist/es5m/index.js
generated
vendored
Normal file
2448
node_modules/minisearch/dist/es5m/index.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
node_modules/minisearch/dist/es5m/index.js.map
generated
vendored
Normal file
1
node_modules/minisearch/dist/es5m/index.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
251
node_modules/minisearch/dist/types/SearchableMap.d.ts
generated
vendored
Normal file
251
node_modules/minisearch/dist/types/SearchableMap.d.ts
generated
vendored
Normal file
|
@ -0,0 +1,251 @@
|
|||
type LeafType = '' & {
|
||||
readonly __tag: unique symbol;
|
||||
};
|
||||
interface RadixTree<T> extends Map<string, T | RadixTree<T>> {
|
||||
get(key: LeafType): T | undefined;
|
||||
get(key: string): RadixTree<T> | undefined;
|
||||
set(key: LeafType, value: T): this;
|
||||
set(key: string, value: RadixTree<T>): this;
|
||||
}
|
||||
type Entry<T> = [string, T];
|
||||
|
||||
interface Iterators<T> {
|
||||
ENTRIES: Entry<T>;
|
||||
KEYS: string;
|
||||
VALUES: T;
|
||||
}
|
||||
type Kind<T> = keyof Iterators<T>;
|
||||
type Result<T, K extends keyof Iterators<T>> = Iterators<T>[K];
|
||||
type IteratorPath<T> = {
|
||||
node: RadixTree<T>;
|
||||
keys: string[];
|
||||
}[];
|
||||
type IterableSet<T> = {
|
||||
_tree: RadixTree<T>;
|
||||
_prefix: string;
|
||||
};
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
declare class TreeIterator<T, K extends Kind<T>> implements Iterator<Result<T, K>> {
|
||||
set: IterableSet<T>;
|
||||
_type: K;
|
||||
_path: IteratorPath<T>;
|
||||
constructor(set: IterableSet<T>, type: K);
|
||||
next(): IteratorResult<Result<T, K>>;
|
||||
dive(): IteratorResult<Result<T, K>>;
|
||||
backtrack(): void;
|
||||
key(): string;
|
||||
value(): T;
|
||||
result(): Result<T, K>;
|
||||
[Symbol.iterator](): this;
|
||||
}
|
||||
|
||||
type FuzzyResult<T> = [T, number];
|
||||
type FuzzyResults<T> = Map<string, FuzzyResult<T>>;
|
||||
|
||||
/**
|
||||
* A class implementing the same interface as a standard JavaScript
|
||||
* [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)
|
||||
* with string keys, but adding support for efficiently searching entries with
|
||||
* prefix or fuzzy search. This class is used internally by [[MiniSearch]] as
|
||||
* the inverted index data structure. The implementation is a radix tree
|
||||
* (compressed prefix tree).
|
||||
*
|
||||
* Since this class can be of general utility beyond _MiniSearch_, it is
|
||||
* exported by the `minisearch` package and can be imported (or required) as
|
||||
* `minisearch/SearchableMap`.
|
||||
*
|
||||
* @typeParam T The type of the values stored in the map.
|
||||
*/
|
||||
declare class SearchableMap<T = any> {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
_tree: RadixTree<T>;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
_prefix: string;
|
||||
private _size;
|
||||
/**
|
||||
* The constructor is normally called without arguments, creating an empty
|
||||
* map. In order to create a [[SearchableMap]] from an iterable or from an
|
||||
* object, check [[SearchableMap.from]] and [[SearchableMap.fromObject]].
|
||||
*
|
||||
* The constructor arguments are for internal use, when creating derived
|
||||
* mutable views of a map at a prefix.
|
||||
*/
|
||||
constructor(tree?: RadixTree<T>, prefix?: string);
|
||||
/**
|
||||
* Creates and returns a mutable view of this [[SearchableMap]], containing only
|
||||
* entries that share the given prefix.
|
||||
*
|
||||
* ### Usage:
|
||||
*
|
||||
* ```javascript
|
||||
* let map = new SearchableMap()
|
||||
* map.set("unicorn", 1)
|
||||
* map.set("universe", 2)
|
||||
* map.set("university", 3)
|
||||
* map.set("unique", 4)
|
||||
* map.set("hello", 5)
|
||||
*
|
||||
* let uni = map.atPrefix("uni")
|
||||
* uni.get("unique") // => 4
|
||||
* uni.get("unicorn") // => 1
|
||||
* uni.get("hello") // => undefined
|
||||
*
|
||||
* let univer = map.atPrefix("univer")
|
||||
* univer.get("unique") // => undefined
|
||||
* univer.get("universe") // => 2
|
||||
* univer.get("university") // => 3
|
||||
* ```
|
||||
*
|
||||
* @param prefix The prefix
|
||||
* @return A [[SearchableMap]] representing a mutable view of the original Map at the given prefix
|
||||
*/
|
||||
atPrefix(prefix: string): SearchableMap<T>;
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear
|
||||
*/
|
||||
clear(): void;
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete
|
||||
* @param key Key to delete
|
||||
*/
|
||||
delete(key: string): void;
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries
|
||||
* @return An iterator iterating through `[key, value]` entries.
|
||||
*/
|
||||
entries(): TreeIterator<T, "ENTRIES">;
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach
|
||||
* @param fn Iteration function
|
||||
*/
|
||||
forEach(fn: (key: string, value: T, map: SearchableMap) => void): void;
|
||||
/**
|
||||
* Returns a Map of all the entries that have a key within the given edit
|
||||
* distance from the search key. The keys of the returned Map are the matching
|
||||
* keys, while the values are two-element arrays where the first element is
|
||||
* the value associated to the key, and the second is the edit distance of the
|
||||
* key to the search key.
|
||||
*
|
||||
* ### Usage:
|
||||
*
|
||||
* ```javascript
|
||||
* let map = new SearchableMap()
|
||||
* map.set('hello', 'world')
|
||||
* map.set('hell', 'yeah')
|
||||
* map.set('ciao', 'mondo')
|
||||
*
|
||||
* // Get all entries that match the key 'hallo' with a maximum edit distance of 2
|
||||
* map.fuzzyGet('hallo', 2)
|
||||
* // => Map(2) { 'hello' => ['world', 1], 'hell' => ['yeah', 2] }
|
||||
*
|
||||
* // In the example, the "hello" key has value "world" and edit distance of 1
|
||||
* // (change "e" to "a"), the key "hell" has value "yeah" and edit distance of 2
|
||||
* // (change "e" to "a", delete "o")
|
||||
* ```
|
||||
*
|
||||
* @param key The search key
|
||||
* @param maxEditDistance The maximum edit distance (Levenshtein)
|
||||
* @return A Map of the matching keys to their value and edit distance
|
||||
*/
|
||||
fuzzyGet(key: string, maxEditDistance: number): FuzzyResults<T>;
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get
|
||||
* @param key Key to get
|
||||
* @return Value associated to the key, or `undefined` if the key is not
|
||||
* found.
|
||||
*/
|
||||
get(key: string): T | undefined;
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has
|
||||
* @param key Key
|
||||
* @return True if the key is in the map, false otherwise
|
||||
*/
|
||||
has(key: string): boolean;
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys
|
||||
* @return An `Iterable` iterating through keys
|
||||
*/
|
||||
keys(): TreeIterator<T, "KEYS">;
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set
|
||||
* @param key Key to set
|
||||
* @param value Value to associate to the key
|
||||
* @return The [[SearchableMap]] itself, to allow chaining
|
||||
*/
|
||||
set(key: string, value: T): SearchableMap<T>;
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size
|
||||
*/
|
||||
get size(): number;
|
||||
/**
|
||||
* Updates the value at the given key using the provided function. The function
|
||||
* is called with the current value at the key, and its return value is used as
|
||||
* the new value to be set.
|
||||
*
|
||||
* ### Example:
|
||||
*
|
||||
* ```javascript
|
||||
* // Increment the current value by one
|
||||
* searchableMap.update('somekey', (currentValue) => currentValue == null ? 0 : currentValue + 1)
|
||||
* ```
|
||||
*
|
||||
* If the value at the given key is or will be an object, it might not require
|
||||
* re-assignment. In that case it is better to use `fetch()`, because it is
|
||||
* faster.
|
||||
*
|
||||
* @param key The key to update
|
||||
* @param fn The function used to compute the new value from the current one
|
||||
* @return The [[SearchableMap]] itself, to allow chaining
|
||||
*/
|
||||
update(key: string, fn: (value: T | undefined) => T): SearchableMap<T>;
|
||||
/**
|
||||
* Fetches the value of the given key. If the value does not exist, calls the
|
||||
* given function to create a new value, which is inserted at the given key
|
||||
* and subsequently returned.
|
||||
*
|
||||
* ### Example:
|
||||
*
|
||||
* ```javascript
|
||||
* const map = searchableMap.fetch('somekey', () => new Map())
|
||||
* map.set('foo', 'bar')
|
||||
* ```
|
||||
*
|
||||
* @param key The key to update
|
||||
* @param defaultValue A function that creates a new value if the key does not exist
|
||||
* @return The existing or new value at the given key
|
||||
*/
|
||||
fetch(key: string, initial: () => T): T;
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/values
|
||||
* @return An `Iterable` iterating through values.
|
||||
*/
|
||||
values(): TreeIterator<T, "VALUES">;
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/@@iterator
|
||||
*/
|
||||
[Symbol.iterator](): TreeIterator<T, "ENTRIES">;
|
||||
/**
|
||||
* Creates a [[SearchableMap]] from an `Iterable` of entries
|
||||
*
|
||||
* @param entries Entries to be inserted in the [[SearchableMap]]
|
||||
* @return A new [[SearchableMap]] with the given entries
|
||||
*/
|
||||
static from<T = any>(entries: Iterable<Entry<T>> | Entry<T>[]): SearchableMap<any>;
|
||||
/**
|
||||
* Creates a [[SearchableMap]] from the iterable properties of a JavaScript object
|
||||
*
|
||||
* @param object Object of entries for the [[SearchableMap]]
|
||||
* @return A new [[SearchableMap]] with the given entries
|
||||
*/
|
||||
static fromObject<T = any>(object: {
|
||||
[key: string]: T;
|
||||
}): SearchableMap<any>;
|
||||
}
|
||||
|
||||
export { SearchableMap as default };
|
1298
node_modules/minisearch/dist/types/index.d.ts
generated
vendored
Normal file
1298
node_modules/minisearch/dist/types/index.d.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
686
node_modules/minisearch/dist/umd/SearchableMap.js
generated
vendored
Normal file
686
node_modules/minisearch/dist/umd/SearchableMap.js
generated
vendored
Normal file
|
@ -0,0 +1,686 @@
|
|||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||
typeof define === 'function' && define.amd ? define(factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.MiniSearch = factory());
|
||||
})(this, (function () { 'use strict';
|
||||
|
||||
/******************************************************************************
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
***************************************************************************** */
|
||||
/* global Reflect, Promise */
|
||||
|
||||
|
||||
function __values(o) {
|
||||
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
||||
if (m) return m.call(o);
|
||||
if (o && typeof o.length === "number") return {
|
||||
next: function () {
|
||||
if (o && i >= o.length) o = void 0;
|
||||
return { value: o && o[i++], done: !o };
|
||||
}
|
||||
};
|
||||
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
||||
}
|
||||
|
||||
function __read(o, n) {
|
||||
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
||||
if (!m) return o;
|
||||
var i = m.call(o), r, ar = [], e;
|
||||
try {
|
||||
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
||||
}
|
||||
catch (error) { e = { error: error }; }
|
||||
finally {
|
||||
try {
|
||||
if (r && !r.done && (m = i["return"])) m.call(i);
|
||||
}
|
||||
finally { if (e) throw e.error; }
|
||||
}
|
||||
return ar;
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
var ENTRIES = 'ENTRIES';
|
||||
/** @ignore */
|
||||
var KEYS = 'KEYS';
|
||||
/** @ignore */
|
||||
var VALUES = 'VALUES';
|
||||
/** @ignore */
|
||||
var LEAF = '';
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
var TreeIterator = /** @class */ (function () {
|
||||
function TreeIterator(set, type) {
|
||||
var node = set._tree;
|
||||
var keys = Array.from(node.keys());
|
||||
this.set = set;
|
||||
this._type = type;
|
||||
this._path = keys.length > 0 ? [{ node: node, keys: keys }] : [];
|
||||
}
|
||||
TreeIterator.prototype.next = function () {
|
||||
var value = this.dive();
|
||||
this.backtrack();
|
||||
return value;
|
||||
};
|
||||
TreeIterator.prototype.dive = function () {
|
||||
if (this._path.length === 0) {
|
||||
return { done: true, value: undefined };
|
||||
}
|
||||
var _a = last$1(this._path), node = _a.node, keys = _a.keys;
|
||||
if (last$1(keys) === LEAF) {
|
||||
return { done: false, value: this.result() };
|
||||
}
|
||||
var child = node.get(last$1(keys));
|
||||
this._path.push({ node: child, keys: Array.from(child.keys()) });
|
||||
return this.dive();
|
||||
};
|
||||
TreeIterator.prototype.backtrack = function () {
|
||||
if (this._path.length === 0) {
|
||||
return;
|
||||
}
|
||||
var keys = last$1(this._path).keys;
|
||||
keys.pop();
|
||||
if (keys.length > 0) {
|
||||
return;
|
||||
}
|
||||
this._path.pop();
|
||||
this.backtrack();
|
||||
};
|
||||
TreeIterator.prototype.key = function () {
|
||||
return this.set._prefix + this._path
|
||||
.map(function (_a) {
|
||||
var keys = _a.keys;
|
||||
return last$1(keys);
|
||||
})
|
||||
.filter(function (key) { return key !== LEAF; })
|
||||
.join('');
|
||||
};
|
||||
TreeIterator.prototype.value = function () {
|
||||
return last$1(this._path).node.get(LEAF);
|
||||
};
|
||||
TreeIterator.prototype.result = function () {
|
||||
switch (this._type) {
|
||||
case VALUES: return this.value();
|
||||
case KEYS: return this.key();
|
||||
default: return [this.key(), this.value()];
|
||||
}
|
||||
};
|
||||
TreeIterator.prototype[Symbol.iterator] = function () {
|
||||
return this;
|
||||
};
|
||||
return TreeIterator;
|
||||
}());
|
||||
var last$1 = function (array) {
|
||||
return array[array.length - 1];
|
||||
};
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
var fuzzySearch = function (node, query, maxDistance) {
|
||||
var results = new Map();
|
||||
if (query === undefined)
|
||||
return results;
|
||||
// Number of columns in the Levenshtein matrix.
|
||||
var n = query.length + 1;
|
||||
// Matching terms can never be longer than N + maxDistance.
|
||||
var m = n + maxDistance;
|
||||
// Fill first matrix row and column with numbers: 0 1 2 3 ...
|
||||
var matrix = new Uint8Array(m * n).fill(maxDistance + 1);
|
||||
for (var j = 0; j < n; ++j)
|
||||
matrix[j] = j;
|
||||
for (var i = 1; i < m; ++i)
|
||||
matrix[i * n] = i;
|
||||
recurse(node, query, maxDistance, results, matrix, 1, n, '');
|
||||
return results;
|
||||
};
|
||||
// Modified version of http://stevehanov.ca/blog/?id=114
|
||||
// This builds a Levenshtein matrix for a given query and continuously updates
|
||||
// it for nodes in the radix tree that fall within the given maximum edit
|
||||
// distance. Keeping the same matrix around is beneficial especially for larger
|
||||
// edit distances.
|
||||
//
|
||||
// k a t e <-- query
|
||||
// 0 1 2 3 4
|
||||
// c 1 1 2 3 4
|
||||
// a 2 2 1 2 3
|
||||
// t 3 3 2 1 [2] <-- edit distance
|
||||
// ^
|
||||
// ^ term in radix tree, rows are added and removed as needed
|
||||
var recurse = function (node, query, maxDistance, results, matrix, m, n, prefix) {
|
||||
var e_1, _a;
|
||||
var offset = m * n;
|
||||
try {
|
||||
key: for (var _b = __values(node.keys()), _c = _b.next(); !_c.done; _c = _b.next()) {
|
||||
var key = _c.value;
|
||||
if (key === LEAF) {
|
||||
// We've reached a leaf node. Check if the edit distance acceptable and
|
||||
// store the result if it is.
|
||||
var distance = matrix[offset - 1];
|
||||
if (distance <= maxDistance) {
|
||||
results.set(prefix, [node.get(key), distance]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Iterate over all characters in the key. Update the Levenshtein matrix
|
||||
// and check if the minimum distance in the last row is still within the
|
||||
// maximum edit distance. If it is, we can recurse over all child nodes.
|
||||
var i = m;
|
||||
for (var pos = 0; pos < key.length; ++pos, ++i) {
|
||||
var char = key[pos];
|
||||
var thisRowOffset = n * i;
|
||||
var prevRowOffset = thisRowOffset - n;
|
||||
// Set the first column based on the previous row, and initialize the
|
||||
// minimum distance in the current row.
|
||||
var minDistance = matrix[thisRowOffset];
|
||||
var jmin = Math.max(0, i - maxDistance - 1);
|
||||
var jmax = Math.min(n - 1, i + maxDistance);
|
||||
// Iterate over remaining columns (characters in the query).
|
||||
for (var j = jmin; j < jmax; ++j) {
|
||||
var different = char !== query[j];
|
||||
// It might make sense to only read the matrix positions used for
|
||||
// deletion/insertion if the characters are different. But we want to
|
||||
// avoid conditional reads for performance reasons.
|
||||
var rpl = matrix[prevRowOffset + j] + +different;
|
||||
var del = matrix[prevRowOffset + j + 1] + 1;
|
||||
var ins = matrix[thisRowOffset + j] + 1;
|
||||
var dist = matrix[thisRowOffset + j + 1] = Math.min(rpl, del, ins);
|
||||
if (dist < minDistance)
|
||||
minDistance = dist;
|
||||
}
|
||||
// Because distance will never decrease, we can stop. There will be no
|
||||
// matching child nodes.
|
||||
if (minDistance > maxDistance) {
|
||||
continue key;
|
||||
}
|
||||
}
|
||||
recurse(node.get(key), query, maxDistance, results, matrix, i, n, prefix + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A class implementing the same interface as a standard JavaScript
|
||||
* [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)
|
||||
* with string keys, but adding support for efficiently searching entries with
|
||||
* prefix or fuzzy search. This class is used internally by [[MiniSearch]] as
|
||||
* the inverted index data structure. The implementation is a radix tree
|
||||
* (compressed prefix tree).
|
||||
*
|
||||
* Since this class can be of general utility beyond _MiniSearch_, it is
|
||||
* exported by the `minisearch` package and can be imported (or required) as
|
||||
* `minisearch/SearchableMap`.
|
||||
*
|
||||
* @typeParam T The type of the values stored in the map.
|
||||
*/
|
||||
var SearchableMap = /** @class */ (function () {
|
||||
/**
|
||||
* The constructor is normally called without arguments, creating an empty
|
||||
* map. In order to create a [[SearchableMap]] from an iterable or from an
|
||||
* object, check [[SearchableMap.from]] and [[SearchableMap.fromObject]].
|
||||
*
|
||||
* The constructor arguments are for internal use, when creating derived
|
||||
* mutable views of a map at a prefix.
|
||||
*/
|
||||
function SearchableMap(tree, prefix) {
|
||||
if (tree === void 0) { tree = new Map(); }
|
||||
if (prefix === void 0) { prefix = ''; }
|
||||
this._size = undefined;
|
||||
this._tree = tree;
|
||||
this._prefix = prefix;
|
||||
}
|
||||
/**
|
||||
* Creates and returns a mutable view of this [[SearchableMap]], containing only
|
||||
* entries that share the given prefix.
|
||||
*
|
||||
* ### Usage:
|
||||
*
|
||||
* ```javascript
|
||||
* let map = new SearchableMap()
|
||||
* map.set("unicorn", 1)
|
||||
* map.set("universe", 2)
|
||||
* map.set("university", 3)
|
||||
* map.set("unique", 4)
|
||||
* map.set("hello", 5)
|
||||
*
|
||||
* let uni = map.atPrefix("uni")
|
||||
* uni.get("unique") // => 4
|
||||
* uni.get("unicorn") // => 1
|
||||
* uni.get("hello") // => undefined
|
||||
*
|
||||
* let univer = map.atPrefix("univer")
|
||||
* univer.get("unique") // => undefined
|
||||
* univer.get("universe") // => 2
|
||||
* univer.get("university") // => 3
|
||||
* ```
|
||||
*
|
||||
* @param prefix The prefix
|
||||
* @return A [[SearchableMap]] representing a mutable view of the original Map at the given prefix
|
||||
*/
|
||||
SearchableMap.prototype.atPrefix = function (prefix) {
|
||||
var e_1, _a;
|
||||
if (!prefix.startsWith(this._prefix)) {
|
||||
throw new Error('Mismatched prefix');
|
||||
}
|
||||
var _b = __read(trackDown(this._tree, prefix.slice(this._prefix.length)), 2), node = _b[0], path = _b[1];
|
||||
if (node === undefined) {
|
||||
var _c = __read(last(path), 2), parentNode = _c[0], key = _c[1];
|
||||
try {
|
||||
for (var _d = __values(parentNode.keys()), _e = _d.next(); !_e.done; _e = _d.next()) {
|
||||
var k = _e.value;
|
||||
if (k !== LEAF && k.startsWith(key)) {
|
||||
var node_1 = new Map();
|
||||
node_1.set(k.slice(key.length), parentNode.get(k));
|
||||
return new SearchableMap(node_1, prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
}
|
||||
}
|
||||
return new SearchableMap(node, prefix);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear
|
||||
*/
|
||||
SearchableMap.prototype.clear = function () {
|
||||
this._size = undefined;
|
||||
this._tree.clear();
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete
|
||||
* @param key Key to delete
|
||||
*/
|
||||
SearchableMap.prototype.delete = function (key) {
|
||||
this._size = undefined;
|
||||
return remove(this._tree, key);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries
|
||||
* @return An iterator iterating through `[key, value]` entries.
|
||||
*/
|
||||
SearchableMap.prototype.entries = function () {
|
||||
return new TreeIterator(this, ENTRIES);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach
|
||||
* @param fn Iteration function
|
||||
*/
|
||||
SearchableMap.prototype.forEach = function (fn) {
|
||||
var e_2, _a;
|
||||
try {
|
||||
for (var _b = __values(this), _c = _b.next(); !_c.done; _c = _b.next()) {
|
||||
var _d = __read(_c.value, 2), key = _d[0], value = _d[1];
|
||||
fn(key, value, this);
|
||||
}
|
||||
}
|
||||
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||
}
|
||||
finally { if (e_2) throw e_2.error; }
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Returns a Map of all the entries that have a key within the given edit
|
||||
* distance from the search key. The keys of the returned Map are the matching
|
||||
* keys, while the values are two-element arrays where the first element is
|
||||
* the value associated to the key, and the second is the edit distance of the
|
||||
* key to the search key.
|
||||
*
|
||||
* ### Usage:
|
||||
*
|
||||
* ```javascript
|
||||
* let map = new SearchableMap()
|
||||
* map.set('hello', 'world')
|
||||
* map.set('hell', 'yeah')
|
||||
* map.set('ciao', 'mondo')
|
||||
*
|
||||
* // Get all entries that match the key 'hallo' with a maximum edit distance of 2
|
||||
* map.fuzzyGet('hallo', 2)
|
||||
* // => Map(2) { 'hello' => ['world', 1], 'hell' => ['yeah', 2] }
|
||||
*
|
||||
* // In the example, the "hello" key has value "world" and edit distance of 1
|
||||
* // (change "e" to "a"), the key "hell" has value "yeah" and edit distance of 2
|
||||
* // (change "e" to "a", delete "o")
|
||||
* ```
|
||||
*
|
||||
* @param key The search key
|
||||
* @param maxEditDistance The maximum edit distance (Levenshtein)
|
||||
* @return A Map of the matching keys to their value and edit distance
|
||||
*/
|
||||
SearchableMap.prototype.fuzzyGet = function (key, maxEditDistance) {
|
||||
return fuzzySearch(this._tree, key, maxEditDistance);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get
|
||||
* @param key Key to get
|
||||
* @return Value associated to the key, or `undefined` if the key is not
|
||||
* found.
|
||||
*/
|
||||
SearchableMap.prototype.get = function (key) {
|
||||
var node = lookup(this._tree, key);
|
||||
return node !== undefined ? node.get(LEAF) : undefined;
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has
|
||||
* @param key Key
|
||||
* @return True if the key is in the map, false otherwise
|
||||
*/
|
||||
SearchableMap.prototype.has = function (key) {
|
||||
var node = lookup(this._tree, key);
|
||||
return node !== undefined && node.has(LEAF);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys
|
||||
* @return An `Iterable` iterating through keys
|
||||
*/
|
||||
SearchableMap.prototype.keys = function () {
|
||||
return new TreeIterator(this, KEYS);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set
|
||||
* @param key Key to set
|
||||
* @param value Value to associate to the key
|
||||
* @return The [[SearchableMap]] itself, to allow chaining
|
||||
*/
|
||||
SearchableMap.prototype.set = function (key, value) {
|
||||
if (typeof key !== 'string') {
|
||||
throw new Error('key must be a string');
|
||||
}
|
||||
this._size = undefined;
|
||||
var node = createPath(this._tree, key);
|
||||
node.set(LEAF, value);
|
||||
return this;
|
||||
};
|
||||
Object.defineProperty(SearchableMap.prototype, "size", {
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size
|
||||
*/
|
||||
get: function () {
|
||||
if (this._size) {
|
||||
return this._size;
|
||||
}
|
||||
/** @ignore */
|
||||
this._size = 0;
|
||||
var iter = this.entries();
|
||||
while (!iter.next().done)
|
||||
this._size += 1;
|
||||
return this._size;
|
||||
},
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
/**
|
||||
* Updates the value at the given key using the provided function. The function
|
||||
* is called with the current value at the key, and its return value is used as
|
||||
* the new value to be set.
|
||||
*
|
||||
* ### Example:
|
||||
*
|
||||
* ```javascript
|
||||
* // Increment the current value by one
|
||||
* searchableMap.update('somekey', (currentValue) => currentValue == null ? 0 : currentValue + 1)
|
||||
* ```
|
||||
*
|
||||
* If the value at the given key is or will be an object, it might not require
|
||||
* re-assignment. In that case it is better to use `fetch()`, because it is
|
||||
* faster.
|
||||
*
|
||||
* @param key The key to update
|
||||
* @param fn The function used to compute the new value from the current one
|
||||
* @return The [[SearchableMap]] itself, to allow chaining
|
||||
*/
|
||||
SearchableMap.prototype.update = function (key, fn) {
|
||||
if (typeof key !== 'string') {
|
||||
throw new Error('key must be a string');
|
||||
}
|
||||
this._size = undefined;
|
||||
var node = createPath(this._tree, key);
|
||||
node.set(LEAF, fn(node.get(LEAF)));
|
||||
return this;
|
||||
};
|
||||
/**
|
||||
* Fetches the value of the given key. If the value does not exist, calls the
|
||||
* given function to create a new value, which is inserted at the given key
|
||||
* and subsequently returned.
|
||||
*
|
||||
* ### Example:
|
||||
*
|
||||
* ```javascript
|
||||
* const map = searchableMap.fetch('somekey', () => new Map())
|
||||
* map.set('foo', 'bar')
|
||||
* ```
|
||||
*
|
||||
* @param key The key to update
|
||||
* @param defaultValue A function that creates a new value if the key does not exist
|
||||
* @return The existing or new value at the given key
|
||||
*/
|
||||
SearchableMap.prototype.fetch = function (key, initial) {
|
||||
if (typeof key !== 'string') {
|
||||
throw new Error('key must be a string');
|
||||
}
|
||||
this._size = undefined;
|
||||
var node = createPath(this._tree, key);
|
||||
var value = node.get(LEAF);
|
||||
if (value === undefined) {
|
||||
node.set(LEAF, value = initial());
|
||||
}
|
||||
return value;
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/values
|
||||
* @return An `Iterable` iterating through values.
|
||||
*/
|
||||
SearchableMap.prototype.values = function () {
|
||||
return new TreeIterator(this, VALUES);
|
||||
};
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/@@iterator
|
||||
*/
|
||||
SearchableMap.prototype[Symbol.iterator] = function () {
|
||||
return this.entries();
|
||||
};
|
||||
/**
|
||||
* Creates a [[SearchableMap]] from an `Iterable` of entries
|
||||
*
|
||||
* @param entries Entries to be inserted in the [[SearchableMap]]
|
||||
* @return A new [[SearchableMap]] with the given entries
|
||||
*/
|
||||
SearchableMap.from = function (entries) {
|
||||
var e_3, _a;
|
||||
var tree = new SearchableMap();
|
||||
try {
|
||||
for (var entries_1 = __values(entries), entries_1_1 = entries_1.next(); !entries_1_1.done; entries_1_1 = entries_1.next()) {
|
||||
var _b = __read(entries_1_1.value, 2), key = _b[0], value = _b[1];
|
||||
tree.set(key, value);
|
||||
}
|
||||
}
|
||||
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (entries_1_1 && !entries_1_1.done && (_a = entries_1.return)) _a.call(entries_1);
|
||||
}
|
||||
finally { if (e_3) throw e_3.error; }
|
||||
}
|
||||
return tree;
|
||||
};
|
||||
/**
|
||||
* Creates a [[SearchableMap]] from the iterable properties of a JavaScript object
|
||||
*
|
||||
* @param object Object of entries for the [[SearchableMap]]
|
||||
* @return A new [[SearchableMap]] with the given entries
|
||||
*/
|
||||
SearchableMap.fromObject = function (object) {
|
||||
return SearchableMap.from(Object.entries(object));
|
||||
};
|
||||
return SearchableMap;
|
||||
}());
|
||||
var trackDown = function (tree, key, path) {
|
||||
var e_4, _a;
|
||||
if (path === void 0) { path = []; }
|
||||
if (key.length === 0 || tree == null) {
|
||||
return [tree, path];
|
||||
}
|
||||
try {
|
||||
for (var _b = __values(tree.keys()), _c = _b.next(); !_c.done; _c = _b.next()) {
|
||||
var k = _c.value;
|
||||
if (k !== LEAF && key.startsWith(k)) {
|
||||
path.push([tree, k]); // performance: update in place
|
||||
return trackDown(tree.get(k), key.slice(k.length), path);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||
}
|
||||
finally { if (e_4) throw e_4.error; }
|
||||
}
|
||||
path.push([tree, key]); // performance: update in place
|
||||
return trackDown(undefined, '', path);
|
||||
};
|
||||
var lookup = function (tree, key) {
|
||||
var e_5, _a;
|
||||
if (key.length === 0 || tree == null) {
|
||||
return tree;
|
||||
}
|
||||
try {
|
||||
for (var _b = __values(tree.keys()), _c = _b.next(); !_c.done; _c = _b.next()) {
|
||||
var k = _c.value;
|
||||
if (k !== LEAF && key.startsWith(k)) {
|
||||
return lookup(tree.get(k), key.slice(k.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||
}
|
||||
finally { if (e_5) throw e_5.error; }
|
||||
}
|
||||
};
|
||||
// Create a path in the radix tree for the given key, and returns the deepest
|
||||
// node. This function is in the hot path for indexing. It avoids unnecessary
|
||||
// string operations and recursion for performance.
|
||||
var createPath = function (node, key) {
|
||||
var e_6, _a;
|
||||
var keyLength = key.length;
|
||||
outer: for (var pos = 0; node && pos < keyLength;) {
|
||||
try {
|
||||
for (var _b = (e_6 = void 0, __values(node.keys())), _c = _b.next(); !_c.done; _c = _b.next()) {
|
||||
var k = _c.value;
|
||||
// Check whether this key is a candidate: the first characters must match.
|
||||
if (k !== LEAF && key[pos] === k[0]) {
|
||||
var len = Math.min(keyLength - pos, k.length);
|
||||
// Advance offset to the point where key and k no longer match.
|
||||
var offset = 1;
|
||||
while (offset < len && key[pos + offset] === k[offset])
|
||||
++offset;
|
||||
var child_1 = node.get(k);
|
||||
if (offset === k.length) {
|
||||
// The existing key is shorter than the key we need to create.
|
||||
node = child_1;
|
||||
}
|
||||
else {
|
||||
// Partial match: we need to insert an intermediate node to contain
|
||||
// both the existing subtree and the new node.
|
||||
var intermediate = new Map();
|
||||
intermediate.set(k.slice(offset), child_1);
|
||||
node.set(key.slice(pos, pos + offset), intermediate);
|
||||
node.delete(k);
|
||||
node = intermediate;
|
||||
}
|
||||
pos += offset;
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_6_1) { e_6 = { error: e_6_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||
}
|
||||
finally { if (e_6) throw e_6.error; }
|
||||
}
|
||||
// Create a final child node to contain the final suffix of the key.
|
||||
var child = new Map();
|
||||
node.set(key.slice(pos), child);
|
||||
return child;
|
||||
}
|
||||
return node;
|
||||
};
|
||||
var remove = function (tree, key) {
|
||||
var _a = __read(trackDown(tree, key), 2), node = _a[0], path = _a[1];
|
||||
if (node === undefined) {
|
||||
return;
|
||||
}
|
||||
node.delete(LEAF);
|
||||
if (node.size === 0) {
|
||||
cleanup(path);
|
||||
}
|
||||
else if (node.size === 1) {
|
||||
var _b = __read(node.entries().next().value, 2), key_1 = _b[0], value = _b[1];
|
||||
merge(path, key_1, value);
|
||||
}
|
||||
};
|
||||
var cleanup = function (path) {
|
||||
if (path.length === 0) {
|
||||
return;
|
||||
}
|
||||
var _a = __read(last(path), 2), node = _a[0], key = _a[1];
|
||||
node.delete(key);
|
||||
if (node.size === 0) {
|
||||
cleanup(path.slice(0, -1));
|
||||
}
|
||||
else if (node.size === 1) {
|
||||
var _b = __read(node.entries().next().value, 2), key_2 = _b[0], value = _b[1];
|
||||
if (key_2 !== LEAF) {
|
||||
merge(path.slice(0, -1), key_2, value);
|
||||
}
|
||||
}
|
||||
};
|
||||
var merge = function (path, key, value) {
|
||||
if (path.length === 0) {
|
||||
return;
|
||||
}
|
||||
var _a = __read(last(path), 2), node = _a[0], nodeKey = _a[1];
|
||||
node.set(nodeKey + key, value);
|
||||
node.delete(nodeKey);
|
||||
};
|
||||
var last = function (array) {
|
||||
return array[array.length - 1];
|
||||
};
|
||||
|
||||
return SearchableMap;
|
||||
|
||||
}));
|
||||
//# sourceMappingURL=SearchableMap.js.map
|
1
node_modules/minisearch/dist/umd/SearchableMap.js.map
generated
vendored
Normal file
1
node_modules/minisearch/dist/umd/SearchableMap.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2456
node_modules/minisearch/dist/umd/index.js
generated
vendored
Normal file
2456
node_modules/minisearch/dist/umd/index.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
node_modules/minisearch/dist/umd/index.js.map
generated
vendored
Normal file
1
node_modules/minisearch/dist/umd/index.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
104
node_modules/minisearch/package.json
generated
vendored
Normal file
104
node_modules/minisearch/package.json
generated
vendored
Normal file
|
@ -0,0 +1,104 @@
|
|||
{
|
||||
"name": "minisearch",
|
||||
"version": "6.1.0",
|
||||
"description": "Tiny but powerful full-text search engine for browser and Node",
|
||||
"main": "dist/umd/index.js",
|
||||
"module": "dist/es5m/index.js",
|
||||
"es2015": "dist/es/index.js",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/types/index.d.ts",
|
||||
"require": "./dist/cjs/index.cjs",
|
||||
"default": "./dist/es/index.js"
|
||||
},
|
||||
"./SearchableMap": {
|
||||
"types": "./dist/types/SearchableMap.d.ts",
|
||||
"require": "./dist/cjs/SearchableMap.cjs",
|
||||
"default": "./dist/es/SearchableMap.js"
|
||||
}
|
||||
},
|
||||
"unpkg": "./dist/umd/index.js",
|
||||
"jsdelivr": "./dist/umd/index.js",
|
||||
"types": "./dist/types/index.d.ts",
|
||||
"author": "Luca Ongaro",
|
||||
"homepage": "https://lucaong.github.io/minisearch/",
|
||||
"bugs": "https://github.com/lucaong/minisearch/issues",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/lucaong/minisearch.git"
|
||||
},
|
||||
"keywords": [
|
||||
"search",
|
||||
"full text",
|
||||
"fuzzy",
|
||||
"prefix",
|
||||
"auto suggest",
|
||||
"auto complete",
|
||||
"index"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-terser": "^0.3.0",
|
||||
"@rollup/plugin-typescript": "^11.0.0",
|
||||
"@types/benchmark": "^2.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.26.0",
|
||||
"@typescript-eslint/parser": "^5.26.0",
|
||||
"benchmark": "^2.1.4",
|
||||
"core-js": "^3.1.4",
|
||||
"coveralls": "^3.0.2",
|
||||
"eslint": "^8.16.0",
|
||||
"eslint-config-standard": "^17.0.0",
|
||||
"eslint-plugin-import": "^2.20.2",
|
||||
"eslint-plugin-n": "^15.2.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^6.0.0",
|
||||
"fast-check": "^3.0.0",
|
||||
"jest": "^29.3.1",
|
||||
"regenerator-runtime": "^0.13.1",
|
||||
"rollup": "^3.4.0",
|
||||
"rollup-plugin-dts": "^5.0.0",
|
||||
"snazzy": "^9.0.0",
|
||||
"ts-jest": "^29.0.3",
|
||||
"tslib": "^2.0.1",
|
||||
"typedoc": "^0.19.1",
|
||||
"typescript": "^4.0.2"
|
||||
},
|
||||
"files": [
|
||||
"/dist/**/*",
|
||||
"/src/**/*"
|
||||
],
|
||||
"jest": {
|
||||
"testEnvironmentOptions": {
|
||||
"url": "http://localhost:3000/"
|
||||
},
|
||||
"transform": {
|
||||
"\\.(js|ts)$": "ts-jest"
|
||||
},
|
||||
"moduleFileExtensions": [
|
||||
"ts",
|
||||
"js"
|
||||
],
|
||||
"testRegex": "\\.test\\.(ts|js)$",
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>/src/testSetup/jest.js"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"test": "jest",
|
||||
"test-watch": "jest --watch",
|
||||
"coverage": "jest --coverage",
|
||||
"benchmark": "yarn build-benchmark && NODE_ENV=production node --expose-gc benchmarks/dist/index.cjs",
|
||||
"build-benchmark": "BENCHMARKS=true yarn build",
|
||||
"build": "yarn clean-build && NODE_ENV=production rollup -c",
|
||||
"clean-build": "rm -rf dist",
|
||||
"build-minified": "MINIFY=true yarn build",
|
||||
"build-docs": "typedoc --options typedoc.json && touch docs/.nojekyll && yarn build-examples",
|
||||
"build-examples": "yarn build && cd examples && yarn install && yarn build && cd ../",
|
||||
"lint": "eslint 'src/**/*.{js,ts}'",
|
||||
"lintfix": "eslint --fix 'src/**/*.{js,ts}'",
|
||||
"prepublishOnly": "yarn test && yarn build"
|
||||
},
|
||||
"sideEffects": false
|
||||
}
|
1833
node_modules/minisearch/src/MiniSearch.test.js
generated
vendored
Normal file
1833
node_modules/minisearch/src/MiniSearch.test.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
1952
node_modules/minisearch/src/MiniSearch.ts
generated
vendored
Normal file
1952
node_modules/minisearch/src/MiniSearch.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
314
node_modules/minisearch/src/SearchableMap/SearchableMap.test.js
generated
vendored
Normal file
314
node_modules/minisearch/src/SearchableMap/SearchableMap.test.js
generated
vendored
Normal file
|
@ -0,0 +1,314 @@
|
|||
/* eslint-env jest */
|
||||
|
||||
import SearchableMap from './SearchableMap'
|
||||
import * as fc from 'fast-check'
|
||||
|
||||
describe('SearchableMap', () => {
|
||||
const strings = ['bin', 'border', 'acqua', 'aqua', 'poisson', 'parachute',
|
||||
'parapendio', 'acquamarina', 'summertime', 'summer', 'join', 'mediterraneo',
|
||||
'perciò', 'borderline', 'bo']
|
||||
const keyValues = strings.map((key, i) => [key, i])
|
||||
const object = keyValues.reduce((obj, [key, value]) => ({ ...obj, [key]: value }))
|
||||
|
||||
const editDistance = function (a, b, mem = [[0]]) {
|
||||
mem[a.length] = mem[a.length] || [a.length]
|
||||
if (mem[a.length][b.length] !== undefined) { return mem[a.length][b.length] }
|
||||
const d = (a[a.length - 1] === b[b.length - 1]) ? 0 : 1
|
||||
const distance = (a.length === 1 && b.length === 1)
|
||||
? d
|
||||
: Math.min(
|
||||
((a.length > 0) ? editDistance(a.slice(0, -1), b, mem) + 1 : Infinity),
|
||||
((b.length > 0) ? editDistance(a, b.slice(0, -1), mem) + 1 : Infinity),
|
||||
((a.length > 0 && b.length > 0) ? editDistance(a.slice(0, -1), b.slice(0, -1), mem) + d : Infinity)
|
||||
)
|
||||
mem[a.length][b.length] = distance
|
||||
return distance
|
||||
}
|
||||
|
||||
describe('clear', () => {
|
||||
it('empties the map', () => {
|
||||
const map = SearchableMap.from(keyValues)
|
||||
map.clear()
|
||||
expect(Array.from(map.entries())).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('delete', () => {
|
||||
it('deletes the entry at the given key', () => {
|
||||
const map = SearchableMap.from(keyValues)
|
||||
map.delete('border')
|
||||
expect(map.has('border')).toBe(false)
|
||||
expect(map.has('summer')).toBe(true)
|
||||
expect(map.has('borderline')).toBe(true)
|
||||
expect(map.has('bo')).toBe(true)
|
||||
})
|
||||
|
||||
it('changes the size of the map', () => {
|
||||
const map = SearchableMap.from(keyValues)
|
||||
const sizeBefore = map.size
|
||||
map.delete('summertime')
|
||||
expect(map.size).toEqual(sizeBefore - 1)
|
||||
})
|
||||
|
||||
it('does nothing if the entry did not exist', () => {
|
||||
const map = new SearchableMap()
|
||||
expect(() => map.delete('something')).not.toThrow()
|
||||
})
|
||||
|
||||
it('leaves the radix tree in the same state as before the entry was added', () => {
|
||||
const map = new SearchableMap()
|
||||
|
||||
map.set('hello', 1)
|
||||
const before = new SearchableMap(new Map(map._tree))
|
||||
|
||||
map.set('help', 2)
|
||||
map.delete('help')
|
||||
|
||||
expect(map).toEqual(before)
|
||||
})
|
||||
})
|
||||
|
||||
describe('entries', () => {
|
||||
it('returns an iterator of entries', () => {
|
||||
const map = SearchableMap.from(keyValues)
|
||||
const entries = Array.from({ [Symbol.iterator]: () => map.entries() })
|
||||
expect(entries.sort()).toEqual(keyValues.sort())
|
||||
})
|
||||
|
||||
it('returns an iterable of entries', () => {
|
||||
const map = SearchableMap.from(keyValues)
|
||||
const entries = Array.from(map.entries())
|
||||
expect(entries.sort()).toEqual(keyValues.sort())
|
||||
})
|
||||
|
||||
it('returns empty iterator, if the map is empty', () => {
|
||||
const map = new SearchableMap()
|
||||
const entries = Array.from(map.entries())
|
||||
expect(entries).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('forEach', () => {
|
||||
it('iterates through each entry', () => {
|
||||
const entries = []
|
||||
const fn = (key, value) => entries.push([key, value])
|
||||
const map = SearchableMap.from(keyValues)
|
||||
map.forEach(fn)
|
||||
expect(entries).toEqual(Array.from(map.entries()))
|
||||
})
|
||||
})
|
||||
|
||||
describe('get', () => {
|
||||
it('gets the value at key', () => {
|
||||
const key = 'foo'
|
||||
const value = 42
|
||||
const map = SearchableMap.fromObject({ [key]: value })
|
||||
expect(map.get(key)).toBe(value)
|
||||
})
|
||||
|
||||
it('returns undefined if the key is not present', () => {
|
||||
const map = new SearchableMap()
|
||||
expect(map.get('not-existent')).toBe(undefined)
|
||||
})
|
||||
})
|
||||
|
||||
describe('has', () => {
|
||||
it('returns true if the given key exists in the map', () => {
|
||||
const map = new SearchableMap()
|
||||
map.set('something', 42)
|
||||
expect(map.has('something')).toBe(true)
|
||||
|
||||
map.set('something else', null)
|
||||
expect(map.has('something else')).toBe(true)
|
||||
})
|
||||
|
||||
it('returns false if the given key does not exist in the map', () => {
|
||||
const map = SearchableMap.fromObject({ something: 42 })
|
||||
expect(map.has('not-existing')).toBe(false)
|
||||
expect(map.has('some')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('keys', () => {
|
||||
it('returns an iterator of keys', () => {
|
||||
const map = SearchableMap.from(keyValues)
|
||||
const keys = Array.from({ [Symbol.iterator]: () => map.keys() })
|
||||
expect(keys.sort()).toEqual(strings.sort())
|
||||
})
|
||||
|
||||
it('returns an iterable of keys', () => {
|
||||
const map = SearchableMap.from(keyValues)
|
||||
const keys = Array.from(map.keys())
|
||||
expect(keys.sort()).toEqual(strings.sort())
|
||||
})
|
||||
|
||||
it('returns empty iterator, if the map is empty', () => {
|
||||
const map = new SearchableMap()
|
||||
const keys = Array.from(map.keys())
|
||||
expect(keys).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('set', () => {
|
||||
it('sets a value at key', () => {
|
||||
const map = new SearchableMap()
|
||||
const key = 'foo'
|
||||
const value = 42
|
||||
map.set(key, value)
|
||||
expect(map.get(key)).toBe(value)
|
||||
})
|
||||
|
||||
it('overrides a value at key if it already exists', () => {
|
||||
const map = SearchableMap.fromObject({ foo: 123 })
|
||||
const key = 'foo'
|
||||
const value = 42
|
||||
map.set(key, value)
|
||||
expect(map.get(key)).toBe(value)
|
||||
})
|
||||
|
||||
it('throws error if the given key is not a string', () => {
|
||||
const map = new SearchableMap()
|
||||
expect(() => map.set(123, 'foo')).toThrow('key must be a string')
|
||||
})
|
||||
})
|
||||
|
||||
describe('size', () => {
|
||||
it('is a property containing the size of the map', () => {
|
||||
const map = SearchableMap.from(keyValues)
|
||||
expect(map.size).toEqual(keyValues.length)
|
||||
map.set('foo', 42)
|
||||
expect(map.size).toEqual(keyValues.length + 1)
|
||||
map.delete('border')
|
||||
expect(map.size).toEqual(keyValues.length)
|
||||
map.clear()
|
||||
expect(map.size).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('update', () => {
|
||||
it('sets a value at key applying a function to the previous value', () => {
|
||||
const map = new SearchableMap()
|
||||
const key = 'foo'
|
||||
const fn = jest.fn(x => (x || 0) + 1)
|
||||
map.update(key, fn)
|
||||
expect(fn).toHaveBeenCalledWith(undefined)
|
||||
expect(map.get(key)).toBe(1)
|
||||
map.update(key, fn)
|
||||
expect(fn).toHaveBeenCalledWith(1)
|
||||
expect(map.get(key)).toBe(2)
|
||||
})
|
||||
|
||||
it('throws error if the given key is not a string', () => {
|
||||
const map = new SearchableMap()
|
||||
expect(() => map.update(123, () => {})).toThrow('key must be a string')
|
||||
})
|
||||
})
|
||||
|
||||
describe('values', () => {
|
||||
it('returns an iterator of values', () => {
|
||||
const map = SearchableMap.fromObject(object)
|
||||
const values = Array.from({ [Symbol.iterator]: () => map.values() })
|
||||
expect(values.sort()).toEqual(Object.values(object).sort())
|
||||
})
|
||||
|
||||
it('returns an iterable of values', () => {
|
||||
const map = SearchableMap.fromObject(object)
|
||||
const values = Array.from(map.values())
|
||||
expect(values.sort()).toEqual(Object.values(object).sort())
|
||||
})
|
||||
|
||||
it('returns empty iterator, if the map is empty', () => {
|
||||
const map = new SearchableMap()
|
||||
const values = Array.from(map.values())
|
||||
expect(values).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('atPrefix', () => {
|
||||
it('returns the submap at the given prefix', () => {
|
||||
const map = SearchableMap.from(keyValues)
|
||||
|
||||
const sum = map.atPrefix('sum')
|
||||
expect(Array.from(sum.keys()).sort()).toEqual(strings.filter(string => string.startsWith('sum')).sort())
|
||||
|
||||
const summer = sum.atPrefix('summer')
|
||||
expect(Array.from(summer.keys()).sort()).toEqual(strings.filter(string => string.startsWith('summer')).sort())
|
||||
|
||||
const xyz = map.atPrefix('xyz')
|
||||
expect(Array.from(xyz.keys())).toEqual([])
|
||||
|
||||
expect(() => sum.atPrefix('xyz')).toThrow()
|
||||
})
|
||||
|
||||
it('correctly computes the size', () => {
|
||||
const map = SearchableMap.from(keyValues)
|
||||
const sum = map.atPrefix('sum')
|
||||
expect(sum.size).toEqual(strings.filter(string => string.startsWith('sum')).length)
|
||||
})
|
||||
})
|
||||
|
||||
describe('fuzzyGet', () => {
|
||||
const terms = ['summer', 'acqua', 'aqua', 'acquire', 'poisson', 'qua']
|
||||
const keyValues = terms.map((key, i) => [key, i])
|
||||
const map = SearchableMap.from(keyValues)
|
||||
|
||||
it('returns all entries having the given maximum edit distance from the given key', () => {
|
||||
[0, 1, 2, 3].forEach(distance => {
|
||||
const results = map.fuzzyGet('acqua', distance)
|
||||
const entries = Array.from(results)
|
||||
expect(entries.map(([key, [value, dist]]) => [key, dist]).sort())
|
||||
.toEqual(terms.map(term => [term, editDistance('acqua', term)]).filter(([, d]) => d <= distance).sort())
|
||||
expect(entries.every(([key, [value]]) => map.get(key) === value)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
it('returns an empty object if no matching entries are found', () => {
|
||||
expect(map.fuzzyGet('winter', 1)).toEqual(new Map())
|
||||
})
|
||||
|
||||
it('returns entries if edit distance is longer than key', () => {
|
||||
const map = SearchableMap.from([['x', 1], [' x', 2]])
|
||||
expect(Array.from(map.fuzzyGet('x', 2).values())).toEqual([[1, 0], [2, 1]])
|
||||
})
|
||||
})
|
||||
|
||||
describe('with generated test data', () => {
|
||||
it('adds and removes entries', () => {
|
||||
const arrayOfStrings = fc.array(fc.oneof(fc.unicodeString(), fc.string()), { maxLength: 70 })
|
||||
const string = fc.oneof(fc.unicodeString({ minLength: 0, maxLength: 4 }), fc.string({ minLength: 0, maxLength: 4 }))
|
||||
const int = fc.integer({ min: 1, max: 4 })
|
||||
|
||||
fc.assert(fc.property(arrayOfStrings, string, int, (terms, prefix, maxDist) => {
|
||||
const map = new SearchableMap()
|
||||
const standardMap = new Map()
|
||||
const uniqueTerms = [...new Set(terms)]
|
||||
|
||||
terms.forEach((term, i) => {
|
||||
map.set(term, i)
|
||||
standardMap.set(term, i)
|
||||
expect(map.has(term)).toBe(true)
|
||||
expect(standardMap.get(term)).toEqual(i)
|
||||
})
|
||||
|
||||
expect(map.size).toEqual(standardMap.size)
|
||||
expect(Array.from(map.entries()).sort()).toEqual(Array.from(standardMap.entries()).sort())
|
||||
|
||||
expect(Array.from(map.atPrefix(prefix).keys()).sort())
|
||||
.toEqual(Array.from(new Set(terms)).filter(t => t.startsWith(prefix)).sort())
|
||||
|
||||
const fuzzy = map.fuzzyGet(terms[0], maxDist)
|
||||
expect(Array.from(fuzzy, ([key, [value, dist]]) => [key, dist]).sort())
|
||||
.toEqual(uniqueTerms.map(term => [term, editDistance(terms[0], term)])
|
||||
.filter(([, dist]) => dist <= maxDist).sort())
|
||||
|
||||
terms.forEach(term => {
|
||||
map.delete(term)
|
||||
expect(map.has(term)).toBe(false)
|
||||
expect(map.get(term)).toEqual(undefined)
|
||||
})
|
||||
|
||||
expect(map.size).toEqual(0)
|
||||
}))
|
||||
})
|
||||
})
|
||||
})
|
422
node_modules/minisearch/src/SearchableMap/SearchableMap.ts
generated
vendored
Normal file
422
node_modules/minisearch/src/SearchableMap/SearchableMap.ts
generated
vendored
Normal file
|
@ -0,0 +1,422 @@
|
|||
/* eslint-disable no-labels */
|
||||
import { TreeIterator, ENTRIES, KEYS, VALUES, LEAF } from './TreeIterator'
|
||||
import fuzzySearch, { FuzzyResults } from './fuzzySearch'
|
||||
import { RadixTree, Entry, Path } from './types'
|
||||
|
||||
/**
|
||||
* A class implementing the same interface as a standard JavaScript
|
||||
* [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)
|
||||
* with string keys, but adding support for efficiently searching entries with
|
||||
* prefix or fuzzy search. This class is used internally by [[MiniSearch]] as
|
||||
* the inverted index data structure. The implementation is a radix tree
|
||||
* (compressed prefix tree).
|
||||
*
|
||||
* Since this class can be of general utility beyond _MiniSearch_, it is
|
||||
* exported by the `minisearch` package and can be imported (or required) as
|
||||
* `minisearch/SearchableMap`.
|
||||
*
|
||||
* @typeParam T The type of the values stored in the map.
|
||||
*/
|
||||
export default class SearchableMap<T = any> {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
_tree: RadixTree<T>
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
_prefix: string
|
||||
|
||||
private _size: number | undefined = undefined
|
||||
|
||||
/**
|
||||
* The constructor is normally called without arguments, creating an empty
|
||||
* map. In order to create a [[SearchableMap]] from an iterable or from an
|
||||
* object, check [[SearchableMap.from]] and [[SearchableMap.fromObject]].
|
||||
*
|
||||
* The constructor arguments are for internal use, when creating derived
|
||||
* mutable views of a map at a prefix.
|
||||
*/
|
||||
constructor (tree: RadixTree<T> = new Map(), prefix = '') {
|
||||
this._tree = tree
|
||||
this._prefix = prefix
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a mutable view of this [[SearchableMap]], containing only
|
||||
* entries that share the given prefix.
|
||||
*
|
||||
* ### Usage:
|
||||
*
|
||||
* ```javascript
|
||||
* let map = new SearchableMap()
|
||||
* map.set("unicorn", 1)
|
||||
* map.set("universe", 2)
|
||||
* map.set("university", 3)
|
||||
* map.set("unique", 4)
|
||||
* map.set("hello", 5)
|
||||
*
|
||||
* let uni = map.atPrefix("uni")
|
||||
* uni.get("unique") // => 4
|
||||
* uni.get("unicorn") // => 1
|
||||
* uni.get("hello") // => undefined
|
||||
*
|
||||
* let univer = map.atPrefix("univer")
|
||||
* univer.get("unique") // => undefined
|
||||
* univer.get("universe") // => 2
|
||||
* univer.get("university") // => 3
|
||||
* ```
|
||||
*
|
||||
* @param prefix The prefix
|
||||
* @return A [[SearchableMap]] representing a mutable view of the original Map at the given prefix
|
||||
*/
|
||||
atPrefix (prefix: string): SearchableMap<T> {
|
||||
if (!prefix.startsWith(this._prefix)) { throw new Error('Mismatched prefix') }
|
||||
|
||||
const [node, path] = trackDown(this._tree, prefix.slice(this._prefix.length))
|
||||
|
||||
if (node === undefined) {
|
||||
const [parentNode, key] = last(path)
|
||||
|
||||
for (const k of parentNode!.keys()) {
|
||||
if (k !== LEAF && k.startsWith(key)) {
|
||||
const node = new Map()
|
||||
node.set(k.slice(key.length), parentNode!.get(k)!)
|
||||
return new SearchableMap(node, prefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new SearchableMap<T>(node, prefix)
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear
|
||||
*/
|
||||
clear (): void {
|
||||
this._size = undefined
|
||||
this._tree.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete
|
||||
* @param key Key to delete
|
||||
*/
|
||||
delete (key: string): void {
|
||||
this._size = undefined
|
||||
return remove(this._tree, key)
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries
|
||||
* @return An iterator iterating through `[key, value]` entries.
|
||||
*/
|
||||
entries () {
|
||||
return new TreeIterator(this, ENTRIES)
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach
|
||||
* @param fn Iteration function
|
||||
*/
|
||||
forEach (fn: (key: string, value: T, map: SearchableMap) => void): void {
|
||||
for (const [key, value] of this) {
|
||||
fn(key, value, this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Map of all the entries that have a key within the given edit
|
||||
* distance from the search key. The keys of the returned Map are the matching
|
||||
* keys, while the values are two-element arrays where the first element is
|
||||
* the value associated to the key, and the second is the edit distance of the
|
||||
* key to the search key.
|
||||
*
|
||||
* ### Usage:
|
||||
*
|
||||
* ```javascript
|
||||
* let map = new SearchableMap()
|
||||
* map.set('hello', 'world')
|
||||
* map.set('hell', 'yeah')
|
||||
* map.set('ciao', 'mondo')
|
||||
*
|
||||
* // Get all entries that match the key 'hallo' with a maximum edit distance of 2
|
||||
* map.fuzzyGet('hallo', 2)
|
||||
* // => Map(2) { 'hello' => ['world', 1], 'hell' => ['yeah', 2] }
|
||||
*
|
||||
* // In the example, the "hello" key has value "world" and edit distance of 1
|
||||
* // (change "e" to "a"), the key "hell" has value "yeah" and edit distance of 2
|
||||
* // (change "e" to "a", delete "o")
|
||||
* ```
|
||||
*
|
||||
* @param key The search key
|
||||
* @param maxEditDistance The maximum edit distance (Levenshtein)
|
||||
* @return A Map of the matching keys to their value and edit distance
|
||||
*/
|
||||
fuzzyGet (key: string, maxEditDistance: number): FuzzyResults<T> {
|
||||
return fuzzySearch<T>(this._tree, key, maxEditDistance)
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get
|
||||
* @param key Key to get
|
||||
* @return Value associated to the key, or `undefined` if the key is not
|
||||
* found.
|
||||
*/
|
||||
get (key: string): T | undefined {
|
||||
const node = lookup<T>(this._tree, key)
|
||||
return node !== undefined ? node.get(LEAF) : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has
|
||||
* @param key Key
|
||||
* @return True if the key is in the map, false otherwise
|
||||
*/
|
||||
has (key: string): boolean {
|
||||
const node = lookup(this._tree, key)
|
||||
return node !== undefined && node.has(LEAF)
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys
|
||||
* @return An `Iterable` iterating through keys
|
||||
*/
|
||||
keys () {
|
||||
return new TreeIterator(this, KEYS)
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set
|
||||
* @param key Key to set
|
||||
* @param value Value to associate to the key
|
||||
* @return The [[SearchableMap]] itself, to allow chaining
|
||||
*/
|
||||
set (key: string, value: T): SearchableMap<T> {
|
||||
if (typeof key !== 'string') { throw new Error('key must be a string') }
|
||||
this._size = undefined
|
||||
const node = createPath(this._tree, key)
|
||||
node.set(LEAF, value)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size
|
||||
*/
|
||||
get size (): number {
|
||||
if (this._size) { return this._size }
|
||||
/** @ignore */
|
||||
this._size = 0
|
||||
|
||||
const iter = this.entries()
|
||||
while (!iter.next().done) this._size! += 1
|
||||
|
||||
return this._size
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the value at the given key using the provided function. The function
|
||||
* is called with the current value at the key, and its return value is used as
|
||||
* the new value to be set.
|
||||
*
|
||||
* ### Example:
|
||||
*
|
||||
* ```javascript
|
||||
* // Increment the current value by one
|
||||
* searchableMap.update('somekey', (currentValue) => currentValue == null ? 0 : currentValue + 1)
|
||||
* ```
|
||||
*
|
||||
* If the value at the given key is or will be an object, it might not require
|
||||
* re-assignment. In that case it is better to use `fetch()`, because it is
|
||||
* faster.
|
||||
*
|
||||
* @param key The key to update
|
||||
* @param fn The function used to compute the new value from the current one
|
||||
* @return The [[SearchableMap]] itself, to allow chaining
|
||||
*/
|
||||
update (key: string, fn: (value: T | undefined) => T): SearchableMap<T> {
|
||||
if (typeof key !== 'string') { throw new Error('key must be a string') }
|
||||
this._size = undefined
|
||||
const node = createPath(this._tree, key)
|
||||
node.set(LEAF, fn(node.get(LEAF)))
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the value of the given key. If the value does not exist, calls the
|
||||
* given function to create a new value, which is inserted at the given key
|
||||
* and subsequently returned.
|
||||
*
|
||||
* ### Example:
|
||||
*
|
||||
* ```javascript
|
||||
* const map = searchableMap.fetch('somekey', () => new Map())
|
||||
* map.set('foo', 'bar')
|
||||
* ```
|
||||
*
|
||||
* @param key The key to update
|
||||
* @param defaultValue A function that creates a new value if the key does not exist
|
||||
* @return The existing or new value at the given key
|
||||
*/
|
||||
fetch (key: string, initial: () => T): T {
|
||||
if (typeof key !== 'string') { throw new Error('key must be a string') }
|
||||
this._size = undefined
|
||||
const node = createPath(this._tree, key)
|
||||
|
||||
let value = node.get(LEAF)
|
||||
if (value === undefined) {
|
||||
node.set(LEAF, value = initial())
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/values
|
||||
* @return An `Iterable` iterating through values.
|
||||
*/
|
||||
values () {
|
||||
return new TreeIterator(this, VALUES)
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/@@iterator
|
||||
*/
|
||||
[Symbol.iterator] () {
|
||||
return this.entries()
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [[SearchableMap]] from an `Iterable` of entries
|
||||
*
|
||||
* @param entries Entries to be inserted in the [[SearchableMap]]
|
||||
* @return A new [[SearchableMap]] with the given entries
|
||||
*/
|
||||
static from<T = any> (entries: Iterable<Entry<T>> | Entry<T>[]) {
|
||||
const tree = new SearchableMap()
|
||||
for (const [key, value] of entries) {
|
||||
tree.set(key, value)
|
||||
}
|
||||
return tree
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [[SearchableMap]] from the iterable properties of a JavaScript object
|
||||
*
|
||||
* @param object Object of entries for the [[SearchableMap]]
|
||||
* @return A new [[SearchableMap]] with the given entries
|
||||
*/
|
||||
static fromObject<T = any> (object: { [key: string]: T }) {
|
||||
return SearchableMap.from<T>(Object.entries(object))
|
||||
}
|
||||
}
|
||||
|
||||
const trackDown = <T = any>(tree: RadixTree<T> | undefined, key: string, path: Path<T> = []): [RadixTree<T> | undefined, Path<T>] => {
|
||||
if (key.length === 0 || tree == null) { return [tree, path] }
|
||||
|
||||
for (const k of tree.keys()) {
|
||||
if (k !== LEAF && key.startsWith(k)) {
|
||||
path.push([tree, k]) // performance: update in place
|
||||
return trackDown(tree.get(k)!, key.slice(k.length), path)
|
||||
}
|
||||
}
|
||||
|
||||
path.push([tree, key]) // performance: update in place
|
||||
return trackDown(undefined, '', path)
|
||||
}
|
||||
|
||||
const lookup = <T = any>(tree: RadixTree<T>, key: string): RadixTree<T> | undefined => {
|
||||
if (key.length === 0 || tree == null) { return tree }
|
||||
|
||||
for (const k of tree.keys()) {
|
||||
if (k !== LEAF && key.startsWith(k)) {
|
||||
return lookup(tree.get(k)!, key.slice(k.length))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a path in the radix tree for the given key, and returns the deepest
|
||||
// node. This function is in the hot path for indexing. It avoids unnecessary
|
||||
// string operations and recursion for performance.
|
||||
const createPath = <T = any>(node: RadixTree<T>, key: string): RadixTree<T> => {
|
||||
const keyLength = key.length
|
||||
|
||||
outer: for (let pos = 0; node && pos < keyLength;) {
|
||||
for (const k of node.keys()) {
|
||||
// Check whether this key is a candidate: the first characters must match.
|
||||
if (k !== LEAF && key[pos] === k[0]) {
|
||||
const len = Math.min(keyLength - pos, k.length)
|
||||
|
||||
// Advance offset to the point where key and k no longer match.
|
||||
let offset = 1
|
||||
while (offset < len && key[pos + offset] === k[offset]) ++offset
|
||||
|
||||
const child = node.get(k)!
|
||||
if (offset === k.length) {
|
||||
// The existing key is shorter than the key we need to create.
|
||||
node = child
|
||||
} else {
|
||||
// Partial match: we need to insert an intermediate node to contain
|
||||
// both the existing subtree and the new node.
|
||||
const intermediate = new Map()
|
||||
intermediate.set(k.slice(offset), child)
|
||||
node.set(key.slice(pos, pos + offset), intermediate)
|
||||
node.delete(k)
|
||||
node = intermediate
|
||||
}
|
||||
|
||||
pos += offset
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
|
||||
// Create a final child node to contain the final suffix of the key.
|
||||
const child = new Map()
|
||||
node.set(key.slice(pos), child)
|
||||
return child
|
||||
}
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
const remove = <T = any>(tree: RadixTree<T>, key: string): void => {
|
||||
const [node, path] = trackDown(tree, key)
|
||||
if (node === undefined) { return }
|
||||
node.delete(LEAF)
|
||||
|
||||
if (node.size === 0) {
|
||||
cleanup(path)
|
||||
} else if (node.size === 1) {
|
||||
const [key, value] = node.entries().next().value
|
||||
merge(path, key, value)
|
||||
}
|
||||
}
|
||||
|
||||
const cleanup = <T = any>(path: Path<T>): void => {
|
||||
if (path.length === 0) { return }
|
||||
|
||||
const [node, key] = last(path)
|
||||
node!.delete(key)
|
||||
|
||||
if (node!.size === 0) {
|
||||
cleanup(path.slice(0, -1))
|
||||
} else if (node!.size === 1) {
|
||||
const [key, value] = node!.entries().next().value
|
||||
if (key !== LEAF) {
|
||||
merge(path.slice(0, -1), key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const merge = <T = any>(path: Path<T>, key: string, value: RadixTree<T>): void => {
|
||||
if (path.length === 0) { return }
|
||||
|
||||
const [node, nodeKey] = last(path)
|
||||
node!.set(nodeKey + key, value)
|
||||
node!.delete(nodeKey)
|
||||
}
|
||||
|
||||
const last = <T = any>(array: T[]): T => {
|
||||
return array[array.length - 1]
|
||||
}
|
103
node_modules/minisearch/src/SearchableMap/TreeIterator.ts
generated
vendored
Normal file
103
node_modules/minisearch/src/SearchableMap/TreeIterator.ts
generated
vendored
Normal file
|
@ -0,0 +1,103 @@
|
|||
import { RadixTree, Entry, LeafType } from './types'
|
||||
|
||||
/** @ignore */
|
||||
const ENTRIES = 'ENTRIES'
|
||||
|
||||
/** @ignore */
|
||||
const KEYS = 'KEYS'
|
||||
|
||||
/** @ignore */
|
||||
const VALUES = 'VALUES'
|
||||
|
||||
/** @ignore */
|
||||
const LEAF = '' as LeafType
|
||||
|
||||
interface Iterators<T> {
|
||||
ENTRIES: Entry<T>
|
||||
KEYS: string
|
||||
VALUES: T
|
||||
}
|
||||
|
||||
type Kind<T> = keyof Iterators<T>
|
||||
type Result<T, K extends keyof Iterators<T>> = Iterators<T>[K]
|
||||
|
||||
type IteratorPath<T> = {
|
||||
node: RadixTree<T>,
|
||||
keys: string[]
|
||||
}[]
|
||||
|
||||
export type IterableSet<T> = {
|
||||
_tree: RadixTree<T>,
|
||||
_prefix: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
class TreeIterator<T, K extends Kind<T>> implements Iterator<Result<T, K>> {
|
||||
set: IterableSet<T>
|
||||
_type: K
|
||||
_path: IteratorPath<T>
|
||||
|
||||
constructor (set: IterableSet<T>, type: K) {
|
||||
const node = set._tree
|
||||
const keys = Array.from(node.keys())
|
||||
this.set = set
|
||||
this._type = type
|
||||
this._path = keys.length > 0 ? [{ node, keys }] : []
|
||||
}
|
||||
|
||||
next (): IteratorResult<Result<T, K>> {
|
||||
const value = this.dive()
|
||||
this.backtrack()
|
||||
return value
|
||||
}
|
||||
|
||||
dive (): IteratorResult<Result<T, K>> {
|
||||
if (this._path.length === 0) { return { done: true, value: undefined } }
|
||||
const { node, keys } = last(this._path)!
|
||||
if (last(keys) === LEAF) { return { done: false, value: this.result() } }
|
||||
|
||||
const child = node.get(last(keys)!)!
|
||||
this._path.push({ node: child, keys: Array.from(child.keys()) })
|
||||
return this.dive()
|
||||
}
|
||||
|
||||
backtrack (): void {
|
||||
if (this._path.length === 0) { return }
|
||||
const keys = last(this._path)!.keys
|
||||
keys.pop()
|
||||
if (keys.length > 0) { return }
|
||||
this._path.pop()
|
||||
this.backtrack()
|
||||
}
|
||||
|
||||
key (): string {
|
||||
return this.set._prefix + this._path
|
||||
.map(({ keys }) => last(keys))
|
||||
.filter(key => key !== LEAF)
|
||||
.join('')
|
||||
}
|
||||
|
||||
value (): T {
|
||||
return last(this._path)!.node.get(LEAF)!
|
||||
}
|
||||
|
||||
result (): Result<T, K> {
|
||||
switch (this._type) {
|
||||
case VALUES: return this.value() as Result<T, K>
|
||||
case KEYS: return this.key() as Result<T, K>
|
||||
default: return [this.key(), this.value()] as Result<T, K>
|
||||
}
|
||||
}
|
||||
|
||||
[Symbol.iterator] () {
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
const last = <T>(array: T[]): T | undefined => {
|
||||
return array[array.length - 1]
|
||||
}
|
||||
|
||||
export { TreeIterator, ENTRIES, KEYS, VALUES, LEAF }
|
130
node_modules/minisearch/src/SearchableMap/fuzzySearch.ts
generated
vendored
Normal file
130
node_modules/minisearch/src/SearchableMap/fuzzySearch.ts
generated
vendored
Normal file
|
@ -0,0 +1,130 @@
|
|||
/* eslint-disable no-labels */
|
||||
import { LEAF } from './TreeIterator'
|
||||
import { RadixTree } from './types'
|
||||
|
||||
export type FuzzyResult<T> = [T, number]
|
||||
|
||||
export type FuzzyResults<T> = Map<string, FuzzyResult<T>>
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
export const fuzzySearch = <T = any>(node: RadixTree<T>, query: string, maxDistance: number): FuzzyResults<T> => {
|
||||
const results: FuzzyResults<T> = new Map()
|
||||
if (query === undefined) return results
|
||||
|
||||
// Number of columns in the Levenshtein matrix.
|
||||
const n = query.length + 1
|
||||
|
||||
// Matching terms can never be longer than N + maxDistance.
|
||||
const m = n + maxDistance
|
||||
|
||||
// Fill first matrix row and column with numbers: 0 1 2 3 ...
|
||||
const matrix = new Uint8Array(m * n).fill(maxDistance + 1)
|
||||
for (let j = 0; j < n; ++j) matrix[j] = j
|
||||
for (let i = 1; i < m; ++i) matrix[i * n] = i
|
||||
|
||||
recurse(
|
||||
node,
|
||||
query,
|
||||
maxDistance,
|
||||
results,
|
||||
matrix,
|
||||
1,
|
||||
n,
|
||||
''
|
||||
)
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// Modified version of http://stevehanov.ca/blog/?id=114
|
||||
|
||||
// This builds a Levenshtein matrix for a given query and continuously updates
|
||||
// it for nodes in the radix tree that fall within the given maximum edit
|
||||
// distance. Keeping the same matrix around is beneficial especially for larger
|
||||
// edit distances.
|
||||
//
|
||||
// k a t e <-- query
|
||||
// 0 1 2 3 4
|
||||
// c 1 1 2 3 4
|
||||
// a 2 2 1 2 3
|
||||
// t 3 3 2 1 [2] <-- edit distance
|
||||
// ^
|
||||
// ^ term in radix tree, rows are added and removed as needed
|
||||
|
||||
const recurse = <T = any>(
|
||||
node: RadixTree<T>,
|
||||
query: string,
|
||||
maxDistance: number,
|
||||
results: FuzzyResults<T>,
|
||||
matrix: Uint8Array,
|
||||
m: number,
|
||||
n: number,
|
||||
prefix: string
|
||||
): void => {
|
||||
const offset = m * n
|
||||
|
||||
key: for (const key of node.keys()) {
|
||||
if (key === LEAF) {
|
||||
// We've reached a leaf node. Check if the edit distance acceptable and
|
||||
// store the result if it is.
|
||||
const distance = matrix[offset - 1]
|
||||
if (distance <= maxDistance) {
|
||||
results.set(prefix, [node.get(key)!, distance])
|
||||
}
|
||||
} else {
|
||||
// Iterate over all characters in the key. Update the Levenshtein matrix
|
||||
// and check if the minimum distance in the last row is still within the
|
||||
// maximum edit distance. If it is, we can recurse over all child nodes.
|
||||
let i = m
|
||||
for (let pos = 0; pos < key.length; ++pos, ++i) {
|
||||
const char = key[pos]
|
||||
const thisRowOffset = n * i
|
||||
const prevRowOffset = thisRowOffset - n
|
||||
|
||||
// Set the first column based on the previous row, and initialize the
|
||||
// minimum distance in the current row.
|
||||
let minDistance = matrix[thisRowOffset]
|
||||
|
||||
const jmin = Math.max(0, i - maxDistance - 1)
|
||||
const jmax = Math.min(n - 1, i + maxDistance)
|
||||
|
||||
// Iterate over remaining columns (characters in the query).
|
||||
for (let j = jmin; j < jmax; ++j) {
|
||||
const different = char !== query[j]
|
||||
|
||||
// It might make sense to only read the matrix positions used for
|
||||
// deletion/insertion if the characters are different. But we want to
|
||||
// avoid conditional reads for performance reasons.
|
||||
const rpl = matrix[prevRowOffset + j] + +different
|
||||
const del = matrix[prevRowOffset + j + 1] + 1
|
||||
const ins = matrix[thisRowOffset + j] + 1
|
||||
|
||||
const dist = matrix[thisRowOffset + j + 1] = Math.min(rpl, del, ins)
|
||||
|
||||
if (dist < minDistance) minDistance = dist
|
||||
}
|
||||
|
||||
// Because distance will never decrease, we can stop. There will be no
|
||||
// matching child nodes.
|
||||
if (minDistance > maxDistance) {
|
||||
continue key
|
||||
}
|
||||
}
|
||||
|
||||
recurse(
|
||||
node.get(key)!,
|
||||
query,
|
||||
maxDistance,
|
||||
results,
|
||||
matrix,
|
||||
i,
|
||||
n,
|
||||
prefix + key
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default fuzzySearch
|
18
node_modules/minisearch/src/SearchableMap/types.ts
generated
vendored
Normal file
18
node_modules/minisearch/src/SearchableMap/types.ts
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
export type LeafType = '' & { readonly __tag: unique symbol }
|
||||
|
||||
export interface RadixTree<T> extends Map<string, T | RadixTree<T>> {
|
||||
// Distinguish between an empty string indicating a leaf node and a non-empty
|
||||
// string indicating a subtree. Overriding these types avoids a lot of type
|
||||
// assertions elsewhere in the code. It is not 100% foolproof because you can
|
||||
// still pass in a blank string '' disguised as `string` and potentially get a
|
||||
// leaf value.
|
||||
get(key: LeafType): T | undefined
|
||||
get(key: string): RadixTree<T> | undefined
|
||||
|
||||
set(key: LeafType, value: T): this
|
||||
set(key: string, value: RadixTree<T>): this
|
||||
}
|
||||
|
||||
export type Entry<T> = [string, T]
|
||||
|
||||
export type Path<T> = [RadixTree<T> | undefined, string][]
|
4
node_modules/minisearch/src/index.ts
generated
vendored
Normal file
4
node_modules/minisearch/src/index.ts
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
import MiniSearch from './MiniSearch'
|
||||
|
||||
export * from './MiniSearch'
|
||||
export default MiniSearch
|
1
node_modules/minisearch/src/testSetup/jest.js
generated
vendored
Normal file
1
node_modules/minisearch/src/testSetup/jest.js
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/* eslint-env jest */
|
Loading…
Add table
Add a link
Reference in a new issue