Skip to content
Open
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
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ ifndef VST_ENABLED
VST_ENABLED := "false"
endif

ifndef ENABLE_VECTOR_INDEX
ENABLE_VECTOR_INDEX := "true"
endif

TESTV2PARALLEL ?= 4

ORGPATH := github.com/arangodb
Expand Down Expand Up @@ -491,7 +495,7 @@ endif
@-docker rm -f -v $(TESTCONTAINER) &> /dev/null
@TESTCONTAINER=$(TESTCONTAINER) ARANGODB=$(ARANGODB) ALPINE_IMAGE=$(ALPINE_IMAGE) ENABLE_BACKUP=$(ENABLE_BACKUP) \
ARANGO_LICENSE_KEY=$(ARANGO_LICENSE_KEY) STARTER=$(STARTER) STARTERMODE=$(TEST_MODE) TMPDIR="${TMPDIR}" \
ENABLE_DATABASE_EXTRA_FEATURES=$(ENABLE_DATABASE_EXTRA_FEATURES) DEBUG_PORT=$(DEBUG_PORT) $(CLUSTERENV) DOCKER_NETWORK=${TEST_NET} "${ROOTDIR}/test/cluster.sh" start
ENABLE_DATABASE_EXTRA_FEATURES=$(ENABLE_DATABASE_EXTRA_FEATURES) ENABLE_VECTOR_INDEX=$(ENABLE_VECTOR_INDEX) DEBUG_PORT=$(DEBUG_PORT) $(CLUSTERENV) DOCKER_NETWORK=${TEST_NET} "${ROOTDIR}/test/cluster.sh" start
endif

__test_cleanup:
Expand Down
3 changes: 3 additions & 0 deletions test/cluster.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ if [ "$CMD" == "start" ]; then
if [ -n "$ENABLE_DATABASE_EXTRA_FEATURES" ]; then
STARTERARGS="$STARTERARGS --all.database.extended-names-databases=true --args.all.http.compress-response-threshold=1 --args.all.http.handle-content-encoding-for-unauthenticated-requests=true"
fi
if [ -n "$ENABLE_VECTOR_INDEX" ]; then
STARTERARGS="$STARTERARGS --args.all.experimental-vector-index=true"
fi
# Use DOCKER_PLATFORM if set (e.g., from CircleCI for ARM), otherwise use macOS default
if [ -n "$DOCKER_PLATFORM" ]; then
DOCKERPLATFORMARG="$DOCKER_PLATFORM"
Expand Down
1 change: 1 addition & 0 deletions v2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Add ARM Support for V2 testcases
- Set TESTV2PARALLEL from 1 to 4
- Disabled V8 related testcases in V1 and V2
- Add Vector index feature

## [2.1.6](https://github.com/arangodb/go-driver/tree/v2.1.6) (2025-11-06)
- Add missing endpoints from replication
Expand Down
54 changes: 54 additions & 0 deletions v2/arangodb/collection_indexes.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ type CollectionIndexes interface {

// DeleteIndexByID deletes an index from the collection.
DeleteIndexByID(ctx context.Context, id string) error

// EnsureVectorIndex creates a vector index in the collection, if it does not already exist.
// The index is returned, together with a boolean indicating if the index was newly created (true) or pre-existing (false).
// Available in ArangoDB 3.12.4 and later.
// VectorParams is an obligatory parameter and must contain at least Dimension,Metric and NLists fields.
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation states that NLists is a required field that must be present, but the validation logic treats it as optional (only validates if non-nil). Either update the documentation to reflect that NLists is optional, or make the validation enforce it as required.

Suggested change
// VectorParams is an obligatory parameter and must contain at least Dimension,Metric and NLists fields.
// VectorParams is an obligatory parameter and must contain at least Dimension and Metric fields. NLists is optional.

Copilot uses AI. Check for mistakes.
EnsureVectorIndex(ctx context.Context, fields []string, params *VectorParams, options *CreateVectorIndexOptions) (IndexResponse, bool, error)
}

// IndexType represents an index type as string
Expand Down Expand Up @@ -118,6 +124,9 @@ const (

// InvertedIndexType can be used to speed up a broad range of AQL queries, from simple to complex, including full-text search
InvertedIndexType = IndexType("inverted")

// VectorIndexType is used for efficient similarity searches on high-dimensional embeddings, enabling fast and scalable AI use cases.
VectorIndexType = IndexType("vector")
)

// IndexResponse is the response from the Index list method
Expand All @@ -135,6 +144,9 @@ type IndexResponse struct {

// InvertedIndex is the inverted index object. It is not empty only for InvertedIndex type.
InvertedIndex *InvertedIndexOptions `json:"invertedIndexes"`

// VectorIndex is the vector index params. It is not empty only for VectorIndex type.
VectorIndex *VectorParams `json:"params,omitempty"`
}

// IndexSharedOptions contains options that are shared between all index types
Expand Down Expand Up @@ -310,3 +322,45 @@ type CreateMDIPrefixedIndexOptions struct {
// Array expansions are not allowed.
PrefixFields []string `json:"prefixFields,required"`
}

