Skip to Content

Java 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 io.endee.client.Endee; // Initialize client Endee client = new Endee(); // Set custom base URL for non-default port 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 io.endee.client.Endee; import io.endee.client.Index; import io.endee.client.types.VectorItem; import java.util.List; import java.util.Map; Endee client = new Endee(); Index index = client.getIndex("my_index"); List<VectorItem> vectors = List.of( VectorItem.builder("vec1", new double[] {0.1, 0.2, 0.3 /* ... */}) .meta(Map.of("title", "First document", "group", 10)) .filter(Map.of("category", "tech", "group", 10)) .build(), VectorItem.builder("vec2", new double[] {0.3, 0.4, 0.5 /* ... */}) .meta(Map.of("title", "Second document")) .filter(Map.of("category", "science")) .build() ); index.upsert(vectors);

Vector Object Fields: (see VectorItem)

FieldRequiredDescription
idYesUnique identifier for the vector (non-empty string)
vectorYesArray of doubles representing the embedding
metaNoArbitrary metadata map
filterNoKey-value pairs for filtering during queries

Maximum 1,000 vectors per upsert call. Vector dimension must match the index dimension. IDs must be unique within a single upsert batch.

Querying the Index

The index.query() method performs a similarity search using a query vector.

import io.endee.client.types.QueryOptions; import io.endee.client.types.QueryResult; List<QueryResult> results = index.query( QueryOptions.builder() .vector(new double[] {0.15, 0.25 /* ... */}) .topK(5) .ef(128) .includeVectors(true) .build() ); for (QueryResult item : results) { System.out.println("ID: " + item.getId()); System.out.println("Similarity: " + item.getSimilarity()); System.out.println("Distance: " + item.getDistance()); System.out.println("Meta: " + item.getMeta()); }

Query Parameters: (see QueryOptions)

ParameterDescription
vectorQuery vector (must match index dimension)
topKNumber of results to return (default: 10, max: 512)
efSearch quality parameter (default: 128, max: 1024)
includeVectorsInclude vector data in results (default: false)
prefilterCardinalityThresholdSwitch to brute-force prefiltering when filter matches ≤ N vectors (default: 10,000, range: 1,000–1,000,000)
filterBoostPercentageExpand the internal candidate pool by this percentage before applying the filter (default: 0, max: 100)

Results are returned as a list of QueryResult objects.

Filtered Querying

Use the filter parameter to restrict results based on filter conditions. All filters are combined with logical AND.

List<QueryResult> results = index.query( QueryOptions.builder() .vector(new double[] {0.15, 0.25 /* ... */}) .topK(5) .filter(List.of( Map.of("category", Map.of("$eq", "tech")), Map.of("score", Map.of("$range", List.of(80, 100))) )) .build() );

Filtering Operators

OperatorDescriptionExample
$eqExact matchMap.of("status", Map.of("$eq", "published"))
$inMatch any in listMap.of("tags", Map.of("$in", List.of("ai", "ml")))
$rangeNumeric range (inclusive)Map.of("score", Map.of("$range", List.of(70, 95)))

The $range operator supports values within [0 – 999]. Normalize larger values before upserting.

Filter Tuning

Use prefilterCardinalityThreshold and filterBoostPercentage to fine-tune how filtering interacts with the ANN search:

