synchronous-autocomplete
Advanced tools
Comparing version 2.3.1 to 3.0.0-alpha.1
105
build.js
@@ -1,83 +0,40 @@ | ||
'use strict'; | ||
const roundTo = (v, p) => parseFloat(v.toFixed(p)) | ||
var roundTo = function roundTo(v, p) { | ||
return parseFloat(v.toFixed(p)); | ||
}; | ||
const buildIndex = (tokenize, items) => { | ||
const originalIds = [] | ||
const tokens = Object.create(null) | ||
const weights = [] | ||
const nrOfTokens = [] | ||
var buildIndexes = function buildIndexes(tokenize, items) { | ||
var originalIds = []; | ||
var tokens = Object.create(null); | ||
var weights = []; | ||
var nrOfTokens = []; | ||
let currentId = 0 | ||
const generateId = (oldId) => { | ||
const newId = currentId++ | ||
originalIds[newId] = oldId | ||
return newId | ||
} | ||
var currentId = 0; | ||
var generateId = function generateId(oldId) { | ||
var newId = currentId++; | ||
originalIds[newId] = oldId; | ||
return newId; | ||
}; | ||
for (let item of items) { | ||
const id = generateId(item.id) | ||
const tokensOfItem = tokenize(item.name) | ||
for (let token of tokensOfItem) { | ||
if (!Array.isArray(tokens[token])) tokens[token] = [id] | ||
else tokens[token].push(id) | ||
} | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
try { | ||
for (var _iterator = items[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var item = _step.value; | ||
var id = generateId(item.id); | ||
var tokensOfItem = tokenize(item.name); | ||
var _iteratorNormalCompletion2 = true; | ||
var _didIteratorError2 = false; | ||
var _iteratorError2 = undefined; | ||
try { | ||
for (var _iterator2 = tokensOfItem[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
var _token = _step2.value; | ||
if (!Array.isArray(tokens[_token])) tokens[_token] = [id];else tokens[_token].push(id); | ||
} | ||
} catch (err) { | ||
_didIteratorError2 = true; | ||
_iteratorError2 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion2 && _iterator2.return) { | ||
_iterator2.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError2) { | ||
throw _iteratorError2; | ||
} | ||
} | ||
} | ||
weights[id] = item.weight; | ||
nrOfTokens[id] = tokensOfItem.length; | ||
} | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion && _iterator.return) { | ||
_iterator.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
} | ||
weights[id] = item.weight | ||
nrOfTokens[id] = tokensOfItem.length | ||
} | ||
var scores = Object.create(null); | ||
for (var token in tokens) { | ||
var nrOfItemsForToken = tokens[token].length; | ||
var score = nrOfItemsForToken / items.length; | ||
scores[token] = roundTo(score, 6 - Math.log10(score) | 0); | ||
const scores = Object.create(null) | ||
for (let token in tokens) { | ||
const nrOfItemsForToken = tokens[token].length | ||
const score = nrOfItemsForToken / items.length | ||
scores[token] = roundTo(score, 6 - Math.log10(score) | 0) | ||
} | ||
return { tokens: tokens, scores: scores, weights: weights, nrOfTokens: nrOfTokens, originalIds: originalIds }; | ||
}; | ||
return {tokens, scores, weights, nrOfTokens, originalIds} | ||
} | ||
module.exports = buildIndexes; | ||
export { | ||
buildIndex, | ||
} |
@@ -1,10 +0,8 @@ | ||
'use strict'; | ||
import * as IndexSchema from './lib/schema.proto.cjs' | ||
const {Index} = IndexSchema | ||
var _require = require('./schema.proto.js'), | ||
Index = _require.Index; | ||
const decodeIndex = (buf) => { | ||
const output = Index.decode(buf) | ||
var decode = function decode(buf) { | ||
var output = Index.decode(buf); | ||
var index = { | ||
const index = { | ||
tokens: Object.create(null), | ||
@@ -15,15 +13,17 @@ weights: output.weights, | ||
originalIds: output.original_ids | ||
}; | ||
for (var i = 0; i < output.tokens.length; i++) { | ||
var t = output.tokens[i]; | ||
index.tokens[t.name] = t.ids; | ||
} | ||
for (var _i = 0; _i < output.scores.length; _i++) { | ||
var s = output.scores[_i]; | ||
index.scores[s.token] = s.score; | ||
for (let i = 0; i < output.tokens.length; i++) { | ||
const t = output.tokens[i] | ||
index.tokens[t.name] = t.ids | ||
} | ||
for (let i = 0; i < output.scores.length; i++) { | ||
const s = output.scores[i] | ||
index.scores[s.token] = s.score | ||
} | ||
return index; | ||
}; | ||
return index | ||
} | ||
module.exports = decode; | ||
export { | ||
decodeIndex, | ||
} |
@@ -1,8 +0,8 @@ | ||
'use strict'; | ||
import * as IndexSchema from './lib/schema.proto.cjs' | ||
const {Index} = IndexSchema | ||
var _require = require('./schema.proto.js'), | ||
Index = _require.Index; | ||
// todo: move into index.js? | ||
var encode = function encode(index) { | ||
var input = { | ||
const encodeIndex = (index) => { | ||
const input = { | ||
tokens: [], | ||
@@ -13,22 +13,25 @@ weights: index.weights, | ||
original_ids: index.originalIds | ||
} | ||
// It would be more efficient not to create these lists, but to directly write | ||
// into the buffer item by item. | ||
// todo | ||
};for (var token in index.tokens) { | ||
if (!Object.prototype.hasOwnProperty.call(index.tokens, token)) continue; | ||
// It would be more efficient not to create these lists, but to directly write | ||
// into the buffer item by item. | ||
// todo | ||
for (let token in index.tokens) { | ||
if (!Object.prototype.hasOwnProperty.call(index.tokens, token)) continue | ||
input.tokens.push({ | ||
name: token, ids: index.tokens[token] | ||
}); | ||
}) | ||
} | ||
for (var _token in index.scores) { | ||
if (!Object.prototype.hasOwnProperty.call(index.scores, _token)) continue; | ||
for (let token in index.scores) { | ||
if (!Object.prototype.hasOwnProperty.call(index.scores, token)) continue | ||
input.scores.push({ | ||
token: _token, score: index.scores[_token] | ||
}); | ||
token, score: index.scores[token] | ||
}) | ||
} | ||
return Index.encode(input); | ||
}; | ||
return Index.encode(input) | ||
} | ||
module.exports = encode; | ||
export { | ||
encodeIndex, | ||
} |
134
index.js
@@ -1,21 +0,19 @@ | ||
'use strict'; | ||
import hifo from 'hifo' | ||
import leven from 'leven' | ||
var hifo = require('hifo'); | ||
var leven = require('leven'); | ||
const internalId = Symbol('internal numeric ID') | ||
var internalId = Symbol('internal numeric ID'); | ||
const createAutocomplete = (tokens, scores, weights, nrOfTokens, originalIds, tokenize) => { | ||
const byFragment = (fragment, completion, fuzzy) => { | ||
const results = [] | ||
const l = fragment.length | ||
var createAutocomplete = function createAutocomplete(tokens, scores, weights, nrOfTokens, originalIds, tokenize) { | ||
var byFragment = function byFragment(fragment, completion, fuzzy) { | ||
var results = []; | ||
var l = fragment.length; | ||
if (tokens[fragment]) { | ||
var relevance = 1 + scores[fragment] + Math.sqrt(l); | ||
const relevance = 1 + scores[fragment] + Math.sqrt(l) | ||
var ids = tokens[fragment]; | ||
for (var i = 0; i < ids.length; i++) { | ||
var id = ids[i]; | ||
if (!results[id]) results[id] = 0; | ||
results[id] += relevance; | ||
const ids = tokens[fragment] | ||
for (let i = 0; i < ids.length; i++) { | ||
const id = ids[i] | ||
if (!results[id]) results[id] = 0 | ||
results[id] += relevance | ||
} | ||
@@ -25,20 +23,20 @@ } | ||
if (completion || fuzzy) { | ||
for (var t in tokens) { | ||
if (fragment === t) continue; // has been dealt with above | ||
for (let t in tokens) { | ||
if (fragment === t) continue // has been dealt with above | ||
var _relevance = void 0; | ||
var distance = void 0; | ||
let relevance | ||
let distance | ||
// add-one smoothing | ||
if (completion && t.length > l && fragment === t.slice(0, l)) { | ||
_relevance = 1 + scores[t] + l / t.length; | ||
relevance = 1 + scores[t] + l / t.length | ||
} else if (fuzzy && (distance = leven(fragment, t)) <= 3) { | ||
_relevance = (1 + scores[t]) / (distance + 1); | ||
} else continue; | ||
relevance = (1 + scores[t]) / (distance + 1) | ||
} else continue | ||
var _ids = tokens[t]; | ||
for (var _i = 0; _i < _ids.length; _i++) { | ||
var _id = _ids[_i]; | ||
if (!results[_id]) results[_id] = 0; | ||
results[_id] += _relevance; | ||
const ids = tokens[t] | ||
for (let i = 0; i < ids.length; i++) { | ||
const id = ids[i] | ||
if (!results[id]) results[id] = 0 | ||
results[id] += relevance | ||
} | ||
@@ -48,59 +46,57 @@ } | ||
return results; | ||
}; | ||
return results | ||
} | ||
var autocomplete = function autocomplete(query) { | ||
var limit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 6; | ||
var fuzzy = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; | ||
var completion = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true; | ||
const autocomplete = (query, limit = 6, fuzzy = false, completion = true) => { | ||
if (query === '') return [] | ||
if (query === '') return []; | ||
var data = Object.create(null); | ||
var fragments = tokenize(query); | ||
for (var i = 0; i < fragments.length; i++) { | ||
var fragment = fragments[i]; | ||
data[fragment] = byFragment(fragment, completion, fuzzy); | ||
const data = Object.create(null) | ||
const fragments = tokenize(query) | ||
for (let i = 0; i < fragments.length; i++) { | ||
const fragment = fragments[i] | ||
data[fragment] = byFragment(fragment, completion, fuzzy) | ||
} | ||
var totalRelevance = function totalRelevance(id) { | ||
var r = 1 / nrOfTokens[id]; | ||
for (var _fragment in data) { | ||
if (!data[_fragment][id]) return false; | ||
r *= data[_fragment][id]; | ||
const totalRelevance = (id) => { | ||
let r = 1 / nrOfTokens[id] | ||
for (let fragment in data) { | ||
if (!data[fragment][id]) return false | ||
r *= data[fragment][id] | ||
} | ||
return r; | ||
}; | ||
return r | ||
} | ||
var results = Object.create(null); | ||
for (var _fragment2 in data) { | ||
for (var id = 0; id < data[_fragment2].length; id++) { | ||
if (id in results) continue; | ||
const results = Object.create(null) | ||
for (let fragment in data) { | ||
for (let id = 0; id < data[fragment].length; id++) { | ||
if (id in results) continue | ||
var relevance = totalRelevance(id); | ||
if (relevance === false) continue; | ||
const relevance = totalRelevance(id) | ||
if (relevance === false) continue | ||
id = parseInt(id); | ||
var score = relevance * Math.pow(weights[id], 1 / 3); | ||
id = parseInt(id) | ||
const score = relevance * Math.pow(weights[id], 1/3) | ||
results[id] = { | ||
id: originalIds[id], | ||
relevance: relevance, | ||
score: score, | ||
relevance, | ||
score, | ||
weight: weights[id] | ||
}; | ||
Object.defineProperty(results[id], internalId, { value: id }); | ||
} | ||
Object.defineProperty(results[id], internalId, {value: id}) | ||
} | ||
} | ||
var relevant = hifo(hifo.highest('score'), limit || 6); | ||
for (var _id2 in results) { | ||
relevant.add(results[_id2]); | ||
}return relevant.data; | ||
}; | ||
const relevant = hifo(hifo.highest('score'), limit || 6) | ||
for (let id in results) relevant.add(results[id]) | ||
return relevant.data | ||
} | ||
autocomplete.byFragment = byFragment; | ||
autocomplete.internalId = internalId; | ||
return autocomplete; | ||
}; | ||
autocomplete.byFragment = byFragment | ||
autocomplete.internalId = internalId | ||
return autocomplete | ||
} | ||
module.exports = createAutocomplete; | ||
export { | ||
internalId, | ||
createAutocomplete, | ||
} |
{ | ||
"name": "synchronous-autocomplete", | ||
"description": "Fast, simple autocompletion.", | ||
"version": "2.3.1", | ||
"version": "3.0.0-alpha.1", | ||
"type": "module", | ||
"main": "index.js", | ||
"todo": "declare entrypoints", | ||
"files": [ | ||
@@ -11,4 +13,3 @@ "index.js", | ||
"decode.js", | ||
"schema.proto.js", | ||
"src" | ||
"lib" | ||
], | ||
@@ -26,26 +27,24 @@ "keywords": [ | ||
"engines": { | ||
"node": ">=6" | ||
"node": ">=18" | ||
}, | ||
"dependencies": { | ||
"hifo": "^1.0.0", | ||
"leven": "^3.0.0", | ||
"leven": "^4.0.0", | ||
"protocol-buffers-encodings": "^1.1.0" | ||
}, | ||
"devDependencies": { | ||
"babel-cli": "^6.26.0", | ||
"babel-preset-env": "^1.6.1", | ||
"benchmark": "^2.1.4", | ||
"eslint": "^8.23.1", | ||
"lodash.sortby": "^4.7.0", | ||
"normalize-for-search": "^2.0.1", | ||
"protocol-buffers": "^4.1.0", | ||
"tap-min": "^1.2.2", | ||
"protocol-buffers": "^5.0.0", | ||
"tap-min": "^2.0.0", | ||
"tape": "^5.0.0" | ||
}, | ||
"scripts": { | ||
"transpile": "babel src --presets env --out-dir .", | ||
"pbf": "protocol-buffers src/schema.proto -o schema.proto.js", | ||
"build": "npm run pbf && npm run transpile", | ||
"build": "protocol-buffers lib/schema.proto -o lib/schema.proto.cjs", | ||
"lint": "eslint .", | ||
"test": "node test.js | tap-min", | ||
"prepublishOnly": "npm run build && npm test" | ||
"prepublishOnly": "npm run build && npm run lint && npm test" | ||
} | ||
} |
@@ -6,3 +6,2 @@ # synchronous-autocomplete | ||
[![npm version](https://img.shields.io/npm/v/synchronous-autocomplete.svg)](https://www.npmjs.com/package/synchronous-autocomplete) | ||
[![build status](https://api.travis-ci.org/derhuerst/synchronous-autocomplete.svg?branch=master)](https://travis-ci.org/derhuerst/synchronous-autocomplete) | ||
![ISC-licensed](https://img.shields.io/github/license/derhuerst/synchronous-autocomplete.svg) | ||
@@ -90,3 +89,3 @@ [![support me via GitHub Sponsors](https://img.shields.io/badge/support%20me-donate-fa7664.svg)](https://github.com/sponsors/derhuerst) | ||
```js | ||
const normalize = require('normalize-for-search') | ||
import normalize from 'normalize-for-search' | ||
@@ -101,5 +100,5 @@ const tokenize = (str) => { | ||
```js | ||
const create = require('synchronous-autocomplete') | ||
import {createAutocomplete} from 'synchronous-autocomplete' | ||
const autocomplete = create(tokens, scores, weights, nrOfTokens, originalIds, tokenize) | ||
const autocomplete = createAutocomplete(tokens, scores, weights, nrOfTokens, originalIds, tokenize) | ||
@@ -136,3 +135,3 @@ autocomplete('bana') | ||
```js | ||
const autocomplete = create(tokens, scores, weights, nrOfTokens, originalIds, tokenize) | ||
const autocomplete = createAutocomplete(tokens, scores, weights, nrOfTokens, originalIds, tokenize) | ||
autocomplete(query, limit = 6, fuzzy = false, completion = true) | ||
@@ -154,11 +153,12 @@ ``` | ||
```js | ||
const encode = require('synchronous-autocomplete/encode') | ||
const fs = require('fs') | ||
import {encodeIndex} from 'synchronous-autocomplete/encode.js' | ||
import {writeFileSync, readFileSync} from 'node:fs' | ||
// encode & write the index | ||
const encoded = encode({tokens, weights, nrOfTokens, scores, originalIds}) | ||
fs.writeFileSync('index.pbf', encoded) | ||
const index = {tokens, scores, weights, nrOfTokens, originalIds} | ||
const encoded = encodeIndex(index) | ||
writeFileSync('index.pbf', encoded) | ||
// read & decode the index | ||
const decoded = decode(fs.readFileSync('index.pbf')) | ||
const decoded = decode(readFileSync('index.pbf')) | ||
``` | ||
@@ -165,0 +165,0 @@ |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
7
Yes
21904
9
486
1
1
+ Addedleven@4.0.0(transitive)
- Removedleven@3.1.0(transitive)
Updatedleven@^4.0.0