| // Copyright 2024 Google LLC | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| package firestore | |
| import ( | |
| "fmt" | |
| pb "cloud.google.com/go/firestore/apiv1/firestorepb" | |
| ) | |
| const ( | |
| typeKey = "__type__" | |
| typeValVector = "__vector__" | |
| valueKey = "value" | |
| ) | |
| // Vector64 is an embedding vector of float64s. | |
| type Vector64 []float64 | |
| // Vector32 is an embedding vector of float32s. | |
| type Vector32 []float32 | |
| // vectorToProtoValue returns a Firestore [pb.Value] representing the Vector. | |
| func vectorToProtoValue[T float32 | float64](v []T) *pb.Value { | |
| if v == nil { | |
| return nullValue | |
| } | |
| pbVals := make([]*pb.Value, len(v)) | |
| for i, val := range v { | |
| pbVals[i] = floatToProtoValue(float64(val)) | |
| } | |
| return &pb.Value{ | |
| ValueType: &pb.Value_MapValue{ | |
| MapValue: &pb.MapValue{ | |
| Fields: map[string]*pb.Value{ | |
| typeKey: stringToProtoValue(typeValVector), | |
| valueKey: { | |
| ValueType: &pb.Value_ArrayValue{ | |
| ArrayValue: &pb.ArrayValue{Values: pbVals}, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| } | |
| func vector32FromProtoValue(v *pb.Value) (Vector32, error) { | |
| return vectorFromProtoValue[float32](v) | |
| } | |
| func vector64FromProtoValue(v *pb.Value) (Vector64, error) { | |
| return vectorFromProtoValue[float64](v) | |
| } | |
| func vectorFromProtoValue[T float32 | float64](v *pb.Value) ([]T, error) { | |
| pbArrVals, err := pbValToVectorVals(v) | |
| if err != nil { | |
| return nil, err | |
| } | |
| floats := make([]T, len(pbArrVals)) | |
| for i, fval := range pbArrVals { | |
| dv, ok := fval.ValueType.(*pb.Value_DoubleValue) | |
| if !ok { | |
| return nil, fmt.Errorf("firestore: failed to convert %v to *pb.Value_DoubleValue", fval.ValueType) | |
| } | |
| floats[i] = T(dv.DoubleValue) | |
| } | |
| return floats, nil | |
| } | |
| func pbValToVectorVals(v *pb.Value) ([]*pb.Value, error) { | |
| /* | |
| Vector is stored as: | |
| { | |
| "__type__": "__vector__", | |
| "value": []float64{}, | |
| } | |
| */ | |
| if v == nil { | |
| return nil, nil | |
| } | |
| pbMap, ok := v.ValueType.(*pb.Value_MapValue) | |
| if !ok { | |
| return nil, fmt.Errorf("firestore: cannot convert %v to *pb.Value_MapValue", v.ValueType) | |
| } | |
| m := pbMap.MapValue.Fields | |
| var typeVal string | |
| typeVal, err := stringFromProtoValue(m[typeKey]) | |
| if err != nil { | |
| return nil, err | |
| } | |
| if typeVal != typeValVector { | |
| return nil, fmt.Errorf("firestore: value of %v : %v is not %v", typeKey, typeVal, typeValVector) | |
| } | |
| pbVal, ok := m[valueKey] | |
| if !ok { | |
| return nil, fmt.Errorf("firestore: %v not present in %v", valueKey, m) | |
| } | |
| pbArr, ok := pbVal.ValueType.(*pb.Value_ArrayValue) | |
| if !ok { | |
| return nil, fmt.Errorf("firestore: failed to convert %v to *pb.Value_ArrayValue", pbVal.ValueType) | |
| } | |
| return pbArr.ArrayValue.Values, nil | |
| } | |
| func stringFromProtoValue(v *pb.Value) (string, error) { | |
| if v == nil { | |
| return "", fmt.Errorf("firestore: failed to convert %v to string", v) | |
| } | |
| sv, ok := v.ValueType.(*pb.Value_StringValue) | |
| if !ok { | |
| return "", fmt.Errorf("firestore: failed to convert %v to *pb.Value_StringValue", v.ValueType) | |
| } | |
| return sv.StringValue, nil | |
| } |