Skip to content
8 changes: 8 additions & 0 deletions bson/raw_value.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,14 @@ func (rv RawValue) AsInt64() int64 { return convertToCoreValue(rv).AsInt64() }
// panicking.
func (rv RawValue) AsInt64OK() (int64, bool) { return convertToCoreValue(rv).AsInt64OK() }

// AsFloat64 returns a BSON number as a float64. If the BSON type is not a numeric one, this method
// will panic.
func (rv RawValue) AsFloat64() float64 { return convertToCoreValue(rv).AsFloat64() }

// AsFloat64OK is the same as AsFloat64, except that it returns a boolean instead of
// panicking.
func (rv RawValue) AsFloat64OK() (float64, bool) { return convertToCoreValue(rv).AsFloat64OK() }
Comment on lines +297 to +303
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The newly added AsFloat64() and AsFloat64OK() methods lack test coverage in bson/raw_value_test.go. Since other methods like AsInt64() and AsInt64OK() are tested through the underlying bsoncore.Value tests, and this file has existing test coverage, consider adding tests to verify the wrapper methods work correctly.

Copilot uses AI. Check for mistakes.
Copy link
Author

@RafaelCenzano RafaelCenzano Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@qingyang-hu I will defer to your review on this, it doesn't seem like most of the functions that handle wrapping RawValue conversions are tested and it just converts the raw value then uses the tested AsType functions so in my opinion unless we want to test the raw value conversion for each conversion type it is probably unneeded


