golang 操作向量數據庫 qdrant

    完成通過 langchain 框架使用大模型後,我們學習 golang 操作向量數據庫,qdrant 向量數據庫支持 http 協議和 grpc 協議。grpc 協議有個客戶端庫

github.com/qdrant/go-client/qdrant

    首先看下如何使用這個庫連接 qdrant

package main
import (
    "fmt"
    "github.com/qdrant/go-client/qdrant"
)
func main() {
    client, err := qdrant.NewClient(&qdrant.Config{
        Host: "localhost",
        Port: 6334,
      })
      if err!= nil {
        panic(err)
      }
      fmt.Println(client)
}

    需要注意的是這裏的 grpc 端口是 6334,如果使用 6333 的話會報如下錯誤,因爲 6333 是 http 協議端口

2025/04/05 16:08:22 WARN Unable to get server version, use default err="rpc error: code = Unavailable desc = connection error: desc = \"error reading server preface: http2: frame too large\"" default=Unknown
2025/04/05 16:08:22 WARN Failed to obtain server version. Unable to check client-server compatibility. Set SkipCompatibilityCheck=true to skip version check.

        接着可以使用對應的函數來進行向量的增刪改查,CreateCollection 來創建集合,需要指定的參數是向量的大小,和計算距離的函數,這裏使用的是 cos 函數,然後使用 Upsert 向集合裏添加點,每個點包含三個元素,ID、向量、和負載。最後通過 Query 函數來進行查詢,查詢的時候需要傳入一個向量。並且可以添加查詢過濾條件,對負載進行過濾,並決定是否要返回負載內容:

package main
import (
    "context"
    "fmt"
    "github.com/qdrant/go-client/qdrant"
)
func main() {
    client, err := qdrant.NewClient(&qdrant.Config{
        Host: "localhost",
        Port: 6334,
      })
      if err!= nil {
        panic(err)
      }
      fmt.Println(client)
    err=client.CreateCollection(context.Background(), &qdrant.CreateCollection{
        CollectionName: "example_collection",
        VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
            Size:     4,
            Distance: qdrant.Distance_Cosine,
        }),
    })
    if err!= nil {
        panic(err)
    }
    operationInfo, err := client.Upsert(context.Background(), &qdrant.UpsertPoints{
        CollectionName: "example_collection",
        Points: []*qdrant.PointStruct{
            {
                Id:      qdrant.NewIDNum(1),
                Vectors: qdrant.NewVectors(0.05, 0.61, 0.76, 0.74),
                Payload: qdrant.NewValueMap(map[string]any{"city": "London"}),
            },
            {
                Id:      qdrant.NewIDNum(2),
                Vectors: qdrant.NewVectors(0.19, 0.81, 0.75, 0.11),
                Payload: qdrant.NewValueMap(map[string]any{"age": 32}),
            },
            {
                Id:      qdrant.NewIDNum(3),
                Vectors: qdrant.NewVectors(0.36, 0.55, 0.47, 0.94),
                Payload: qdrant.NewValueMap(map[string]any{"vegan": true}),
            },
        },
    })
    if err != nil {
        panic(err)
    }
    fmt.Println(operationInfo)
    searchResult, err := client.Query(context.Background(), &qdrant.QueryPoints{
        CollectionName: "example_collection",
        Query:          qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
    })
    if err != nil {
        panic(err)
    }
    fmt.Println(searchResult)
    searchResult, err = client.Query(context.Background(), &qdrant.QueryPoints{
        CollectionName: "example_collection",
        Query:          qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
        Filter: &qdrant.Filter{
            Must: []*qdrant.Condition{
                qdrant.NewMatch("city", "London"),
            },
        },
        WithPayload: qdrant.NewWithPayload(true),
    })
    if err != nil {
        panic(err)
    }
    fmt.Println(searchResult)
}

     如果條件不適合使用 grpc,也可以自己手寫 http 請求,走 http 協議,下面是一個簡單的例子:

package main
import(
    "net/http"
    "bytes"
    "encoding/json"
    "io/ioutil"
    "fmt"
)
func main() {
    createCollection()
    addData()
}
func createCollection() {
data := Payload {
    // fill struct
    Vectors {
        Size: 768,
        Distance: "Dot",
    },
}
payloadBytes, err := json.Marshal(data)
if err != nil {
    // handle err
}
body := bytes.NewReader(payloadBytes)
req, err := http.NewRequest(http.MethodPut, "http://localhost:6333/collections/romeo", body)
if err != nil {
    // handle err
}
req.Header.Set("Content-Type""application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
    // handle err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
    // handle err
}
// handle response
responseBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
    // handle err
}
responseData := Payload{}
err = json.Unmarshal(responseBytes, &responseData)
if err != nil {
    // handle err   
}   
fmt.Println(string(responseBytes))
}   
func addData() {
data := AddReq{
// fill struct
    Points: []Points{
        Points{
            ID: 1,
            Vector: []float64{0.05, 0.61, 0.76, 0.74},
            Payload: AddPayload{
                Colony: "Mars",
            },
        },
        Points{
            ID: 2,
            Vector: []float64{0.19, 0.81, 0.75, 0.11},
            Payload: AddPayload{
                Colony: "Jupiter",
            },
        },
        Points{
            ID: 3,
            Vector: []float64{0.36, 0.55, 0.47, 0.94},
            Payload: AddPayload{
                Colony: "Venus",
            },
        },
        Points{
            ID: 4,
            Vector: []float64{0.18, 0.01, 0.85, 0.80},
            Payload: AddPayload{
                Colony: "Moon",
            },
        },
        Points{
            ID: 5,
            Vector: []float64{0.24, 0.18, 0.22, 0.44},
            Payload: AddPayload{
                Colony: "Pluto",
            },
        },  
    },
}
payloadBytes, err := json.Marshal(data)
if err != nil {
    // handle err
}
body := bytes.NewReader(payloadBytes)
req, err := http.NewRequest(http.MethodPut, "http://localhost:6333/collections/star_charts/points", body)
if err != nil {
    // handle err
}
req.Header.Set("Content-Type""application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
    // handle err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
    // handle err
}
// handle response
responseBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
    // handle err
}
responseData := Payload{}
err = json.Unmarshal(responseBytes, &responseData)
if err != nil {
    // handle err   
}
fmt.Println(string(responseBytes))
}
type Payload struct {
    Vectors Vectors `json:"vectors"`
}
type Vectors struct {
    Size int `json:"size"`
    Distance string `json:"distance"`
}
type AddReq struct {
    Points []Points `json:"points"`
}
type AddPayload struct {
    Colony string `json:"colony"`
}
type Points struct {
    ID      int       `json:"id"`
    Vector  []float64 `json:"vector"`
    Payload AddPayload   `json:"payload"`
}

      創建 collection 的路徑是 http://localhost:6333/collections/{collection_name},保存數據的路徑是

http://localhost:6333/collections/{collection_name}/points,返回值如下:

{"status":{"error":"Wrong input: Collection `romeo` already exists!"},"time":0.00016268}
{"result":{"operation_id":2,"status":"acknowledged"},"status":"ok","time":0.010663987}
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/LYlTJ0OpWAQLpX8eT1XrAg