List<QueryResult> results = index.query( QueryOptions.builder() .vector(new double[] {0.15, 0.25 /* ... */}) .topK(5) .filter(List.of( Map.of("category", Map.of("$eq", "tech")) )) .prefilterCardinalityThreshold(50_000) // Use postfilter when >50k vectors match .filterBoostPercentage(20) // Bias 20% toward filter-matching vectors .build() );
ParameterDescriptionDefaultRange
prefilterCardinalityThresholdWhen the estimated number of vectors matching the filter exceeds this value, postfiltering is used instead of prefiltering.10,0001,000–1,000,000
filterBoostPercentagePercentage by which filter-matching vectors are boosted during scoring. Set to 0 to disable. Higher values favor filtered results.00–100

Updating Filters

The index.updateFilters() method updates the filters for one or more vectors without modifying the vector data or metadata.

import io.endee.client.types.UpdateFilterParams; import java.util.List; import java.util.Map; index.updateFilters(List.of( new UpdateFilterParams("vec1", Map.of("category", "ml", "score", 95)), new UpdateFilterParams("vec2", Map.of("category", "science", "score", 80)) ));

UpdateFilterParams Fields: (see UpdateFilterParams)

FieldRequiredDescription
idYesID of the vector to update
filterYesNew 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 indexes combine dense and sparse vector search. Create a hybrid index by specifying sparseDimension:

CreateIndexOptions options = CreateIndexOptions.builder("hybrid_index", 384) .sparseDimension(30000) .spaceType(SpaceType.COSINE) .build(); client.createIndex(options);

Upserting Hybrid Vectors

Provide both dense vectors and sparse vector representations using sparseIndices and sparseValues:

Index index = client.getIndex("hybrid_index"); List<VectorItem> vectors = List.of( VectorItem.builder("doc1", new double[] {0.1, 0.2 /* ... */}) .sparseIndices(new int[] {10, 50, 200}) // Non-zero term positions .sparseValues(new double[] {0.8, 0.5, 0.3}) // Weights for each position .meta(Map.of("title", "Document 1")) .build(), VectorItem.builder("doc2", new double[] {0.3, 0.4 /* ... */}) .sparseIndices(new int[] {15, 100, 500}) .sparseValues(new double[] {0.9, 0.4, 0.6}) .meta(Map.of("title", "Document 2")) .build() ); index.upsert(vectors);

Hybrid Vector Fields: (see VectorItem)

FieldRequiredDescription
idYesUnique identifier
vectorYesDense embedding vector
sparseIndicesYes (hybrid)Non-zero term positions in sparse vector
sparseValuesYes (hybrid)Weights for each sparse index
metaNoMetadata map
filterNoFilter 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:

List<QueryResult> results = index.query( QueryOptions.builder() .vector(new double[] {0.15, 0.25 /* ... */}) // Dense query .sparseIndices(new int[] {10, 100, 300}) // Sparse query positions .sparseValues(new double[] {0.7, 0.5, 0.4}) // Sparse query weights .topK(5) .build() ); for (QueryResult item : results) { System.out.println("ID: " + item.getId() + ", Similarity: " + item.getSimilarity()); }

You can also query with:

  • Dense only: Provide only vector
  • Sparse only: Provide only sparseIndices and sparseValues
  • Hybrid: Provide all three for combined results

Deletion Methods

Delete by ID

index.deleteVector("vec1");

Delete by Filter

Delete all vectors matching specific filters:

index.deleteWithFilter(List.of( Map.of("category", Map.of("$eq", "tech")) ));

Delete Index

client.deleteIndex("my_index");

Deletion operations are irreversible.

Additional Operations

Get Vector by ID

Returns a VectorInfo object:

import io.endee.client.types.VectorInfo; VectorInfo vector = index.getVector("vec1"); System.out.println("ID: " + vector.getId()); System.out.println("Vector: " + Arrays.toString(vector.getVector())); System.out.println("Meta: " + vector.getMeta()); System.out.println("Norm: " + vector.getNorm());

Describe Index

Returns an IndexDescription object:

import io.endee.client.types.IndexDescription; IndexDescription info = index.describe(); System.out.println(info); // IndexDescription{name='my_index', spaceType=COSINE, dimension=384, // sparseDimension=0, isHybrid=false, count=1000, // precision=INT8, m=16}

Check if Index is Hybrid

boolean isHybrid = index.isHybrid();

Precision Options

Endee supports different quantization precision levels via the Precision enum:

import io.endee.client.types.Precision; 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 precision

Choosing Precision:

  • BINARY: Best for very large datasets where speed and storage are critical
  • INT8: Recommended for most use cases - good balance of accuracy and performance
  • INT16 (default): When you need better accuracy than INT8 but less storage than FLOAT32
  • FLOAT16: Good compromise between precision and storage for embeddings
  • FLOAT32: When you need maximum precision and storage is not a concern

See the Types Reference for complete details.

Error Handling

The Java SDK uses specific exception classes for error handling:

import io.endee.client.exception.EndeeException; import io.endee.client.exception.EndeeApiException; try { client.createIndex(options); } catch (EndeeApiException e) { // API-specific errors (e.g., 400, 401, 404, 409, 500) System.err.println("Status Code: " + e.getStatusCode()); System.err.println("Error Body: " + e.getErrorBody()); } catch (EndeeException e) { // Client errors (network, serialization, etc.) System.err.println("Client Error: " + e.getMessage()); } catch (IllegalArgumentException e) { // Validation errors (invalid parameters) System.err.println("Validation Error: " + e.getMessage()); }

HTTP Status Codes:

CodeDescription
400Bad Request - Invalid parameters
401Unauthorized - Invalid or missing authentication
403Forbidden - Insufficient permissions
404Not Found - Index or vector doesn’t exist
409Conflict - Index already exists
500Internal Server Error

See EndeeApiException and EndeeException for details.

API Reference

Endee Class

MethodDescription
Endee()Create client without auth (connects to localhost:8080)
Endee(String token)Create client with auth token
setBaseUrl(String url)Set a custom base URL
createIndex(CreateIndexOptions)Create a new index - accepts CreateIndexOptions
listIndexes()List all indexes (returns JSON string)
deleteIndex(String name)Delete an index
getIndex(String name)Get reference to an index - returns Index

Index Class

MethodDescription
upsert(List<VectorItem>)Insert or update vectors - accepts VectorItem list
query(QueryOptions)Search for similar vectors - accepts QueryOptions, returns QueryResult list
updateFilters(List<UpdateFilterParams>)Replace filters for one or more vectors - accepts UpdateFilterParams list
deleteVector(String id)Delete a vector by ID
deleteWithFilter(List<Map>)Delete vectors by filter
getVector(String id)Get a vector by ID - returns VectorInfo
describe()Get index info - returns IndexDescription
isHybrid()Check if index supports hybrid search - returns boolean