@@ -5,6 +5,7 @@ import * as utils from "../utils";
5
5
import * as validator from "./validator" ;
6
6
7
7
import * as types from "./api-types" ;
8
+ import { DatabaseEdition , Density } from "./api-types" ;
8
9
import * as Spec from "./api-spec" ;
9
10
import * as sort from "./api-sort" ;
10
11
import * as util from "./util" ;
@@ -13,6 +14,7 @@ import { firestoreOrigin } from "../api";
13
14
import { FirebaseError } from "../error" ;
14
15
import { Client } from "../apiv2" ;
15
16
import { PrettyPrint } from "./pretty-print" ;
17
+ import { optionalValueMatches } from "../functional" ;
16
18
17
19
export class FirestoreApi {
18
20
apiClient = new Client ( { urlPrefix : firestoreOrigin ( ) , apiVersion : "v1" } ) ;
@@ -74,8 +76,10 @@ export class FirestoreApi {
74
76
databaseId ,
75
77
) ;
76
78
79
+ const database = await this . getDatabase ( options . project , databaseId ) ;
80
+ const edition = database . databaseEdition ?? DatabaseEdition . STANDARD ;
77
81
const indexesToDelete = existingIndexes . filter ( ( index ) => {
78
- return ! indexesToDeploy . some ( ( spec ) => this . indexMatchesSpec ( index , spec ) ) ;
82
+ return ! indexesToDeploy . some ( ( spec ) => this . indexMatchesSpec ( index , spec , edition ) ) ;
79
83
} ) ;
80
84
81
85
// We only want to delete fields where there is nothing in the local file with the same
@@ -127,7 +131,7 @@ export class FirestoreApi {
127
131
}
128
132
129
133
for ( const index of indexesToDeploy ) {
130
- const exists = existingIndexes . some ( ( x ) => this . indexMatchesSpec ( x , index ) ) ;
134
+ const exists = existingIndexes . some ( ( x ) => this . indexMatchesSpec ( x , index , edition ) ) ;
131
135
if ( exists ) {
132
136
logger . debug ( `Skipping existing index: ${ JSON . stringify ( index ) } ` ) ;
133
137
} else {
@@ -325,8 +329,11 @@ export class FirestoreApi {
325
329
validator . assertType ( "multikey" , index . multikey , "boolean" ) ;
326
330
}
327
331
328
- if ( index . unique ) {
332
+ if ( index . unique !== undefined ) {
329
333
validator . assertType ( "unique" , index . unique , "boolean" ) ;
334
+ // TODO(b/439901837): Remove this check and update indexMatchesSpec once
335
+ // unique index configuration is supported.
336
+ throw new FirebaseError ( "The `unique` index configuration is not supported yet." ) ;
330
337
}
331
338
332
339
validator . assertHas ( index , "fields" ) ;
@@ -479,10 +486,51 @@ export class FirestoreApi {
479
486
return this . apiClient . delete ( `/${ url } ` ) ;
480
487
}
481
488
489
+ /**
490
+ * Returns true if the given ApiScope values match.
491
+ * If either one is undefined, the default value is used for comparison.
492
+ * @param lhs the first ApiScope value.
493
+ * @param rhs the second ApiScope value.
494
+ */
495
+ optionalApiScopeMatches (
496
+ lhs : types . ApiScope | undefined ,
497
+ rhs : types . ApiScope | undefined ,
498
+ ) : boolean {
499
+ return optionalValueMatches < types . ApiScope > ( lhs , rhs , types . ApiScope . ANY_API ) ;
500
+ }
501
+
502
+ /**
503
+ * Returns true if the given Density values match.
504
+ * If either one is undefined, the default value is used for comparison based on Database Edition.
505
+ * @param lhs the first Density value.
506
+ * @param rhs the second Density value.
507
+ * @param edition the database edition used to determine the default value.
508
+ */
509
+ optionalDensityMatches (
510
+ lhs : Density | undefined ,
511
+ rhs : Density | undefined ,
512
+ edition : types . DatabaseEdition ,
513
+ ) : boolean {
514
+ const defaultValue =
515
+ edition === DatabaseEdition . STANDARD ? types . Density . SPARSE_ALL : types . Density . DENSE ;
516
+ return optionalValueMatches < types . Density > ( lhs , rhs , defaultValue ) ;
517
+ }
518
+
519
+ /**
520
+ * Returns true if the given Multikey values match.
521
+ * If either one is undefined, the default value is used for comparison.
522
+ * @param lhs the first Multikey value.
523
+ * @param rhs the second Multikey value.
524
+ */
525
+ optionalMultikeyMatches ( lhs : boolean | undefined , rhs : boolean | undefined ) : boolean {
526
+ const defaultValue = false ;
527
+ return optionalValueMatches < boolean > ( lhs , rhs , defaultValue ) ;
528
+ }
529
+
482
530
/**
483
531
* Determine if an API Index and a Spec Index are functionally equivalent.
484
532
*/
485
- indexMatchesSpec ( index : types . Index , spec : Spec . Index ) : boolean {
533
+ indexMatchesSpec ( index : types . Index , spec : Spec . Index , edition : types . DatabaseEdition ) : boolean {
486
534
const collection = util . parseIndexName ( index . name ) . collectionGroupId ;
487
535
if ( collection !== spec . collectionGroup ) {
488
536
return false ;
@@ -492,21 +540,24 @@ export class FirestoreApi {
492
540
return false ;
493
541
}
494
542
495
- if ( index . apiScope !== spec . apiScope ) {
543
+ // apiScope is an optional value and may be missing in firestore.indexes.json,
544
+ // and may also be missing from the server value (when default is picked).
545
+ if ( ! this . optionalApiScopeMatches ( index . apiScope , spec . apiScope ) ) {
496
546
return false ;
497
547
}
498
548
499
- if ( index . density !== spec . density ) {
549
+ // density is an optional value and may be missing in firestore.indexes.json,
550
+ // and may also be missing from the server value (when default is picked).
551
+ if ( ! this . optionalDensityMatches ( index . density , spec . density , edition ) ) {
500
552
return false ;
501
553
}
502
-
503
- if ( index . multikey !== spec . multikey ) {
554
+ // multikey is an optional value and may be missing in firestore.indexes.json,
555
+ // and may also be missing from the server value (when default is picked).
556
+ if ( ! this . optionalMultikeyMatches ( index . multikey , spec . multikey ) ) {
504
557
return false ;
505
558
}
506
559
507
- if ( index . unique !== spec . unique ) {
508
- return false ;
509
- }
560
+ // TODO(b/439901837): Compare `unique` index configuration when it's supported.
510
561
511
562
if ( index . fields . length !== spec . fields . length ) {
512
563
return false ;
@@ -529,7 +580,8 @@ export class FirestoreApi {
529
580
return false ;
530
581
}
531
582
532
- if ( iField . vectorConfig !== sField . vectorConfig ) {
583
+ // Note: vectorConfig is an object, and using '!==' should not be used.
584
+ if ( ! utils . deepEqual ( iField . vectorConfig , sField . vectorConfig ) ) {
533
585
return false ;
534
586
}
535
587
0 commit comments