forked from grijjy/GrijjyFoundation
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Grijjy.Bson.pas
9916 lines (8210 loc) · 269 KB
/
Grijjy.Bson.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
unit Grijjy.Bson;
(*< A light-weight and fast BSON and JSON object model, with support for
efficiently parsing and writing in JSON and BSON format.
The code in this unit is fully compatible with the BSON and JSON used by
MongoDB. It supports all JSON extensions used by MongoDB.
However, this unit does @bold(not) have any dependencies on the MongoDB units
and can be used as a stand-alone BSON/JSON library. It is therefore also
suitable as a general purpose JSON library.
@bold(Quick Start)
<source>
var
Doc: TgoBsonDocument;
A: TgoBsonArray;
Json: String;
Bson: TBytes;
begin
Doc := TgoBsonDocument.Create;
Doc.Add('Hello', 'World');
A := TgoBsonArray.Create(['awesome', 5.05, 1986]);
Doc.Add('BSON', A);
Json := Doc.ToJson; // Returns:
// { "hello" : "world",
// "BSON": ["awesone", 5.05, 1986] }
Bson := Doc.ToBson; // Saves to binary BSON
Doc := TgoBsonDocument.Parse('{ "Answer" : 42 }');
WriteLn(Doc['Answer']); // Outputs 42
Doc['Answer'] := 'Unknown';
Doc['Pi'] := 3.14;
WriteLn(Doc.ToJson); // Outputs { "Answer" : "Unknown", "Pi" : 3.14 }
end;
</source>
@bold(Object Model)
The root type in the BSON object model is TgoBsonValue. TgoBsonValue is a
record type which can hold any type of BSON value. Some implicit class
operators make it easy to assign basic types:
<source>
var
Value: TgoBsonValue;
begin
Value := True; // Creates a Boolean value
Value := 1; // Creates an Integer value
Value := 3.14; // Creates a Double value
Value := 'Foo'; // Creates a String value
Value := TBytes.Create(1, 2, 3); // Creates a binary (TBytes) value
Value := TgoObjectId.GenerateNewId; // Creates an ObjectId value
end;
</source>
Note that you can change the type later by assigning a value of another type.
You can also go the other way around:
<source>
var
Value: TgoBsonValue;
FloatVal: Double;
begin
Value := 3.14; // Creates a Double value
FloatVal := Value; // Uses implicit cast
FloatVal := Value.AsDouble; // Or more explicit cast
Value := 42; // Creates an Integer value
FloatVal := Value; // Converts an Integer BSON value to a Double
FloatVal := Value.AsDouble; // Raises exception because types don't match exactly
if (Value.BsonType = TgoBsonType.Double) then
FloatVal := Value.AsDouble; // Now it is safe to cast
// Or identical:
if (Value.IsDouble) then
FloatVal := Value.AsDouble;
end;
</source>
Note that the implicit operators will try to convert if the types don't match
exactly. For example, a BSON value containing an Integer value can be
implicitly converted to a Double. If the conversion fails, it returns a zero
value (or empty string).
The "As*" methods however will raise an exception if the types don't match
exactly. You should use these methods if you know that the type you request
matches the value's type exactly. These methods are a bit more efficient than
the implicit operators.
You can check the value type using the BsonType-property or one of the "Is*"
methods.
For non-basic types, there are value types that are "derived" from
TgoBsonValue:
* TgoBsonNull: the special "null" value
* TgoBsonArray: an array of other BSON values.
* TgoBsonDocument: a document containing key/value pairs, where the key is a
string and the value can be any BSON value. This is usually the main
starting point in Mongo, since all database "records" are represented as
BSON documents in Mongo. A document is similar to a dictionary in many
programming languages, or the "object" type in JSON.
* TgoBsonBinaryData: arbitrary binary data. Is also used to store GUID's (but
not ObjectId's).
* TgoBsonDateTime: a date/time value with support for conversion to and from
UTC (Universal) time. Is always stored in UTC format (as the number of UTC
milliseconds since the Unix epoch).
* TgoBsonRegularExpression: a regular expression with options.
* TgoBsonJavaScript: a piece of JavaScript code.
* TgoBsonJavaScriptWithScope: a piece of JavaScript code with a scope (a set
of variables with values, as defined in another document).
* TgoBsonTimestamp: special internal type used by MongoDB replication and
sharding.
* TgoBsonMaxKey: special type which compares higher than all other possible
BSON element values.
* TgoBsonMinKey: special type which compares lower than all other possible
BSON element values.
* TgoBsonUndefined: an undefined value (deprecated by BSON)
* TgoBsonSymbol: a symbol from a lookup table (deprecated by BSON)
Note that these are not "real" derived types, since they are implemented as
Delphi records (which do not support inheritance). But the implicit operators
make it possible to treat each of these types as a TgoBsonValue. For example
<source>
var
MyArray: TgoBsonArray;
Value: TgoBsonValue;
begin
MyArray := TgoBsonArray.Create([1, 3.14, 'Foo', False]);
Value := MyArray; // "subtypes" are compatible with TgoBsonValue
// Or shorter:
Value := TgoBsonArray.Create([1, 3.14, 'Foo', False]);
end;
</source>
@Bold(Arrays)
The example above also shows that arrays can be created very easily. An array
contains a collection of BSON values of any type. Since BSON values can be
implicitly created from basic types, you can pass multiple types in the
array constructor. In the example above, 4 BSON values will be added to the
array of types Integer, Double, String and Boolean.
You can also add items using the Add or AddRange methods:
<source>
MyArray := TgoBsonArray.Create;
MyArray.Add(1);
MyArray.Add(3.14);
MyArray.Add('Foo');
</source>
Some methods return the array (or document) itself, so they can be used for
chaining (aka as a "fluent interface"). The example above is equivalent to:
<source>
MyArray := TgoBsonArray.Create;
MyArray.Add(1).Add(3.14).Add('Foo');
</source>
You can change values (and types) like this:
<source>
// Changes entry 1 from Double to Boolean
MyArray[1] := True;
</source>
@Bold(Documents)
Documents (or dictionaries) can also be created easily:
<source>
var
Doc: TgoBsonDocument;
begin
Doc := TgoBsonDocument.Create('Answer', 42);
end;
</source>
This creates a document with a single entry called 'Answer' with a value of
42. Keep in mind that the value can be any BSON type:
<source>
Doc := TgoBsonDocument.Create('Answer', TgoBsonArray.Create([42, False]));
</source>
You can Add, Remove and Delete (Adds can be fluent):
<source>
Doc := TgoBsonDocument.Create;
Doc.Add('Answer', 42);
Doc.Add('Pi', 3.14).Add('Pie', 'Yummi');
// Deletes second item (Pi):
Doc.Delete(1);
// Removes first item (Answer):
Doc.Remove('Answer');
</source>
Like Delphi dictionaries, the Add method will raise an exception if an item
with the given name already exists. Unlike Delphi dictionaries however, you
can easily set an item using its default accessor:
<source>
// Adds Answer:
Doc['Answer'] := 42;
// Adds Pi:
Doc['Pi'] := 3.14;
// Updates Answer:
Doc['Answer'] := 'Everything';
</source>
This adds the item if it does not yet exists, or replaces it otherwise (there
is no (need for an) AddOrSet method).
Also unlike Delphi dictionaries, documents maintain insertion order and you
can also access the items by index:
<source>
// Returns item by name:
V := Doc['Pi'];
// Returns item by index:
V := Doc.Values[1];
</source>
Documents can be easily parsed from JSON:
<source>
Doc := TgoBsonDocument.Parse('{ "Answer" : 42 }');
</source>
The parser understands standard JSON as well as the MongoDB JSON extensions.
You can also load a document from a BSON byte array:
<source>
Bytes := LoadSomeBSONData();
Doc := TgoBsonDocument.Load(Bytes);
</source>
These methods will raise exceptions if the JSON or BSON data is invalid.
@bold(Memory Management)
All memory management in this library is automatic. You never need to (and you
never must) destroy any objects yourself.
The object model types (TgoBsonValue and friends) are all Delphi records. The
actual implementations of these records use interfaces to manage memory.
There is no concept of ownership in the object model. An array does @bold(not)
own its elements and a document does @bold(not) own its elements. So you are
free to add the same value to multiple arrays and/or documents without
ownership concerns:
<source>
var
Array1, Array2, SubArray, Doc1, Doc2: TgoBsonValue;
begin
SubArray := TgoBsonArray.Create([42, 'Foo', True]);
Array1 := TgoBsonArray.Create;
Array2 := TgoBsonArray.Create([123, 'Abc']);
Doc1 := TgoBsonDocument.Create;
Doc2 := TgoBsonDocument.Create('Pi', 3.14);
Array1.Add(SubArray);
Array2.Add(SubArray); // Add same value to other container
Doc1.Add('Bar', SubArray); // And again
Doc2.Add('Baz', SubArray); // And again
end;
</source>
Non-object model types are defined as interfaces, so their memory is managed
automatically as well. For example JSON/BSON readers and writer are
interfaces:
<source>
var
Reader: IgoJsonReader;
Value: TgoBsonValue;
begin
Reader := TgoJsonReader.Create('{ "Pi" : 3.14 }');
Value := Reader.ReadValue;
Assert(Value.IsDocument);
Assert(Value.AsDocument['Pi'] = 3.14);
end;
</source>
Just keep in mind that you must always declare your variable (Reader) as an
interface type (IgoJsonReader), but you construct it using the class type
(TgoJsonReader).
@bold(JSON and BSON reading and writing)
For easy storing, all BSON values have methods called ToJson and ToBson to
store its value into JSON or BSON format:
<source>
var
A: TgoBsonValue;
B: TBytes;
begin
A := 42;
WriteLn(A.ToJson); // Outputs '42'
A := 'Foo';
WriteLn(A.ToJson); // Outputs '"Foo"'
A := TgoBsonArray.Create([1, 'Foo', True]);
WriteLn(A.ToJson); // Outputs '[1, "Foo", true]'
A := TgoBsonDocument.Create('Pi', 3.14);
WriteLn(A.ToJson); // Outputs '{ "Pi" : 3.14 }'
B := A.ToBson; // Outputs document in BSON format
end;
</source>
When outputting to JSON, you can optionally supply a settings record to
customize the output:
* Whether to pretty-print the output
* What strings to use for indentation and line breaks
* Whether to output standard JSON or use the MongoDB shell syntax extension
If you don's supply any settings, then output will be in Strict JSON format
without pretty printing.
Easy loading is only supported at the Value, Document and Array level, using
the Parse and Load methods:
<source>
var
Doc: TgoBsonDocument;
Bytes: TBytes;
begin
Doc := TgoBsonDocument.Parse('{ "Pi" : 3.14 }');
Bytes := LoadSomeBSONData();
Doc := TgoBsonDocument.Load(Bytes);
end;
</source>
You can load other types using the IgoJsonReader and IgoBsonReader
interfaces:
<source>
var
Reader: IgoBsonReader;
Value: TgoBsonValue;
Bytes: TBytes;
begin
Bytes := LoadSomeBSONData();
Reader := TgoBsonReader.Create(Bytes);
Value := Reader.ReadValue;
end;
</source>
The JSON reader and writer supports both the "strict" JSON syntax, as well as
the "Mongo Shell" syntax (see https://docs.mongodb.org/manual/reference/mongodb-extended-json/).
Extended JSON is supported for both reading and writing. This library supports
all the current extensions, as well as some deprecated legacy extensions.
The JSON reader accepts both key names with double quotes (as per JSON spec)
as without quotes.
@bold(Manual reading and writing)
For all situations, the methods ToJson, ToBson, Parse and Load can take care
of reading and writing any kind of JSON and BSON data.
However, you can use the reading and writing interfaces directly if you want
for some reason. One reason may be that you want the fastest performance when
creating BSON payloads, without the overhead of creating a document object
model in memory.
For information, see the unit Grijjy.Data.Bson.IO
@bold(Serialization)
For even easier reading and writing, you can use serialization to directory
store a Delphi record or object in JSON or BSON format (or convert it to a
TgoBsonDocument).
For information, see the unit Grijjy.Data.Bson.Serialization *)
{$INCLUDE 'Grijjy.inc'}
interface
uses
System.Classes,
System.SysUtils,
System.SyncObjs,
System.Generics.Collections;
type
{ Supported BSON types. As returned by TgoBsonValue.BsonType.
Tech note: Ordinal values must match BSON spec (http://bsonspec.org) }
TgoBsonType = (
{ Not a real BSON type. Used to signal the end of a document. }
EndOfDocument = $00,
{ A BSON double }
Double = $01,
{ A BSON string }
&String = $02,
{ A BSON document (see TgoBsonDocument) }
Document = $03,
{ A BSON array (see TgoBsonArray) }
&Array = $04,
{ BSON binary data (see TgoBsonBinaryData) }
Binary = $05,
{ A BSON undefined value (see TgoBsonUndefined) }
Undefined = $06,
{ A ObjectId, generally used with MongoDB (see TgoObjectId) }
ObjectId = $07,
{ A BSON boolean }
Boolean = $08,
{ A BSON DateTime (see TgoBsonDateTime) }
DateTime = $09,
{ A BSON null value (see TgoBsonNull) }
Null = $0A,
{ A BSON regular expression (see TgoBsonRegularExpression) }
RegularExpression = $0B,
{ BSON JavaScript code (see TgoBsonJavaScript) }
JavaScript = $0D,
{ A BSON Symbol (see TgoBsonSymbol, deprecated) }
Symbol = $0E,
{ BSON JavaScript code with a scope (see TgoBsonJavaScriptWithScope) }
JavaScriptWithScope = $0F,
{ A BSON 32-bit integer }
Int32 = $10,
{ A BSON Timestamp (see TgoBsonTimestamp) }
Timestamp = $11,
{ A BSON 64-bit integer }
Int64 = $12,
{ A BSON MaxKey value (see TgoBsonMaxKey) }
MaxKey = $7F,
{ A BSON MinKey value (see TgoBsonMinKey) }
MinKey = $FF);
type
{ Supported BSON binary sub types.
As returned by TgoBsonBinaryData.SubType.
Tech note: Ordinal values must match BSON spec (http://bsonspec.org) }
TgoBsonBinarySubType = (
{ Binary data in an arbitrary format }
Binary = $00,
{ A function }
&Function = $01,
{ Obsolete binary type }
OldBinary = $02,
{ A UUID/GUID in driver dependent legacy byte order }
UuidLegacy = $03,
{ A UUID/GUID in standard network byte order (big endian) }
UuidStandard = $04,
{ A MD5 hash }
MD5 = $05,
{ User defined type }
UserDefined = $80);
type
{ The output mode of a IgoJsonWriter, as set using TgoJsonWriterSettings. }
TgoJsonOutputMode = (
{ Outputs strict JSON }
Strict,
{ Outputs a format that can be used by the MongoDB shell }
Shell);
type
{ Settings for a IgoJsonWriter }
TgoJsonWriterSettings = record
{$REGION 'Internal Declarations'}
private class var
FDefault: TgoJsonWriterSettings;
FShell: TgoJsonWriterSettings;
FPretty: TgoJsonWriterSettings;
private
FPrettyPrint: Boolean;
FIndent: String;
FLineBreak: String;
FOutputMode: TgoJsonOutputMode;
public
{ @exclude }
class constructor Create;
{$ENDREGION 'Internal Declarations'}
public
{ Creates a settings record using the default settings:
* PrettyPrint: False
* OutputMode: Strict
* Indent: 2 spaces (not used unless PrettyPrint is set to True later)
* LineBreak: CR+LF (not used unless PrettyPrint is set to True later)
Returns:
The settings }
class function Create: TgoJsonWriterSettings; overload; static;
{ Creates a settings record:
Parameters:
APrettyPrint: whether to use indentation (see Indent) and line breaks
(see LineBreak).
AOutputMode: (optional) output mode. Defaults to Strict.
Returns:
The settings }
class function Create(const APrettyPrint: Boolean;
const AOutputMode: TgoJsonOutputMode = TgoJsonOutputMode.Strict): TgoJsonWriterSettings; overload; static;
{ Creates a settings record:
Parameters:
AIndent: the string to use for indentation. Should only contain
whitespace characters to create valid output.
ALineBreak: the string to use for line breaks. Should only contain
whitespace characters to create valid output.
AOutputMode: (optional) output mode. Defaults to Strict.
Returns:
The settings.
@bold(Note): this constructor sets PrettyPrint to True. }
class function Create(const AIndent, ALineBreak: String;
const AOutputMode: TgoJsonOutputMode = TgoJsonOutputMode.Strict): TgoJsonWriterSettings; overload; static;
{ Creates a settings record:
Parameters:
AOutputMode: output mode to use.
Returns:
The settings
@bold(Note): this constructor sets PrettyPrint to False. }
class function Create(const AOutputMode: TgoJsonOutputMode): TgoJsonWriterSettings; overload; static;
{ The default settings:
* PrettyPrint: False
* OutputMode: Strict
* Indent: 2 spaces (not used unless PrettyPrint is set to True later)
* LineBreak: CR+LF (not used unless PrettyPrint is set to True later) }
class property Default: TgoJsonWriterSettings read FDefault;
{ "Shell" settings for outputing JSON with MongoDB shell extensions.
* PrettyPrint: False
* OutputMode: Shell
* Indent: 2 spaces (not used unless PrettyPrint is set to True later)
* LineBreak: CR+LF (not used unless PrettyPrint is set to True later) }
class property Shell: TgoJsonWriterSettings read FShell;
{ Settings for outputing JSON compliant JSON in a pretty format.
* PrettyPrint: True
* OutputMode: Strict
* Indent: 2 spaces
* LineBreak: CR+LF }
class property Pretty: TgoJsonWriterSettings read FPretty;
{ Whether to use indentation (see Indent) and line breaks (see LineBreak).
Default False. }
property PrettyPrint: Boolean read FPrettyPrint write FPrettyPrint;
{ String to use for indentation. Should only contain whitespace characters
to create valid output. Not used unless PrettyPrint is True.
Defaults to 2 spaces }
property Indent: String read FIndent write FIndent;
{ String to use for line breaks. Should only contain whitespace characters
to create valid output. Not used unless PrettyPrint is True.
Defaults to CR+LF }
property LineBreak: String read FLineBreak write FLineBreak;
{ Output mode to use.
Defaults to Strict }
property OutputMode: TgoJsonOutputMode read FOutputMode write FOutputMode;
end;
type
{ Represents an ObjectId. This is a 12-byte (96-bit) value that is regularly
used for (unique) primary keys in MongoDB databases.
Internally, an ObjectId is composed of:
* A 4-byte value containing the number of seconds since the Unix epoch.
* A 3-byte machine identifier
* A 2-byte process identifier
* A 3-byte counter, starting from a random value
This makes ObjectId's fairly unique (but not as unique as GUID's though) }
TgoObjectId = record
{$REGION 'Internal Declarations'}
private class var
FIncrement: Integer;
FMachine: Integer;
FPid: UInt16;
FInitialized: Boolean;
private
function GetIsEmpty: Boolean;
function GetTimestamp: Integer;
function GetMachine: Integer;
function GetPid: UInt16;
function GetIncrement: Integer;
function GetCreationTime: TDateTime;
private
class procedure Initialize; static;
class function GetTimestampFromDateTime(const ATimestamp: TDateTime;
const ATimestampIsUTC: Boolean): Integer; static;
private
procedure FromByteArray(const ABytes: TBytes);
public
{ @exclude }
class constructor Create;
{$ENDREGION 'Internal Declarations'}
public
{ Creates an ObjectId from a byte array.
Parameters:
ABytes: the array of bytes to use for the ObjectId.
Must be 12 bytes long.
Returns:
The ObjectId.
Raises:
EArgumentException if ABytes is not 12 bytes long }
class function Create(const ABytes: TBytes): TgoObjectId; overload; static;
{ Creates an ObjectId from a byte array.
Parameters:
ABytes: the array of bytes to use for the ObjectId.
Must be 12 bytes long.
Returns:
The ObjectId.
Raises:
EArgumentException if ABytes is not 12 bytes long }
class function Create(const ABytes: array of Byte): TgoObjectId; overload; static;
{ Creates an ObjectId from its components.
Parameters:
ATimestamp: 32-bit number of seconds since Unix epoch.
AMachine: 24-bit machine identifier. Must be >= 0 and < $01000000.
APid: 16-bit process identifier.
AIncrement: 24-bit counter. Must be >= 0 and < $01000000.
Returns:
The ObjectId.
Raises:
EArgumentOutOfRangeException if AMachine or AIncrement are out of range. }
class function Create(const ATimestamp, AMachine: Integer; const APid: UInt16;
const AIncrement: Integer): TgoObjectId; overload; static;
{ Creates an ObjectId from its components.
Parameters:
ATimestamp: the date/time to use as a timestamp.
ATimestampIsUTC: whether ATimestamp is in universal time.
AMachine: 24-bit machine identifier. Must be >= 0 and < $01000000.
APid: 16-bit process identifier.
AIncrement: 24-bit counter. Must be >= 0 and < $01000000.
Returns:
The ObjectId.
Raises:
EArgumentOutOfRangeException if AMachine or AIncrement are out of range. }
class function Create(const ATimestamp: TDateTime;
const ATimestampIsUTC: Boolean; const AMachine: Integer;
const APid: UInt16; const AIncrement: Integer): TgoObjectId; overload; static;
{ Creates an ObjectId from its string representation (see ToString).
Parameters:
AString: the string representation of the ObjectId. Must contain 24
hex digits.
Returns:
The ObjectId.
Raises:
EArgumentException if AString does not contain 24 hex digits.
@bold(Note): this constructor is equal to the Parse method. }
class function Create(const AString: String): TgoObjectId; overload; static;
{ Generates a new ObjectId using the current timestamp, machine, process
and counter settings.
Returns:
The newly generated ObjectId.
@bold(Note): the returned ObjectId is guaranteed to be unique on the
current system, even if this function is called at the same time from
the same or other processes on the machine. However, the ObjectId is not
neccesarily globally unique since another machine with the same hostname
or computer name can theoretically generate the same Id. }
class function GenerateNewId: TgoObjectId; overload; static;
{ Generates a new ObjectId using a given timestamp and the current machine,
process and counter settings.
Parameters:
ATimestamp: the date/time to use as a timestamp.
ATimestampIsUTC: whether ATimestamp is in universal time.
Returns:
The newly generated ObjectId.
@bold(Note): the returned ObjectId is guaranteed to be unique on the
current system, even if this function is called at the same time from
the same or other processes on the machine. However, the ObjectId is not
neccesarily globally unique since another machine with the same hostname
or computer name can theoretically generate the same Id. }
class function GenerateNewId(const ATimestamp: TDateTime;
const ATimestampIsUTC: Boolean): TgoObjectId; overload; static;
{ Generates a new ObjectId using a given timestamp and the current machine,
process and counter settings.
Parameters:
ATimestamp: 32-bit number of seconds since Unix epoch.
Returns:
The newly generated ObjectId.
@bold(Note): the returned ObjectId is guaranteed to be unique on the
current system, even if this function is called at the same time from
the same or other processes on the machine. However, the ObjectId is not
neccesarily globally unique since another machine with the same hostname
or computer name can theoretically generate the same Id. }
class function GenerateNewId(const ATimestamp: Integer): TgoObjectId; overload; static;
{ Parses an ObjectId from its string representation (see ToString).
Parameters:
AString: the string representation of the ObjectId. Must contain 24
hex digits.
Returns:
The ObjectId.
Raises:
EArgumentException if AString does not contain 24 hex digits }
class function Parse(const AString: String): TgoObjectId; overload; static;
{ Tries to parse an ObjectId from its string representation (see ToString).
Parameters:
AString: the string representation of the ObjectId. Must contain 24
hex digits.
AObjectId: is set to the parsed ObjectId, or all zeros if AString could
not be parsed.
Returns:
True if AString could be successfully parsed. }
class function TryParse(const AString: String;
out AObjectId: TgoObjectId): Boolean; overload; static;
{ Returns an empty ObjectId (with all zeros)
Returns:
The empty ObjectId. }
class function Empty: TgoObjectId; static;
{ Implicitly converts a string to an ObjectId. The string @bold(must)
contain 24 hex digits. An EArgumentException will be raised if this is not
the case }
class operator Implicit(const A: String): TgoObjectId;
{ Implicitly convers an ObjectId to a string }
class operator Implicit(const A: TgoObjectId): String;
{ Tests 2 ObjectId's for equality }
class operator Equal(const A, B: TgoObjectId): Boolean; static;
{ Tests 2 ObjectId's for inequality }
class operator NotEqual(const A, B: TgoObjectId): Boolean; static;
{ Compares 2 ObjectId's using the ">" operator }
class operator GreaterThan(const A, B: TgoObjectId): Boolean; static;
{ Compares 2 ObjectId's using the ">=" operator }
class operator GreaterThanOrEqual(const A, B: TgoObjectId): Boolean; static;
{ Compares 2 ObjectId's using the "<" operator }
class operator LessThan(const A, B: TgoObjectId): Boolean; static;
{ Compares 2 ObjectId's using the "<=" operator }
class operator LessThanOrEqual(const A, B: TgoObjectId): Boolean; static;
{ Converts the ObjectId to an array of 12 bytes.
Returns:
The ObjectId as 12 bytes. }
function ToByteArray: TBytes; overload;
{ Converts the ObjectId to an array of bytes.
Parameters:
ADestination: byte array to store the ObjectId into.
AOffset: starting offset in ADestination to use.
Raises:
EArgumentException if ADestination does not have room enough to store
(AOffset+12) bytes. }
procedure ToByteArray(const ADestination: TBytes; const AOffset: Integer); overload;
{ Converts the ObjectId to its string representation. This is a string
containing 24 hex digits.
Returns:
The string representation of the ObjectId. }
function ToString: String;
{ Compare this ObjectId to another one.
Parameters:
AOther: the other ObjectId.
Returns:
* -1 if Self < AOther
* 0 if Self = AOther
* 1 if Self > AOther }
function CompareTo(const AOther: TgoObjectId): Integer;
{ Returns True if this ObjectId is empty (all zeros) }
property IsEmpty: Boolean read GetIsEmpty;
{ Timestamp component of the ObjectId.
If the 32-bit number of seconds since Unix epoch. }
property Timestamp: Integer read GetTimestamp;
{ Machine component of the ObjectId.
Is a 24-bit machine identifier. }
property Machine: Integer read GetMachine;
{ Process component of the ObjectId.
Is a 16-bit process identifier. }
property Pid: UInt16 read GetPid;
{ Counter component of the ObjectId.
Is a 32-bit increment. }
property Increment: Integer read GetIncrement;
{ The creation time of the ObjectId, as stored inside its Timestamp
component. The time is in UTC. }
property CreationTime: TDateTime read GetCreationTime;
{$REGION 'Internal Declarations'}
private
case Byte of
0: (FData: array [0..2] of UInt32);
1: (FBytes: array [0..11] of Byte);
{$ENDREGION 'Internal Declarations'}
end;
PgoObjectId = ^TgoObjectId;
type
{ The base "class" for all BSON values. It is implemented as a record type
which can hold any type of BSON value. }
TgoBsonValue = record
{$REGION 'Internal Declarations'}
public type
{ @exclude }
_IValue = interface
['{290B24D7-1D64-4F76-93C8-1B9D92658018}']
function GetBsonType: TgoBsonType;
function AsBoolean: Boolean;
function AsInteger: Integer;
function AsInt64: Int64;
function AsDouble: Double;
function AsString: String;
function AsArray: TArray<TgoBsonValue>;
function AsByteArray: TBytes;
function AsGuid: TGUID;
function AsObjectId: TgoObjectId;
function ToBoolean(const ADefault: Boolean): Boolean;
function ToDouble(const ADefault: Double): Double;
function ToInteger(const ADefault: Integer): Integer;
function ToInt64(const ADefault: Int64): Int64;
function ToString(const ADefault: String): String;
function ToLocalTime: TDateTime;
function ToUniversalTime: TDateTime;
function ToByteArray: TBytes;
function ToGuid: TGUID;
function ToObjectId: TgoObjectId;
function Equals(const AOther: _IValue): Boolean;
function Clone: _IValue;
function DeepClone: _IValue;
property BsonType: TgoBsonType read GetBsonType;
end;
private
FImpl: _IValue;
function GetBsonType: TgoBsonType; inline;
function GetIsBoolean: Boolean; inline;
function GetIsBsonArray: Boolean; inline;
function GetIsBsonBinaryData: Boolean; inline;
function GetIsBsonDateTime: Boolean; inline;
function GetIsBsonDocument: Boolean; inline;
function GetIsBsonJavaScript: Boolean; inline;
function GetIsBsonJavaScriptWithScope: Boolean; inline;
function GetIsBsonMaxKey: Boolean; inline;
function GetIsBsonMinKey: Boolean; inline;
function GetIsBsonNull: Boolean; inline;
function GetIsBsonRegularExpression: Boolean; inline;
function GetIsBsonSymbol: Boolean; inline;
function GetIsBsonTimestamp: Boolean; inline;
function GetIsBsonUndefined: Boolean; inline;
function GetIsDateTime: Boolean; inline;
function GetIsDouble: Boolean; inline;
function GetIsGuid: Boolean; inline;
function GetIsInt32: Boolean; inline;
function GetIsInt64: Boolean; inline;
function GetIsNumeric: Boolean; inline;
function GetIsObjectId: Boolean; inline;
function GetIsString: Boolean; inline;
public
{ @exclude }
class operator Implicit(const A: TgoBsonValue): Int8; static;
{ @exclude }
class operator Implicit(const A: TgoBsonValue): UInt8; static;
{ @exclude }
class operator Implicit(const A: TgoBsonValue): Int16; static;
{ @exclude }
class operator Implicit(const A: TgoBsonValue): UInt16; static;
{ @exclude }
class operator Implicit(const A: TgoBsonValue): UInt32; static;
{ @exclude }
class operator Implicit(const A: TgoBsonValue): Single; static;
{ @exclude }
class operator Implicit(const A: UInt32): TgoBsonValue; static;
{ @exclude }
class operator Implicit(const A: UInt64): TgoBsonValue; static;
{ @exclude }
class operator Implicit(const A: Single): TgoBsonValue; static;
{ @exclude }
property _Impl: _IValue read FImpl write FImpl;
{$ENDREGION 'Internal Declarations'}
public
{ Creates a BSON value by paring a JSON string.
Parameters:
AJson: the JSON string to parse.
Returns:
The BSON value
Raises:
EgoJsonParserError or EInvalidOperation on parse errors }
class function Parse(const AJson: String): TgoBsonValue; static;
{ Tries to parse a JSON string to a BSON value.
Parameters:
AJson: the JSON string to parse.
AArray: is set to the parsed JSON on success.
Returns:
True if the JSON string could be successfully parsed. }
class function TryParse(const AJson: String; out AValue: TgoBsonValue): Boolean; static;
{ Creates a BSON value from a BSON byte array.