@@ -22,32 +22,45 @@ import {
2222 normalizeTimestamp
2323} from '../model/normalize' ;
2424import {
25- isVectorValue ,
2625 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
2835} from '../model/values' ;
2936import { ArrayValue , MapValue , Value } from '../protos/firestore_proto_api' ;
3037import { fail } from '../util/assert' ;
3138import { isNegativeZero } from '../util/types' ;
3239
3340import { DirectionalIndexByteEncoder } from './directional_index_byte_encoder' ;
3441
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.
3744
3845const INDEX_TYPE_NULL = 5 ;
46+ const INDEX_TYPE_MIN_KEY = 7 ;
3947const INDEX_TYPE_BOOLEAN = 10 ;
4048const INDEX_TYPE_NAN = 13 ;
4149const INDEX_TYPE_NUMBER = 15 ;
4250const INDEX_TYPE_TIMESTAMP = 20 ;
51+ const INDEX_TYPE_BSON_TIMESTAMP = 22 ;
4352const INDEX_TYPE_STRING = 25 ;
4453const INDEX_TYPE_BLOB = 30 ;
54+ const INDEX_TYPE_BSON_BINARY = 31 ;
4555const INDEX_TYPE_REFERENCE = 37 ;
56+ const INDEX_TYPE_BSON_OBJECT_ID = 43 ;
4657const INDEX_TYPE_GEOPOINT = 45 ;
58+ const INDEX_TYPE_REGEX = 47 ;
4759const INDEX_TYPE_ARRAY = 50 ;
4860const INDEX_TYPE_VECTOR = 53 ;
4961const INDEX_TYPE_MAP = 55 ;
5062const INDEX_TYPE_REFERENCE_SEGMENT = 60 ;
63+ const INDEX_TYPE_MAX_VALUE = 999 ;
5164
5265// A terminator that indicates that a truncatable value was not truncated.
5366// This must be smaller than all other type labels.
@@ -124,11 +137,30 @@ export class FirestoreIndexValueWriter {
124137 encoder . writeNumber ( geoPoint . latitude || 0 ) ;
125138 encoder . writeNumber ( geoPoint . longitude || 0 ) ;
126139 } 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 ) {
129142 this . writeValueTypeLabel ( encoder , Number . MAX_SAFE_INTEGER ) ;
130- } else if ( isVectorValue ( indexValue ) ) {
143+ } else if ( type === SpecialMapValueType . VECTOR ) {
131144 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+ ) ;
132164 } else {
133165 this . writeIndexMap ( indexValue . mapValue ! , encoder ) ;
134166 this . writeTruncationMarker ( encoder ) ;
@@ -202,7 +234,10 @@ export class FirestoreIndexValueWriter {
202234 encoder : DirectionalIndexByteEncoder
203235 ) : void {
204236 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 ;
206241 path . forEach ( segment => {
207242 this . writeValueTypeLabel ( encoder , INDEX_TYPE_REFERENCE_SEGMENT ) ;
208243 this . writeUnlabeledIndexString ( segment , encoder ) ;
@@ -222,4 +257,55 @@ export class FirestoreIndexValueWriter {
222257 // references, arrays and maps).
223258 encoder . writeNumber ( NOT_TRUNCATED ) ;
224259 }
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+ }
225311}
0 commit comments