|
| 1 | +package main |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "log" |
| 6 | + "time" |
| 7 | + |
| 8 | + "github.com/tencent/vectordatabase-sdk-go/tcvectordb" |
| 9 | +) |
| 10 | + |
| 11 | +type Demo struct { |
| 12 | + //client *tcvectordb.Client |
| 13 | + client *tcvectordb.RpcClient |
| 14 | +} |
| 15 | + |
| 16 | +func NewDemo(url, username, key string) (*Demo, error) { |
| 17 | + // cli, err := tcvectordb.NewClient(url, username, key, &tcvectordb.ClientOption{ |
| 18 | + // ReadConsistency: tcvectordb.EventualConsistency}) |
| 19 | + cli, err := tcvectordb.NewRpcClient(url, username, key, &tcvectordb.ClientOption{ |
| 20 | + ReadConsistency: tcvectordb.StrongConsistency}) |
| 21 | + if err != nil { |
| 22 | + return nil, err |
| 23 | + } |
| 24 | + // disable/enable http request log print |
| 25 | + // cli.Debug(false) |
| 26 | + return &Demo{client: cli}, nil |
| 27 | +} |
| 28 | + |
| 29 | +func (d *Demo) Clear(ctx context.Context, database string) error { |
| 30 | + log.Println("--------------------------- DropDatabase ---------------------------") |
| 31 | + result, err := d.client.DropDatabase(ctx, database) |
| 32 | + if err != nil { |
| 33 | + return err |
| 34 | + } |
| 35 | + log.Printf("drop database result: %+v", result) |
| 36 | + return nil |
| 37 | +} |
| 38 | + |
| 39 | +func (d *Demo) DeleteAndDrop(ctx context.Context, database, collection string) error { |
| 40 | + // 删除collection,删除collection的同时,其中的数据也将被全部删除 |
| 41 | + log.Println("-------------------------- DropCollection --------------------------") |
| 42 | + colDropResult, err := d.client.Database(database).DropCollection(ctx, collection) |
| 43 | + if err != nil { |
| 44 | + return err |
| 45 | + } |
| 46 | + log.Printf("drop collection result: %+v", colDropResult) |
| 47 | + |
| 48 | + log.Println("--------------------------- DropDatabase ---------------------------") |
| 49 | + // 删除db,db下的所有collection都将被删除 |
| 50 | + dbDropResult, err := d.client.DropDatabase(ctx, database) |
| 51 | + if err != nil { |
| 52 | + return err |
| 53 | + } |
| 54 | + log.Printf("drop database result: %+v", dbDropResult) |
| 55 | + return nil |
| 56 | +} |
| 57 | + |
| 58 | +func (d *Demo) CreateDBAndCollection(ctx context.Context, database, collection, alias string) error { |
| 59 | + // 创建DB--'book' |
| 60 | + log.Println("-------------------------- CreateDatabase --------------------------") |
| 61 | + db, err := d.client.CreateDatabaseIfNotExists(ctx, database) |
| 62 | + if err != nil { |
| 63 | + return err |
| 64 | + } |
| 65 | + |
| 66 | + log.Println("------------------------- CreateCollection -------------------------") |
| 67 | + // 创建 Collection |
| 68 | + |
| 69 | + // 第一步,设计索引(不是设计 Collection 的结构) |
| 70 | + // 1. 【重要的事】向量对应的文本字段不要建立索引,会浪费较大的内存,并且没有任何作用。 |
| 71 | + // 2. 【必须的索引】:主键id、向量字段 vector 这两个字段目前是固定且必须的,参考下面的例子; |
| 72 | + // 3. 【其他索引】:检索时需作为条件查询的字段,比如要按书籍的作者进行过滤,这个时候 author 字段就需要建立索引, |
| 73 | + // 否则无法在查询的时候对 author 字段进行过滤,不需要过滤的字段无需加索引,会浪费内存; |
| 74 | + // 4. 向量数据库支持动态 Schema,写入数据时可以写入任何字段,无需提前定义,类似 MongoDB. |
| 75 | + // 5. 例子中创建一个书籍片段的索引,例如书籍片段的信息包括 {id, vector, segment, bookName, author, page}, |
| 76 | + // id 为主键需要全局唯一,segment 为文本片段, vector 字段需要建立向量索引,假如我们在查询的时候要查询指定书籍 |
| 77 | + // 名称的内容,这个时候需要对 bookName 建立索引,其他字段没有条件查询的需要,无需建立索引。 |
| 78 | + index := tcvectordb.Indexes{} |
| 79 | + index.VectorIndex = append(index.VectorIndex, tcvectordb.VectorIndex{ |
| 80 | + FilterIndex: tcvectordb.FilterIndex{ |
| 81 | + FieldName: "vector", |
| 82 | + FieldType: tcvectordb.Vector, |
| 83 | + IndexType: tcvectordb.HNSW, |
| 84 | + }, |
| 85 | + Dimension: 3, |
| 86 | + MetricType: tcvectordb.IP, |
| 87 | + Params: &tcvectordb.HNSWParam{ |
| 88 | + M: 16, |
| 89 | + EfConstruction: 200, |
| 90 | + }, |
| 91 | + }) |
| 92 | + index.FilterIndex = append(index.FilterIndex, tcvectordb.FilterIndex{FieldName: "id", FieldType: tcvectordb.String, IndexType: tcvectordb.PRIMARY}) |
| 93 | + index.FilterIndex = append(index.FilterIndex, tcvectordb.FilterIndex{FieldName: "bookName", FieldType: tcvectordb.String, IndexType: tcvectordb.FILTER}) |
| 94 | + index.FilterIndex = append(index.FilterIndex, tcvectordb.FilterIndex{FieldName: "page", FieldType: tcvectordb.Uint64, IndexType: tcvectordb.FILTER}) |
| 95 | + |
| 96 | + // 第二步:创建 Collection |
| 97 | + // 创建collection耗时较长,需要调整客户端的timeout |
| 98 | + // 这里以三可用区实例作为参考,具体实例不同的规格所支持的shard和replicas区间不同,需要参考官方文档 |
| 99 | + db.WithTimeout(time.Second * 30) |
| 100 | + _, err = db.CreateCollection(ctx, collection, 3, 1, "test collection", index) |
| 101 | + if err != nil { |
| 102 | + return err |
| 103 | + } |
| 104 | + |
| 105 | + log.Println("------------------------ DescribeCollection ------------------------") |
| 106 | + // 查看 Collection 信息 |
| 107 | + colRes, err := db.DescribeCollection(ctx, collection) |
| 108 | + if err != nil { |
| 109 | + return err |
| 110 | + } |
| 111 | + log.Printf("DescribeCollection: %+v", colRes) |
| 112 | + return nil |
| 113 | +} |
| 114 | + |
| 115 | +func (d *Demo) UpsertData(ctx context.Context, database, collection string) error { |
| 116 | + // 获取 Collection 对象 |
| 117 | + coll := d.client.Database(database).Collection(collection) |
| 118 | + |
| 119 | + log.Println("------------------------------ Upsert ------------------------------") |
| 120 | + // upsert 写入数据,可能会有一定延迟 |
| 121 | + // 1. 支持动态 Schema,除了 id、vector 字段必须写入,可以写入其他任意字段; |
| 122 | + // 2. upsert 会执行覆盖写,若文档id已存在,则新数据会直接覆盖原有数据(删除原有数据,再插入新数据) |
| 123 | + |
| 124 | + documentList := []tcvectordb.Document{ |
| 125 | + { |
| 126 | + Id: "0001", |
| 127 | + Vector: []float32{0.2123, 0.21, 0.213}, |
| 128 | + Fields: map[string]tcvectordb.Field{ |
| 129 | + "bookName": {Val: "西游记"}, |
| 130 | + "author": {Val: "吴承恩"}, |
| 131 | + "page": {Val: 21}, |
| 132 | + "segment": {Val: "富贵功名,前缘分定,为人切莫欺心。"}, |
| 133 | + }, |
| 134 | + }, |
| 135 | + { |
| 136 | + Id: "0002", |
| 137 | + Vector: []float32{0.2123, 0.22, 0.213}, |
| 138 | + Fields: map[string]tcvectordb.Field{ |
| 139 | + "bookName": {Val: "西游记"}, |
| 140 | + "author": {Val: "吴承恩"}, |
| 141 | + "page": {Val: 22}, |
| 142 | + "segment": {Val: "正大光明,忠良善果弥深。些些狂妄天加谴,眼前不遇待时临。"}, |
| 143 | + }, |
| 144 | + }, |
| 145 | + { |
| 146 | + Id: "0003", |
| 147 | + Vector: []float32{0.2123, 0.23, 0.213}, |
| 148 | + Fields: map[string]tcvectordb.Field{ |
| 149 | + "bookName": {Val: "三国演义"}, |
| 150 | + "author": {Val: "罗贯中"}, |
| 151 | + "page": {Val: 23}, |
| 152 | + "segment": {Val: "细作探知这个消息,飞报吕布。"}, |
| 153 | + }, |
| 154 | + }, |
| 155 | + { |
| 156 | + Id: "0004", |
| 157 | + Vector: []float32{0.2123, 0.24, 0.213}, |
| 158 | + Fields: map[string]tcvectordb.Field{ |
| 159 | + "bookName": {Val: "三国演义"}, |
| 160 | + "author": {Val: "罗贯中"}, |
| 161 | + "page": {Val: 24}, |
| 162 | + "segment": {Val: "布大惊,与陈宫商议。宫曰:“闻刘玄德新领徐州,可往投之。”布从其言,竟投徐州来。有人报知玄德。"}, |
| 163 | + }, |
| 164 | + }, |
| 165 | + { |
| 166 | + Id: "0005", |
| 167 | + Vector: []float32{0.2123, 0.25, 0.213}, |
| 168 | + Fields: map[string]tcvectordb.Field{ |
| 169 | + "bookName": {Val: "三国演义"}, |
| 170 | + "author": {Val: "罗贯中"}, |
| 171 | + "page": {Val: 25}, |
| 172 | + "segment": {Val: "玄德曰:“布乃当今英勇之士,可出迎之。”糜竺曰:“吕布乃虎狼之徒,不可收留;收则伤人矣。"}, |
| 173 | + }, |
| 174 | + }, |
| 175 | + } |
| 176 | + result, err := coll.Upsert(ctx, documentList) |
| 177 | + if err != nil { |
| 178 | + return err |
| 179 | + } |
| 180 | + log.Printf("UpsertResult: %+v", result) |
| 181 | + return nil |
| 182 | +} |
| 183 | + |
| 184 | +func (d *Demo) AddIndex(ctx context.Context, database, collection string) error { |
| 185 | + log.Println("------------------------------ AddIndex ------------------------------") |
| 186 | + |
| 187 | + addFilterIndexs := []tcvectordb.FilterIndex{ |
| 188 | + {FieldName: "author", FieldType: tcvectordb.String, IndexType: tcvectordb.FILTER}} |
| 189 | + buildExistedData := true |
| 190 | + err := d.client.AddIndex(ctx, database, collection, &tcvectordb.AddIndexParams{ |
| 191 | + FilterIndexs: addFilterIndexs, |
| 192 | + BuildExistedData: &buildExistedData}) |
| 193 | + if err != nil { |
| 194 | + return err |
| 195 | + } |
| 196 | + |
| 197 | + db := d.client.Database(database) |
| 198 | + for { |
| 199 | + time.Sleep(10 * time.Second) |
| 200 | + colRes, err := db.DescribeCollection(ctx, collection) |
| 201 | + if err != nil { |
| 202 | + return err |
| 203 | + } |
| 204 | + if colRes.IndexStatus.Status == "ready" { |
| 205 | + break |
| 206 | + } |
| 207 | + } |
| 208 | + |
| 209 | + queryResult, queryErr := d.client.Query(ctx, database, collection, nil, &tcvectordb.QueryDocumentParams{ |
| 210 | + Filter: tcvectordb.NewFilter("author=\"罗贯中\""), |
| 211 | + Limit: 100, |
| 212 | + }) |
| 213 | + printErr(queryErr) |
| 214 | + log.Printf("query by new added filter, total doc: %d, should be 3", queryResult.Total) |
| 215 | + for _, doc := range queryResult.Documents { |
| 216 | + log.Printf("document: %+v", doc) |
| 217 | + } |
| 218 | + return nil |
| 219 | +} |
| 220 | + |
| 221 | +func (d *Demo) DropIndex(ctx context.Context, database, collection string) error { |
| 222 | + log.Println("------------------------------ DropIndex ------------------------------") |
| 223 | + |
| 224 | + err := d.client.DropIndex(ctx, database, collection, tcvectordb.DropIndexParams{ |
| 225 | + FieldNames: []string{"author"}}) |
| 226 | + if err != nil { |
| 227 | + return err |
| 228 | + } |
| 229 | + |
| 230 | + _, queryErr := d.client.Query(ctx, database, collection, nil, &tcvectordb.QueryDocumentParams{ |
| 231 | + Filter: tcvectordb.NewFilter("author=\"罗贯中\""), |
| 232 | + Limit: 100, |
| 233 | + }) |
| 234 | + if queryErr != nil { |
| 235 | + log.Printf("query by dropped filter, queryErr: %+v", queryErr.Error()) |
| 236 | + } |
| 237 | + return nil |
| 238 | +} |
| 239 | + |
| 240 | +func (d *Demo) DescribeCollection(ctx context.Context, database, collection string) error { |
| 241 | + db := d.client.Database(database) |
| 242 | + log.Println("------------------------ DescribeCollection ------------------------") |
| 243 | + // 查看 Collection 信息 |
| 244 | + colRes, err := db.DescribeCollection(ctx, collection) |
| 245 | + if err != nil { |
| 246 | + return err |
| 247 | + } |
| 248 | + log.Printf("DescribeCollection: %+v", colRes) |
| 249 | + log.Printf("DescribeCollection: %+v", colRes.Indexes.VectorIndex[0].Params) |
| 250 | + return nil |
| 251 | +} |
| 252 | + |
| 253 | +func printErr(err error) { |
| 254 | + if err != nil { |
| 255 | + log.Fatal(err) |
| 256 | + } |
| 257 | +} |
| 258 | + |
| 259 | +func main() { |
| 260 | + database := "go-sdk-demo-db" |
| 261 | + collectionName := "go-sdk-demo-col" |
| 262 | + collectionAlias := "go-sdk-demo-alias" |
| 263 | + |
| 264 | + ctx := context.Background() |
| 265 | + testVdb, err := NewDemo("vdb http url or ip and port", "vdb username", "key get from web console") |
| 266 | + printErr(err) |
| 267 | + err = testVdb.Clear(ctx, database) |
| 268 | + printErr(err) |
| 269 | + err = testVdb.CreateDBAndCollection(ctx, database, collectionName, collectionAlias) |
| 270 | + printErr(err) |
| 271 | + |
| 272 | + err = testVdb.UpsertData(ctx, database, collectionName) |
| 273 | + printErr(err) |
| 274 | + time.Sleep(2 * time.Second) |
| 275 | + |
| 276 | + err = testVdb.AddIndex(ctx, database, collectionName) |
| 277 | + printErr(err) |
| 278 | + |
| 279 | + err = testVdb.DropIndex(ctx, database, collectionName) |
| 280 | + printErr(err) |
| 281 | + err = testVdb.DeleteAndDrop(ctx, database, collectionName) |
| 282 | + printErr(err) |
| 283 | +} |
0 commit comments