// Decimal128 returns the decimal the Value represents. It panics if the value is a BSON type other than
// decimal.
func (rv RawValue) Decimal128() Decimal128 { return NewDecimal128(convertToCoreValue(rv).Decimal128()) }
Expand Down
18 changes: 3 additions & 15 deletions internal/integration/unified/matches.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,26 +245,14 @@ func evaluateSpecialComparison(ctx context.Context, assertionDoc bson.Raw, actua
if assertionVal.Type != bson.TypeInt32 && assertionVal.Type != bson.TypeInt64 && assertionVal.Type != bson.TypeDouble {
return fmt.Errorf("expected assertionVal to be an Int32, Int64, or Double but got a %s", assertionVal.Type)
}
if actual.Type != bson.TypeInt32 && actual.Type != bson.TypeInt64 && assertionVal.Type != bson.TypeDouble {
if actual.Type != bson.TypeInt32 && actual.Type != bson.TypeInt64 && actual.Type != bson.TypeDouble {
return fmt.Errorf("expected value to be an Int32, Int64, or Double but got a %s", actual.Type)
}

// Numeric values can be compared even if their types are different (e.g. if expected is an int32 and actual
// is an int64).

// TODO(GODRIVER-3594): If we decide to add AsDoubleOK() as a method to RawValue, this following conversion should be updated.
var expectedF64 float64
if assertionVal.Type == bson.TypeDouble {
expectedF64 = assertionVal.Double()
} else {
expectedF64 = float64(assertionVal.AsInt64())
}
var actualF64 float64
if actual.Type == bson.TypeDouble {
actualF64 = actual.Double()
} else {
actualF64 = float64(actual.AsInt64())
}
var expectedF64 = assertionVal.AsFloat64()
var actualF64 = actual.AsFloat64()

if actualF64 > expectedF64 {
return fmt.Errorf("expected numeric value %f to be less than or equal %f", actualF64, expectedF64)
Expand Down
66 changes: 58 additions & 8 deletions x/bsonx/bsoncore/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ func (v Value) AsInt64() int64 {
}
i64 = int64(f64)
case TypeInt32:
var ok bool
i32, _, ok := ReadInt32(v.Data)
if !ok {
panic(NewInsufficientBytesError(v.Data, v.Data))
Expand Down Expand Up @@ -170,7 +169,6 @@ func (v Value) AsInt64OK() (int64, bool) {
}
i64 = int64(f64)
case TypeInt32:
var ok bool
i32, _, ok := ReadInt32(v.Data)
if !ok {
return 0, false
Expand All @@ -190,15 +188,67 @@ func (v Value) AsInt64OK() (int64, bool) {

// AsFloat64 returns a BSON number as an float64. If the BSON type is not a numeric one, this method
// will panic.
//
// TODO(GODRIVER-2751): Implement AsFloat64.
// func (v Value) AsFloat64() float64
func (v Value) AsFloat64() float64 {
if !v.IsNumber() {
panic(ElementTypeError{"bsoncore.Value.AsFloat64", v.Type})
}
var f64 float64
switch v.Type {
case TypeDouble:
var ok bool
f64, _, ok = ReadDouble(v.Data)
if !ok {
panic(NewInsufficientBytesError(v.Data, v.Data))
}
case TypeInt32:
i32, _, ok := ReadInt32(v.Data)
if !ok {
panic(NewInsufficientBytesError(v.Data, v.Data))
}
f64 = float64(i32)
case TypeInt64:
i64, _, ok := ReadInt64(v.Data)
if !ok {
panic(NewInsufficientBytesError(v.Data, v.Data))
}
f64 = float64(i64)
case TypeDecimal128:
panic(ElementTypeError{"bsoncore.Value.AsFloat64", v.Type})
}
return f64
}

// AsFloat64OK functions the same as AsFloat64 but returns a boolean instead of panicking. False
// indicates an error.
//
// TODO(GODRIVER-2751): Implement AsFloat64OK.
// func (v Value) AsFloat64OK() (float64, bool)
func (v Value) AsFloat64OK() (float64, bool) {
if !v.IsNumber() {
return 0, false
}
var f64 float64
switch v.Type {
case TypeDouble:
var ok bool
f64, _, ok = ReadDouble(v.Data)
if !ok {
return 0, false
}
case TypeInt32:
i32, _, ok := ReadInt32(v.Data)
if !ok {
return 0, false
}
f64 = float64(i32)
case TypeInt64:
i64, _, ok := ReadInt64(v.Data)
if !ok {
return 0, false
}
f64 = float64(i64)
case TypeDecimal128:
return 0, false
}
return f64, true
}

// Equal compaes v to v2 and returns true if they are equal.
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in the comment: "compaes" should be "compares".

Suggested change
// Equal compaes v to v2 and returns true if they are equal.
// Equal compares v to v2 and returns true if they are equal.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated to the changes but simple non controversial fix, afe7a03

func (v Value) Equal(v2 Value) bool {
Expand Down
240 changes: 240 additions & 0 deletions x/bsonx/bsoncore/value_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,246 @@ func TestValue(t *testing.T) {
nil,
[]any{uint64(12345), uint64(67890), true},
},
{
"AsInt32/Not Number", Value.AsInt32, Value{Type: TypeString},
ElementTypeError{"bsoncore.Value.AsInt32", TypeString},
nil,
},
{
"AsInt32/Double/Insufficient Bytes", Value.AsInt32, Value{Type: TypeDouble, Data: []byte{0x01, 0x02, 0x03}},
NewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),
nil,
},
{
"AsInt32/Int32/Insufficient Bytes", Value.AsInt32, Value{Type: TypeInt32, Data: []byte{0x01, 0x02}},
NewInsufficientBytesError([]byte{0x01, 0x02}, []byte{0x01, 0x02}),
nil,
},
{
"AsInt32/Int64/Insufficient Bytes", Value.AsInt32, Value{Type: TypeInt64, Data: []byte{0x01, 0x02, 0x03}},
NewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),
nil,
},
{
"AsInt32/Decimal128", Value.AsInt32, Value{Type: TypeDecimal128, Data: AppendDecimal128(nil, 12345, 67890)},
ElementTypeError{"bsoncore.Value.AsInt32", TypeDecimal128},
nil,
},
{
"AsInt32/From Double", Value.AsInt32, Value{Type: TypeDouble, Data: AppendDouble(nil, 42.7)},
nil,
[]any{int32(42)},
},
{
"AsInt32/From Int32", Value.AsInt32, Value{Type: TypeInt32, Data: AppendInt32(nil, 12345)},
nil,
[]any{int32(12345)},
},
{
"AsInt32/From Int64", Value.AsInt32, Value{Type: TypeInt64, Data: AppendInt64(nil, 98765)},
nil,
[]any{int32(98765)},
},
{
"AsInt32OK/Not Number", Value.AsInt32OK, Value{Type: TypeString},
nil,
[]any{int32(0), false},
},
{
"AsInt32OK/Double/Insufficient Bytes", Value.AsInt32OK, Value{Type: TypeDouble, Data: []byte{0x01, 0x02, 0x03}},
nil,
[]any{int32(0), false},
},
{
"AsInt32OK/Int32/Insufficient Bytes", Value.AsInt32OK, Value{Type: TypeInt32, Data: []byte{0x01, 0x02}},
nil,
[]any{int32(0), false},
},
{
"AsInt32OK/Int64/Insufficient Bytes", Value.AsInt32OK, Value{Type: TypeInt64, Data: []byte{0x01, 0x02, 0x03}},
nil,
[]any{int32(0), false},
},
{
"AsInt32OK/Decimal128", Value.AsInt32OK, Value{Type: TypeDecimal128, Data: AppendDecimal128(nil, 12345, 67890)},
nil,
[]any{int32(0), false},
},
{
"AsInt32OK/From Double", Value.AsInt32OK, Value{Type: TypeDouble, Data: AppendDouble(nil, 42.7)},
nil,
[]any{int32(42), true},
},
{
"AsInt32OK/From Int32", Value.AsInt32OK, Value{Type: TypeInt32, Data: AppendInt32(nil, 12345)},
nil,
[]any{int32(12345), true},
},
{
"AsInt32OK/From Int64", Value.AsInt32OK, Value{Type: TypeInt64, Data: AppendInt64(nil, 98765)},
nil,
[]any{int32(98765), true},
},
{
"AsInt64/Not Number", Value.AsInt64, Value{Type: TypeString},
ElementTypeError{"bsoncore.Value.AsInt64", TypeString},
nil,
},
{
"AsInt64/Double/Insufficient Bytes", Value.AsInt64, Value{Type: TypeDouble, Data: []byte{0x01, 0x02, 0x03}},
NewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),
nil,
},
{
"AsInt64/Int32/Insufficient Bytes", Value.AsInt64, Value{Type: TypeInt32, Data: []byte{0x01, 0x02}},
NewInsufficientBytesError([]byte{0x01, 0x02}, []byte{0x01, 0x02}),
nil,
},
{
"AsInt64/Int64/Insufficient Bytes", Value.AsInt64, Value{Type: TypeInt64, Data: []byte{0x01, 0x02, 0x03}},
NewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),
nil,
},
{
"AsInt64/Decimal128", Value.AsInt64, Value{Type: TypeDecimal128, Data: AppendDecimal128(nil, 12345, 67890)},
ElementTypeError{"bsoncore.Value.AsInt64", TypeDecimal128},
nil,
},
{
"AsInt64/From Double", Value.AsInt64, Value{Type: TypeDouble, Data: AppendDouble(nil, 123456.9)},
nil,
[]any{int64(123456)},
},
{
"AsInt64/From Int32", Value.AsInt64, Value{Type: TypeInt32, Data: AppendInt32(nil, 12345)},
nil,
[]any{int64(12345)},
},
{
"AsInt64/From Int64", Value.AsInt64, Value{Type: TypeInt64, Data: AppendInt64(nil, 9876543210)},
nil,
[]any{int64(9876543210)},
},
{
"AsInt64OK/Not Number", Value.AsInt64OK, Value{Type: TypeString},
nil,
[]any{int64(0), false},
},
{
"AsInt64OK/Double/Insufficient Bytes", Value.AsInt64OK, Value{Type: TypeDouble, Data: []byte{0x01, 0x02, 0x03}},
nil,
[]any{int64(0), false},
},
{
"AsInt64OK/Int32/Insufficient Bytes", Value.AsInt64OK, Value{Type: TypeInt32, Data: []byte{0x01, 0x02}},
nil,
[]any{int64(0), false},
},
{
"AsInt64OK/Int64/Insufficient Bytes", Value.AsInt64OK, Value{Type: TypeInt64, Data: []byte{0x01, 0x02, 0x03}},
nil,
[]any{int64(0), false},
},
{
"AsInt64OK/Decimal128", Value.AsInt64OK, Value{Type: TypeDecimal128, Data: AppendDecimal128(nil, 12345, 67890)},
nil,
[]any{int64(0), false},
},
{
"AsInt64OK/From Double", Value.AsInt64OK, Value{Type: TypeDouble, Data: AppendDouble(nil, 123456.9)},
nil,
[]any{int64(123456), true},
},
{
"AsInt64OK/From Int32", Value.AsInt64OK, Value{Type: TypeInt32, Data: AppendInt32(nil, 12345)},
nil,
[]any{int64(12345), true},
},
{
"AsInt64OK/From Int64", Value.AsInt64OK, Value{Type: TypeInt64, Data: AppendInt64(nil, 9876543210)},
nil,
[]any{int64(9876543210), true},
},
{
"AsFloat64/Not Number", Value.AsFloat64, Value{Type: TypeString},
ElementTypeError{"bsoncore.Value.AsFloat64", TypeString},
nil,
},
{
"AsFloat64/Double/Insufficient Bytes", Value.AsFloat64, Value{Type: TypeDouble, Data: []byte{0x01, 0x02, 0x03}},
NewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),
nil,
},
{
"AsFloat64/Int32/Insufficient Bytes", Value.AsFloat64, Value{Type: TypeInt32, Data: []byte{0x01, 0x02}},
NewInsufficientBytesError([]byte{0x01, 0x02}, []byte{0x01, 0x02}),
nil,
},
{
"AsFloat64/Int64/Insufficient Bytes", Value.AsFloat64, Value{Type: TypeInt64, Data: []byte{0x01, 0x02, 0x03}},
NewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),
nil,
},
{
"AsFloat64/Decimal128", Value.AsFloat64, Value{Type: TypeDecimal128, Data: AppendDecimal128(nil, 12345, 67890)},
ElementTypeError{"bsoncore.Value.AsFloat64", TypeDecimal128},
nil,
},
{
"AsFloat64/From Double", Value.AsFloat64, Value{Type: TypeDouble, Data: AppendDouble(nil, 3.14159)},
nil,
[]any{float64(3.14159)},
},
{
"AsFloat64/From Int32", Value.AsFloat64, Value{Type: TypeInt32, Data: AppendInt32(nil, 42)},
nil,
[]any{float64(42)},
},
{
"AsFloat64/From Int64", Value.AsFloat64, Value{Type: TypeInt64, Data: AppendInt64(nil, 1234567890)},
nil,
[]any{float64(1234567890)},
},
{
"AsFloat64OK/Not Number", Value.AsFloat64OK, Value{Type: TypeString},
nil,
[]any{float64(0), false},
},
{
"AsFloat64OK/Double/Insufficient Bytes", Value.AsFloat64OK, Value{Type: TypeDouble, Data: []byte{0x01, 0x02, 0x03}},
nil,
[]any{float64(0), false},
},
{
"AsFloat64OK/Int32/Insufficient Bytes", Value.AsFloat64OK, Value{Type: TypeInt32, Data: []byte{0x01, 0x02}},
nil,
[]any{float64(0), false},
},
{
"AsFloat64OK/Int64/Insufficient Bytes", Value.AsFloat64OK, Value{Type: TypeInt64, Data: []byte{0x01, 0x02, 0x03}},
nil,
[]any{float64(0), false},
},
{
"AsFloat64OK/Decimal128", Value.AsFloat64OK, Value{Type: TypeDecimal128, Data: AppendDecimal128(nil, 12345, 67890)},
nil,
[]any{float64(0), false},
},
{
"AsFloat64OK/From Double", Value.AsFloat64OK, Value{Type: TypeDouble, Data: AppendDouble(nil, 3.14159)},
nil,
[]any{float64(3.14159), true},
},
{
"AsFloat64OK/From Int32", Value.AsFloat64OK, Value{Type: TypeInt32, Data: AppendInt32(nil, 42)},
nil,
[]any{float64(42), true},
},
{
"AsFloat64OK/From Int64", Value.AsFloat64OK, Value{Type: TypeInt64, Data: AppendInt64(nil, 1234567890)},
nil,
[]any{float64(1234567890), true},
},
{
"Timestamp.String/Success", Value.String, Value{Type: TypeTimestamp, Data: AppendTimestamp(nil, 12345, 67890)},
nil,
Expand Down
Loading