Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func main() {
},
}

results, err := index.Query(queryVector, nil, nil, 10, filter, 128, false)
results, err := index.Query(queryVector, nil, nil, 10, filter, 128, false, nil)
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -313,10 +313,13 @@ if err != nil {
queryVector := []float32{/* your query vector */}
results, err := index.Query(
queryVector, // query vector
nil, // sparseIndices (optional, for hybrid search)
nil, // sparseValues (optional, for hybrid search)
5, // top_k - number of results (max 512)
nil, // filter (optional)
128, // ef - runtime parameter (max 1024)
true, // include_vectors
nil, // filterParams (optional, for advanced filtering)
)
if err != nil {
log.Fatal(err)
Expand All @@ -331,10 +334,15 @@ for _, result := range results {
**Query Parameters:**

- `vector`: Query vector (must match index dimension)
- `sparseIndices`: Sparse vector indices (optional, for hybrid search)
- `sparseValues`: Sparse vector values (optional, for hybrid search)
- `k`: Number of nearest neighbors to return (max 512, default: 10)
- `filter`: Optional filter criteria (map[string]interface{})
- `ef`: Runtime search parameter - higher values improve recall but increase latency (max 1024, default: 128)
- `includeVectors`: Whether to return the actual vector data in results (default: false)
- `filterParams`: Advanced filter parameters (optional, *FilterParams):
- `BoostPercentage`: Expand candidate pool by X% during filtered search (0-100, default: 0)
- `PrefilterThreshold`: Switch to brute-force when matches < threshold (1000-1000000, default: 10000)

**Result Fields:**

Expand Down Expand Up @@ -368,7 +376,7 @@ filter := map[string]interface{}{
},
}

results, err := index.Query(queryVector, 5, filter, 128, true)
results, err := index.Query(queryVector, nil, nil, 5, filter, 128, true, nil)
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -527,7 +535,7 @@ index, err := client.GetIndexWithContext(ctx, "my_index")

err = index.UpsertWithContext(ctx, vectors)

results, err := index.QueryWithContext(ctx, queryVector, 10, nil, 128, false)
results, err := index.QueryWithContext(ctx, queryVector, nil, nil, 10, nil, 128, false, nil)

err = client.DeleteIndexWithContext(ctx, "my_index")
```
Expand All @@ -551,7 +559,7 @@ err = client.DeleteIndexWithContext(ctx, "my_index")
| Method | Description |
|--------|-------------|
| `Upsert(vectors []VectorItem) error` | Insert or update vectors (max 1000 per batch) |
| `Query(vector, sparseIndices, sparseValues, k, filter, ef, includeVectors) ([]QueryResult, error)` | Search for similar vectors |
| `Query(vector, sparseIndices, sparseValues, k, filter, ef, includeVectors, filterParams) ([]QueryResult, error)` | Search for similar vectors |
| `DeleteVectorById(id string) (string, error)` | Delete a vector by ID |
| `DeleteVectorByFilter(filter map[string]interface{}) (string, error)` | Delete vectors matching a specific filter |
| `GetVector(id string) (VectorItem, error)` | Get a specific vector by ID |
Expand Down
39 changes: 29 additions & 10 deletions index.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,22 @@ type QueryResult struct {
Vector []float32 `json:"vector,omitempty"`
}

// FilterParams represents advanced filtering parameters for HNSW search
type FilterParams struct {
BoostPercentage int `json:"boost_percentage,omitempty"`
PrefilterThreshold int `json:"prefilter_threshold,omitempty"`
}

// QueryRequest represents the search request payload
type QueryRequest struct {
Vector []float32 `json:"vector,omitempty"`
SparseIndices []int `json:"sparse_indices,omitempty"`
SparseValues []float32 `json:"sparse_values,omitempty"`
TopK int `json:"k"`
Ef int `json:"ef"`
IncludeVectors bool `json:"include_vectors"`
Filter string `json:"filter,omitempty"`
Vector []float32 `json:"vector,omitempty"`
SparseIndices []int `json:"sparse_indices,omitempty"`
SparseValues []float32 `json:"sparse_values,omitempty"`
TopK int `json:"k"`
Ef int `json:"ef"`
IncludeVectors bool `json:"include_vectors"`
Filter string `json:"filter,omitempty"`
FilterParams *FilterParams `json:"filter_params,omitempty"`
}

// NewIndex creates a new Index instance similar to Python's __init__
Expand Down Expand Up @@ -388,12 +395,12 @@ func (i *Index) GetInfo() string {
i.Name, i.Dimension, i.SparseDim, i.SpaceType, i.Count, i.Precision, i.M)
}

func (i *Index) Query(vector []float32, sparseIndices []int, sparseValues []float32, k int, filter map[string]interface{}, ef int, includeVectors bool) ([]QueryResult, error) {
return i.QueryWithContext(context.Background(), vector, sparseIndices, sparseValues, k, filter, ef, includeVectors)
func (i *Index) Query(vector []float32, sparseIndices []int, sparseValues []float32, k int, filter map[string]interface{}, ef int, includeVectors bool, filterParams *FilterParams) ([]QueryResult, error) {
return i.QueryWithContext(context.Background(), vector, sparseIndices, sparseValues, k, filter, ef, includeVectors, filterParams)
}

// QueryWithContext performs vector similarity search with context support
func (i *Index) QueryWithContext(ctx context.Context, vector []float32, sparseIndices []int, sparseValues []float32, k int, filter map[string]interface{}, ef int, includeVectors bool) ([]QueryResult, error) {
func (i *Index) QueryWithContext(ctx context.Context, vector []float32, sparseIndices []int, sparseValues []float32, k int, filter map[string]interface{}, ef int, includeVectors bool, filterParams *FilterParams) ([]QueryResult, error) {
// Validate parameters
if k <= 0 || k > MaxTopKAllowed {
return nil, fmt.Errorf("top_k must be between 1 and %d", MaxTopKAllowed)
Expand All @@ -420,6 +427,17 @@ func (i *Index) QueryWithContext(ctx context.Context, vector []float32, sparseIn
return nil, fmt.Errorf("sparse_indices and sparse_values must have the same length")
}

// Validate filter parameters if provided
if filterParams != nil {
if filterParams.BoostPercentage < 0 || filterParams.BoostPercentage > 100 {
return nil, fmt.Errorf("filter_boost_percentage must be between 0 and 100")
}
if filterParams.PrefilterThreshold != 0 &&
(filterParams.PrefilterThreshold < 1000 || filterParams.PrefilterThreshold > 1000000) {
return nil, fmt.Errorf("prefilter_cardinality_threshold must be between 1,000 and 1,000,000")
}
}

// Normalize query vector
normalizedVector, norm, err := i.normalizeVector(vector)
if err != nil {
Expand All @@ -435,6 +453,7 @@ func (i *Index) QueryWithContext(ctx context.Context, vector []float32, sparseIn
TopK: k,
Ef: ef,
IncludeVectors: includeVectors,
FilterParams: filterParams,
}

// Add filter if provided
Expand Down