Quick Start¶
This guide will help you get started with HNSW by walking through a simple example.
Basic Usage¶
Here's a complete example that demonstrates how to create a graph, add vectors, and perform a search:
package main
import (
"fmt"
"github.com/TFMV/hnsw"
)
func main() {
// Create a new HNSW graph with default parameters
graph := hnsw.NewGraph[int]()
// Add some vectors to the graph
for i := 0; i < 1000; i++ {
// Create a vector with 10 dimensions
vector := make([]float32, 10)
for j := 0; j < 10; j++ {
vector[j] = float32(i) * 0.01 * float32(j+1)
}
// Add the vector to the graph
node := hnsw.Node[int]{
Key: i,
Value: vector,
}
err := graph.Add(node)
if err != nil {
fmt.Printf("Error adding node: %v\n", err)
}
}
// Create a query vector
query := []float32{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}
// Search for the 5 nearest neighbors
results, err := graph.Search(query, 5)
if err != nil {
fmt.Printf("Error searching: %v\n", err)
return
}
// Print the results
fmt.Println("Search results:")
for i, result := range results {
fmt.Printf("%d. Key: %d, Distance: %f\n", i+1, result.Key, result.Dist)
}
}
Step-by-Step Explanation¶
1. Creating a Graph¶
This creates a new HNSW graph with default parameters. The type parameter int
specifies the type of the keys used to identify vectors in the graph.
You can also create a graph with custom parameters:
graph := hnsw.NewGraph[int]()
graph.M = 16 // Maximum number of connections per node
graph.EfConstruction = 200 // Size of the dynamic candidate list during construction
graph.EfSearch = 100 // Size of the dynamic candidate list during search
graph.Distance = hnsw.CosineDistance // Distance function to use
2. Adding Vectors¶
Each vector is added to the graph as a Node
with a unique key and a vector value. The vector is a slice of float32
values.
You can also add multiple vectors in a batch:
nodes := []hnsw.Node[int]{
{Key: 1, Value: []float32{0.1, 0.2, 0.3}},
{Key: 2, Value: []float32{0.2, 0.3, 0.4}},
{Key: 3, Value: []float32{0.3, 0.4, 0.5}},
}
err := graph.BatchAdd(nodes)
3. Searching¶
This searches for the 5 nearest neighbors to the query vector. The results are returned as a slice of SearchResult
structs, which contain the key of the node and the distance to the query vector.
4. Using Different Distance Functions¶
HNSW provides several built-in distance functions:
// Euclidean distance
graph.Distance = hnsw.EuclideanDistance
// Cosine distance
graph.Distance = hnsw.CosineDistance
// Dot product distance
graph.Distance = hnsw.DotProductDistance
You can also define your own distance function:
graph.Distance = func(a, b []float32) float32 {
// Custom distance calculation
var sum float32
for i := range a {
sum += (a[i] - b[i]) * (a[i] - b[i])
}
return sum
}
Next Steps¶
Now that you've seen the basic usage of HNSW, you can explore more features:
- Configuration: Learn how to configure HNSW for optimal performance
- Metadata Extension: Store and retrieve metadata alongside vectors
- Faceted Search: Filter search results based on facets
- Examples: See more examples of using HNSW