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