type CreateVectorIndexOptions struct {
// Allow writes during creation.
InBackground *bool `json:"inBackground,omitempty"`
// Optional index name.
Name *string `json:"name,omitempty"`
// Number of threads to use for index creation.
Parallelism *int `json:"parallelism,omitempty"`
// Exclude docs missing the field.
Sparse *bool `json:"sparse,omitempty"`
// Introduced in v3.12.7
// Up to 32 additional attributes can be stored in the index.
StoredValues []string `json:"storedValues,omitempty"`
}

type VectorParams struct {
// Neighbors considered in search.
DefaultNProbe *int `json:"defaultNProbe,omitempty"`
// Vector length.
Dimension *int `json:"dimension,omitempty"`
// Faiss factory string.
Factory *string `json:"factory,omitempty"`
// Similarity measure.
Metric *VectorMetric `json:"metric,omitempty"`
// Number of centroids.
NLists *int `json:"nLists,omitempty"`
// Faiss training iterations
TrainingIterations *int `json:"trainingIterations,omitempty"`
}

// VectorMetric defines the type of similarity metric for vector comparison.
type VectorMetric string

const (
// Cosine similarity between vectors.
VectorMetricCosine VectorMetric = "cosine"
// Introduced in v3.12.6
// Inner product similarity.
VectorMetricInnerProduct VectorMetric = "innerProduct"
// Euclidean (L2) distance between vectors.
VectorMetricL2 VectorMetric = "l2"
)
97 changes: 93 additions & 4 deletions v2/arangodb/collection_indexes_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ package arangodb
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"

Expand Down Expand Up @@ -306,23 +307,111 @@ func (i *IndexResponse) UnmarshalJSON(data []byte) error {
i.Name = respSimple.Name
i.Type = respSimple.Type

if respSimple.Type == InvertedIndexType {
switch respSimple.Type {
case InvertedIndexType:
result := responseInvertedIndex{}
if err := json.Unmarshal(data, &result); err != nil {
return err
}

i.IndexSharedOptions = result.IndexSharedOptions
i.InvertedIndex = &result.InvertedIndexOptions
} else {
case VectorIndexType:
result := responseVectorIndex{}
if err := json.Unmarshal(data, &result); err != nil {
return err
}
i.IndexSharedOptions = result.IndexSharedOptions
i.VectorIndex = result.Params
default:
result := responseIndex{}
if err := json.Unmarshal(data, &result); err != nil {
return err
}

i.IndexSharedOptions = result.IndexSharedOptions
i.RegularIndex = &result.IndexOptions
}
return nil
}

func (p *VectorParams) validate() error {
if p == nil {
return errors.New("params must be provided for vector index")
}

if p.Dimension == nil || *p.Dimension <= 0 {
return errors.New("params.Dimension must be provided and greater than zero for vector index")
}

if p.Metric == nil {
return errors.New("params.Metric must be provided for vector index")
}

switch *p.Metric {
case VectorMetricCosine, VectorMetricL2, VectorMetricInnerProduct:
// valid
default:
return errors.New("params.Metric must be one of 'cosine', 'l2', or 'innerProduct' for vector index")
}

if p.NLists != nil && *p.NLists <= 0 {
return errors.New("params.NLists must be greater than zero for vector index")
}

return nil
}

func (c *collectionIndexes) EnsureVectorIndex(
ctx context.Context,
fields []string,
params *VectorParams,
options *CreateVectorIndexOptions,
) (IndexResponse, bool, error) {

if len(fields) != 1 {
return IndexResponse{}, false, errors.New("vector index requires exactly one field")
}
if err := params.validate(); err != nil {
return IndexResponse{}, false, err
}

reqData := struct {
Type IndexType `json:"type"`
Fields []string `json:"fields"`
Params *VectorParams `json:"params"`
*CreateVectorIndexOptions
}{
Type: VectorIndexType,
Fields: fields,
Params: params,
CreateVectorIndexOptions: options,
}

result := responseVectorIndex{}
fmt.Printf("reqData: %+v", reqData)
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug print statement should be removed before merging to production. This line outputs debug information to stdout which is not appropriate for production code.

Suggested change
fmt.Printf("reqData: %+v", reqData)

Copilot uses AI. Check for mistakes.
created, err := c.ensureIndex(ctx, &reqData, &result)
if err != nil {
return IndexResponse{}, false, err
}

return newVectorIndexResponse(&result), created, nil
}

type responseVectorIndex struct {
Name string `json:"name,omitempty"`
Type IndexType `json:"type"`
IndexSharedOptions `json:",inline"`
Params *VectorParams `json:"params,omitempty"`
}

func newVectorIndexResponse(res *responseVectorIndex) IndexResponse {
if res == nil {
return IndexResponse{}
}

return IndexResponse{
Name: res.Name,
Type: res.Type,
IndexSharedOptions: res.IndexSharedOptions,
VectorIndex: res.Params,
}
}
Loading