@@ -18,6 +18,23 @@ const (
1818 OutputFormatArrowIPC OutputFormat = "arrow_ipc"
1919)
2020
21+ type InputFormat string
22+
23+ const (
24+ InputFormatParquet InputFormat = "parquet"
25+ InputFormatArrowIPC InputFormat = "arrow_ipc"
26+ )
27+
28+ type DistanceMetric string
29+
30+ const (
31+ DistanceMetricEuclidean DistanceMetric = "euclidean"
32+ DistanceMetricSquaredEuclidean DistanceMetric = "squared_euclidean"
33+ DistanceMetricCosine DistanceMetric = "cosine"
34+ DistanceMetricDotProduct DistanceMetric = "dot_product"
35+ DistanceMetricManhattan DistanceMetric = "manhattan"
36+ )
37+
2138type ExtractRequest struct {
2239 IndexDir string
2340 OutputPath string
@@ -40,6 +57,35 @@ type ExtractResponse struct {
4057 Summary ExtractSummary `json:"summary"`
4158}
4259
60+ type BuildRequest struct {
61+ InputPath string
62+ OutputPath string
63+ InputFormat InputFormat
64+ Metric DistanceMetric
65+ IncludeDeleted bool
66+ M int
67+ M0 * int
68+ EfConstruction int
69+ BatchSize int
70+ Capacity * int
71+ Seed * uint64
72+ }
73+
74+ type BuildSummary struct {
75+ Scanned uint64 `json:"scanned"`
76+ Inserted uint64 `json:"inserted"`
77+ DeletedSkipped uint64 `json:"deleted_skipped"`
78+ Dimension int `json:"dimension"`
79+ }
80+
81+ type BuildResponse struct {
82+ InputPath string `json:"input_path"`
83+ OutputPath string `json:"output_path"`
84+ InputFormat InputFormat `json:"input_format"`
85+ Metric DistanceMetric `json:"metric"`
86+ Summary BuildSummary `json:"summary"`
87+ }
88+
4389type extractPayload struct {
4490 IndexDir string `json:"index_dir"`
4591 OutputPath string `json:"output_path"`
@@ -49,6 +95,20 @@ type extractPayload struct {
4995 BatchSize int `json:"batch_size,omitempty"`
5096}
5197
98+ type buildPayload struct {
99+ InputPath string `json:"input_path"`
100+ OutputPath string `json:"output_path"`
101+ InputFormat InputFormat `json:"input_format,omitempty"`
102+ Metric DistanceMetric `json:"metric,omitempty"`
103+ IncludeDeleted bool `json:"include_deleted,omitempty"`
104+ M int `json:"m,omitempty"`
105+ M0 * int `json:"m0,omitempty"`
106+ EfConstruction int `json:"ef_construction,omitempty"`
107+ BatchSize int `json:"batch_size,omitempty"`
108+ Capacity * int `json:"capacity,omitempty"`
109+ Seed * uint64 `json:"seed,omitempty"`
110+ }
111+
52112var (
53113 stateMu sync.Mutex
54114 callMu sync.Mutex
57117 loaded bool
58118
59119 fnExtractIndex func (* byte ) * byte
120+ fnBuildIndex func (* byte ) * byte
60121 fnLastError func () * byte
61122 fnFreeCString func (* byte )
62123 fnVersion func () * byte
@@ -82,6 +143,10 @@ func Init(libraryPath string) error {
82143 _ = purego .Dlclose (handle )
83144 return err
84145 }
146+ if err := register (handle , & fnBuildIndex , "hnsw_toolbox_build_index" ); err != nil {
147+ _ = purego .Dlclose (handle )
148+ return err
149+ }
85150 if err := register (handle , & fnLastError , "hnsw_toolbox_get_last_error" ); err != nil {
86151 _ = purego .Dlclose (handle )
87152 return err
@@ -189,6 +254,59 @@ func ExtractIndex(request ExtractRequest) (*ExtractResponse, error) {
189254 return & response , nil
190255}
191256
257+ func BuildIndex (request BuildRequest ) (* BuildResponse , error ) {
258+ if err := ensureLoaded (); err != nil {
259+ return nil , err
260+ }
261+ if strings .TrimSpace (request .InputPath ) == "" {
262+ return nil , errors .New ("InputPath is required" )
263+ }
264+ if strings .TrimSpace (request .OutputPath ) == "" {
265+ return nil , errors .New ("OutputPath is required" )
266+ }
267+
268+ payload := buildPayload {
269+ InputPath : request .InputPath ,
270+ OutputPath : request .OutputPath ,
271+ InputFormat : request .InputFormat ,
272+ Metric : request .Metric ,
273+ IncludeDeleted : request .IncludeDeleted ,
274+ M : request .M ,
275+ M0 : request .M0 ,
276+ EfConstruction : request .EfConstruction ,
277+ BatchSize : request .BatchSize ,
278+ Capacity : request .Capacity ,
279+ Seed : request .Seed ,
280+ }
281+
282+ rawPayload , err := json .Marshal (payload )
283+ if err != nil {
284+ return nil , fmt .Errorf ("failed to marshal build request: %w" , err )
285+ }
286+ cPayload := append (rawPayload , 0 )
287+
288+ callMu .Lock ()
289+ responsePtr := fnBuildIndex (& cPayload [0 ])
290+ if responsePtr == nil {
291+ errorMessage := goStringFromPtr (fnLastError ())
292+ callMu .Unlock ()
293+ if strings .TrimSpace (errorMessage ) == "" {
294+ errorMessage = "hnsw_toolbox_build_index failed without error message"
295+ }
296+ return nil , errors .New (errorMessage )
297+ }
298+
299+ responseJSON := goStringFromPtr (responsePtr )
300+ fnFreeCString (responsePtr )
301+ callMu .Unlock ()
302+
303+ var response BuildResponse
304+ if err := json .Unmarshal ([]byte (responseJSON ), & response ); err != nil {
305+ return nil , fmt .Errorf ("failed to parse build response: %w" , err )
306+ }
307+ return & response , nil
308+ }
309+
192310func isLoaded () bool {
193311 stateMu .Lock ()
194312 defer stateMu .Unlock ()
0 commit comments