Skip to content

Commit 4f22156

Browse files
authored
feat: Firestore Enterprise edition indexes (#8939)
* WIP: Add new Firestore index configurations. * WIP: Continue working on index configuration flow. * Fix firestore.indexes.json template example. * minor fixes and adding tests. * Remove the `unique` from the example since it's not implemented yet. * address feedback. * Add changelog. * prettier.
1 parent d3588e9 commit 4f22156

File tree

8 files changed

+638
-7
lines changed

8 files changed

+638
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- [Added] Support for Firestore Enterprise database index configurations. (#8939)

src/firestore/api-sort.ts

Lines changed: 120 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,21 @@ const QUERY_SCOPE_SEQUENCE = [
99
undefined,
1010
];
1111

12+
const API_SCOPE_SEQUENCE = [
13+
API.ApiScope.ANY_API,
14+
API.ApiScope.DATASTORE_MODE_API,
15+
API.ApiScope.MONGODB_COMPATIBLE_API,
16+
undefined,
17+
];
18+
19+
const DENSITY_SEQUENCE = [
20+
API.Density.DENSITY_UNSPECIFIED,
21+
API.Density.SPARSE_ALL,
22+
API.Density.SPARSE_ANY,
23+
API.Density.DENSE,
24+
undefined,
25+
];
26+
1227
const ORDER_SEQUENCE = [API.Order.ASCENDING, API.Order.DESCENDING, undefined];
1328

1429
const ARRAY_CONFIG_SEQUENCE = [API.ArrayConfig.CONTAINS, undefined];
@@ -20,6 +35,10 @@ const ARRAY_CONFIG_SEQUENCE = [API.ArrayConfig.CONTAINS, undefined];
2035
* 1) The collection group.
2136
* 2) The query scope.
2237
* 3) The fields list.
38+
* 4) The API scope.
39+
* 5) The index density.
40+
* 6) Whether it's multikey.
41+
* 7) Whether it's unique.
2342
*/
2443
export function compareSpecIndex(a: Spec.Index, b: Spec.Index): number {
2544
if (a.collectionGroup !== b.collectionGroup) {
@@ -30,7 +49,27 @@ export function compareSpecIndex(a: Spec.Index, b: Spec.Index): number {
3049
return compareQueryScope(a.queryScope, b.queryScope);
3150
}
3251

33-
return compareArrays(a.fields, b.fields, compareIndexField);
52+
let cmp = compareArrays(a.fields, b.fields, compareIndexField);
53+
if (cmp !== 0) {
54+
return cmp;
55+
}
56+
57+
cmp = compareApiScope(a.apiScope, b.apiScope);
58+
if (cmp !== 0) {
59+
return cmp;
60+
}
61+
62+
cmp = compareDensity(a.density, b.density);
63+
if (cmp !== 0) {
64+
return cmp;
65+
}
66+
67+
cmp = compareBoolean(a.multikey, b.multikey);
68+
if (cmp !== 0) {
69+
return cmp;
70+
}
71+
72+
return compareBoolean(a.unique, b.unique);
3473
}
3574

3675
/**
@@ -40,6 +79,10 @@ export function compareSpecIndex(a: Spec.Index, b: Spec.Index): number {
4079
* 1) The collection group.
4180
* 2) The query scope.
4281
* 3) The fields list.
82+
* 4) The API scope.
83+
* 5) The index density.
84+
* 6) Whether it's multikey.
85+
* 7) Whether it's unique.
4386
*/
4487
export function compareApiIndex(a: API.Index, b: API.Index): number {
4588
// When these indexes are used as part of a field override, the name is
@@ -57,7 +100,27 @@ export function compareApiIndex(a: API.Index, b: API.Index): number {
57100
return compareQueryScope(a.queryScope, b.queryScope);
58101
}
59102

60-
return compareArrays(a.fields, b.fields, compareIndexField);
103+
let cmp = compareArrays(a.fields, b.fields, compareIndexField);
104+
if (cmp !== 0) {
105+
return cmp;
106+
}
107+
108+
cmp = compareApiScope(a.apiScope, b.apiScope);
109+
if (cmp !== 0) {
110+
return cmp;
111+
}
112+
113+
cmp = compareDensity(a.density, b.density);
114+
if (cmp !== 0) {
115+
return cmp;
116+
}
117+
118+
cmp = compareBoolean(a.multikey, b.multikey);
119+
if (cmp !== 0) {
120+
return cmp;
121+
}
122+
123+
return compareBoolean(a.unique, b.unique);
61124
}
62125

63126
/**
@@ -215,17 +278,71 @@ function compareFieldIndex(a: Spec.FieldIndex, b: Spec.FieldIndex): number {
215278
return compareArrayConfig(a.arrayConfig, b.arrayConfig);
216279
}
217280

218-
return 0;
281+
let cmp = compareApiScope(a.apiScope, b.apiScope);
282+
if (cmp !== 0) {
283+
return cmp;
284+
}
285+
286+
cmp = compareDensity(a.density, b.density);
287+
if (cmp !== 0) {
288+
return cmp;
289+
}
290+
291+
cmp = compareBoolean(a.multikey, b.multikey);
292+
if (cmp !== 0) {
293+
return cmp;
294+
}
295+
296+
return compareBoolean(a.unique, b.unique);
219297
}
220298

221299
function compareQueryScope(a: API.QueryScope, b: API.QueryScope): number {
222300
return QUERY_SCOPE_SEQUENCE.indexOf(a) - QUERY_SCOPE_SEQUENCE.indexOf(b);
223301
}
224302

303+
function compareApiScope(a?: API.ApiScope, b?: API.ApiScope): number {
304+
if (a === b) {
305+
return 0;
306+
}
307+
if (a === undefined) {
308+
return -1;
309+
}
310+
if (b === undefined) {
311+
return 1;
312+
}
313+
return API_SCOPE_SEQUENCE.indexOf(a) - API_SCOPE_SEQUENCE.indexOf(b);
314+
}
315+
316+
function compareDensity(a?: API.Density, b?: API.Density): number {
317+
if (a === b) {
318+
return 0;
319+
}
320+
if (a === undefined) {
321+
return -1;
322+
}
323+
if (b === undefined) {
324+
return 1;
325+
}
326+
return DENSITY_SEQUENCE.indexOf(a) - DENSITY_SEQUENCE.indexOf(b);
327+
}
328+
225329
function compareOrder(a?: API.Order, b?: API.Order): number {
226330
return ORDER_SEQUENCE.indexOf(a) - ORDER_SEQUENCE.indexOf(b);
227331
}
228332

333+
function compareBoolean(a?: boolean, b?: boolean): number {
334+
if (a === b) {
335+
return 0;
336+
}
337+
if (a === undefined) {
338+
return -1;
339+
}
340+
if (b === undefined) {
341+
return 1;
342+
}
343+
return Number(a) - Number(b);
344+
}
345+
229346
function compareArrayConfig(a?: API.ArrayConfig, b?: API.ArrayConfig): number {
230347
return ARRAY_CONFIG_SEQUENCE.indexOf(a) - ARRAY_CONFIG_SEQUENCE.indexOf(b);
231348
}

src/firestore/api-spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ export interface Index {
1313
collectionGroup: string;
1414
queryScope: API.QueryScope;
1515
fields: API.IndexField[];
16+
apiScope?: API.ApiScope;
17+
density?: API.Density;
18+
multikey?: boolean;
19+
unique?: boolean;
1620
}
1721

1822
/**
@@ -32,6 +36,10 @@ export interface FieldIndex {
3236
queryScope: API.QueryScope;
3337
order?: API.Order;
3438
arrayConfig?: API.ArrayConfig;
39+
apiScope?: API.ApiScope;
40+
density?: API.Density;
41+
multikey?: boolean;
42+
unique?: boolean;
3543
}
3644

3745
/**

src/firestore/api-types.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@ export enum QueryScope {
1515
COLLECTION_GROUP = "COLLECTION_GROUP",
1616
}
1717

18+
export enum ApiScope {
19+
ANY_API = "ANY_API",
20+
DATASTORE_MODE_API = "DATASTORE_MODE_API",
21+
MONGODB_COMPATIBLE_API = "MONGODB_COMPATIBLE_API",
22+
}
23+
24+
export enum Density {
25+
DENSITY_UNSPECIFIED = "DENSITY_UNSPECIFIED",
26+
SPARSE_ALL = "SPARSE_ALL",
27+
SPARSE_ANY = "SPARSE_ANY",
28+
DENSE = "DENSE",
29+
}
30+
1831
export enum Order {
1932
ASCENDING = "ASCENDING",
2033
DESCENDING = "DESCENDING",
@@ -49,6 +62,10 @@ export interface Index {
4962
queryScope: QueryScope;
5063
fields: IndexField[];
5164
state?: State;
65+
apiScope?: ApiScope;
66+
density?: Density;
67+
multikey?: boolean;
68+
unique?: boolean;
5269
}
5370

5471
/**

src/firestore/api.ts

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,10 @@ export class FirestoreApi {
244244
collectionGroup: util.parseIndexName(index.name).collectionGroupId,
245245
queryScope: index.queryScope,
246246
fields: index.fields,
247+
apiScope: index.apiScope,
248+
density: index.density,
249+
multikey: index.multikey,
250+
unique: index.unique,
247251
};
248252
});
249253

@@ -266,6 +270,10 @@ export class FirestoreApi {
266270
order: firstField.order,
267271
arrayConfig: firstField.arrayConfig,
268272
queryScope: index.queryScope,
273+
apiScope: index.apiScope,
274+
density: index.density,
275+
multikey: index.multikey,
276+
unique: index.unique,
269277
};
270278
}),
271279
};
@@ -305,6 +313,22 @@ export class FirestoreApi {
305313
validator.assertHas(index, "queryScope");
306314
validator.assertEnum(index, "queryScope", Object.keys(types.QueryScope));
307315

316+
if (index.apiScope) {
317+
validator.assertEnum(index, "apiScope", Object.keys(types.ApiScope));
318+
}
319+
320+
if (index.density) {
321+
validator.assertEnum(index, "density", Object.keys(types.Density));
322+
}
323+
324+
if (index.multikey) {
325+
validator.assertType("multikey", index.multikey, "boolean");
326+
}
327+
328+
if (index.unique) {
329+
validator.assertType("unique", index.unique, "boolean");
330+
}
331+
308332
validator.assertHas(index, "fields");
309333

310334
index.fields.forEach((field: any) => {
@@ -353,6 +377,22 @@ export class FirestoreApi {
353377
if (index.queryScope) {
354378
validator.assertEnum(index, "queryScope", Object.keys(types.QueryScope));
355379
}
380+
381+
if (index.apiScope) {
382+
validator.assertEnum(index, "apiScope", Object.keys(types.ApiScope));
383+
}
384+
385+
if (index.density) {
386+
validator.assertEnum(index, "density", Object.keys(types.Density));
387+
}
388+
389+
if (index.multikey) {
390+
validator.assertType("multikey", index.multikey, "boolean");
391+
}
392+
393+
if (index.unique) {
394+
validator.assertType("unique", index.unique, "boolean");
395+
}
356396
});
357397
}
358398

@@ -373,6 +413,10 @@ export class FirestoreApi {
373413
const indexes = spec.indexes.map((index) => {
374414
return {
375415
queryScope: index.queryScope,
416+
apiScope: index.apiScope,
417+
density: index.density,
418+
multikey: index.multikey,
419+
unique: index.unique,
376420
fields: [
377421
{
378422
fieldPath: spec.fieldPath,
@@ -420,6 +464,10 @@ export class FirestoreApi {
420464
return this.apiClient.post(url, {
421465
fields: index.fields,
422466
queryScope: index.queryScope,
467+
apiScope: index.apiScope,
468+
density: index.density,
469+
multikey: index.multikey,
470+
unique: index.unique,
423471
});
424472
}
425473

@@ -444,6 +492,22 @@ export class FirestoreApi {
444492
return false;
445493
}
446494

495+
if (index.apiScope !== spec.apiScope) {
496+
return false;
497+
}
498+
499+
if (index.density !== spec.density) {
500+
return false;
501+
}
502+
503+
if (index.multikey !== spec.multikey) {
504+
return false;
505+
}
506+
507+
if (index.unique !== spec.unique) {
508+
return false;
509+
}
510+
447511
if (index.fields.length !== spec.fields.length) {
448512
return false;
449513
}
@@ -465,6 +529,10 @@ export class FirestoreApi {
465529
return false;
466530
}
467531

532+
if (iField.vectorConfig !== sField.vectorConfig) {
533+
return false;
534+
}
535+
468536
i++;
469537
}
470538

@@ -549,12 +617,24 @@ export class FirestoreApi {
549617
}
550618

551619
result.indexes = spec.indexes.map((index: any) => {
552-
const i = {
620+
const i: any = {
553621
collectionGroup: index.collectionGroup || index.collectionId,
554622
queryScope: index.queryScope || types.QueryScope.COLLECTION,
555-
fields: [],
556623
};
557624

625+
if (index.apiScope) {
626+
i.apiScope = index.apiScope;
627+
}
628+
if (index.density) {
629+
i.density = index.density;
630+
}
631+
if (index.multikey !== undefined) {
632+
i.multikey = index.multikey;
633+
}
634+
if (index.unique !== undefined) {
635+
i.unique = index.unique;
636+
}
637+
558638
if (index.fields) {
559639
i.fields = index.fields.map((field: any) => {
560640
const f: any = {

0 commit comments

Comments
 (0)