@@ -17,22 +17,22 @@ package firestore
1717import (
1818 "bytes"
1919 "context"
20- "os "
20+ "fmt "
2121 "strings"
2222 "testing"
2323
2424 "cloud.google.com/go/firestore"
25+ apiv1 "cloud.google.com/go/firestore/apiv1/admin"
26+ "cloud.google.com/go/firestore/apiv1/admin/adminpb"
2527 "github.com/GoogleCloudPlatform/golang-samples/internal/testutil"
28+ "google.golang.org/genproto/protobuf/field_mask"
2629)
2730
2831func TestCollectionGroup (t * testing.T ) {
2932 tc := testutil .SystemTest (t )
3033 // TODO(#559): revert this to testutil.SystemTest(t).ProjectID
3134 // when datastore and firestore can co-exist in a project.
32- projectID := os .Getenv ("GOLANG_SAMPLES_FIRESTORE_PROJECT" )
33- if projectID == "" {
34- t .Skip ("Skipping firestore test. Set GOLANG_SAMPLES_FIRESTORE_PROJECT." )
35- }
35+ projectID := getProjectID (t )
3636
3737 ctx := context .Background ()
3838
@@ -45,18 +45,25 @@ func TestCollectionGroup(t *testing.T) {
4545 collection := tc .ProjectID + "-collection-group-cities"
4646
4747 // Delete all docs first to make sure collectionGroupSetup works.
48- docs , err := client .Collection (collection ).DocumentRefs (ctx ).GetAll ()
49- // Ignore errors; this isn't essential.
50- if err == nil {
51- for _ , d := range docs {
52- d .Delete (ctx )
53- }
54- }
48+ cleanupCollection (ctx , t , client , collection )
5549
5650 if err := collectionGroupSetup (projectID , collection ); err != nil {
5751 t .Fatalf ("collectionGroupSetup: %v" , err )
5852 }
5953
54+ if indexCleanup , err := createIndexExemption (projectID ); err != nil {
55+ t .Fatalf ("createCollectionGroupIndex: %v" , err )
56+ } else {
57+ t .Cleanup (func () {
58+ if indexCleanup != nil {
59+ err := indexCleanup ()
60+ if err != nil {
61+ t .Errorf ("cleanup failed: %v" , err )
62+ }
63+ }
64+ })
65+ }
66+
6067 buf := & bytes.Buffer {}
6168 if err := collectionGroupQuery (buf , projectID ); err != nil {
6269 t .Fatalf ("collectionGroupQuery: %v" , err )
@@ -67,14 +74,122 @@ func TestCollectionGroup(t *testing.T) {
6774 }
6875}
6976
77+ func createIndexExemption (projectID string ) (func () error , error ) {
78+ ctx := context .Background ()
79+
80+ // Create admin client
81+ adminClient , err := apiv1 .NewFirestoreAdminClient (ctx )
82+ if err != nil {
83+ return nil , fmt .Errorf ("NewFirestoreAdminClient: %v" , err )
84+ }
85+ defer adminClient .Close ()
86+
87+ fieldResourceName := fmt .Sprintf ("projects/%s/databases/%s/collectionGroups/%s/fields/%s" ,
88+ projectID , "(default)" , "landmarks" , "type" )
89+
90+ getFieldRequest := & adminpb.GetFieldRequest {
91+ Name : fieldResourceName ,
92+ }
93+
94+ origField , err := adminClient .GetField (ctx , getFieldRequest )
95+ if err != nil {
96+ return nil , fmt .Errorf ("GetField: %v" , err )
97+ }
98+
99+ updateReq := & adminpb.UpdateFieldRequest {
100+ Field : & adminpb.Field {
101+ Name : fieldResourceName ,
102+ IndexConfig : & adminpb.Field_IndexConfig {
103+ // Providing an empty list of indexes disables all automatic indexes
104+ Indexes : []* adminpb.Index {
105+ {
106+ QueryScope : adminpb .Index_COLLECTION ,
107+ Fields : []* adminpb.Index_IndexField {
108+ {
109+ FieldPath : "type" ,
110+ ValueMode : & adminpb.Index_IndexField_Order_ {
111+ Order : adminpb .Index_IndexField_ASCENDING ,
112+ },
113+ },
114+ },
115+ },
116+ {
117+ QueryScope : adminpb .Index_COLLECTION ,
118+ Fields : []* adminpb.Index_IndexField {
119+ {
120+ FieldPath : "type" ,
121+ ValueMode : & adminpb.Index_IndexField_Order_ {
122+ Order : adminpb .Index_IndexField_DESCENDING ,
123+ },
124+ },
125+ },
126+ },
127+ {
128+ QueryScope : adminpb .Index_COLLECTION ,
129+ Fields : []* adminpb.Index_IndexField {
130+ {
131+ FieldPath : "type" ,
132+ ValueMode : & adminpb.Index_IndexField_ArrayConfig_ {
133+ ArrayConfig : adminpb .Index_IndexField_CONTAINS ,
134+ },
135+ },
136+ },
137+ },
138+ {
139+ QueryScope : adminpb .Index_COLLECTION_GROUP ,
140+ Fields : []* adminpb.Index_IndexField {
141+ {
142+ FieldPath : "type" ,
143+ ValueMode : & adminpb.Index_IndexField_Order_ {
144+ Order : adminpb .Index_IndexField_ASCENDING ,
145+ },
146+ },
147+ },
148+ },
149+ },
150+ },
151+ },
152+ UpdateMask : & field_mask.FieldMask {
153+ Paths : []string {"index_config" },
154+ },
155+ }
156+
157+ op , updateErr := adminClient .UpdateField (ctx , updateReq )
158+ if updateErr != nil {
159+ if strings .Contains (updateErr .Error (), "already exists" ) {
160+ return nil , nil
161+ }
162+ return nil , fmt .Errorf ("UpdateField: %v" , updateErr )
163+ }
164+ // Wait until the operation completes.
165+ _ , waitErr := op .Wait (ctx )
166+ if waitErr != nil {
167+ return nil , fmt .Errorf ("UpdateField.Wait: %v" , waitErr )
168+ }
169+ return func () error {
170+ adminClient , err := apiv1 .NewFirestoreAdminClient (ctx )
171+ if err != nil {
172+ return fmt .Errorf ("NewFirestoreAdminClient: %v" , err )
173+ }
174+ defer adminClient .Close ()
175+ _ , updateErr := adminClient .UpdateField (ctx , & adminpb.UpdateFieldRequest {
176+ Field : origField ,
177+ UpdateMask : & field_mask.FieldMask {
178+ Paths : []string {"index_config" },
179+ },
180+ })
181+ if updateErr != nil {
182+ return fmt .Errorf ("UpdateField: %v" , updateErr )
183+ }
184+ return nil
185+ }, nil
186+ }
187+
70188func TestCollectionGroupPartitionQueries (t * testing.T ) {
71189 tc := testutil .SystemTest (t )
72190 // TODO(#559): revert this to testutil.SystemTest(t).ProjectID
73191 // when datastore and firestore can co-exist in a project.
74- projectID := os .Getenv ("GOLANG_SAMPLES_FIRESTORE_PROJECT" )
75- if projectID == "" {
76- t .Skip ("Skipping firestore test. Set GOLANG_SAMPLES_FIRESTORE_PROJECT." )
77- }
192+ projectID := getProjectID (t )
78193
79194 ctx := context .Background ()
80195
@@ -87,13 +202,7 @@ func TestCollectionGroupPartitionQueries(t *testing.T) {
87202 collection := tc .ProjectID + "-collection-group-cities"
88203
89204 // Delete all docs first to make sure collectionGroupSetup works.
90- docs , err := client .Collection (collection ).DocumentRefs (ctx ).GetAll ()
91- // Ignore errors; this isn't essential.
92- if err == nil {
93- for _ , d := range docs {
94- d .Delete (ctx )
95- }
96- }
205+ cleanupCollection (ctx , t , client , collection )
97206
98207 if err := collectionGroupSetup (projectID , collection ); err != nil {
99208 t .Fatalf ("collectionGroupSetup: %v" , err )
@@ -109,3 +218,17 @@ func TestCollectionGroupPartitionQueries(t *testing.T) {
109218 t .Errorf ("serializePartitionQuery unexpected result: %s" , err )
110219 }
111220}
221+
222+ func cleanupCollection (ctx context.Context , t * testing.T , client * firestore.Client , collection string ) {
223+ t .Helper ()
224+ docs , err := client .Collection (collection ).DocumentRefs (ctx ).GetAll ()
225+ if err != nil {
226+ t .Logf ("Warning: failed to get document refs for cleanup: %v" , err )
227+ return
228+ }
229+ for _ , d := range docs {
230+ if _ , err := d .Delete (ctx ); err != nil {
231+ t .Logf ("Warning: failed to delete doc %s: %v" , d .ID , err )
232+ }
233+ }
234+ }
0 commit comments