TypeScript SDK Usage
This guide covers the core operations for working with vectors in Endee: upserting, querying, and deleting data.
Setting Up Your Domain
The Endee client allows you to configure a custom domain URL and port (default port is 8080):
import { Endee } from 'endee';
// Initialize with your API token
const client = new Endee();
// Set custom base URL
client.setBaseUrl('http://0.0.0.0:8081/api/v1');Use setBaseUrl() when running on a non-default port.
Upserting Vectors
The index.upsert() method adds or updates vectors in an existing index.
import { Endee } from 'endee';
const client = new Endee();
const index = await client.getIndex('my_index');
await index.upsert([
{
id: 'vec1',
vector: [...],
meta: { title: 'First document' },
filter: { category: 'tech' },
},
{
id: 'vec2',
vector: [...],
meta: { title: 'Second document' },
filter: { category: 'science' },
},
]);Vector Object Fields: (see VectorItem)
| Field | Required | Description |
|---|---|---|
id | Yes | Unique identifier for the vector |
vector | Yes | Array of floats representing the embedding |
meta | No | Arbitrary metadata object |
filter | No | Key-value pairs for filtering during queries |
Querying the Index
The index.query() method performs a similarity search using a query vector.
const results = await index.query({
vector: [...],
topK: 5,
ef: 128,
includeVectors: true,
});
for (const item of results) {
console.log(`ID: ${item.id}, Similarity: ${item.similarity}`);
}Query Parameters: (see QueryOptions)
| Parameter | Description |
|---|---|
vector | Query vector (must match index dimension) |
topK | Number of results to return (default: 10, max: 512) |
ef | Search quality parameter (default: 128, max: 1024) |
includeVectors | Include vector data in results (default: false) |
filter | Optional filter criteria (array of filter objects) |
sparseIndices | Sparse vector indices for hybrid search (default: null) |
sparseValues | Sparse vector values for hybrid search (default: null) |
prefilterCardinalityThreshold | Controls when search switches from HNSW filtered search to brute-force prefiltering on the matched subset (default: 10,000, range: 1,000–1,000,000) |
filterBoostPercentage | Expands the internal HNSW candidate pool by this percentage when a filter is active, compensating for filtered-out results (default: 0, range: 0–100) |
Results are returned as an array of QueryResult objects.
Filtered Querying
Use the filter parameter to restrict results based on filter conditions. All filters are combined with logical AND.
const results = await index.query({
vector: [...],
topK: 5,
filter: [
{ category: { $eq: 'tech' } },
{ score: { $range: [80, 100] } },
],
});Filtering Operators
| Operator | Description | Example |
|---|---|---|
$eq | Exact match | { status: { $eq: 'published' } } |
$in | Match any in list | { tags: { $in: ['ai', 'ml'] } } |
$range | Numeric range (inclusive) | { score: { $range: [70, 95] } } |
The $range operator supports values within [0 – 999]. Normalize larger values before upserting.
Filter Tuning
When using filtered queries, two optional parameters let you tune the trade-off between search speed and recall:
Prefilter Cardinality Threshold
Controls when the search strategy switches from HNSW filtered search (fast, graph-based) to brute-force prefiltering (exhaustive scan on the matched subset).
| Value | Behavior |
|---|---|
1,000 | Prefilter only for very selective filters — minimum value |
10,000 | Prefilter only when the filter matches ≤10,000 vectors (default) |
1,000,000 | Prefilter for almost all filtered searches — maximum value |
The intuition: when very few vectors match your filter, HNSW may struggle to find enough valid candidates through graph traversal. In that case, scanning the filtered subset directly (prefiltering) is faster and more accurate. Raising the threshold means prefiltering kicks in more often; lowering it favors HNSW graph search.
// Only prefilter when filter matches ≤5,000 vectors
const results = await index.query({
vector: [/* ... */],
topK: 10,
filter: [{ category: { $eq: 'rare' } }],
prefilterCardinalityThreshold: 5000,
});Filter Boost Percentage
When using HNSW filtered search, some candidates explored during graph traversal are discarded by the filter, which can leave you with fewer results than topK. filterBoostPercentage compensates by expanding the internal candidate pool before filtering is applied.
0→ no boost, standard candidate pool size (default)20→ fetch 20% more candidates internally before applying the filter- Maximum:
100(doubles the candidate pool)
// Fetch 30% more candidates to compensate for aggressive filtering
const results = await index.query({
vector: [/* ... */],
topK: 10,
filter: [{ visibility: { $eq: 'public' } }],
filterBoostPercentage: 30,
});Using Both Together
const results = await index.query({
vector: [/* ... */],
topK: 10,
filter: [{ category: { $eq: 'rare' } }],
prefilterCardinalityThreshold: 5000, // switch to brute-force for small match sets
filterBoostPercentage: 25, // boost candidates for HNSW filtered search
});Start with the defaults (prefilterCardinalityThreshold: 10,000, filterBoostPercentage: 0). If filtered queries return fewer results than expected, try increasing filterBoostPercentage. If filtered queries are slow on selective filters, try lowering prefilterCardinalityThreshold. Valid range for the threshold is 1,000–1,000,000.
Updating Filters
The index.updateFilters() method updates the filters for one or more vectors without modifying the vector data or metadata.
await index.updateFilters([
{ id: 'vec1', filter: { category: 'ml', score: 95 } },
{ id: 'vec2', filter: { category: 'science', score: 80 } },
]);UpdateFilterParams Fields: (see UpdateFilterParams)
| Field | Required | Description |
|---|---|---|
id | Yes | ID of the vector to update |
filter | Yes | New filter object that replaces the existing filters |
Filter updates are destructive replacements. Any filter keys not included in the new filter object will be removed from the vector.
Hybrid Search
Hybrid indexes combine dense and sparse vector search. Create a hybrid index by specifying sparseDimension:
// Option 1: Use Endee's BM25 model (recommended for most use cases)
await client.createIndex({
name: 'hybrid_index',
dimension: 384,
sparseModel: "endee_bm25", // Use python endee-model package for BM25 vectors
spaceType: 'cosine',
});
// Option 2: Bring your own sparse vectors
await client.createIndex({
name: 'hybrid_index',
dimension: 384,
sparseModel: "default", // Provide your own sparse indices/values
spaceType: 'cosine',
});See the Endee BM25 guide from generating BM25 sparse vectors with endee-model.
Upserting Hybrid Vectors
Provide both dense vectors and sparse vector representations using sparseIndices and sparseValues:
const index = await client.getIndex('hybrid_index');
await index.upsert([
{
id: 'doc1',
vector: [0.1, 0.2, ...], // Dense vector
sparseIndices: [10, 50, 200], // Non-zero term positions
sparseValues: [0.8, 0.5, 0.3], // Weights for each position
meta: { title: 'Document 1' },
},
{
id: 'doc2',
vector: [0.3, 0.4, ...],
sparseIndices: [15, 100, 500],
sparseValues: [0.9, 0.4, 0.6],
meta: { title: 'Document 2' },
},
]);Hybrid Vector Fields: (see VectorItem)
| Field | Required | Description |
|---|---|---|
id | Yes | Unique identifier |
vector | Yes | Dense embedding vector |
sparseIndices | Yes (hybrid) | Non-zero term positions in sparse vector |
sparseValues | Yes (hybrid) | Weights for each sparse index |
meta | No | Metadata dictionary |
filter | No | Filter fields |
sparseIndices and sparseValues must have the same length. Values in sparseIndices must be within [0, sparseDimension).
Querying Hybrid Index
Provide both dense and sparse query vectors:
const results = await index.query({
vector: [0.15, 0.25, ...], // Dense query
sparseIndices: [10, 100, 300], // Sparse query positions
sparseValues: [0.7, 0.5, 0.4], // Sparse query weights
topK: 5,
});
for (const item of results) {
console.log(`ID: ${item.id}, Similarity: ${item.similarity}`);
}You can also query with:
- Dense only: Provide only
vector - Sparse only: Provide only
sparseIndicesandsparseValues - Hybrid: Provide all three for combined results
Deletion Methods
Delete by ID
await index.deleteVector('vec1');Delete by Filter
Delete all vectors matching specific filters:
await index.deleteWithFilter([{ category: { $eq: 'tech' } }]);Delete Index
await client.deleteIndex('my_index');Deletion operations are irreversible.
Additional Operations
Get Vector by ID
Returns a VectorInfo object:
const vector = await index.getVector('vec1');Describe Index
Returns an IndexDescription object:
const info = index.describe();
console.log(info);
// { name, spaceType, dimension, sparseDimension, isHybrid, count, precision, M }Precision Options
Endee supports different quantization precision levels via the Precision enum:
import { Precision } from 'endee';
Precision.BINARY; // Binary quantization (1-bit) - smallest storage, fastest search
Precision.INT8; // 8-bit integer quantization - balanced performance
Precision.INT16; // 16-bit integer quantization (default) - higher precision
Precision.FLOAT16; // 16-bit floating point - good balance
Precision.FLOAT32; // 32-bit floating point - highest precisionChoosing Precision:
BINARY: Best for very large datasets where speed and storage are criticalINT8: Recommended for most use cases - good balance of accuracy and performanceINT16(default): When you need better accuracy than INT8 but less storage than FLOAT32FLOAT16: Good compromise between precision and storage for embeddingsFLOAT32: When you need maximum precision and storage is not a concern
See the Types Reference for complete details.
TypeScript Types
The package includes comprehensive TypeScript types. See the Types Reference for complete documentation of all types:
import type {
VectorItem,
QueryOptions,
QueryResult,
CreateIndexOptions,
IndexDescription,
SpaceType,
Precision,
} from 'endee';| Type | Description |
|---|---|
SpaceType | Distance metric options |
Precision | Quantization precision levels |
CreateIndexOptions | Index creation parameters |
VectorItem | Vector for upserting |
QueryOptions | Query parameters |
QueryResult | Single query result |
IndexDescription | Index description |
VectorInfo | Single vector information |
API Reference
Endee Class
| Method | Description |
|---|---|
createIndex(options) | Create a new index - accepts CreateIndexOptions |
listIndexes() | List all indexes - returns IndexInfo[] |
deleteIndex(name) | Delete an index |
getIndex(name) | Get reference to an index |
setBaseUrl(url) | Set a custom base URL |
Index Class
| Method | Description |
|---|---|
upsert(vectors) | Insert or update vectors - accepts VectorItem[] |
query(options) | Search for similar vectors - accepts QueryOptions, returns QueryResult[] |
updateFilters(updates) | Replace filters for one or more vectors - accepts UpdateFilterParams[] |
deleteVector(id) | Delete a vector by ID |
deleteWithFilter(filter) | Delete vectors by filter |
getVector(id) | Get a vector by ID - returns VectorInfo |
describe() | Get index info - returns IndexDescription |