@@ -2,9 +2,11 @@ import * as clc from "colorette";
2
2
3
3
import { logger } from "../logger" ;
4
4
import * as utils from "../utils" ;
5
+ import { deepEqual } from "../utils" ;
5
6
import * as validator from "./validator" ;
6
7
7
8
import * as types from "./api-types" ;
9
+ import { DatabaseEdition , Density } from "./api-types" ;
8
10
import * as Spec from "./api-spec" ;
9
11
import * as sort from "./api-sort" ;
10
12
import * as util from "./util" ;
@@ -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,62 @@ export class FirestoreApi {
479
486
return this . apiClient . delete ( `/${ url } ` ) ;
480
487
}
481
488
489
+ /**
490
+ * Returns true if the given values match. If either one is undefined, the default value is used for comparison.
491
+ * @param lhs the first value.
492
+ * @param rhs the second value.
493
+ */
494
+ optionalValueMatches < T > ( lhs : T | undefined , rhs : T | undefined , defaultValue : T ) : boolean {
495
+ lhs = lhs === undefined ? defaultValue : lhs ;
496
+ rhs = rhs === undefined ? defaultValue : rhs ;
497
+ return lhs === rhs ;
498
+ }
499
+
500
+ /**
501
+ * Returns true if the given ApiScope values match.
502
+ * If either one is undefined, the default value is used for comparison.
503
+ * @param lhs the first ApiScope value.
504
+ * @param rhs the second ApiScope value.
505
+ */
506
+ optionalApiScopeMatches (
507
+ lhs : types . ApiScope | undefined ,
508
+ rhs : types . ApiScope | undefined ,
509
+ ) : boolean {
510
+ return this . optionalValueMatches < types . ApiScope > ( lhs , rhs , types . ApiScope . ANY_API ) ;
511
+ }
512
+
513
+ /**
514
+ * Returns true if the given Density values match.
515
+ * If either one is undefined, the default value is used for comparison based on Database Edition.
516
+ * @param lhs the first ApiScope value.
517
+ * @param rhs the second ApiScope value.
518
+ * @param edition the database edition used to determine the default value.
519
+ */
520
+ optionalDensityMatches (
521
+ lhs : Density | undefined ,
522
+ rhs : Density | undefined ,
523
+ edition : types . DatabaseEdition ,
524
+ ) {
525
+ const defaultValue =
526
+ edition === DatabaseEdition . STANDARD ? types . Density . SPARSE_ALL : types . Density . DENSE ;
527
+ return this . optionalValueMatches < types . Density > ( lhs , rhs , defaultValue ) ;
528
+ }
529
+
530
+ /**
531
+ * Returns true if the given Multikey values match.
532
+ * If either one is undefined, the default value is used for comparison.
533
+ * @param lhs the first ApiScope value.
534
+ * @param rhs the second ApiScope value.
535
+ */
536
+ optionalMultikeyMatches ( lhs : boolean | undefined , rhs : boolean | undefined ) {
537
+ const defaultValue = false ;
538
+ return this . optionalValueMatches < boolean > ( lhs , rhs , defaultValue ) ;
539
+ }
540
+
482
541
/**
483
542
* Determine if an API Index and a Spec Index are functionally equivalent.
484
543
*/
485
- indexMatchesSpec ( index : types . Index , spec : Spec . Index ) : boolean {
544
+ indexMatchesSpec ( index : types . Index , spec : Spec . Index , edition : types . DatabaseEdition ) : boolean {
486
545
const collection = util . parseIndexName ( index . name ) . collectionGroupId ;
487
546
if ( collection !== spec . collectionGroup ) {
488
547
return false ;
@@ -492,21 +551,24 @@ export class FirestoreApi {
492
551
return false ;
493
552
}
494
553
495
- if ( index . apiScope !== spec . apiScope ) {
554
+ // apiScope 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 . optionalApiScopeMatches ( index . apiScope , spec . apiScope ) ) {
496
557
return false ;
497
558
}
498
559
499
- if ( index . density !== spec . density ) {
560
+ // density is an optional value and may be missing in firestore.indexes.json,
561
+ // and may also be missing from the server value (when default is picked).
562
+ if ( ! this . optionalDensityMatches ( index . density , spec . density , edition ) ) {
500
563
return false ;
501
564
}
502
-
503
- if ( index . multikey !== spec . multikey ) {
565
+ // multikey is an optional value and may be missing in firestore.indexes.json,
566
+ // and may also be missing from the server value (when default is picked).
567
+ if ( ! this . optionalMultikeyMatches ( index . multikey , spec . multikey ) ) {
504
568
return false ;
505
569
}
506
570
507
- if ( index . unique !== spec . unique ) {
508
- return false ;
509
- }
571
+ // TODO(b/439901837): Compare `unique` index configuration when it's supported.
510
572
511
573
if ( index . fields . length !== spec . fields . length ) {
512
574
return false ;
@@ -529,7 +591,8 @@ export class FirestoreApi {
529
591
return false ;
530
592
}
531
593
532
- if ( iField . vectorConfig !== sField . vectorConfig ) {
594
+ // Note: vectorConfig is an object, and using '!==' should not be used.
595
+ if ( ! deepEqual ( iField . vectorConfig , sField . vectorConfig ) ) {
533
596
return false ;
534
597
}
535
598
0 commit comments