1
2
3
4
5
6
7 package integration
8
9 import (
10 "context"
11 "os"
12 "sync"
13 "testing"
14 "time"
15
16 "go.mongodb.org/mongo-driver/bson"
17 "go.mongodb.org/mongo-driver/internal/assert"
18 "go.mongodb.org/mongo-driver/internal/require"
19 "go.mongodb.org/mongo-driver/internal/uuid"
20 "go.mongodb.org/mongo-driver/mongo"
21 "go.mongodb.org/mongo-driver/mongo/integration/mtest"
22 "go.mongodb.org/mongo-driver/mongo/options"
23 )
24
25 func TestSearchIndexProse(t *testing.T) {
26 t.Parallel()
27
28 const timeout = 5 * time.Minute
29
30 uri := os.Getenv("TEST_INDEX_URI")
31 if uri == "" {
32 t.Skip("skipping")
33 }
34
35 opts := options.Client().ApplyURI(uri).SetTimeout(timeout)
36 mt := mtest.New(t, mtest.NewOptions().ClientOptions(opts).MinServerVersion("7.0").Topologies(mtest.ReplicaSet))
37
38 mt.Run("case 1: Driver can successfully create and list search indexes", func(mt *mtest.T) {
39 ctx := context.Background()
40
41 _, err := mt.Coll.InsertOne(ctx, bson.D{})
42 require.NoError(mt, err, "failed to insert")
43
44 view := mt.Coll.SearchIndexes()
45
46 definition := bson.D{{"mappings", bson.D{{"dynamic", false}}}}
47 searchName := "test-search-index"
48 opts := options.SearchIndexes().SetName(searchName)
49 index, err := view.CreateOne(ctx, mongo.SearchIndexModel{
50 Definition: definition,
51 Options: opts,
52 })
53 require.NoError(mt, err, "failed to create index")
54 require.Equal(mt, searchName, index, "unmatched name")
55
56 var doc bson.Raw
57 for doc == nil {
58 cursor, err := view.List(ctx, opts)
59 require.NoError(mt, err, "failed to list")
60
61 if !cursor.Next(ctx) {
62 break
63 }
64 if cursor.Current.Lookup("queryable").Boolean() {
65 doc = cursor.Current
66 } else {
67 t.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String())
68 time.Sleep(5 * time.Second)
69 }
70 }
71 require.NotNil(mt, doc, "got empty document")
72 assert.Equal(mt, searchName, doc.Lookup("name").StringValue(), "unmatched name")
73 expected, err := bson.Marshal(definition)
74 require.NoError(mt, err, "failed to marshal definition")
75 actual := doc.Lookup("latestDefinition").Value
76 assert.Equal(mt, expected, actual, "unmatched definition")
77 })
78
79 mt.Run("case 2: Driver can successfully create multiple indexes in batch", func(mt *mtest.T) {
80 ctx := context.Background()
81
82 _, err := mt.Coll.InsertOne(ctx, bson.D{})
83 require.NoError(mt, err, "failed to insert")
84
85 view := mt.Coll.SearchIndexes()
86
87 definition := bson.D{{"mappings", bson.D{{"dynamic", false}}}}
88 models := []mongo.SearchIndexModel{
89 {
90 Definition: definition,
91 Options: options.SearchIndexes().SetName("test-search-index-1"),
92 },
93 {
94 Definition: definition,
95 Options: options.SearchIndexes().SetName("test-search-index-2"),
96 },
97 }
98 indexes, err := view.CreateMany(ctx, models)
99 require.NoError(mt, err, "failed to create index")
100 require.Equal(mt, len(indexes), 2, "expected 2 indexes")
101 for _, model := range models {
102 require.Contains(mt, indexes, *model.Options.Name)
103 }
104
105 getDocument := func(opts *options.SearchIndexesOptions) bson.Raw {
106 for {
107 cursor, err := view.List(ctx, opts)
108 require.NoError(mt, err, "failed to list")
109
110 if !cursor.Next(ctx) {
111 return nil
112 }
113 if cursor.Current.Lookup("queryable").Boolean() {
114 return cursor.Current
115 }
116 t.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String())
117 time.Sleep(5 * time.Second)
118 }
119 }
120
121 var wg sync.WaitGroup
122 wg.Add(len(models))
123 for i := range models {
124 go func(opts *options.SearchIndexesOptions) {
125 defer wg.Done()
126
127 doc := getDocument(opts)
128 require.NotNil(mt, doc, "got empty document")
129 assert.Equal(mt, *opts.Name, doc.Lookup("name").StringValue(), "unmatched name")
130 expected, err := bson.Marshal(definition)
131 require.NoError(mt, err, "failed to marshal definition")
132 actual := doc.Lookup("latestDefinition").Value
133 assert.Equal(mt, expected, actual, "unmatched definition")
134 }(models[i].Options)
135 }
136 wg.Wait()
137 })
138
139 mt.Run("case 3: Driver can successfully drop search indexes", func(mt *mtest.T) {
140 ctx := context.Background()
141
142 _, err := mt.Coll.InsertOne(ctx, bson.D{})
143 require.NoError(mt, err, "failed to insert")
144
145 view := mt.Coll.SearchIndexes()
146
147 definition := bson.D{{"mappings", bson.D{{"dynamic", false}}}}
148 searchName := "test-search-index"
149 opts := options.SearchIndexes().SetName(searchName)
150 index, err := view.CreateOne(ctx, mongo.SearchIndexModel{
151 Definition: definition,
152 Options: opts,
153 })
154 require.NoError(mt, err, "failed to create index")
155 require.Equal(mt, searchName, index, "unmatched name")
156
157 var doc bson.Raw
158 for doc == nil {
159 cursor, err := view.List(ctx, opts)
160 require.NoError(mt, err, "failed to list")
161
162 if !cursor.Next(ctx) {
163 break
164 }
165 if cursor.Current.Lookup("queryable").Boolean() {
166 doc = cursor.Current
167 } else {
168 t.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String())
169 time.Sleep(5 * time.Second)
170 }
171 }
172 require.NotNil(mt, doc, "got empty document")
173 require.Equal(mt, searchName, doc.Lookup("name").StringValue(), "unmatched name")
174
175 err = view.DropOne(ctx, searchName)
176 require.NoError(mt, err, "failed to drop index")
177 for {
178 cursor, err := view.List(ctx, opts)
179 require.NoError(mt, err, "failed to list")
180
181 if !cursor.Next(ctx) {
182 break
183 }
184 t.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String())
185 time.Sleep(5 * time.Second)
186 }
187 })
188
189 mt.Run("case 4: Driver can update a search index", func(mt *mtest.T) {
190 ctx := context.Background()
191
192 _, err := mt.Coll.InsertOne(ctx, bson.D{})
193 require.NoError(mt, err, "failed to insert")
194
195 view := mt.Coll.SearchIndexes()
196
197 definition := bson.D{{"mappings", bson.D{{"dynamic", false}}}}
198 searchName := "test-search-index"
199 opts := options.SearchIndexes().SetName(searchName)
200 index, err := view.CreateOne(ctx, mongo.SearchIndexModel{
201 Definition: definition,
202 Options: opts,
203 })
204 require.NoError(mt, err, "failed to create index")
205 require.Equal(mt, searchName, index, "unmatched name")
206
207 getDocument := func() bson.Raw {
208 for {
209 cursor, err := view.List(ctx, opts)
210 require.NoError(mt, err, "failed to list")
211
212 if !cursor.Next(ctx) {
213 return nil
214 }
215 if cursor.Current.Lookup("queryable").Boolean() {
216 return cursor.Current
217 }
218 t.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String())
219 time.Sleep(5 * time.Second)
220 }
221 }
222
223 doc := getDocument()
224 require.NotNil(mt, doc, "got empty document")
225 require.Equal(mt, searchName, doc.Lookup("name").StringValue(), "unmatched name")
226
227 definition = bson.D{{"mappings", bson.D{{"dynamic", true}}}}
228 err = view.UpdateOne(ctx, searchName, definition)
229 require.NoError(mt, err, "failed to drop index")
230 doc = getDocument()
231 require.NotNil(mt, doc, "got empty document")
232 assert.Equal(mt, searchName, doc.Lookup("name").StringValue(), "unmatched name")
233 assert.Equal(mt, "READY", doc.Lookup("status").StringValue(), "unexpected status")
234 expected, err := bson.Marshal(definition)
235 require.NoError(mt, err, "failed to marshal definition")
236 actual := doc.Lookup("latestDefinition").Value
237 assert.Equal(mt, expected, actual, "unmatched definition")
238 })
239
240 mt.Run("case 5: dropSearchIndex suppresses namespace not found errors", func(mt *mtest.T) {
241 ctx := context.Background()
242
243 id, err := uuid.New()
244 require.NoError(mt, err)
245
246 collection := mt.CreateCollection(mtest.Collection{
247 Name: id.String(),
248 }, false)
249
250 err = collection.SearchIndexes().DropOne(ctx, "foo")
251 require.NoError(mt, err)
252 })
253 }
254
View as plain text