@@ -22,32 +22,45 @@ import {
22
22
normalizeTimestamp
23
23
} from '../model/normalize' ;
24
24
import {
25
- isVectorValue ,
26
25
VECTOR_MAP_VECTORS_KEY ,
27
- isMaxValue
26
+ detectSpecialMapType ,
27
+ RESERVED_BSON_TIMESTAMP_KEY ,
28
+ RESERVED_REGEX_KEY ,
29
+ RESERVED_BSON_OBJECT_ID_KEY ,
30
+ RESERVED_BSON_BINARY_KEY ,
31
+ SpecialMapValueType ,
32
+ RESERVED_REGEX_PATTERN_KEY ,
33
+ RESERVED_REGEX_OPTIONS_KEY ,
34
+ RESERVED_INT32_KEY
28
35
} from '../model/values' ;
29
36
import { ArrayValue , MapValue , Value } from '../protos/firestore_proto_api' ;
30
37
import { fail } from '../util/assert' ;
31
38
import { isNegativeZero } from '../util/types' ;
32
39
33
40
import { DirectionalIndexByteEncoder } from './directional_index_byte_encoder' ;
34
41
35
- // Note: This code is copied from the backend. Code that is not used by
36
- // Firestore was removed.
42
+ // Note: This file is copied from the backend. Code that is not used by
43
+ // Firestore was removed. Code that has different behavior was modified.
37
44
38
45
const INDEX_TYPE_NULL = 5 ;
46
+ const INDEX_TYPE_MIN_KEY = 7 ;
39
47
const INDEX_TYPE_BOOLEAN = 10 ;
40
48
const INDEX_TYPE_NAN = 13 ;
41
49
const INDEX_TYPE_NUMBER = 15 ;
42
50
const INDEX_TYPE_TIMESTAMP = 20 ;
51
+ const INDEX_TYPE_BSON_TIMESTAMP = 22 ;
43
52
const INDEX_TYPE_STRING = 25 ;
44
53
const INDEX_TYPE_BLOB = 30 ;
54
+ const INDEX_TYPE_BSON_BINARY = 31 ;
45
55
const INDEX_TYPE_REFERENCE = 37 ;
56
+ const INDEX_TYPE_BSON_OBJECT_ID = 43 ;
46
57
const INDEX_TYPE_GEOPOINT = 45 ;
58
+ const INDEX_TYPE_REGEX = 47 ;
47
59
const INDEX_TYPE_ARRAY = 50 ;
48
60
const INDEX_TYPE_VECTOR = 53 ;
49
61
const INDEX_TYPE_MAP = 55 ;
50
62
const INDEX_TYPE_REFERENCE_SEGMENT = 60 ;
63
+ const INDEX_TYPE_MAX_VALUE = 999 ;
51
64
52
65
// A terminator that indicates that a truncatable value was not truncated.
53
66
// This must be smaller than all other type labels.
@@ -124,11 +137,30 @@ export class FirestoreIndexValueWriter {
124
137
encoder . writeNumber ( geoPoint . latitude || 0 ) ;
125
138
encoder . writeNumber ( geoPoint . longitude || 0 ) ;
126
139
} else if ( 'mapValue' in indexValue ) {
127
- // TODO(Mila/BSON): add bson types for indexing
128
- if ( isMaxValue ( indexValue ) ) {
140
+ const type = detectSpecialMapType ( indexValue ) ;
141
+ if ( type === SpecialMapValueType . INTERNAL_MAX ) {
129
142
this . writeValueTypeLabel ( encoder , Number . MAX_SAFE_INTEGER ) ;
130
- } else if ( isVectorValue ( indexValue ) ) {
143
+ } else if ( type === SpecialMapValueType . VECTOR ) {
131
144
this . writeIndexVector ( indexValue . mapValue ! , encoder ) ;
145
+ } else if ( type === SpecialMapValueType . MAX_KEY ) {
146
+ this . writeValueTypeLabel ( encoder , INDEX_TYPE_MAX_VALUE ) ;
147
+ } else if ( type === SpecialMapValueType . MIN_KEY ) {
148
+ this . writeValueTypeLabel ( encoder , INDEX_TYPE_MIN_KEY ) ;
149
+ } else if ( type === SpecialMapValueType . BSON_BINARY ) {
150
+ this . writeIndexBsonBinaryData ( indexValue . mapValue ! , encoder ) ;
151
+ } else if ( type === SpecialMapValueType . REGEX ) {
152
+ this . writeIndexRegex ( indexValue . mapValue ! , encoder ) ;
153
+ } else if ( type === SpecialMapValueType . BSON_TIMESTAMP ) {
154
+ this . writeIndexBsonTimestamp ( indexValue . mapValue ! , encoder ) ;
155
+ } else if ( type === SpecialMapValueType . BSON_OBJECT_ID ) {
156
+ this . writeIndexBsonObjectId ( indexValue . mapValue ! , encoder ) ;
157
+ } else if ( type === SpecialMapValueType . INT32 ) {
158
+ this . writeValueTypeLabel ( encoder , INDEX_TYPE_NUMBER ) ;
159
+ encoder . writeNumber (
160
+ normalizeNumber (
161
+ indexValue . mapValue ! . fields ! [ RESERVED_INT32_KEY ] ! . integerValue !
162
+ )
163
+ ) ;
132
164
} else {
133
165
this . writeIndexMap ( indexValue . mapValue ! , encoder ) ;
134
166
this . writeTruncationMarker ( encoder ) ;
@@ -202,7 +234,10 @@ export class FirestoreIndexValueWriter {
202
234
encoder : DirectionalIndexByteEncoder
203
235
) : void {
204
236
this . writeValueTypeLabel ( encoder , INDEX_TYPE_REFERENCE ) ;
205
- const path = DocumentKey . fromName ( referenceValue ) . path ;
237
+ const segments : string [ ] = referenceValue
238
+ . split ( '/' )
239
+ . filter ( segment => segment . length > 0 ) ;
240
+ const path = DocumentKey . fromSegments ( segments . slice ( 5 ) ) . path ;
206
241
path . forEach ( segment => {
207
242
this . writeValueTypeLabel ( encoder , INDEX_TYPE_REFERENCE_SEGMENT ) ;
208
243
this . writeUnlabeledIndexString ( segment , encoder ) ;
@@ -222,4 +257,55 @@ export class FirestoreIndexValueWriter {
222
257
// references, arrays and maps).
223
258
encoder . writeNumber ( NOT_TRUNCATED ) ;
224
259
}
260
+
261
+ private writeIndexBsonTimestamp (
262
+ mapValue : MapValue ,
263
+ encoder : DirectionalIndexByteEncoder
264
+ ) : void {
265
+ this . writeValueTypeLabel ( encoder , INDEX_TYPE_BSON_TIMESTAMP ) ;
266
+ const fields = mapValue . fields || { } ;
267
+ if ( fields ) {
268
+ // The JS SDK encodes BSON timestamps differently than the backend.
269
+ // This is due to the limitation of `number` in JS which handles up to 53-bit precision.
270
+ this . writeIndexMap (
271
+ fields [ RESERVED_BSON_TIMESTAMP_KEY ] . mapValue ! ,
272
+ encoder
273
+ ) ;
274
+ }
275
+ }
276
+
277
+ private writeIndexBsonObjectId (
278
+ mapValue : MapValue ,
279
+ encoder : DirectionalIndexByteEncoder
280
+ ) : void {
281
+ this . writeValueTypeLabel ( encoder , INDEX_TYPE_BSON_OBJECT_ID ) ;
282
+ const fields = mapValue . fields || { } ;
283
+ const oid = fields [ RESERVED_BSON_OBJECT_ID_KEY ] ?. stringValue || '' ;
284
+ encoder . writeBytes ( normalizeByteString ( oid ) ) ;
285
+ }
286
+
287
+ private writeIndexBsonBinaryData (
288
+ mapValue : MapValue ,
289
+ encoder : DirectionalIndexByteEncoder
290
+ ) : void {
291
+ this . writeValueTypeLabel ( encoder , INDEX_TYPE_BSON_BINARY ) ;
292
+ const fields = mapValue . fields || { } ;
293
+ const binary = fields [ RESERVED_BSON_BINARY_KEY ] ?. bytesValue || '' ;
294
+ encoder . writeBytes ( normalizeByteString ( binary ) ) ;
295
+ this . writeTruncationMarker ( encoder ) ;
296
+ }
297
+
298
+ private writeIndexRegex (
299
+ mapValue : MapValue ,
300
+ encoder : DirectionalIndexByteEncoder
301
+ ) : void {
302
+ this . writeValueTypeLabel ( encoder , INDEX_TYPE_REGEX ) ;
303
+ const fields = mapValue . fields || { } ;
304
+ const regex = fields [ RESERVED_REGEX_KEY ] ?. mapValue ?. fields || { } ;
305
+ if ( regex ) {
306
+ encoder . writeString ( regex [ RESERVED_REGEX_PATTERN_KEY ] ?. stringValue || '' ) ;
307
+ encoder . writeString ( regex [ RESERVED_REGEX_OPTIONS_KEY ] ?. stringValue || '' ) ;
308
+ }
309
+ this . writeTruncationMarker ( encoder ) ;
310
+ }
225
311
}
0 commit comments