@@ -2,9 +2,11 @@ import * as clc from "colorette";
22
33import { logger } from "../logger" ;
44import * as utils from "../utils" ;
5+ import { deepEqual } from "../utils" ;
56import * as validator from "./validator" ;
67
78import * as types from "./api-types" ;
9+ import { DatabaseEdition , Density } from "./api-types" ;
810import * as Spec from "./api-spec" ;
911import * as sort from "./api-sort" ;
1012import * as util from "./util" ;
@@ -74,8 +76,10 @@ export class FirestoreApi {
7476 databaseId ,
7577 ) ;
7678
79+ const database = await this . getDatabase ( options . project , databaseId ) ;
80+ const edition = database . databaseEdition ?? DatabaseEdition . STANDARD ;
7781 const indexesToDelete = existingIndexes . filter ( ( index ) => {
78- return ! indexesToDeploy . some ( ( spec ) => this . indexMatchesSpec ( index , spec ) ) ;
82+ return ! indexesToDeploy . some ( ( spec ) => this . indexMatchesSpec ( index , spec , edition ) ) ;
7983 } ) ;
8084
8185 // We only want to delete fields where there is nothing in the local file with the same
@@ -127,7 +131,7 @@ export class FirestoreApi {
127131 }
128132
129133 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 ) ) ;
131135 if ( exists ) {
132136 logger . debug ( `Skipping existing index: ${ JSON . stringify ( index ) } ` ) ;
133137 } else {
@@ -325,8 +329,11 @@ export class FirestoreApi {
325329 validator . assertType ( "multikey" , index . multikey , "boolean" ) ;
326330 }
327331
328- if ( index . unique ) {
332+ if ( index . unique !== undefined ) {
329333 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." ) ;
330337 }
331338
332339 validator . assertHas ( index , "fields" ) ;
@@ -479,10 +486,62 @@ export class FirestoreApi {
479486 return this . apiClient . delete ( `/${ url } ` ) ;
480487 }
481488
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+
482541 /**
483542 * Determine if an API Index and a Spec Index are functionally equivalent.
484543 */
485- indexMatchesSpec ( index : types . Index , spec : Spec . Index ) : boolean {
544+ indexMatchesSpec ( index : types . Index , spec : Spec . Index , edition : types . DatabaseEdition ) : boolean {
486545 const collection = util . parseIndexName ( index . name ) . collectionGroupId ;
487546 if ( collection !== spec . collectionGroup ) {
488547 return false ;
@@ -492,21 +551,24 @@ export class FirestoreApi {
492551 return false ;
493552 }
494553
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 ) ) {
496557 return false ;
497558 }
498559
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 ) ) {
500563 return false ;
501564 }
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 ) ) {
504568 return false ;
505569 }
506570
507- if ( index . unique !== spec . unique ) {
508- return false ;
509- }
571+ // TODO(b/439901837): Compare `unique` index configuration when it's supported.
510572
511573 if ( index . fields . length !== spec . fields . length ) {
512574 return false ;
@@ -529,7 +591,8 @@ export class FirestoreApi {
529591 return false ;
530592 }
531593
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 ) ) {
533596 return false ;
534597 }
535598
0 commit comments