11package connmongo
22
33import (
4- "context"
54 "encoding/base64"
65 "encoding/hex"
76 "fmt"
@@ -13,80 +12,30 @@ import (
1312 "go.mongodb.org/mongo-driver/v2/bson"
1413 "go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore"
1514
16- "github.com/PeerDB-io/peerdb/flow/internal"
1715 "github.com/PeerDB-io/peerdb/flow/shared"
1816 "github.com/PeerDB-io/peerdb/flow/shared/types"
1917)
2018
2119type BsonToQValueConverter interface {
22- // QValueStringFromId QValueStringFromId converts a raw _id value to a QValueString.
20+ // QValueStringFromId converts a raw _id value to a QValueString.
2321 QValueStringFromId (id bson.RawValue , version uint32 ) (types.QValueString , error )
2422 // QValueJSONFromDocument converts a raw BSON document to a QValueJSON.
2523 QValueJSONFromDocument (raw bson.Raw ) (types.QValueJSON , error )
2624}
2725
28- func NewBsonConverter (ctx context.Context , env map [string ]string ) (BsonToQValueConverter , error ) {
29- direct , err := internal .PeerDBMongoDBDirectBsonConverter (ctx , env )
30- if err != nil {
31- return nil , err
32- }
33-
34- if ! direct {
35- return & LegacyBsonConverter {
36- api : CreateExtendedJSONMarshaler (),
37- }, nil
38- }
26+ // DirectBsonConverter converts BSON directly to JSON string without intermediate deserialization,
27+ // it uses jsoniter.Stream to build JSON output incrementally into a reusable buffer (to avoid allocation)
28+ type DirectBsonConverter struct {
29+ stream * jsoniter.Stream
30+ }
3931
32+ func NewDirectBsonConverter () * DirectBsonConverter {
4033 return & DirectBsonConverter {
4134 // technically we write JSON directly via raw stream methods and do not use jsoniter's
4235 // config-driven serialization specified here, but this config is applied consistent with our
43- // custom serialization (and used by LegacyBsonConverter) so specifying it here for consistency
36+ // custom serialization so specifying it here for consistency
4437 stream : jsoniter .NewStream (jsoniter .ConfigCompatibleWithStandardLibrary , nil , 512 ),
45- }, nil
46- }
47-
48- // LegacyBsonConverter converts BSON to JSON by first deserializing BSON to bson.D,
49- // and then serialize to JSON string via json-iterator.
50- type LegacyBsonConverter struct {
51- api jsoniter.API
52- }
53-
54- func (c * LegacyBsonConverter ) QValueJSONFromDocument (raw bson.Raw ) (types.QValueJSON , error ) {
55- var d bson.D
56- if err := bson .Unmarshal (raw , & d ); err != nil {
57- return types.QValueJSON {}, fmt .Errorf ("failed to unmarshal document: %w" , err )
58- }
59- jsonb , err := c .api .Marshal (d )
60- if err != nil {
61- return types.QValueJSON {}, fmt .Errorf ("failed to marshal document: %w" , err )
6238 }
63- return types.QValueJSON {Val : string (jsonb )}, nil
64- }
65-
66- func (c * LegacyBsonConverter ) QValueStringFromId (id bson.RawValue , version uint32 ) (types.QValueString , error ) {
67- if version >= shared .InternalVersion_MongoDBIdWithoutRedundantQuotes {
68- switch id .Type {
69- case bson .TypeObjectID :
70- return types.QValueString {Val : id .ObjectID ().Hex ()}, nil
71- case bson .TypeString :
72- return types.QValueString {Val : id .StringValue ()}, nil
73- }
74- }
75- var val any
76- if err := id .Unmarshal (& val ); err != nil {
77- return types.QValueString {}, fmt .Errorf ("failed to unmarshal %s: %w" , DefaultDocumentKeyColumnName , err )
78- }
79- jsonb , err := c .api .Marshal (val )
80- if err != nil {
81- return types.QValueString {}, fmt .Errorf ("failed to marshal %s: %w" , DefaultDocumentKeyColumnName , err )
82- }
83- return types.QValueString {Val : string (jsonb )}, nil
84- }
85-
86- // DirectBsonConverter converts BSON directly to JSON string without intermediate deserialization,
87- // it uses jsoniter.Stream to build JSON output incrementally into a reusable buffer (to avoid allocation)
88- type DirectBsonConverter struct {
89- stream * jsoniter.Stream
9039}
9140
9241func (c * DirectBsonConverter ) QValueJSONFromDocument (raw bson.Raw ) (types.QValueJSON , error ) {
@@ -276,8 +225,15 @@ func rawValueToJSON(v bsoncore.Value, stream *jsoniter.Stream) error {
276225 return nil
277226}
278227
279- // writeFloat64JSON matches the encoding behavior of encodeCustom + writeFloat64WithExplicitDecimal:
280- // NaN/Inf → quoted strings, integer-valued floats → explicit ".0", others → standard notation
228+ // Assume (and test) that values outside of these limits will come out in scientific notation
229+ // and will be parsed as floats either way
230+ var (
231+ floatLimit = math .Pow10 (21 )
232+ floatNegLimit = - floatLimit
233+ )
234+
235+ // writeFloat64JSON encodes NaN/Inf as quoted strings, integer-valued floats with an explicit
236+ // ".0" suffix (to hint ClickHouse to parse as float), and other values in standard notation.
281237func writeFloat64JSON (stream * jsoniter.Stream , v float64 ) {
282238 if math .IsNaN (v ) {
283239 stream .WriteRaw (`"NaN"` )
0 commit comments