Skip to content

Commit

Permalink
Merge branch 'main-dev' of https://github.com/unum-cloud/usearch into…
Browse files Browse the repository at this point in the history
… main-dev
  • Loading branch information
ashvardanian committed Jul 31, 2023
2 parents 284b058 + 3effb50 commit fa70779
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 25 deletions.
65 changes: 45 additions & 20 deletions javascript/lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,36 +184,61 @@ void Index::View(Napi::CallbackInfo const& ctx) {

void Index::Add(Napi::CallbackInfo const& ctx) {
Napi::Env env = ctx.Env();
if (ctx.Length() < 2 || !ctx[0].IsNumber() || !ctx[1].IsTypedArray())
return Napi::TypeError::New(env, "Expects an integral key and a float vector").ThrowAsJavaScriptException();

Napi::Number key_js = ctx[0].As<Napi::Number>();
Napi::Float32Array vector_js = ctx[1].As<Napi::Float32Array>();
if (ctx.Length() < 2)
return Napi::TypeError::New(env, "Expects at least two arguments").ThrowAsJavaScriptException();

using key_t = typename index_dense_t::key_t;
key_t key = key_js.Uint32Value();
float const* vector = vector_js.Data();
std::size_t dimensions = static_cast<std::size_t>(vector_js.ElementLength());
if (dimensions != native_->dimensions())
return Napi::TypeError::New(env, "Wrong number of dimensions").ThrowAsJavaScriptException();
std::size_t index_dimensions = native_->dimensions();

auto add = [&](Napi::Number key_js, Napi::Float32Array vector_js) {
key_t key = key_js.Uint32Value();
float const* vector = vector_js.Data();
std::size_t dimensions = static_cast<std::size_t>(vector_js.ElementLength());

if (dimensions != index_dimensions)
return Napi::TypeError::New(env, "Wrong number of dimensions").ThrowAsJavaScriptException();

try {
native_->add(key, vector);
} catch (std::bad_alloc const&) {
return Napi::TypeError::New(env, "Out of memory").ThrowAsJavaScriptException();
} catch (...) {
return Napi::TypeError::New(env, "Insertion failed").ThrowAsJavaScriptException();
}
};

if (native_->size() + 1 >= native_->capacity())
native_->reserve(ceil2(native_->size() + 1));
if (ctx[0].IsArray() && ctx[1].IsArray()) {
Napi::Array keys_js = ctx[0].As<Napi::Array>();
Napi::Array vectors_js = ctx[1].As<Napi::Array>();
auto length = keys_js.Length();

try {
native_->add(key, vector);
} catch (std::bad_alloc const&) {
Napi::TypeError::New(env, "Out of memory").ThrowAsJavaScriptException();
} catch (...) {
Napi::TypeError::New(env, "Insertion failed").ThrowAsJavaScriptException();
}
if (length != vectors_js.Length())
return Napi::TypeError::New(env, "The number of keys must match the number of vectors")
.ThrowAsJavaScriptException();

if (native_->size() + length >= native_->capacity())
native_->reserve(ceil2(native_->size() + length));

for (std::size_t i = 0; i < length; i++) {
Napi::Value key_js = keys_js[i];
Napi::Value vector_js = vectors_js[i];
add(key_js.As<Napi::Number>(), vector_js.As<Napi::Float32Array>());
}

} else if (ctx[0].IsNumber() && ctx[1].IsTypedArray()) {
if (native_->size() + 1 >= native_->capacity())
native_->reserve(ceil2(native_->size() + 1));
add(ctx[0].As<Napi::Number>(), ctx[1].As<Napi::Float32Array>());
} else
return Napi::TypeError::New(env, "Invalid argument type, expects integral key(s) and float vector(s)")
.ThrowAsJavaScriptException();
}

Napi::Value Index::Search(Napi::CallbackInfo const& ctx) {
Napi::Env env = ctx.Env();
if (ctx.Length() < 2 || !ctx[0].IsTypedArray() || !ctx[1].IsNumber()) {
Napi::TypeError::New(env, "Expects a float vector and the number of wanted results")
.ThrowAsJavaScriptException();
Napi::TypeError::New(env, "Expects a and the number of wanted results").ThrowAsJavaScriptException();
return {};
}

Expand Down
13 changes: 13 additions & 0 deletions javascript/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,17 @@ var results = index.search(new Float32Array([13, 14]), 2)
assert.deepEqual(results.keys, new BigUint64Array([15n, 16n]))
assert.deepEqual(results.distances, new Float32Array([45, 130]))

// Batch

var index2 = new usearch.Index({ metric: 'l2sq', connectivity: 16, dimensions: 2 })

const keys = [15, 16]
const vectors = [new Float32Array([10, 20]), new Float32Array([10, 25])]
index2.add(keys, vectors)
assert.equal(index.size(), 2)

var results = index.search(new Float32Array([13, 14]), 2)
assert.deepEqual(results.keys, new BigUint64Array([15n, 16n]))
assert.deepEqual(results.distances, new Float32Array([45, 130]))

console.log('JavaScript tests passed!')
6 changes: 3 additions & 3 deletions javascript/usearch.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ export class Index {
/**
* Add n vectors of dimension d to the index.
*
* @param {BigUint64Array} keys Input identifiers for every vector.
* @param {Float32Array} mat Input matrix, matrix of size n * d.
* @param {number | number[]} keys Input identifiers for every vector.
* @param {Float32Array | Float32Array[]} mat Input matrix, matrix of size n * d.
*/
add(keys: BigUint64Array, mat: Float32Array): void;
add(keys: number | number[], mat: Float32Array | Float32Array[]): void;

/**
* Query n vectors of dimension d to the index. Return at most k vectors for each.
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"node": "~10 >=10.20 || >=12.17"
},
"dependencies": {
"@types/node": "^20.4.5",
"bindings": "~1.2.1",
"node-addon-api": "^3.0.0"
},
Expand All @@ -24,6 +25,7 @@
"@semantic-release/exec": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"conventional-changelog-eslint": "^3.0.9",
"semantic-release": "^21.0.7"
"semantic-release": "^21.0.7",
"typescript": "^5.1.6"
}
}
}
8 changes: 8 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"typedocOptions": {
"entryPoints": [
"javascript/usearch.d.ts"
],
"out": "docs"
}
}

0 comments on commit fa70779

Please sign in to comment.